- Organized 252 files across project - Root directory: 187 → 2 files (98.9% reduction) - Moved configuration guides to docs/04-configuration/ - Moved troubleshooting guides to docs/09-troubleshooting/ - Moved quick start guides to docs/01-getting-started/ - Moved reports to reports/ directory - Archived temporary files - Generated comprehensive reports and documentation - Created maintenance scripts and guides All files organized according to established standards.
394 lines
15 KiB
JavaScript
Executable File
394 lines
15 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
||
|
||
/**
|
||
* Query Omada Controller firewall rules for Blockscout access
|
||
* Based on test-omada-direct.js - uses direct API calls
|
||
*/
|
||
|
||
import https from 'https';
|
||
import { readFileSync } from 'fs';
|
||
import { join } from 'path';
|
||
import { homedir } from 'os';
|
||
|
||
const BLOCKSCOUT_IP = '192.168.11.140';
|
||
const BLOCKSCOUT_PORT = '80';
|
||
|
||
// Load environment variables
|
||
const envPath = join(homedir(), '.env');
|
||
let envVars = {};
|
||
|
||
try {
|
||
const envFile = readFileSync(envPath, 'utf8');
|
||
envFile.split('\n').forEach(line => {
|
||
if (line.includes('=') && !line.trim().startsWith('#')) {
|
||
const [key, ...values] = line.split('=');
|
||
if (key && /^[A-Z_][A-Z0-9_]*$/.test(key.trim())) {
|
||
let value = values.join('=').trim();
|
||
if ((value.startsWith('"') && value.endsWith('"')) ||
|
||
(value.startsWith("'") && value.endsWith("'"))) {
|
||
value = value.slice(1, -1);
|
||
}
|
||
envVars[key.trim()] = value;
|
||
}
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('Error loading .env file:', error.message);
|
||
process.exit(1);
|
||
}
|
||
|
||
const baseUrl = envVars.OMADA_CONTROLLER_URL || envVars.OMADA_CONTROLLER_BASE_URL || 'https://192.168.11.8:8043';
|
||
const username = envVars.OMADA_ADMIN_USERNAME || envVars.OMADA_API_KEY || envVars.OMADA_CLIENT_ID;
|
||
const password = envVars.OMADA_ADMIN_PASSWORD || envVars.OMADA_API_SECRET || envVars.OMADA_CLIENT_SECRET;
|
||
const siteId = envVars.OMADA_SITE_ID || '090862bebcb1997bb263eea9364957fe';
|
||
const verifySSL = envVars.OMADA_VERIFY_SSL !== 'false';
|
||
|
||
if (!username || !password) {
|
||
console.error('Error: Missing credentials');
|
||
console.error('Required: OMADA_ADMIN_USERNAME/OMADA_API_KEY and OMADA_ADMIN_PASSWORD/OMADA_API_SECRET');
|
||
process.exit(1);
|
||
}
|
||
|
||
// Parse base URL
|
||
const urlObj = new URL(baseUrl);
|
||
const hostname = urlObj.hostname;
|
||
const port = urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80);
|
||
|
||
console.log('════════════════════════════════════════');
|
||
console.log('Omada Firewall Rules Check for Blockscout');
|
||
console.log('════════════════════════════════════════');
|
||
console.log('');
|
||
console.log(`Controller URL: ${baseUrl}`);
|
||
console.log(`Site ID: ${siteId}`);
|
||
console.log(`Blockscout IP: ${BLOCKSCOUT_IP}`);
|
||
console.log(`Blockscout Port: ${BLOCKSCOUT_PORT}`);
|
||
console.log('');
|
||
|
||
// Create HTTPS agent
|
||
const agent = new https.Agent({
|
||
rejectUnauthorized: verifySSL,
|
||
});
|
||
|
||
// Function to make API request
|
||
function apiRequest(method, path, data = null, token = null, cookies = null) {
|
||
return new Promise((resolve, reject) => {
|
||
const options = {
|
||
hostname,
|
||
port,
|
||
path,
|
||
method,
|
||
agent,
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
};
|
||
|
||
if (token) {
|
||
options.headers['Csrf-Token'] = token;
|
||
}
|
||
|
||
if (cookies) {
|
||
options.headers['Cookie'] = cookies;
|
||
}
|
||
|
||
const req = https.request(options, (res) => {
|
||
let body = '';
|
||
res.on('data', (chunk) => {
|
||
body += chunk;
|
||
});
|
||
res.on('end', () => {
|
||
try {
|
||
const json = JSON.parse(body);
|
||
resolve(json);
|
||
} catch (e) {
|
||
resolve({ raw: body, statusCode: res.statusCode, headers: res.headers });
|
||
}
|
||
});
|
||
});
|
||
|
||
req.on('error', (error) => {
|
||
reject(error);
|
||
});
|
||
|
||
if (data) {
|
||
req.write(JSON.stringify(data));
|
||
}
|
||
req.end();
|
||
});
|
||
}
|
||
|
||
async function main() {
|
||
try {
|
||
console.log('1. Authenticating to Omada Controller...');
|
||
const loginResponse = await apiRequest('POST', '/api/v2/login', {
|
||
username,
|
||
password,
|
||
});
|
||
|
||
if (loginResponse.errorCode !== 0) {
|
||
console.error(` ✗ Login failed: ${loginResponse.msg || 'Unknown error'}`);
|
||
console.error(` Error Code: ${loginResponse.errorCode}`);
|
||
process.exit(1);
|
||
}
|
||
|
||
const token = loginResponse.result?.token || loginResponse.token;
|
||
if (!token) {
|
||
console.error(' ✗ No token received');
|
||
process.exit(1);
|
||
}
|
||
|
||
console.log(' ✓ Login successful');
|
||
console.log('');
|
||
|
||
// Build cookie string for subsequent requests
|
||
const cookies = `TOKEN=${token}`;
|
||
const effectiveSiteId = siteId;
|
||
|
||
console.log(`2. Querying firewall rules for site: ${effectiveSiteId}...`);
|
||
console.log('');
|
||
|
||
// Query firewall rules - try different endpoint formats
|
||
let rulesResponse;
|
||
let firewallPath;
|
||
|
||
// Try different endpoint paths
|
||
const endpointPaths = [
|
||
`/api/v2/sites/${effectiveSiteId}/firewall/rules`,
|
||
`/api/v2/sites/${effectiveSiteId}/firewall`,
|
||
`/sites/${effectiveSiteId}/firewall/rules`,
|
||
`/sites/${effectiveSiteId}/firewall`,
|
||
`/firewall/rules`,
|
||
`/firewall`,
|
||
];
|
||
|
||
let rulesFound = false;
|
||
for (const path of endpointPaths) {
|
||
try {
|
||
firewallPath = path;
|
||
rulesResponse = await apiRequest('GET', path, null, token, cookies);
|
||
|
||
// Check if we got a valid response (not a redirect)
|
||
if (rulesResponse.errorCode === 0 || Array.isArray(rulesResponse.result) || Array.isArray(rulesResponse)) {
|
||
rulesFound = true;
|
||
break;
|
||
}
|
||
} catch (e) {
|
||
// Try next path
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (!rulesFound) {
|
||
console.error(` ✗ Could not find valid firewall rules endpoint`);
|
||
console.error(` Tried paths: ${endpointPaths.join(', ')}`);
|
||
console.error(` Last response: ${JSON.stringify(rulesResponse, null, 2)}`);
|
||
console.error('');
|
||
console.error('Note: Firewall rules may need to be checked via Omada Controller web interface:');
|
||
console.error(` ${baseUrl}`);
|
||
console.error(' Navigate to: Settings → Firewall → Firewall Rules');
|
||
process.exit(1);
|
||
}
|
||
|
||
if (rulesResponse.errorCode !== 0) {
|
||
console.error(` ✗ Failed to query firewall rules: ${rulesResponse.msg || 'Unknown error'}`);
|
||
console.error(` Error Code: ${rulesResponse.errorCode}`);
|
||
console.error(` Response: ${JSON.stringify(rulesResponse, null, 2)}`);
|
||
process.exit(1);
|
||
}
|
||
|
||
const rules = Array.isArray(rulesResponse.result) ? rulesResponse.result : [];
|
||
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 ||
|
||
(typeof rule.dstIp === 'string' && rule.dstIp.includes('192.168.11')) ||
|
||
rule.dstIp === '192.168.11.0/24';
|
||
|
||
const affectsPort80 =
|
||
!rule.dstPort ||
|
||
rule.dstPort === BLOCKSCOUT_PORT ||
|
||
(typeof rule.dstPort === 'string' && 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('════════════════════════════════════════');
|
||
console.log(`🔍 Found ${relevantRules.length} rule(s) that might affect Blockscout:`);
|
||
console.log('════════════════════════════════════════');
|
||
console.log('');
|
||
|
||
relevantRules.forEach((rule, index) => {
|
||
console.log(`Rule ${index + 1}: ${rule.name || rule.id || 'Unnamed'}`);
|
||
console.log(` ID: ${rule.id || 'N/A'}`);
|
||
console.log(` Enabled: ${rule.enable ? 'Yes ✓' : 'No ✗'}`);
|
||
console.log(` Action: ${rule.action || 'N/A'}`);
|
||
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 !== undefined ? 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('');
|
||
console.log('✅ Recommended Action:');
|
||
console.log(' Create an allow rule in Omada Controller with HIGH priority:');
|
||
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 (above deny rules)');
|
||
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('');
|
||
|
||
const highestAllowPriority = Math.max(...allowRules.map((r) => r.priority || 0));
|
||
const lowestDenyPriority = Math.min(...denyRules.map((r) => r.priority || 9999));
|
||
|
||
if (highestAllowPriority < lowestDenyPriority) {
|
||
console.log('✅ Priority order looks correct (allow rules above deny rules).');
|
||
} else {
|
||
console.log('❌ Issue: Some deny rules have higher priority than allow rules.');
|
||
console.log(' Adjust rule priority so allow rules are 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('════════════════════════════════════════');
|
||
console.log('ℹ️ No firewall rules found that specifically target Blockscout.');
|
||
console.log('════════════════════════════════════════');
|
||
console.log('');
|
||
|
||
// Check for deny rules
|
||
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 in total:`);
|
||
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 IP: ${rule.dstIp}`);
|
||
if (rule.dstPort) console.log(` Dest Port: ${rule.dstPort}`);
|
||
if (rule.direction) console.log(` Direction: ${rule.direction}`);
|
||
});
|
||
if (denyRules.length > 10) {
|
||
console.log(` ... and ${denyRules.length - 10} more`);
|
||
}
|
||
console.log('');
|
||
console.log('Note: These rules may affect Blockscout if they match the traffic pattern.');
|
||
console.log('');
|
||
}
|
||
|
||
console.log('✅ Recommendation:');
|
||
console.log(' Create an explicit allow rule to ensure Blockscout access:');
|
||
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');
|
||
console.log(' Destination IP: 192.168.11.140');
|
||
console.log(' Destination Port: 80');
|
||
console.log(' Priority: High');
|
||
console.log('');
|
||
}
|
||
|
||
// Show all rules summary
|
||
console.log('════════════════════════════════════════');
|
||
console.log('All Firewall Rules Summary');
|
||
console.log('════════════════════════════════════════');
|
||
console.log('');
|
||
|
||
const enabledRules = rules.filter((r) => r.enable);
|
||
const disabledRules = rules.filter((r) => !r.enable);
|
||
const allowCount = enabledRules.filter((r) => r.action === 'allow').length;
|
||
const denyCount = enabledRules.filter((r) => r.action === 'deny' || r.action === 'reject').length;
|
||
|
||
console.log(`Total Rules: ${rules.length}`);
|
||
console.log(` Enabled: ${enabledRules.length}`);
|
||
console.log(` Disabled: ${disabledRules.length}`);
|
||
console.log(` Allow Actions: ${allowCount}`);
|
||
console.log(` Deny/Reject Actions: ${denyCount}`);
|
||
console.log('');
|
||
|
||
if (rules.length > 0) {
|
||
console.log('First 10 enabled rules:');
|
||
enabledRules.slice(0, 10).forEach((rule, index) => {
|
||
const actionIcon = rule.action === 'allow' ? '✓' : rule.action === 'deny' ? '✗' : '?';
|
||
console.log(` ${index + 1}. [${actionIcon}] ${rule.name || rule.id || 'Unnamed'} (Priority: ${rule.priority || 'N/A'})`);
|
||
});
|
||
if (enabledRules.length > 10) {
|
||
console.log(` ... and ${enabledRules.length - 10} more`);
|
||
}
|
||
console.log('');
|
||
}
|
||
|
||
console.log('════════════════════════════════════════');
|
||
|
||
} catch (error) {
|
||
console.error('\n❌ Error:');
|
||
console.error('');
|
||
if (error.message) {
|
||
console.error(` ${error.message}`);
|
||
} else {
|
||
console.error(' ', error);
|
||
}
|
||
if (error.stack) {
|
||
console.error('');
|
||
console.error('Stack trace:');
|
||
console.error(error.stack);
|
||
}
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
main();
|
||
|