about summary refs log tree commit diff
path: root/bundles/firewall
diff options
context:
space:
mode:
authorRobert Günzler <r@gnzler.io>2025-09-07 17:32:13 +0200
committerRobert Günzler <r@gnzler.io>2025-09-07 17:42:42 +0200
commit38be6c13f76e893cf47636257071b440dec0c5b2 (patch)
treef7ded3235a7e3362cbd1ed3d91523968fe0faf90 /bundles/firewall
initial commit
Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to 'bundles/firewall')
-rw-r--r--bundles/firewall/.editorconfig4
-rw-r--r--bundles/firewall/files/etc/nftables.d/00-basic.nft59
-rw-r--r--bundles/firewall/files/etc/nftables.d/01-my-filter.nft18
-rw-r--r--bundles/firewall/files/etc/nftables.d/10-tailscale.nft16
-rw-r--r--bundles/firewall/files/etc/nftables.d/22-ssh.nft10
-rw-r--r--bundles/firewall/files/etc/nftables.d/50-www.nft10
-rw-r--r--bundles/firewall/files/etc/nftables.d/80-podman.nft15
-rw-r--r--bundles/firewall/files/etc/nftables.nft21
-rw-r--r--bundles/firewall/items.py43
-rw-r--r--bundles/firewall/metadata.py7
10 files changed, 203 insertions, 0 deletions
diff --git a/bundles/firewall/.editorconfig b/bundles/firewall/.editorconfig
new file mode 100644
index 0000000..205511f
--- /dev/null
+++ b/bundles/firewall/.editorconfig
@@ -0,0 +1,4 @@
+root = false
+
+[*.nft]
+indent_size = 2
diff --git a/bundles/firewall/files/etc/nftables.d/00-basic.nft b/bundles/firewall/files/etc/nftables.d/00-basic.nft
new file mode 100644
index 0000000..493ab38
--- /dev/null
+++ b/bundles/firewall/files/etc/nftables.d/00-basic.nft
@@ -0,0 +1,59 @@
+#!/usr/sbin/nft -f
+# vim: set ts=4 sw=4:
+table inet filter {
+	chain input {
+		iifname lo accept \
+		comment "Accept any localhost traffic"
+
+		ct state { established, related } accept \
+		comment "Accept traffic originated from us"
+
+		ct state invalid drop \
+		comment "Drop invalid connections"
+
+		tcp dport 113 reject with icmpx type port-unreachable \
+		comment "Reject AUTH to make it fail fast"
+
+		# ICMPv4
+
+		ip protocol icmp icmp type {
+			echo-reply,  # type 0
+			destination-unreachable,  # type 3
+			echo-request,  # type 8
+			time-exceeded,  # type 11
+			parameter-problem,  # type 12
+		} accept \
+		comment "Accept ICMP"
+
+		# ICMPv6
+
+		icmpv6 type {
+			destination-unreachable,  # type 1
+			packet-too-big,  # type 2
+			time-exceeded,  # type 3
+			parameter-problem,  # type 4
+			echo-request,  # type 128
+			echo-reply,  # type 129
+		} accept \
+		comment "Accept basic IPv6 functionality"
+
+		icmpv6 type {
+			nd-router-solicit,  # type 133
+			nd-router-advert,  # type 134
+			nd-neighbor-solicit,  # type 135
+			nd-neighbor-advert,  # type 136
+		} ip6 hoplimit 255 accept \
+		comment "Allow IPv6 SLAAC"
+
+		icmpv6 type {
+			mld-listener-query,  # type 130
+			mld-listener-report,  # type 131
+			mld-listener-reduction,  # type 132
+			mld2-listener-report,  # type 143
+		} ip6 saddr fe80::/10 accept \
+		comment "Allow IPv6 multicast listener discovery on link-local"
+
+		ip6 saddr fe80::/10 udp sport 547 udp dport 546 accept \
+		comment "Accept DHCPv6 replies from IPv6 link-local addresses"
+	}
+}
diff --git a/bundles/firewall/files/etc/nftables.d/01-my-filter.nft b/bundles/firewall/files/etc/nftables.d/01-my-filter.nft
new file mode 100644
index 0000000..fffd9f2
--- /dev/null
+++ b/bundles/firewall/files/etc/nftables.d/01-my-filter.nft
@@ -0,0 +1,18 @@
+#!/usr/sbin/nft -f
+# vim: set ts=4 sw=4:
+table inet filter {
+
+	chain my_filter {
+	}
+
+	chain input {
+		jump my_filter
+		log prefix "blocked input traffic: " counter
+	}
+
+	chain forward {
+		jump my_filter
+		log prefix "blocked input traffic: " counter
+	}
+
+}
diff --git a/bundles/firewall/files/etc/nftables.d/10-tailscale.nft b/bundles/firewall/files/etc/nftables.d/10-tailscale.nft
new file mode 100644
index 0000000..f95e142
--- /dev/null
+++ b/bundles/firewall/files/etc/nftables.d/10-tailscale.nft
@@ -0,0 +1,16 @@
+#!/usr/sbin/nft -f
+# vim: set ts=4 sw=4:
+table inet filter {
+
+    chain my_filter {
+        iifname tailscale0 accept \
+        comment "Accept inbount tailscale traffic"
+
+        oifname tailscale0 accept \
+        comment "Accept outbound tailscale traffic"
+
+        udp dport { 41641 } accept \
+        comment "Accept tailscale NAT traffic"
+    }
+
+}
diff --git a/bundles/firewall/files/etc/nftables.d/22-ssh.nft b/bundles/firewall/files/etc/nftables.d/22-ssh.nft
new file mode 100644
index 0000000..c8c91a1
--- /dev/null
+++ b/bundles/firewall/files/etc/nftables.d/22-ssh.nft
@@ -0,0 +1,10 @@
+#!/bin/nft -f
+# vim: set ts=4 sw=4:
+table inet filter {
+
+    chain my_filter {
+        tcp dport { ${node.metadata.get("ssh/port", 22)} } accept \
+        comment "Accept SSH traffic"
+    }
+
+}
diff --git a/bundles/firewall/files/etc/nftables.d/50-www.nft b/bundles/firewall/files/etc/nftables.d/50-www.nft
new file mode 100644
index 0000000..6969f6d
--- /dev/null
+++ b/bundles/firewall/files/etc/nftables.d/50-www.nft
@@ -0,0 +1,10 @@
+#!/usr/sbin/nft -f
+# vim: set ts=4 sw=4:
+table inet filter {
+
+    chain my_filter {
+        tcp dport { http, https } accept \
+        comment "Accept HTTP traffic"
+    }
+
+}
diff --git a/bundles/firewall/files/etc/nftables.d/80-podman.nft b/bundles/firewall/files/etc/nftables.d/80-podman.nft
new file mode 100644
index 0000000..90142bc
--- /dev/null
+++ b/bundles/firewall/files/etc/nftables.d/80-podman.nft
@@ -0,0 +1,15 @@
+#!/usr/sbin/nft -f
+# vim: set ts=4 sw=4:
+table inet filter {
+
+    # HACK: this would not be necessary if not for the two chains being
+    # incompatible
+    chain my_filter {
+        ip daddr 10.89.0.0/24 ct state established,related accept \
+        comment "Accept incoming podman traffic"
+
+        ip saddr 10.89.0.0/24 accept \
+        comment "Accept outgoing podman traffic"
+    }
+
+}
diff --git a/bundles/firewall/files/etc/nftables.nft b/bundles/firewall/files/etc/nftables.nft
new file mode 100644
index 0000000..35b10dd
--- /dev/null
+++ b/bundles/firewall/files/etc/nftables.nft
@@ -0,0 +1,21 @@
+#!/usr/sbin/nft -f
+# vim: set ts=4 sw=4:
+# You can find examples in /usr/share/nftables/.
+
+# Clear all prior state
+flush ruleset
+
+# Basic IPv4/IPv6 stateful firewall for server/workstation.
+table inet filter {
+
+	chain input   { type filter hook input priority 0;   policy drop; }
+	chain forward { type filter hook forward priority 0; policy drop; }
+	chain output  { type filter hook output priority 0;  policy accept; }
+
+}
+
+# The state of stateful objects saved on the nftables service stop.
+include "/var/lib/nftables/*.nft"
+
+# Rules
+include "/etc/nftables.d/*.nft"
diff --git a/bundles/firewall/items.py b/bundles/firewall/items.py
new file mode 100644
index 0000000..1949d13
--- /dev/null
+++ b/bundles/firewall/items.py
@@ -0,0 +1,43 @@
+if node.os != "alpine":
+    raise BundleError(f"{node.name}: OS {node.os} is not supported.")
+
+from os.path import join
+
+actions = {
+    "restart_container_bundle": {
+        "command": "s6-rc -bt 30000 stop containers && s6-rc -bt 30000 start containers",
+        "triggered": True,
+    }
+}
+
+# TODO: fix on shimakaze, missing rules for photos./tv.
+svc_openrc = {
+    "nftables": {
+        "runlevel": "boot",
+        "enabled": True,
+        "running": True,
+        "needs": {
+            "pkg_apk:nftables",
+        },
+        "triggers": {
+            # NOTE: required because restart nftables drops all rules and
+            # podman will install rules to enable container networking
+            "action:restart_container_bundle",
+        },
+    },
+}
+
+files = {
+    # overwrite content_type to allowing templating ssh port
+    "/etc/nftables.d/22-ssh.nft": { "content_type": "mako" }
+}
+
+repo.libs.gen.add_files_recursive(
+    files,
+    join(repo.path, "bundles", "firewall", "files"),
+    {
+        "triggers": {
+            "svc_openrc:nftables:restart",
+        },
+    }
+)
diff --git a/bundles/firewall/metadata.py b/bundles/firewall/metadata.py
new file mode 100644
index 0000000..76ba53c
--- /dev/null
+++ b/bundles/firewall/metadata.py
@@ -0,0 +1,7 @@
+defaults = {
+    "apk": {
+        "packages": {
+            "nftables": {},
+        },
+    },
+}