# /etc/pf.conf - fw.internal # Default deny. Blocked traffic is logged, and so is every new connection # allowed by a pass rule marked "log". Every rule has a label; pflog2syslog # adds it to each log line as label=, which Vector turns into rule.name. # --- Interfaces ---- wan = "igc0" admin = "igc1" apmgmt = "igc2" ps5 = "igc3" media = "igc4" servers = "igc5" trusted = "vlan10" iot = "vlan20" guest = "vlan30" internal = "{ igc1 igc2 igc3 igc4 igc5 vlan10 vlan20 vlan30 }" # --- Tables ---- table const { 192.168.1.10 192.168.10.10 } table const { 192.168.5.10 192.168.5.20 192.168.5.30 192.168.5.40 192.168.5.50 } table const { 192.168.5.100 } table const { 192.168.5.200/29 192.168.5.208/29 192.168.5.216/30 192.168.5.220 } table const { 192.168.5.60 } table const { 192.168.2.2 } table const { 192.168.5.140 } table const { 192.168.4.10 } table const { 9.9.9.9 149.112.112.112 } table const { 192.168.1.0/24 192.168.2.0/24 192.168.3.0/24 192.168.4.0/24 \ 192.168.5.0/24 192.168.10.0/24 192.168.20.0/24 192.168.30.0/24 } table const { 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16 \ 172.16.0.0/12 192.0.0.0/24 192.0.2.0/24 192.168.0.0/16 \ 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24 224.0.0.0/3 } # --- Options ---- set block-policy drop set skip on lo set loginterface $wan # --- Normalisation and NAT ---- match in all scrub (no-df random-id) match out on $wan inet from to any nat-to ($wan) # --- Default: block and log everything, no IPv6 ---- block log all label "drop_all" block quick inet6 all label "drop_ipv6" # --- DHCP (before the spoofing checks; not logged, dhcpd logs it) ---- pass in quick on $wan inet proto udp from any port bootps to any port bootpc label "allow_wan_dhcp" pass in quick on $internal inet proto udp from any port bootpc to any port bootps label "allow_dhcp" # --- Spoofing ---- block in log quick from urpf-failed label "block_spoofed_source" block in log quick on $wan from label "block_wan_martians" # --- Everything the firewall lets in may also leave (logged inbound) ---- pass out quick inet label "allow_out" # --- Services on the firewall (not logged: very chatty) ---- pass in quick on $internal inet proto { tcp udp } to (self) port domain label "allow_dns_to_fw" pass in quick on $internal inet proto udp to (self) port ntp label "allow_ntp_to_fw" pass in quick on $internal inet proto icmp to (self) icmp-type echoreq label "allow_ping_to_fw" # The PS5 and guests bypass the tunnel, so their DNS does too: a public resolver, straight out pass in quick on { $ps5 $guest } inet proto { tcp udp } to port { domain 853 } label "allow_direct_dns" block in log quick on $internal inet proto { tcp udp } to ! (self) port { domain 853 } label "block_external_dns" # --- Admin access (logged) ---- pass in log quick on { $admin $trusted } inet proto tcp from to (self) port ssh label "allow_admin_ssh_fw" pass in log quick on { $admin $trusted } inet proto tcp from to { } port ssh label "allow_admin_ssh_hosts" pass in log quick on { $admin $trusted } inet proto tcp from to port 6443 label "allow_admin_k8s_api" pass in log quick on { $admin $trusted } inet proto tcp from to port https label "allow_admin_ingress" pass in log quick on { $admin $trusted } inet proto tcp from to port 11443 label "allow_admin_unifi_ui" # WebRTC from the UniFi web UI: direct path from the admin laptop to the controller pass in log quick on { $admin $trusted } inet proto { tcp udp } from to label "allow_admin_unifi_webrtc" pass in log quick on { $admin $trusted } inet proto icmp from to icmp-type echoreq label "allow_admin_ping" # --- Known noise: dropped without logging ---- # Broadcast and multicast discovery (NetBIOS, SSDP, ...) block in quick on $internal inet to { 255.255.255.255 224.0.0.0/4 192.168.1.255 192.168.2.255 192.168.3.255 192.168.4.255 192.168.5.255 192.168.10.255 192.168.20.255 192.168.30.255 } label "drop_broadcast_multicast" # Late TCP packets (FIN, ACK, RST+ACK) for connections pf no longer tracks. # New connections (bare SYN) and tracked connections never reach this rule. block in quick on $internal inet proto tcp from flags A/A label "drop_late_tcp" # WebRTC (UniFi UI) also tries the firewall's public address as a detour. # Reject it immediately, so it gives up at once. block return in quick on { $admin $trusted $servers } inet proto udp from { } to ($wan) label "reject_webrtc_wan_detour" # --- Nothing else may reach any of the firewall's own addresses ---- block in log quick on $internal to (self) label "block_to_fw" # --- Between networks (logged) ---- pass in log quick on $trusted inet proto tcp from $trusted:network to port 8123 label "allow_trusted_homeassistant" pass in log quick on $trusted inet proto tcp from $trusted:network to port { 18080 9090 } label "allow_trusted_kodi" pass in log quick on $servers inet from to $iot:network label "allow_homeassistant_iot" pass in quick on $apmgmt inet proto tcp from to port 8080 label "allow_ap_unifi_inform" pass in log quick on $apmgmt inet proto udp from to port 3478 label "allow_ap_unifi_stun" pass in log quick on $apmgmt inet proto udp from to port 5514 label "allow_ap_unifi_logs" # The AC Lite checks its uplink by pinging 1.1.1.1 (not configurable in UniFi 10.x); not logged pass in quick on $apmgmt inet proto icmp from to 1.1.1.1 icmp-type echoreq label "allow_ap_uplink_ping" pass in log quick on $servers inet proto tcp from to port ssh label "allow_unifi_ap_ssh" # --- Internet access (logged; APMGMT gets none) ---- pass in log quick on $admin inet from $admin:network to ! label "allow_admin_internet" pass in log quick on $trusted inet from $trusted:network to ! label "allow_trusted_internet" pass in log quick on $ps5 inet from $ps5:network to ! label "allow_ps5_internet" pass in log quick on $media inet from $media:network to ! label "allow_media_internet" pass in log quick on $servers inet from $servers:network to ! label "allow_servers_internet" pass in log quick on $iot inet from $iot:network to ! label "allow_iot_internet" pass in log quick on $guest inet from $guest:network to ! label "allow_guest_internet"