Files
proxmox/scripts/unifi/create-management-firewall-rules-node.js
defiQUG fbda1b4beb
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
docs: Ledger Live integration, contract deploy learnings, NEXT_STEPS updates
- ADD_CHAIN138_TO_LEDGER_LIVE: Ledger form done; public code review repo bis-innovations/LedgerLive; init/push commands
- CONTRACT_DEPLOYMENT_RUNBOOK: Chain 138 gas price 1 gwei, 36-addr check, TransactionMirror workaround
- CONTRACT_*: AddressMapper, MirrorManager deployed 2026-02-12; 36-address on-chain check
- NEXT_STEPS_FOR_YOU: Ledger done; steps completable now (no LAN); run-completable-tasks-from-anywhere
- MASTER_INDEX, OPERATOR_OPTIONAL, SMART_CONTRACTS_INVENTORY_SIMPLE: updates
- LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE: bis-innovations/LedgerLive reference

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 15:46:57 -08:00

237 lines
6.6 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Create management VLAN and monitoring firewall rules via UniFi Network API
* These rules CAN be automated (non-overlapping source/destination)
*/
import { readFileSync } from 'fs';
import { join } from 'path';
import { homedir } from 'os';
// Load environment variables
const envPath = join(homedir(), '.env');
function loadEnvFile(filePath) {
try {
const envFile = readFileSync(filePath, 'utf8');
const envVars = envFile.split('\n').filter(
(line) => line.includes('=') && !line.trim().startsWith('#')
);
for (const line of envVars) {
const [key, ...values] = line.split('=');
if (key && values.length > 0 && /^[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);
}
process.env[key.trim()] = value;
}
}
return true;
} catch {
return false;
}
}
loadEnvFile(envPath);
const baseUrl = process.env.UNIFI_UDM_URL || 'https://192.168.0.1';
const apiKey = process.env.UNIFI_API_KEY;
const siteId = '88f7af54-98f8-306a-a1c7-c9349722b1f6';
if (!apiKey) {
console.error('❌ UNIFI_API_KEY not set in environment');
process.exit(1);
}
console.log('Creating Management VLAN Firewall Rules via API');
console.log('=================================================');
console.log('');
// Fetch networks
async function fetchNetworks() {
const response = await fetch(`${baseUrl}/proxy/network/integration/v1/sites/${siteId}/networks`, {
headers: {
'X-API-KEY': apiKey,
'Accept': 'application/json',
},
});
if (!response.ok) {
throw new Error(`Failed to fetch networks: ${response.status} ${response.statusText}`);
}
const data = await response.json();
const networks = data.data || [];
const vlanMap = {};
for (const net of networks) {
const vlanId = net.vlanId;
if (vlanId && vlanId > 1) {
vlanMap[vlanId] = {
id: net.id,
name: net.name,
};
}
}
return vlanMap;
}
// Create ACL rule
async function createACLRule(ruleConfig) {
const { name, description, action, index, sourceNetworks, destNetworks, protocolFilter } = ruleConfig;
const rule = {
type: 'IPV4',
enabled: true,
name,
description,
action,
index,
sourceFilter: sourceNetworks && sourceNetworks.length > 0
? { type: 'NETWORKS', networkIds: sourceNetworks }
: null,
destinationFilter: destNetworks && destNetworks.length > 0
? { type: 'NETWORKS', networkIds: destNetworks }
: null,
protocolFilter: protocolFilter || null,
enforcingDeviceFilter: null,
};
const response = await fetch(`${baseUrl}/proxy/network/integration/v1/sites/${siteId}/acl-rules`, {
method: 'POST',
headers: {
'X-API-KEY': apiKey,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify(rule),
});
const responseText = await response.text();
let responseData;
try {
responseData = JSON.parse(responseText);
} catch {
responseData = responseText;
}
if (response.ok) {
return { success: true, data: responseData };
} else {
return { success: false, error: responseData, status: response.status };
}
}
// Main function
async function main() {
try {
console.log('Fetching network IDs...');
const vlanMap = await fetchNetworks();
const mgmtVlanId = vlanMap[11]?.id;
if (!mgmtVlanId) {
console.error('❌ VLAN 11 (MGMT-LAN) not found');
process.exit(1);
}
// Service VLANs (excluding sovereign tenants for now)
const serviceVlanIds = [110, 111, 112, 120, 121, 130, 132, 133, 134, 140, 141, 150, 160]
.map(v => vlanMap[v]?.id)
.filter(Boolean);
console.log(`✅ Found ${serviceVlanIds.length} service VLANs`);
console.log('');
const rules = [];
// Rule 1: Allow Management VLAN → Service VLANs (TCP for SSH, HTTPS, etc.)
if (serviceVlanIds.length > 0) {
rules.push({
name: 'Allow Management to Service VLANs (TCP)',
description: 'Allow Management VLAN (11) to access Service VLANs via TCP (SSH, HTTPS, database admin ports)',
action: 'ALLOW',
index: 10,
sourceNetworks: [mgmtVlanId],
destNetworks: serviceVlanIds,
protocolFilter: ['TCP'],
});
}
// Rule 2: Allow Service VLANs → Management VLAN (UDP/TCP for monitoring)
if (serviceVlanIds.length > 0) {
rules.push({
name: 'Allow Monitoring to Management VLAN',
description: 'Allow Service VLANs to send monitoring/logging data to Management VLAN (SNMP, monitoring agents)',
action: 'ALLOW',
index: 20,
sourceNetworks: serviceVlanIds,
destNetworks: [mgmtVlanId],
protocolFilter: ['TCP', 'UDP'],
});
}
console.log(`Creating ${rules.length} firewall rules...`);
console.log('');
let successCount = 0;
let failCount = 0;
for (const ruleConfig of rules) {
console.log(`Creating rule: ${ruleConfig.name}`);
const result = await createACLRule(ruleConfig);
if (result.success) {
console.log(' ✅ Rule created successfully');
successCount++;
} else {
console.log(` ❌ Failed to create rule (HTTP ${result.status})`);
if (result.error && typeof result.error === 'object') {
console.log(` Error: ${result.error.message || JSON.stringify(result.error)}`);
} else {
console.log(` Error: ${result.error}`);
}
failCount++;
}
console.log('');
// Small delay between requests
await new Promise(resolve => setTimeout(resolve, 500));
}
console.log('='.repeat(50));
console.log(`✅ Successfully created: ${successCount}/${rules.length} rules`);
if (failCount > 0) {
console.log(`❌ Failed: ${failCount} rules`);
}
console.log('');
// Verify rules
console.log('Verifying created rules...');
const verifyResponse = await fetch(`${baseUrl}/proxy/network/integration/v1/sites/${siteId}/acl-rules`, {
headers: {
'X-API-KEY': apiKey,
'Accept': 'application/json',
},
});
if (verifyResponse.ok) {
const verifyData = await verifyResponse.json();
const ruleCount = verifyData.count || 0;
console.log(`✅ Found ${ruleCount} ACL rules total`);
}
} catch (error) {
console.error('❌ Error:', error.message);
if (error.stack) {
console.error(error.stack);
}
process.exit(1);
}
}
main();