271 lines
9.7 KiB
JavaScript
271 lines
9.7 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
/**
|
|||
|
|
* Query Omada Controller firewall rules for Blockscout access
|
|||
|
|
* Uses direct API calls with admin credentials
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import dotenv from 'dotenv';
|
|||
|
|
import { fileURLToPath } from 'url';
|
|||
|
|
import { dirname, join } from 'path';
|
|||
|
|
|
|||
|
|
const __filename = fileURLToPath(import.meta.url);
|
|||
|
|
const __dirname = dirname(__filename);
|
|||
|
|
|
|||
|
|
// Load environment variables
|
|||
|
|
dotenv.config({ path: join(__dirname, '..', '.env') });
|
|||
|
|
|
|||
|
|
const BLOCKSCOUT_IP = '192.168.11.140';
|
|||
|
|
const BLOCKSCOUT_PORT = '80';
|
|||
|
|
|
|||
|
|
// Get configuration from environment
|
|||
|
|
const controllerUrl = process.env.OMADA_CONTROLLER_URL || process.env.OMADA_CONTROLLER_BASE_URL || 'https://192.168.11.8:8043';
|
|||
|
|
const username = process.env.OMADA_ADMIN_USERNAME || process.env.OMADA_API_KEY || process.env.OMADA_CLIENT_ID;
|
|||
|
|
const password = process.env.OMADA_ADMIN_PASSWORD || process.env.OMADA_API_SECRET || process.env.OMADA_CLIENT_SECRET;
|
|||
|
|
const siteId = process.env.OMADA_SITE_ID || '090862bebcb1997bb263eea9364957fe';
|
|||
|
|
const verifySSL = process.env.OMADA_VERIFY_SSL !== 'false';
|
|||
|
|
|
|||
|
|
// Disable SSL verification if needed
|
|||
|
|
if (!verifySSL) {
|
|||
|
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function login() {
|
|||
|
|
const url = `${controllerUrl}/api/v2/login`;
|
|||
|
|
|
|||
|
|
const response = await fetch(url, {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify({
|
|||
|
|
username: username,
|
|||
|
|
password: password,
|
|||
|
|
}),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!response.ok) {
|
|||
|
|
const errorText = await response.text();
|
|||
|
|
throw new Error(`Login failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const data = await response.json();
|
|||
|
|
|
|||
|
|
if (data.errorCode !== 0) {
|
|||
|
|
throw new Error(`Login failed: ${data.msg || 'Unknown error'}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const token = data.result?.token || data.token;
|
|||
|
|
if (!token) {
|
|||
|
|
throw new Error('No token received from server');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return token;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function requestWithToken(token, endpoint, method = 'GET', body = null) {
|
|||
|
|
const url = `${controllerUrl}${endpoint}`;
|
|||
|
|
|
|||
|
|
const options = {
|
|||
|
|
method,
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
'Csrf-Token': token,
|
|||
|
|
'Cookie': `TOKEN=${token}`,
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
if (body) {
|
|||
|
|
options.body = JSON.stringify(body);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const response = await fetch(url, options);
|
|||
|
|
|
|||
|
|
if (!response.ok) {
|
|||
|
|
const errorText = await response.text();
|
|||
|
|
throw new Error(`API request failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const data = await response.json();
|
|||
|
|
|
|||
|
|
if (data.errorCode !== 0) {
|
|||
|
|
throw new Error(`API error: ${data.msg || 'Unknown error'}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return data.result || data;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function main() {
|
|||
|
|
console.log('════════════════════════════════════════');
|
|||
|
|
console.log('Omada Firewall Rules Check for Blockscout');
|
|||
|
|
console.log('════════════════════════════════════════');
|
|||
|
|
console.log('');
|
|||
|
|
console.log(`Controller: ${controllerUrl}`);
|
|||
|
|
console.log(`Site ID: ${siteId}`);
|
|||
|
|
console.log(`Blockscout IP: ${BLOCKSCOUT_IP}`);
|
|||
|
|
console.log(`Blockscout Port: ${BLOCKSCOUT_PORT}`);
|
|||
|
|
console.log('');
|
|||
|
|
|
|||
|
|
if (!username || !password) {
|
|||
|
|
console.error('❌ Missing Omada credentials in .env file');
|
|||
|
|
console.error('');
|
|||
|
|
console.error('Required environment variables:');
|
|||
|
|
console.error(' OMADA_ADMIN_USERNAME (or OMADA_API_KEY/OMADA_CLIENT_ID)');
|
|||
|
|
console.error(' OMADA_ADMIN_PASSWORD (or OMADA_API_SECRET/OMADA_CLIENT_SECRET)');
|
|||
|
|
console.error('');
|
|||
|
|
console.error('Optional:');
|
|||
|
|
console.error(' OMADA_CONTROLLER_URL (default: https://192.168.11.8:8043)');
|
|||
|
|
console.error(' OMADA_SITE_ID (default: auto-detect)');
|
|||
|
|
process.exit(1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
console.log('Authenticating to Omada Controller...');
|
|||
|
|
const token = await login();
|
|||
|
|
console.log('✓ Authentication successful');
|
|||
|
|
console.log('');
|
|||
|
|
|
|||
|
|
console.log('Fetching firewall rules...');
|
|||
|
|
const rules = await requestWithToken(token, `/api/v2/sites/${siteId}/firewall/rules`);
|
|||
|
|
console.log(`✓ Found ${rules.length} firewall rules`);
|
|||
|
|
console.log('');
|
|||
|
|
|
|||
|
|
// Filter rules that might affect Blockscout
|
|||
|
|
const relevantRules = rules.filter((rule) => {
|
|||
|
|
const affectsBlockscoutIP =
|
|||
|
|
!rule.dstIp ||
|
|||
|
|
rule.dstIp === BLOCKSCOUT_IP ||
|
|||
|
|
rule.dstIp.includes(BLOCKSCOUT_IP.split('.').slice(0, 3).join('.')) ||
|
|||
|
|
rule.dstIp === '192.168.11.0/24' ||
|
|||
|
|
rule.dstIp === '192.168.11.0/255.255.255.0';
|
|||
|
|
|
|||
|
|
const affectsPort80 =
|
|||
|
|
!rule.dstPort ||
|
|||
|
|
rule.dstPort === BLOCKSCOUT_PORT ||
|
|||
|
|
rule.dstPort.includes(BLOCKSCOUT_PORT) ||
|
|||
|
|
rule.dstPort === 'all' ||
|
|||
|
|
rule.dstPort === '0-65535';
|
|||
|
|
|
|||
|
|
const isTCP =
|
|||
|
|
!rule.protocol ||
|
|||
|
|
rule.protocol === 'tcp' ||
|
|||
|
|
rule.protocol === 'tcp/udp' ||
|
|||
|
|
rule.protocol === 'all';
|
|||
|
|
|
|||
|
|
return rule.enable && (affectsBlockscoutIP || affectsPort80) && isTCP;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (relevantRules.length > 0) {
|
|||
|
|
console.log(`🔍 Found ${relevantRules.length} rule(s) that might affect Blockscout:`);
|
|||
|
|
console.log('');
|
|||
|
|
|
|||
|
|
relevantRules.forEach((rule) => {
|
|||
|
|
console.log(`Rule: ${rule.name || rule.id}`);
|
|||
|
|
console.log(` ID: ${rule.id}`);
|
|||
|
|
console.log(` Enabled: ${rule.enable ? 'Yes ✓' : 'No ✗'}`);
|
|||
|
|
console.log(` Action: ${rule.action}`);
|
|||
|
|
console.log(` Direction: ${rule.direction || 'N/A'}`);
|
|||
|
|
console.log(` Protocol: ${rule.protocol || 'all'}`);
|
|||
|
|
console.log(` Source IP: ${rule.srcIp || 'Any'}`);
|
|||
|
|
console.log(` Source Port: ${rule.srcPort || 'Any'}`);
|
|||
|
|
console.log(` Destination IP: ${rule.dstIp || 'Any'}`);
|
|||
|
|
console.log(` Destination Port: ${rule.dstPort || 'Any'}`);
|
|||
|
|
console.log(` Priority: ${rule.priority || 'N/A'}`);
|
|||
|
|
console.log('');
|
|||
|
|
|
|||
|
|
if (rule.action === 'deny' || rule.action === 'reject') {
|
|||
|
|
console.log(' ⚠️ WARNING: This rule BLOCKS traffic!');
|
|||
|
|
console.log('');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Separate allow and deny rules
|
|||
|
|
const allowRules = relevantRules.filter((rule) => rule.action === 'allow');
|
|||
|
|
const denyRules = relevantRules.filter((rule) => rule.action === 'deny' || rule.action === 'reject');
|
|||
|
|
|
|||
|
|
console.log('════════════════════════════════════════');
|
|||
|
|
console.log('Analysis');
|
|||
|
|
console.log('════════════════════════════════════════');
|
|||
|
|
console.log('');
|
|||
|
|
|
|||
|
|
if (denyRules.length > 0 && allowRules.length === 0) {
|
|||
|
|
console.log('❌ Issue Found:');
|
|||
|
|
console.log(' Deny rules exist that block Blockscout, but no allow rules found.');
|
|||
|
|
console.log(' This explains the "No route to host" error.');
|
|||
|
|
console.log('');
|
|||
|
|
} else if (allowRules.length > 0 && denyRules.length > 0) {
|
|||
|
|
console.log('⚠️ Both allow and deny rules exist.');
|
|||
|
|
console.log(' Check rule priority - allow rules must be above deny rules.');
|
|||
|
|
console.log('');
|
|||
|
|
} else if (allowRules.length > 0) {
|
|||
|
|
console.log('✅ Allow rules exist for Blockscout.');
|
|||
|
|
console.log(' If issues persist, check rule priority or default policies.');
|
|||
|
|
console.log('');
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
console.log('ℹ️ No firewall rules found that specifically target Blockscout.');
|
|||
|
|
console.log('');
|
|||
|
|
console.log('Checking all deny/reject rules...');
|
|||
|
|
console.log('');
|
|||
|
|
|
|||
|
|
const denyRules = rules.filter(
|
|||
|
|
(rule) => rule.enable && (rule.action === 'deny' || rule.action === 'reject')
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (denyRules.length > 0) {
|
|||
|
|
console.log(`⚠️ Found ${denyRules.length} deny/reject rules:`);
|
|||
|
|
console.log('');
|
|||
|
|
denyRules.slice(0, 10).forEach((rule) => {
|
|||
|
|
console.log(` - ${rule.name || rule.id} (Priority: ${rule.priority || 'N/A'})`);
|
|||
|
|
if (rule.dstIp) console.log(` Dest: ${rule.dstIp}`);
|
|||
|
|
if (rule.dstPort) console.log(` Port: ${rule.dstPort}`);
|
|||
|
|
});
|
|||
|
|
if (denyRules.length > 10) {
|
|||
|
|
console.log(` ... and ${denyRules.length - 10} more`);
|
|||
|
|
}
|
|||
|
|
console.log('');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('════════════════════════════════════════');
|
|||
|
|
console.log('Recommendations');
|
|||
|
|
console.log('════════════════════════════════════════');
|
|||
|
|
console.log('');
|
|||
|
|
|
|||
|
|
if (relevantRules.length === 0 || relevantRules.filter(r => r.action === 'allow').length === 0) {
|
|||
|
|
console.log('✅ Recommended Action:');
|
|||
|
|
console.log(' Create an allow rule in Omada Controller:');
|
|||
|
|
console.log('');
|
|||
|
|
console.log(' Name: Allow Internal to Blockscout HTTP');
|
|||
|
|
console.log(' Enable: Yes');
|
|||
|
|
console.log(' Action: Allow');
|
|||
|
|
console.log(' Direction: Forward');
|
|||
|
|
console.log(' Protocol: TCP');
|
|||
|
|
console.log(' Source IP: 192.168.11.0/24 (or leave blank for Any)');
|
|||
|
|
console.log(' Destination IP: 192.168.11.140');
|
|||
|
|
console.log(' Destination Port: 80');
|
|||
|
|
console.log(' Priority: High (ensure it\'s above any deny rules)');
|
|||
|
|
console.log('');
|
|||
|
|
console.log(' Then apply the configuration.');
|
|||
|
|
console.log('');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('════════════════════════════════════════');
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Error:');
|
|||
|
|
console.error('');
|
|||
|
|
if (error.message) {
|
|||
|
|
console.error(` ${error.message}`);
|
|||
|
|
} else {
|
|||
|
|
console.error(' ', error);
|
|||
|
|
}
|
|||
|
|
console.error('');
|
|||
|
|
process.exit(1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
main().catch((error) => {
|
|||
|
|
console.error('Fatal error:', error);
|
|||
|
|
process.exit(1);
|
|||
|
|
});
|
|||
|
|
|