Files
proxmox/scripts/query-omada-firewall-blockscout-direct.js

394 lines
15 KiB
JavaScript
Raw Permalink Normal View History

#!/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();