#!/usr/sbin/nft -f # BetterFrame kiosk firewall. Default-drop inbound; only LAN can reach the # kiosk local HTTP API. Outbound stays wide open — kiosk needs to pull RTSP # from arbitrary cameras + reach the BF server (which may be on the public # internet) without explicit allowlisting. # # Reload after edits: sudo nft -f /etc/nftables.conf flush ruleset table inet betterframe { # --- helper sets ---------------------------------------------------------- set rfc1918_v4 { type ipv4_addr flags interval elements = { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 # link-local; on by default for DHCP fallback discovery } } set rfc4193_v6 { type ipv6_addr flags interval elements = { fc00::/7, fe80::/10 # link-local } } # --- input chain ---------------------------------------------------------- chain input { type filter hook input priority filter; policy drop; # Sanity: existing flows always pass. ct state established,related accept ct state invalid drop # Loopback — needed for any IPC, healthchecks, local-only services. iifname "lo" accept # ICMP for diagnostics. Rate-limited so we don't help amplification. ip protocol icmp limit rate 4/second accept ip6 nexthdr icmpv6 limit rate 4/second accept # SSH never opens at the firewall — image build sets `enable-ssh: 0` and # disables/masks sshd. If you intentionally re-enable it for triage, # uncomment the next line and lock it down further: # tcp dport 22 ip saddr @rfc1918_v4 accept # Kiosk local HTTP API (:18090) — LAN-only. Lets house-side automation # hit `/local/layout/` + `/local/snapshot/` via the local_key. # Public-internet traffic is dropped here so an exposed-port misconfig # downstream (e.g. an upstream NAT/UPnP slip) doesn't open it to the # world. tcp dport 18090 ip saddr @rfc1918_v4 accept tcp dport 18090 ip6 saddr @rfc4193_v6 accept # Everything else: drop (policy already covers this; explicit for clarity # so future edits can insert above this comment without changing policy). # counter drop # uncomment when debugging which packets are getting blocked } # --- forward / output ----------------------------------------------------- chain forward { type filter hook forward priority filter; policy drop; } chain output { # Kiosk is the initiator for everything (RTSP pulls, BF server REST, # firmware downloads). Allow all outbound. type filter hook output priority filter; policy accept; } }