Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
- Config, docs, scripts, and backup manifests - Submodule refs unchanged (m = modified content in submodules) Made-with: Cursor
219 lines
5.4 KiB
Bash
Executable File
219 lines
5.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Create firewall rules via UniFi Network API
|
|
# This script creates ACL rules for network segmentation and security
|
|
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
|
|
cd "$PROJECT_ROOT"
|
|
|
|
# Load UNIFI_* from repo .env, unifi-api/.env, or ~/.env
|
|
if [ -f "$PROJECT_ROOT/.env" ]; then
|
|
set -a && source "$PROJECT_ROOT/.env" 2>/dev/null && set +a
|
|
fi
|
|
if [ -f "$PROJECT_ROOT/unifi-api/.env" ]; then
|
|
set -a && source "$PROJECT_ROOT/unifi-api/.env" 2>/dev/null && set +a
|
|
fi
|
|
if [ -f ~/.env ]; then
|
|
source <(grep "^UNIFI_" ~/.env 2>/dev/null | sed 's/^/export /') 2>/dev/null || true
|
|
fi
|
|
|
|
UDM_URL="${UNIFI_UDM_URL:-https://192.168.0.1}"
|
|
API_KEY="${UNIFI_API_KEY}"
|
|
SITE_ID="88f7af54-98f8-306a-a1c7-c9349722b1f6"
|
|
|
|
if [ -z "$API_KEY" ]; then
|
|
echo "❌ UNIFI_API_KEY not set in environment"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Creating Firewall Rules via API"
|
|
echo "=================================="
|
|
echo ""
|
|
echo "UDM URL: $UDM_URL"
|
|
echo "Site ID: $SITE_ID"
|
|
echo ""
|
|
|
|
# Get network IDs
|
|
echo "Fetching network IDs..."
|
|
NETWORKS_JSON=$(curl -k -s -X GET "$UDM_URL/proxy/network/integration/v1/sites/$SITE_ID/networks" \
|
|
-H "X-API-KEY: $API_KEY" \
|
|
-H 'Accept: application/json')
|
|
|
|
# Extract network IDs using Python
|
|
NETWORK_IDS=$(python3 << 'PYEOF'
|
|
import sys, json
|
|
data = json.load(sys.stdin)
|
|
networks = data.get('data', [])
|
|
vlan_map = {}
|
|
for net in networks:
|
|
vlan_id = net.get('vlanId')
|
|
if vlan_id and vlan_id > 1:
|
|
vlan_map[vlan_id] = net.get('id')
|
|
|
|
# Key VLANs we need
|
|
key_vlans = {
|
|
11: 'MGMT-LAN',
|
|
110: 'BESU-VAL',
|
|
111: 'BESU-SEN',
|
|
112: 'BESU-RPC',
|
|
120: 'BLOCKSCOUT',
|
|
121: 'CACTI',
|
|
130: 'CCIP-OPS',
|
|
132: 'CCIP-COMMIT',
|
|
133: 'CCIP-EXEC',
|
|
134: 'CCIP-RMN',
|
|
140: 'FABRIC',
|
|
141: 'FIREFLY',
|
|
150: 'INDY',
|
|
160: 'SANKOFA-SVC',
|
|
200: 'PHX-SOV-SMOM',
|
|
201: 'PHX-SOV-ICCC',
|
|
202: 'PHX-SOV-DBIS',
|
|
203: 'PHX-SOV-AR'
|
|
}
|
|
|
|
# Export as shell variables
|
|
for vlan, name in key_vlans.items():
|
|
if vlan in vlan_map:
|
|
print(f"NETWORK_ID_VLAN{vlan}={vlan_map[vlan]}")
|
|
print(f"NETWORK_NAME_VLAN{vlan}={name}")
|
|
PYEOF
|
|
)
|
|
|
|
# Source the network IDs
|
|
eval "$NETWORK_IDS"
|
|
|
|
echo "✅ Network IDs loaded"
|
|
echo ""
|
|
|
|
# Function to create ACL rule
|
|
create_acl_rule() {
|
|
local name=$1
|
|
local description=$2
|
|
local action=$3
|
|
local index=$4
|
|
local source_networks=$5
|
|
local dest_networks=$6
|
|
local protocol=$7
|
|
|
|
echo "Creating rule: $name"
|
|
|
|
# Build source filter
|
|
if [ -n "$source_networks" ]; then
|
|
SOURCE_FILTER=$(python3 << PYEOF
|
|
import json
|
|
networks = "$source_networks".split()
|
|
network_ids = []
|
|
for net in networks:
|
|
var_name = f"NETWORK_ID_VLAN{net}"
|
|
import os
|
|
net_id = os.environ.get(var_name)
|
|
if net_id:
|
|
network_ids.append(net_id)
|
|
print(json.dumps({
|
|
"type": "NETWORK",
|
|
"networkIds": network_ids
|
|
}))
|
|
PYEOF
|
|
)
|
|
else
|
|
SOURCE_FILTER="null"
|
|
fi
|
|
|
|
# Build destination filter
|
|
if [ -n "$dest_networks" ]; then
|
|
DEST_FILTER=$(python3 << PYEOF
|
|
import json
|
|
networks = "$dest_networks".split()
|
|
network_ids = []
|
|
for net in networks:
|
|
var_name = f"NETWORK_ID_VLAN{net}"
|
|
import os
|
|
net_id = os.environ.get(var_name)
|
|
if net_id:
|
|
network_ids.append(net_id)
|
|
print(json.dumps({
|
|
"type": "NETWORK",
|
|
"networkIds": network_ids
|
|
}))
|
|
PYEOF
|
|
)
|
|
else
|
|
DEST_FILTER="null"
|
|
fi
|
|
|
|
# Build protocol filter
|
|
if [ -n "$protocol" ]; then
|
|
PROTOCOL_FILTER="[\"$protocol\"]"
|
|
else
|
|
PROTOCOL_FILTER="null"
|
|
fi
|
|
|
|
# Create rule JSON
|
|
RULE_JSON=$(python3 << PYEOF
|
|
import json, sys
|
|
source = json.loads('$SOURCE_FILTER') if '$SOURCE_FILTER' != 'null' else None
|
|
dest = json.loads('$DEST_FILTER') if '$DEST_FILTER' != 'null' else None
|
|
protocol = json.loads('$PROTOCOL_FILTER') if '$PROTOCOL_FILTER' != 'null' else None
|
|
|
|
rule = {
|
|
"type": "IPV4",
|
|
"enabled": True,
|
|
"name": "$name",
|
|
"description": "$description",
|
|
"action": "$action",
|
|
"index": $index,
|
|
"sourceFilter": source,
|
|
"destinationFilter": dest,
|
|
"protocolFilter": protocol,
|
|
"enforcingDeviceFilter": None
|
|
}
|
|
print(json.dumps(rule))
|
|
PYEOF
|
|
)
|
|
|
|
# Create the rule
|
|
RESPONSE=$(curl -k -s -w "\n%{http_code}" -X POST "$UDM_URL/proxy/network/integration/v1/sites/$SITE_ID/acl-rules" \
|
|
-H "X-API-KEY: $API_KEY" \
|
|
-H 'Content-Type: application/json' \
|
|
-H 'Accept: application/json' \
|
|
-d "$RULE_JSON")
|
|
|
|
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
|
RESPONSE_BODY=$(echo "$RESPONSE" | sed '$d')
|
|
|
|
if [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "200" ]; then
|
|
echo " ✅ Rule created successfully"
|
|
return 0
|
|
else
|
|
echo " ❌ Failed to create rule (HTTP $HTTP_CODE)"
|
|
echo " Response: $RESPONSE_BODY"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Create firewall rules
|
|
echo "Creating firewall rules..."
|
|
echo ""
|
|
|
|
# Rule 1: Block sovereign tenant inter-VLAN traffic (VLANs 200-203)
|
|
create_acl_rule \
|
|
"Block Sovereign Tenant East-West Traffic" \
|
|
"Deny traffic between sovereign tenant VLANs (200-203) for isolation" \
|
|
"BLOCK" \
|
|
100 \
|
|
"200 201 202 203" \
|
|
"200 201 202 203" \
|
|
""
|
|
|
|
echo ""
|
|
echo "✅ Firewall rule creation complete!"
|
|
echo ""
|
|
echo "Note: Additional rules (management access, monitoring) can be added"
|
|
echo " after verifying the API request format works correctly."
|