diff options
| author | Maxim Cournoyer <maxim.cournoyer@gmail.com> | 2025-05-10 22:54:19 +0900 |
|---|---|---|
| committer | Maxim Cournoyer <maxim.cournoyer@gmail.com> | 2025-05-26 13:43:28 +0900 |
| commit | cfa2de2a77df3876061c8d26c104d2ebbae2631b (patch) | |
| tree | 85f0a654fe923bd39d1705939b00bc023a0236c8 | |
| parent | 8c5be5f31c6181eb71212f055b6dad216b5f60f4 (diff) | |
services: Modernize and test nftables service.
* doc/guix.texi (Networking Services) <nftables>: Update doc.
* gnu/services/networking.scm (list-of-debug-levels?):
(debug-level?, maybe-list-of-debug-levels?):
(nftables-configuration): Rewrite using `define-configuration'.
[debug-levels]: New field.
(nftables-shepherd-service): Honor it.
* gnu/tests/networking.scm (%inetd-echo-port): Extract to top level.
(run-iptables-test): Adjust accordingly.
(make-nftables-os): New procedure.
(%default-nftables-ruleset-for-tests): New variable.
(%nftables-os): Likewise.
(%test-nftables): New test.
Change-Id: I2889603342ff6d2be6261c3de6e4fddd9a9bbe2d
| -rw-r--r-- | doc/guix.texi | 40 | ||||
| -rw-r--r-- | gnu/services/networking.scm | 49 | ||||
| -rw-r--r-- | gnu/tests/networking.scm | 113 |
3 files changed, 172 insertions, 30 deletions
diff --git a/doc/guix.texi b/doc/guix.texi index 65c64f022ef..d69e6ed215b 100644 --- a/doc/guix.texi +++ b/doc/guix.texi | |||
| @@ -22632,32 +22632,48 @@ objects}). | |||
| 22632 | @end deftp | 22632 | @end deftp |
| 22633 | 22633 | ||
| 22634 | @cindex nftables | 22634 | @cindex nftables |
| 22635 | @cindex firewall, nftables | ||
| 22635 | @defvar nftables-service-type | 22636 | @defvar nftables-service-type |
| 22636 | This is the service type to set up a nftables configuration. nftables is a | 22637 | This is the service type to set up a nftables configuration. nftables |
| 22637 | netfilter project that aims to replace the existing iptables, ip6tables, | 22638 | is a netfilter project that aims to replace the iptables, ip6tables, |
| 22638 | arptables and ebtables framework. It provides a new packet filtering | 22639 | arptables and ebtables framework. It provides a new packet filtering |
| 22639 | framework, a new user-space utility @command{nft}, and a compatibility layer | 22640 | framework, a new user-space utility @command{nft}, and a compatibility |
| 22640 | for iptables. This service comes with a default ruleset | 22641 | layer for iptables. This service comes with a default ruleset, |
| 22641 | @code{%default-nftables-ruleset} that rejecting all incoming connections | 22642 | @code{%default-nftables-ruleset}, that rejects all incoming connections |
| 22642 | except those to the ssh port 22. To use it, simply write: | 22643 | except those to the SSH port 22 (TCP). To use it, simply write: |
| 22643 | 22644 | ||
| 22644 | @lisp | 22645 | @lisp |
| 22645 | (service nftables-service-type) | 22646 | (service nftables-service-type) |
| 22646 | @end lisp | 22647 | @end lisp |
| 22647 | @end defvar | 22648 | @end defvar |
| 22648 | 22649 | ||
| 22650 | @c %start of fragment | ||
| 22651 | |||
| 22649 | @deftp {Data Type} nftables-configuration | 22652 | @deftp {Data Type} nftables-configuration |
| 22650 | The data type representing the configuration of nftables. | 22653 | Available @code{nftables-configuration} fields are: |
| 22651 | 22654 | ||
| 22652 | @table @asis | 22655 | @table @asis |
| 22653 | @item @code{package} (default: @code{nftables}) | 22656 | @item @code{package} (default: @code{nftables}) (type: file-like) |
| 22654 | The nftables package that provides @command{nft}. | 22657 | The @code{nftables} package to use. |
| 22655 | @item @code{ruleset} (default: @code{%default-nftables-ruleset}) | 22658 | |
| 22656 | The nftables ruleset to use. This may be any ``file-like'' object | 22659 | @item @code{debug-levels} (type: maybe-list-of-debug-levels) |
| 22657 | (@pxref{G-Expressions, file-like objects}). | 22660 | A list of debug levels, for enabling debugging output. Valid debug |
| 22661 | level values are the @samp{scanner}, @samp{parser}, @samp{eval}, | ||
| 22662 | @samp{netlink}, @samp{mnl}, @samp{proto-ctx}, @samp{segtree} or | ||
| 22663 | @samp{all} symbols. | ||
| 22664 | |||
| 22665 | @item @code{ruleset} (type: file-like) | ||
| 22666 | A file-like object containing the complete nftables ruleset. The | ||
| 22667 | default ruleset rejects all incoming connections except those to TCP | ||
| 22668 | port 22, with connections from the loopback interface are allowed. | ||
| 22669 | |||
| 22658 | @end table | 22670 | @end table |
| 22671 | |||
| 22659 | @end deftp | 22672 | @end deftp |
| 22660 | 22673 | ||
| 22674 | |||
| 22675 | @c %end of fragment | ||
| 22676 | |||
| 22661 | @cindex NTP (Network Time Protocol), service | 22677 | @cindex NTP (Network Time Protocol), service |
| 22662 | @cindex ntpd, service for the Network Time Protocol daemon | 22678 | @cindex ntpd, service for the Network Time Protocol daemon |
| 22663 | @cindex real time clock | 22679 | @cindex real time clock |
diff --git a/gnu/services/networking.scm b/gnu/services/networking.scm index 67653e2cbf5..8b7bf668927 100644 --- a/gnu/services/networking.scm +++ b/gnu/services/networking.scm | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | ;;; Copyright © 2018 Chris Marusich <cmmarusich@gmail.com> | 10 | ;;; Copyright © 2018 Chris Marusich <cmmarusich@gmail.com> |
| 11 | ;;; Copyright © 2018 Arun Isaac <arunisaac@systemreboot.net> | 11 | ;;; Copyright © 2018 Arun Isaac <arunisaac@systemreboot.net> |
| 12 | ;;; Copyright © 2019 Florian Pelz <pelzflorian@pelzflorian.de> | 12 | ;;; Copyright © 2019 Florian Pelz <pelzflorian@pelzflorian.de> |
| 13 | ;;; Copyright © 2019, 2021, 2024 Maxim Cournoyer <maxim.cournoyer@gmail.com> | 13 | ;;; Copyright © 2019, 2021, 2024, 2025 Maxim Cournoyer <maxim.cournoyer@gmail.com> |
| 14 | ;;; Copyright © 2019 Sou Bunnbu <iyzsong@member.fsf.org> | 14 | ;;; Copyright © 2019 Sou Bunnbu <iyzsong@member.fsf.org> |
| 15 | ;;; Copyright © 2019 Alex Griffin <a@ajgrf.com> | 15 | ;;; Copyright © 2019 Alex Griffin <a@ajgrf.com> |
| 16 | ;;; Copyright © 2020 Brice Waegeneire <brice@waegenei.re> | 16 | ;;; Copyright © 2020 Brice Waegeneire <brice@waegenei.re> |
| @@ -80,6 +80,7 @@ | |||
| 80 | #:use-module (srfi srfi-9) | 80 | #:use-module (srfi srfi-9) |
| 81 | #:use-module (srfi srfi-26) | 81 | #:use-module (srfi srfi-26) |
| 82 | #:use-module (srfi srfi-43) | 82 | #:use-module (srfi srfi-43) |
| 83 | #:use-module (ice-9 format) | ||
| 83 | #:use-module (ice-9 match) | 84 | #:use-module (ice-9 match) |
| 84 | #:use-module (ice-9 string-fun) | 85 | #:use-module (ice-9 string-fun) |
| 85 | #:use-module (json) | 86 | #:use-module (json) |
| @@ -258,6 +259,7 @@ | |||
| 258 | nftables-configuration | 259 | nftables-configuration |
| 259 | nftables-configuration? | 260 | nftables-configuration? |
| 260 | nftables-configuration-package | 261 | nftables-configuration-package |
| 262 | nftables-configuration-debug-levels | ||
| 261 | nftables-configuration-ruleset | 263 | nftables-configuration-ruleset |
| 262 | %default-nftables-ruleset | 264 | %default-nftables-ruleset |
| 263 | 265 | ||
| @@ -2279,12 +2281,12 @@ COMMIT | |||
| 2279 | (compose list iptables-shepherd-service)))))) | 2281 | (compose list iptables-shepherd-service)))))) |
| 2280 | 2282 | ||
| 2281 | ;;; | 2283 | ;;; |
| 2282 | ;;; nftables | 2284 | ;;; nftables. |
| 2283 | ;;; | 2285 | ;;; |
| 2284 | 2286 | ||
| 2285 | (define %default-nftables-ruleset | 2287 | (define %default-nftables-ruleset |
| 2286 | (plain-file "nftables.conf" | 2288 | (plain-file "nftables.conf" "\ |
| 2287 | "# A simple and safe firewall | 2289 | # A simple and safe firewall |
| 2288 | table inet filter { | 2290 | table inet filter { |
| 2289 | chain input { | 2291 | chain input { |
| 2290 | type filter hook input priority 0; policy drop; | 2292 | type filter hook input priority 0; policy drop; |
| @@ -2320,25 +2322,44 @@ table inet filter { | |||
| 2320 | } | 2322 | } |
| 2321 | ")) | 2323 | ")) |
| 2322 | 2324 | ||
| 2323 | (define-record-type* <nftables-configuration> | 2325 | (define (debug-level? x) |
| 2324 | nftables-configuration | 2326 | (member x '(scanner parser eval netlink mnl proto-ctx segtree all))) |
| 2325 | make-nftables-configuration | 2327 | |
| 2326 | nftables-configuration? | 2328 | (define list-of-debug-levels? |
| 2327 | (package nftables-configuration-package | 2329 | (list-of debug-level?)) |
| 2328 | (default nftables)) | 2330 | |
| 2329 | (ruleset nftables-configuration-ruleset ; file-like object | 2331 | (define-maybe/no-serialization list-of-debug-levels) |
| 2330 | (default %default-nftables-ruleset))) | 2332 | |
| 2333 | (define-configuration/no-serialization nftables-configuration | ||
| 2334 | (package | ||
| 2335 | (file-like nftables) | ||
| 2336 | "The @code{nftables} package to use.") | ||
| 2337 | (debug-levels | ||
| 2338 | maybe-list-of-debug-levels | ||
| 2339 | "A list of debug levels, for enabling debugging output. Valid debug level values | ||
| 2340 | are the @samp{scanner}, @samp{parser}, @samp{eval}, @samp{netlink}, | ||
| 2341 | @samp{mnl}, @samp{proto-ctx}, @samp{segtree} or @samp{all} symbols.") | ||
| 2342 | (ruleset | ||
| 2343 | (file-like %default-nftables-ruleset) | ||
| 2344 | "A file-like object containing the complete nftables ruleset. The default | ||
| 2345 | ruleset rejects all incoming connections except those to TCP port 22, with | ||
| 2346 | connections from the loopback interface are allowed.")) | ||
| 2331 | 2347 | ||
| 2332 | (define (nftables-shepherd-service config) | 2348 | (define (nftables-shepherd-service config) |
| 2333 | (match-record config <nftables-configuration> | 2349 | (match-record config <nftables-configuration> |
| 2334 | (package ruleset) | 2350 | (package debug-levels ruleset) |
| 2335 | (let ((nft (file-append package "/sbin/nft"))) | 2351 | (let ((nft (file-append package "/sbin/nft"))) |
| 2336 | (shepherd-service | 2352 | (shepherd-service |
| 2337 | (documentation "Packet filtering and classification") | 2353 | (documentation "Packet filtering and classification") |
| 2338 | (actions (list (shepherd-configuration-action ruleset))) | 2354 | (actions (list (shepherd-configuration-action ruleset))) |
| 2339 | (provision '(nftables)) | 2355 | (provision '(nftables)) |
| 2340 | (start #~(lambda _ | 2356 | (start #~(lambda _ |
| 2341 | (invoke #$nft "--file" #$ruleset))) | 2357 | (invoke #$nft |
| 2358 | #$@(if (maybe-value-set? debug-levels) | ||
| 2359 | (list (format #f "--debug=~{~a~^,~}" | ||
| 2360 | debug-levels)) | ||
| 2361 | #~()) | ||
| 2362 | "--file" #$ruleset))) | ||
| 2342 | (stop #~(lambda _ | 2363 | (stop #~(lambda _ |
| 2343 | (invoke #$nft "flush" "ruleset"))))))) | 2364 | (invoke #$nft "flush" "ruleset"))))))) |
| 2344 | 2365 | ||
diff --git a/gnu/tests/networking.scm b/gnu/tests/networking.scm index 25f61034c66..2865e6ff350 100644 --- a/gnu/tests/networking.scm +++ b/gnu/tests/networking.scm | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | ;;; Copyright © 2018 Arun Isaac <arunisaac@systemreboot.net> | 5 | ;;; Copyright © 2018 Arun Isaac <arunisaac@systemreboot.net> |
| 6 | ;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be> | 6 | ;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be> |
| 7 | ;;; Copyright © 2021, 2023-2024 Ludovic Courtès <ludo@gnu.org> | 7 | ;;; Copyright © 2021, 2023-2024 Ludovic Courtès <ludo@gnu.org> |
| 8 | ;;; Copyright © 2025 Maxim Cournoyer <maxim.cournoyer@gmail.com> | ||
| 8 | ;;; | 9 | ;;; |
| 9 | ;;; This file is part of GNU Guix. | 10 | ;;; This file is part of GNU Guix. |
| 10 | ;;; | 11 | ;;; |
| @@ -29,6 +30,7 @@ | |||
| 29 | #:use-module (gnu services base) | 30 | #:use-module (gnu services base) |
| 30 | #:use-module (gnu services dns) | 31 | #:use-module (gnu services dns) |
| 31 | #:use-module (gnu services networking) | 32 | #:use-module (gnu services networking) |
| 33 | #:use-module (gnu services ssh) | ||
| 32 | #:use-module (guix gexp) | 34 | #:use-module (guix gexp) |
| 33 | #:use-module (guix store) | 35 | #:use-module (guix store) |
| 34 | #:use-module (guix monads) | 36 | #:use-module (guix monads) |
| @@ -50,6 +52,7 @@ | |||
| 50 | %test-dnsmasq | 52 | %test-dnsmasq |
| 51 | %test-tor | 53 | %test-tor |
| 52 | %test-iptables | 54 | %test-iptables |
| 55 | %test-nftables | ||
| 53 | %test-ipfs)) | 56 | %test-ipfs)) |
| 54 | 57 | ||
| 55 | 58 | ||
| @@ -968,6 +971,8 @@ subnet 192.168.1.0 netmask 255.255.255.0 { | |||
| 968 | (description "Test a running Tor daemon configuration.") | 971 | (description "Test a running Tor daemon configuration.") |
| 969 | (value (run-tor-test)))) | 972 | (value (run-tor-test)))) |
| 970 | 973 | ||
| 974 | (define %inetd-echo-port 7) | ||
| 975 | |||
| 971 | (define* (run-iptables-test) | 976 | (define* (run-iptables-test) |
| 972 | "Run tests of 'iptables-service-type'." | 977 | "Run tests of 'iptables-service-type'." |
| 973 | (define iptables-rules | 978 | (define iptables-rules |
| @@ -988,8 +993,6 @@ COMMIT | |||
| 988 | COMMIT | 993 | COMMIT |
| 989 | ") | 994 | ") |
| 990 | 995 | ||
| 991 | (define inetd-echo-port 7) | ||
| 992 | |||
| 993 | (define os | 996 | (define os |
| 994 | (marionette-operating-system | 997 | (marionette-operating-system |
| 995 | (simple-operating-system | 998 | (simple-operating-system |
| @@ -1065,7 +1068,8 @@ COMMIT | |||
| 1065 | 1068 | ||
| 1066 | (test-error "iptables firewall blocks access to inetd echo service" | 1069 | (test-error "iptables firewall blocks access to inetd echo service" |
| 1067 | 'misc-error | 1070 | 'misc-error |
| 1068 | (wait-for-tcp-port inetd-echo-port marionette #:timeout 5)) | 1071 | (wait-for-tcp-port #$%inetd-echo-port marionette |
| 1072 | #:timeout 5)) | ||
| 1069 | 1073 | ||
| 1070 | ;; TODO: This test freezes up at the login prompt without any | 1074 | ;; TODO: This test freezes up at the login prompt without any |
| 1071 | ;; relevant messages on the console. Perhaps it is waiting for some | 1075 | ;; relevant messages on the console. Perhaps it is waiting for some |
| @@ -1077,7 +1081,7 @@ COMMIT | |||
| 1077 | ;; (use-modules (gnu services herd)) | 1081 | ;; (use-modules (gnu services herd)) |
| 1078 | ;; (stop-service 'iptables)) | 1082 | ;; (stop-service 'iptables)) |
| 1079 | ;; marionette) | 1083 | ;; marionette) |
| 1080 | ;; (wait-for-tcp-port inetd-echo-port marionette #:timeout 5))) | 1084 | ;; (wait-for-tcp-port #$%inetd-echo-port marionette #:timeout 5))) |
| 1081 | 1085 | ||
| 1082 | (test-end)))) | 1086 | (test-end)))) |
| 1083 | 1087 | ||
| @@ -1091,6 +1095,107 @@ COMMIT | |||
| 1091 | 1095 | ||
| 1092 | 1096 | ||
| 1093 | ;;; | 1097 | ;;; |
| 1098 | ;;; nftables. | ||
| 1099 | ;;; | ||
| 1100 | |||
| 1101 | (define (make-nftables-os ruleset) | ||
| 1102 | (simple-operating-system | ||
| 1103 | (service dhcp-client-service-type) | ||
| 1104 | (service inetd-service-type | ||
| 1105 | (inetd-configuration | ||
| 1106 | (entries (list | ||
| 1107 | (inetd-entry | ||
| 1108 | (name "echo") | ||
| 1109 | (socket-type 'stream) | ||
| 1110 | (protocol "tcp") | ||
| 1111 | (wait? #f) | ||
| 1112 | (user "root")))))) | ||
| 1113 | (service openssh-service-type) | ||
| 1114 | (service nftables-service-type | ||
| 1115 | (nftables-configuration | ||
| 1116 | (debug-levels '(all)) | ||
| 1117 | (ruleset ruleset))))) | ||
| 1118 | |||
| 1119 | (define %default-nftables-ruleset-for-tests | ||
| 1120 | ;; This is like the %default-nftables-ruleset, but without allowing any | ||
| 1121 | ;; connections from the loopback interface. | ||
| 1122 | (plain-file "nftables.conf" "\ | ||
| 1123 | table inet filter { | ||
| 1124 | chain input { | ||
| 1125 | type filter hook input priority 0; policy drop; | ||
| 1126 | |||
| 1127 | # early drop of invalid connections | ||
| 1128 | ct state invalid drop | ||
| 1129 | |||
| 1130 | # allow established/related connections | ||
| 1131 | ct state { established, related } accept | ||
| 1132 | |||
| 1133 | # allow from loopback | ||
| 1134 | # iif lo accept # COMMENTED OUT FOR TESTS | ||
| 1135 | # drop connections to lo not coming from lo | ||
| 1136 | iif != lo ip daddr 127.0.0.1/8 drop | ||
| 1137 | iif != lo ip6 daddr ::1/128 drop | ||
| 1138 | |||
| 1139 | # allow icmp | ||
| 1140 | ip protocol icmp accept | ||
| 1141 | ip6 nexthdr icmpv6 accept | ||
| 1142 | |||
| 1143 | # allow ssh | ||
| 1144 | tcp dport ssh accept | ||
| 1145 | |||
| 1146 | # reject everything else | ||
| 1147 | reject with icmpx type port-unreachable | ||
| 1148 | } | ||
| 1149 | chain forward { | ||
| 1150 | type filter hook forward priority 0; policy drop; | ||
| 1151 | } | ||
| 1152 | chain output { | ||
| 1153 | type filter hook output priority 0; policy accept; | ||
| 1154 | } | ||
| 1155 | }")) | ||
| 1156 | |||
| 1157 | (define %nftables-os | ||
| 1158 | (make-nftables-os %default-nftables-ruleset-for-tests)) | ||
| 1159 | |||
| 1160 | (define (run-nftables-test) | ||
| 1161 | (define os | ||
| 1162 | (marionette-operating-system | ||
| 1163 | %nftables-os | ||
| 1164 | #:imported-modules '((gnu services herd)) | ||
| 1165 | #:requirements '(inetd nftables ssh))) | ||
| 1166 | |||
| 1167 | (define test | ||
| 1168 | (with-imported-modules '((gnu build marionette)) | ||
| 1169 | #~(begin | ||
| 1170 | (use-modules (gnu build marionette) | ||
| 1171 | (srfi srfi-64)) | ||
| 1172 | (define marionette | ||
| 1173 | (make-marionette (list #$(virtual-machine os)))) | ||
| 1174 | |||
| 1175 | (test-runner-current (system-test-runner #$output)) | ||
| 1176 | (test-begin "nftables") | ||
| 1177 | |||
| 1178 | (test-error "nftables blocks access to inetd echo service" | ||
| 1179 | 'misc-error | ||
| 1180 | (wait-for-tcp-port #$%inetd-echo-port marionette | ||
| 1181 | #:timeout 5)) | ||
| 1182 | |||
| 1183 | (test-assert "nftables allows access to SSH TCP port 22" | ||
| 1184 | (wait-for-tcp-port 22 marionette)) | ||
| 1185 | |||
| 1186 | (test-end)))) | ||
| 1187 | |||
| 1188 | (gexp->derivation "nftables-test" test)) | ||
| 1189 | |||
| 1190 | (define %test-nftables | ||
| 1191 | (system-test | ||
| 1192 | (name "nftables") | ||
| 1193 | (description "Test the nftables service properly allow or block | ||
| 1194 | connection to ports.") | ||
| 1195 | (value (run-nftables-test)))) | ||
| 1196 | |||
| 1197 | |||
| 1198 | ;;; | ||
| 1094 | ;;; IPFS service | 1199 | ;;; IPFS service |
| 1095 | ;;; | 1200 | ;;; |
| 1096 | 1201 | ||
