--- # Port and address maps are name -> value so group_vars and host_vars levels # merge into one map (hash_behaviour = merge); a null value drops an entry. # Ports are numbers or "first-last" ranges. firewall: public: tcp: {} udp: {} # Accepted only from the geo sets or the admin set. geo: tcp: {} # Accepted on wt0 before the public zone is evaluated. vpn: tcp: {} udp: {} # tcp: ports accepted from the admin set alone, never from the geo sets. admin: tcp: {} v4: {} v6: {} # Metrics scrapers: on wt0 only these source addresses reach the listed tcp # ports (the vpn zone applies to every peer). Rendered only when both are set. scrapers: tcp: {} v4: {} v6: {} # Interface names (nft wildcards allowed) whose traffic is trusted. internal_ifaces: {} # Guest bridges (Proxmox vmbr*): DHCP, router solicitations and the public # zone only, never the geo ports. guest_ifaces: {} # Kubernetes ring: pod interfaces, and node NetBird IPs / pod CIDRs on wt0. cluster: ifaces: {} v4: {} v6: {} # IPv4 port forwards: name -> {daddr, to, tcp: ports, udp: ports}, ports as in # the maps above. Forwarded traffic passes the forward hook, not input. dnat: {} # IPv4 source NAT: name -> nft match, e.g. 'oifname "vmbr0"'. masquerade: {} # Forwarded traffic to guarded guests: name -> {ifaces, v4, v6}, where v4 and # v6 are {daddr, tcp: ports, icmp: icmp or icmpv6 types}. Replies and traffic # in on ifaces pass, the listed ports and types pass, the rest to daddr drops; # traffic to other addresses is not filtered. forward_guard: {} ssh_rate_limit: "10/minute burst 5 packets" # Kernel log of dropped inbound packets (fw-in-drop: ) and of rejected # outbound mail (fw-out-reject: ), read from the journal by CrowdSec. Each # rate is one limit shared by all matching packets. log: in_rate: "5/second burst 30 packets" out_rate: "6/minute burst 5 packets" # Lets the kernel log the nft log statements of container network # namespaces (LXC guests) into the host kernel log. all_netns: false # Shared by the Spamhaus (fw-spamhaus-in: , fw-spamhaus-out: ), FireHOL (fw-firehol-in: , fw-firehol-out: ) and scanner (fw-scanners-in: ) chains, one limit each. spamhaus_rate: "6/minute burst 10 packets" # Spamhaus DROP, DROPv6 and ASN-DROP: traffic from the listed networks drops # (input, forward), traffic to them is rejected (output, forward). The sets # are refreshed daily by nft-host-spamhaus. spamhaus: true # FireHOL level1 (IPv4): treated like the Spamhaus lists, with the prefixes # fw-firehol-in: and fw-firehol-out: and the same log limit. Entries in # private or reserved space and ones holding an address of the host are left # out. Refreshed daily by nft-host-firehol. firehol: true # Internet-wide scanners (Censys, Shodan, ONYPHE, Shadowserver, ...), IPv4 # and IPv6: the cidr files of github.com/OpenFilters/internet-scanners # (Unlicense). Inbound traffic from the listed networks drops (input, # forward) once the established rule has passed; outbound traffic is never # filtered. Log prefix fw-scanners-in: with the Spamhaus log limit, which # CrowdSec whitelists. Entries in private or reserved space, overlapping the # admin, scraper and cluster sets, the CrowdSec whitelist or the public_ips # of any host, and ones holding an address of the host are left out # (/etc/nft-host-scanners.allowlist). Refreshed daily by nft-host-scanners. scanners: true # Reject outbound TCP 25; the mail group turns it off. block_smtp_out: true # EU-27 plus CH, NO, GB, IS, LI (ipdeny country codes). Empty leaves the geo # ports to the admin set; a load peaks at about 60 MB of kernel memory, too much for a 1 GB host. geo_countries: [at, be, bg, hr, cy, cz, dk, ee, fi, fr, de, gr, hu, ie, it, lv, lt, lu, mt, nl, pl, pt, ro, sk, si, es, se, ch, "no", gb, is, li] rollback_seconds: 120 # name -> shell command that has to exit 0 through the new rule set before the # rollback is disarmed; retried for a quarter of the rollback window. confirm: {}