Files
proxmox/scripts/query-omada-firewall-blockscout-direct.js
defiQUG cb47cce074 Complete markdown files cleanup and organization
- 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.
2026-01-06 01:46:25 -08:00

394 lines
15 KiB
JavaScript
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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();