Files
proxmox/scripts/query-omada-cloud-firewall-blockscout.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

390 lines
15 KiB
JavaScript
Executable File
Raw Permalink 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 Cloud Controller firewall rules for Blockscout access
* Uses cloud controller API if credentials are available
*/
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);
}
// Try to detect cloud controller URL
// Omada Cloud Controller typically uses: https://controller.tplinkcloud.com or specific domain
const cloudControllerUrl = envVars.OMADA_CLOUD_CONTROLLER_URL ||
envVars.OMADA_CONTROLLER_URL ||
envVars.OMADA_CONTROLLER_BASE_URL ||
'https://192.168.11.8:8043'; // Fallback to local
// Check if this is a cloud URL (contains tplinkcloud.com or is not a local IP)
const isCloudController = cloudControllerUrl.includes('tplinkcloud.com') ||
cloudControllerUrl.includes('cloud') ||
(!cloudControllerUrl.match(/^https?:\/\/192\.168\./) &&
!cloudControllerUrl.match(/^https?:\/\/10\./) &&
!cloudControllerUrl.match(/^https?:\/\/172\.(1[6-9]|2[0-9]|3[01])\./));
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(cloudControllerUrl);
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(isCloudController ? '(Cloud Controller)' : '(Local Controller)');
console.log('════════════════════════════════════════');
console.log('');
console.log(`Controller URL: ${cloudControllerUrl}`);
console.log(`Controller Type: ${isCloudController ? 'Cloud' : 'Local'}`);
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...');
// Try login endpoint
let loginResponse;
try {
loginResponse = await apiRequest('POST', '/api/v2/login', {
username,
password,
});
} catch (error) {
console.error(` ✗ Login failed: ${error.message}`);
console.error('');
console.error('Note: Cloud controllers may use different authentication endpoints.');
console.error('Please check Omada Controller documentation for cloud API endpoints.');
process.exit(1);
}
if (loginResponse.errorCode !== 0) {
console.error(` ✗ Login failed: ${loginResponse.msg || 'Unknown error'}`);
console.error(` Error Code: ${loginResponse.errorCode}`);
// If cloud controller, suggest alternative authentication
if (isCloudController) {
console.error('');
console.error('Cloud Controller Authentication Notes:');
console.error(' - Cloud controllers may require different authentication');
console.error(' - May need to use OAuth token endpoint instead of /api/v2/login');
console.error(' - Check Omada Cloud Controller documentation');
}
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('');
// Try multiple endpoint paths for firewall rules
const endpointPaths = [
`/api/v2/sites/${effectiveSiteId}/firewall/rules`,
`/sites/${effectiveSiteId}/firewall/rules`,
`/api/firewall/rules?siteId=${effectiveSiteId}`,
`/api/v2/firewall/rules?siteId=${effectiveSiteId}`,
];
let rulesResponse;
let rulesFound = false;
for (const path of endpointPaths) {
try {
rulesResponse = await apiRequest('GET', path, null, token, cookies);
// Check if we got a valid response
if (rulesResponse.errorCode === 0 && Array.isArray(rulesResponse.result)) {
rulesFound = true;
break;
} else if (Array.isArray(rulesResponse)) {
rulesResponse = { errorCode: 0, result: rulesResponse };
rulesFound = true;
break;
}
} catch (e) {
continue;
}
}
if (!rulesFound) {
console.error(` ✗ Could not query firewall rules via API`);
console.error('');
console.error('Note: Firewall rules may need to be checked via Omada Controller web interface:');
console.error(` ${cloudControllerUrl}`);
console.error(' Navigate to: Settings → Firewall → Firewall Rules');
console.error('');
console.error('Or firewall rules API may not be available for this controller type.');
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) {
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 (denyRules.length > 10) {
console.log(` ... and ${denyRules.length - 10} more`);
}
console.log('');
}
console.log('✅ Recommendation:');
console.log(' Create an explicit allow rule to ensure Blockscout access.');
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 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(` Allow Actions: ${allowCount}`);
console.log(` Deny/Reject Actions: ${denyCount}`);
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();