diff options
| author | 2026-05-13 13:43:22 +0100 | |
|---|---|---|
| committer | 2026-05-13 13:43:22 +0100 | |
| commit | ac0654daf06a9d01fd264d96c00c8ab47b90cb73 (patch) | |
| tree | a52a85553116dbc671bf43a7414c34959ca0b8eb | |
| parent | b459f8eef44afaab44e38b8a5946974a4d107301 (diff) | |
| download | dotfiles-ac0654daf06a9d01fd264d96c00c8ab47b90cb73.tar.gz dotfiles-ac0654daf06a9d01fd264d96c00c8ab47b90cb73.tar.bz2 dotfiles-ac0654daf06a9d01fd264d96c00c8ab47b90cb73.zip | |
feat(net): nftables laptop firewall
Default-deny inbound, allow outbound. Scoped to 'inet filter' with
'destroy table' on reload so podman/netavark tables are preserved.
- meta/base.txt: add nftables
- systemd-units/system/base.txt: enable nftables.service
- etc/nftables.conf: laptop ruleset (loopback, ct state, ICMP/ICMPv6
essentials, DHCPv6 client, default-drop input/forward, accept output)
- etc/sysctl.d/99-sysctl.conf: rp_filter=2, no redirects, no source-route,
log_martians
- README.md: firewall section with reload caveat
| -rw-r--r-- | README.md | 8 | ||||
| -rw-r--r-- | etc/nftables.conf | 50 | ||||
| -rw-r--r-- | etc/sysctl.d/99-sysctl.conf | 13 | ||||
| -rw-r--r-- | meta/base.txt | 1 | ||||
| -rw-r--r-- | systemd-units/system/base.txt | 1 |
5 files changed, 73 insertions, 0 deletions
@@ -121,6 +121,14 @@ Four sources of drift are tracked independently and combined by `just status`: - **/etc** (`just etc-status` / `just etc-diff`): repo-tracked files in `etc/` that differ from or are missing on the live `/etc`. Resolve with `just etc-apply` (repo → live), `just etc-re-add PATH` (live → repo), or `just etc-untrack PATH`. - **Units** (`just unit-status`): enabled units not in any `systemd-units/{system,user}/*.txt`, or declared units that aren't enabled (checked for both scopes). +## Firewall + +Stateful nftables firewall with a laptop profile: default-deny inbound, allow outbound, loopback + established + ICMP/ICMPv6 + DHCPv6 client only. Ruleset at `etc/nftables.conf`; enabled via `nftables.service` in `systemd-units/system/base.txt`. Kernel hardening (rp_filter, no redirects, no source-route, log_martians) lives in `etc/sysctl.d/99-sysctl.conf`. + +The ruleset is scoped to `table inet filter` and uses `destroy table inet filter` on reload, so podman/netavark's own tables are preserved. Don't `systemctl stop nftables` — the package ExecStop runs a global `nft flush ruleset` which would nuke podman rules. Reload with `sudo systemctl reload nftables` or `sudo nft -f /etc/nftables.conf` instead. + +Verify with `sudo nft list ruleset`. + ## Git hooks Activated by `just init` via `git config core.hooksPath .githooks`: diff --git a/etc/nftables.conf b/etc/nftables.conf new file mode 100644 index 0000000..c7eada2 --- /dev/null +++ b/etc/nftables.conf @@ -0,0 +1,50 @@ +#!/usr/bin/nft -f +# Laptop firewall: default-deny inbound, allow outbound. +# Scoped to `inet filter` so podman/netavark tables are preserved on reload. + +destroy table inet filter + +table inet filter { + chain input { + type filter hook input priority filter; policy drop; + + iif "lo" accept + ct state vmap { established : accept, related : accept, invalid : drop } + + # IPv4 ICMP essentials + ip protocol icmp icmp type { + echo-request, + destination-unreachable, + time-exceeded, + parameter-problem + } accept + + # IPv6 ICMP: NDP, PMTUD, echo, MLD + meta l4proto icmpv6 icmpv6 type { + destination-unreachable, + packet-too-big, + time-exceeded, + parameter-problem, + echo-request, + nd-router-solicit, + nd-router-advert, + nd-neighbor-solicit, + nd-neighbor-advert, + mld-listener-query, + mld-listener-report, + mld-listener-done, + mld2-listener-report + } accept + + # DHCPv6 client + ip6 saddr fe80::/10 udp dport 546 accept + } + + chain forward { + type filter hook forward priority filter; policy drop; + } + + chain output { + type filter hook output priority filter; policy accept; + } +} diff --git a/etc/sysctl.d/99-sysctl.conf b/etc/sysctl.d/99-sysctl.conf index 6d21fda..3177c28 100644 --- a/etc/sysctl.d/99-sysctl.conf +++ b/etc/sysctl.d/99-sysctl.conf @@ -17,3 +17,16 @@ net.ipv4.tcp_mtu_probing = 1 net.core.default_qdisc = cake net.ipv4.tcp_congestion_control = bbr vm.vfs_cache_pressure = 50 + +# Network hardening +net.ipv4.conf.all.rp_filter = 2 +net.ipv4.conf.default.rp_filter = 2 +net.ipv4.conf.all.accept_redirects = 0 +net.ipv4.conf.default.accept_redirects = 0 +net.ipv6.conf.all.accept_redirects = 0 +net.ipv6.conf.default.accept_redirects = 0 +net.ipv4.conf.all.send_redirects = 0 +net.ipv4.conf.default.send_redirects = 0 +net.ipv4.conf.all.accept_source_route = 0 +net.ipv6.conf.all.accept_source_route = 0 +net.ipv4.conf.all.log_martians = 1 diff --git a/meta/base.txt b/meta/base.txt index da40c3c..f3c3e5a 100644 --- a/meta/base.txt +++ b/meta/base.txt @@ -27,6 +27,7 @@ man-db man-pages neovim nfs-utils +nftables nmap ocl-icd openssh diff --git a/systemd-units/system/base.txt b/systemd-units/system/base.txt index 6f8582a..1e3af9b 100644 --- a/systemd-units/system/base.txt +++ b/systemd-units/system/base.txt @@ -9,6 +9,7 @@ paccache.timer acpid.service cpupower.service iwd.service +nftables.service systemd-networkd.service systemd-networkd-wait-online.service tlp.service |
