commit 7f1712503946767b25bb966f8c807f7cbd3b524f
parent 4bb081ffcf355b7560e5e22763ecffb19f033bb0
Author: Daniel Barlow <dan@telent.net>
Date: Mon, 10 Feb 2025 00:42:27 +0000
firewall: update zones with interface names as they appear
Diffstat:
2 files changed, 78 insertions(+), 4 deletions(-)
diff --git a/modules/firewall/ifwatch.fnl b/modules/firewall/ifwatch.fnl
@@ -0,0 +1,63 @@
+(local { : system : join } (require :anoia))
+(local svc (require :anoia.svc))
+(local ll (require :lualinux))
+
+;; ifwatch.fnl wan:/nix/store/eee/.outputs/ifname wan:/nix/store/ffff/.outputs/ifname lan:/nix/store/abc123/.outputs/ifname
+
+(fn parse-options [cmdline]
+ (let [interfaces {}]
+ (each [_ s (ipairs cmdline)]
+ (let [(zone service) (string.match s "(.-):(.+)")]
+ (tset interfaces (svc.open service) zone)))
+ interfaces))
+
+(local POLLIN 1)
+(local POLLHUP 16)
+
+(fn zone-contents [interfaces]
+ (accumulate [zones {}
+ intf zone (pairs interfaces)]
+ (let [ifs (or (. zones zone) [])]
+ (table.insert ifs (intf:output "ifname"))
+ (tset zones zone ifs)
+ zones)))
+
+(fn wait-for-change [interfaces]
+ (let [pollfds (icollect [k _ (pairs interfaces)]
+ (bor (lshift (k:fileno) 32)
+ (lshift (bor POLLIN POLLHUP) 16)))]
+ (ll.poll pollfds)))
+
+(fn fail [msg]
+ (io.stderr:write (.. "ERROR: " msg "\n")))
+
+(macro with-popen [[handle command mode] & body]
+ `(let [,handle (assert (io.popen ,command ,mode))
+ val# (do ,(unpack body))]
+ (case (: ,handle :close)
+ ok# val#
+ (nil :exit code#) (fail (.. ,command " exited " code#))
+ (nil :signal sig#) (fail (.. ,command " killed by " sig#)))))
+
+(fn update-zone-str [zone ifnames]
+ (if (> (# ifnames) 0)
+ (..
+ "flush set ip table-ip " zone " ; add element ip table-ip " zone " { " (table.concat ifnames ", ") " };\n"
+ "flush set ip6 table-ip6 " zone " ; add element ip6 table-ip6 " zone " { " (table.concat ifnames ", ") " };\n"
+ )
+ (..
+ "flush set ip table-ip " zone "; \n"
+ "flush set ip6 table-ip6 " zone "; \n"
+ )))
+
+(fn run []
+ (while true
+ (let [interfaces (parse-options arg)]
+ (with-popen [nft "nft -f -" :w]
+ (each [zone ifnames (pairs (zone-contents interfaces))]
+ (nft:write (update-zone-str zone ifnames))))
+ (wait-for-change interfaces)
+ (each [k _ (pairs interfaces)]
+ (k:close)))))
+
+{ : run }
diff --git a/modules/firewall/service.nix b/modules/firewall/service.nix
@@ -3,11 +3,17 @@
, lib
, firewallgen
, nftables
+, writeFennel
+, anoia
+, lualinux
+, linotify
}:
{ rules, extraRules, zones }:
let
- inherit (liminix.services) longrun ; # oneshot;
- inherit (lib.attrsets) mapAttrs' nameValuePair;
+ inherit (liminix.services) longrun;
+ inherit (lib.attrsets) mapAttrs' nameValuePair mapAttrsToList;
+ inherit (lib.strings) concatStringsSep;
+ inherit (lib.lists) flatten;
mkSet = family : name :
nameValuePair
"${name}-set-${family}"
@@ -20,12 +26,17 @@ let
(mapAttrs' (n : _ : mkSet "ip6" n) zones);
allRules = lib.recursiveUpdate extraRules (lib.recursiveUpdate (builtins.trace sets sets) rules);
script = firewallgen "firewall1.nft" allRules;
-
+ ifwatch = writeFennel "ifwatch" {
+ packages = [anoia lualinux linotify];
+ mainFunction = "run";
+ } ./ifwatch.fnl ;
+ watchArg = z : intfs : map (i: "${z}:${i}/.outputs") intfs;
in longrun {
name = "firewall";
run = ''
${script}
- while : ; do sleep 86400 ; done
+ PATH=${nftables}/bin:$PATH
+ ${ifwatch} ${concatStringsSep " " (flatten (mapAttrsToList watchArg zones))}
'';
finish = "${nftables}/bin/nft flush ruleset";
}