#!/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."