GNU bug report logs - #78352
[PATCH] services: Modernize and test nftables service.

Previous Next

Package: guix-patches;

Reported by: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>

Date: Sat, 10 May 2025 14:37:01 UTC

Severity: normal

Tags: patch

To reply to this bug, email your comments to 78352 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to gabriel <at> erlikon.ch, ludo <at> gnu.org, maxim.cournoyer <at> gmail.com, guix-patches <at> gnu.org:
bug#78352; Package guix-patches. (Sat, 10 May 2025 14:37:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Maxim Cournoyer <maxim.cournoyer <at> gmail.com>:
New bug report received and forwarded. Copy sent to gabriel <at> erlikon.ch, ludo <at> gnu.org, maxim.cournoyer <at> gmail.com, guix-patches <at> gnu.org. (Sat, 10 May 2025 14:37:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
To: guix-patches <at> gnu.org
Cc: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
Subject: [PATCH] services: Modernize and test nftables service.
Date: Sat, 10 May 2025 23:33:44 +0900
* 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
---

I investigated to also have a validated ruleset file done in a
computed-file:

modified   gnu/services/networking.scm
@@ -2345,6 +2345,16 @@ (define-configuration/no-serialization nftables-configuration
 ruleset rejects all incoming connections except those to TCP port 22, with
 connections from the loopback interface are allowed."))
 
+(define (validated-ruleset nft ruleset)
+  "Check the nftables RULESET.  Return a build error in case RULESET is not
+valid, else a computed-file object of the validated RULESET."
+  (computed-file "nftables.conf"
+                 (with-imported-modules '((guix build utils))
+                   #~(begin
+                       (use-modules (guix build utils))
+                       (invoke #+nft "--check" "--file" #$ruleset)
+                       (copy-file #$ruleset #$output)))))
+
 (define (nftables-shepherd-service config)
   (match-record config <nftables-configuration>
                 (package debug-levels ruleset)
@@ -2359,8 +2369,8 @@ (define (nftables-shepherd-service config)
                                  (list (format #f "--debug=~{~a~^,~}"
                                                debug-levels))
                                  #~())
-                          "--file" #$ruleset)))
+                          "--file" #+(validated-ruleset nft ruleset))))
        (stop #~(lambda _
                  (invoke #$nft "flush" "ruleset")))))))

but 'nft' is not happy to run in the Guix build
environment:

  building /gnu/store/g4czvsmaccx181h395hp4992i0y3rqqx-nftables.conf.drv...
  netlink: Error: cache initialization failed: Operation not permitted

 doc/guix.texi               |  40 +++++++++----
 gnu/services/networking.scm |  49 +++++++++++-----
 gnu/tests/networking.scm    | 113 ++++++++++++++++++++++++++++++++++--
 3 files changed, 172 insertions(+), 30 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 889eab2ab35..2f0cd117a03 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -22606,32 +22606,48 @@ Networking Services
 @end deftp
 
 @cindex nftables
+@cindex firewall, nftables
 @defvar nftables-service-type
-This is the service type to set up a nftables configuration.  nftables is a
-netfilter project that aims to replace the existing iptables, ip6tables,
+This is the service type to set up a nftables configuration.  nftables
+is a netfilter project that aims to replace the iptables, ip6tables,
 arptables and ebtables framework.  It provides a new packet filtering
-framework, a new user-space utility @command{nft}, and a compatibility layer
-for iptables.  This service comes with a default ruleset
-@code{%default-nftables-ruleset} that rejecting all incoming connections
-except those to the ssh port 22.  To use it, simply write:
+framework, a new user-space utility @command{nft}, and a compatibility
+layer for iptables.  This service comes with a default ruleset,
+@code{%default-nftables-ruleset}, that rejects all incoming connections
+except those to the SSH port 22 (TCP).  To use it, simply write:
 
 @lisp
 (service nftables-service-type)
 @end lisp
 @end defvar
 
+@c %start of fragment
+
 @deftp {Data Type} nftables-configuration
-The data type representing the configuration of nftables.
+Available @code{nftables-configuration} fields are:
 
 @table @asis
-@item @code{package} (default: @code{nftables})
-The nftables package that provides @command{nft}.
-@item @code{ruleset} (default: @code{%default-nftables-ruleset})
-The nftables ruleset to use.  This may be any ``file-like'' object
-(@pxref{G-Expressions, file-like objects}).
+@item @code{package} (default: @code{nftables}) (type: file-like)
+The @code{nftables} package to use.
+
+@item @code{debug-levels} (type: maybe-list-of-debug-levels)
+A list of debug levels, for enabling debugging output.  Valid debug
+level values are the @samp{scanner}, @samp{parser}, @samp{eval},
+@samp{netlink}, @samp{mnl}, @samp{proto-ctx}, @samp{segtree} or
+@samp{all} symbols.
+
+@item @code{ruleset} (type: file-like)
+A file-like object containing the complete nftables ruleset.  The
+default ruleset rejects all incoming connections except those to TCP
+port 22, with connections from the loopback interface are allowed.
+
 @end table
+
 @end deftp
 
+
+@c %end of fragment
+
 @cindex NTP (Network Time Protocol), service
 @cindex ntpd, service for the Network Time Protocol daemon
 @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 @@
 ;;; Copyright © 2018 Chris Marusich <cmmarusich <at> gmail.com>
 ;;; Copyright © 2018 Arun Isaac <arunisaac <at> systemreboot.net>
 ;;; Copyright © 2019 Florian Pelz <pelzflorian <at> pelzflorian.de>
-;;; Copyright © 2019, 2021, 2024 Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
+;;; Copyright © 2019, 2021, 2024, 2025 Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
 ;;; Copyright © 2019 Sou Bunnbu <iyzsong <at> member.fsf.org>
 ;;; Copyright © 2019 Alex Griffin <a <at> ajgrf.com>
 ;;; Copyright © 2020 Brice Waegeneire <brice <at> waegenei.re>
@@ -80,6 +80,7 @@ (define-module (gnu services networking)
   #:use-module (srfi srfi-9)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-43)
+  #:use-module (ice-9 format)
   #:use-module (ice-9 match)
   #:use-module (ice-9 string-fun)
   #:use-module (json)
@@ -258,6 +259,7 @@ (define-module (gnu services networking)
             nftables-configuration
             nftables-configuration?
             nftables-configuration-package
+            nftables-configuration-debug-levels
             nftables-configuration-ruleset
             %default-nftables-ruleset
 
@@ -2279,12 +2281,12 @@ (define iptables-service-type
                              (compose list iptables-shepherd-service))))))
 
 ;;;
-;;; nftables
+;;; nftables.
 ;;;
 
 (define %default-nftables-ruleset
-  (plain-file "nftables.conf"
-              "# A simple and safe firewall
+  (plain-file "nftables.conf" "\
+# A simple and safe firewall
 table inet filter {
   chain input {
     type filter hook input priority 0; policy drop;
@@ -2320,25 +2322,44 @@ (define %default-nftables-ruleset
 }
 "))
 
-(define-record-type* <nftables-configuration>
-  nftables-configuration
-  make-nftables-configuration
-  nftables-configuration?
-  (package nftables-configuration-package
-           (default nftables))
-  (ruleset nftables-configuration-ruleset ; file-like object
-           (default %default-nftables-ruleset)))
+(define (debug-level? x)
+  (member x '(scanner parser eval netlink mnl proto-ctx segtree all)))
+
+(define list-of-debug-levels?
+  (list-of debug-level?))
+
+(define-maybe/no-serialization list-of-debug-levels)
+
+(define-configuration/no-serialization nftables-configuration
+  (package
+    (file-like nftables)
+    "The @code{nftables} package to use.")
+  (debug-levels
+   maybe-list-of-debug-levels
+   "A list of debug levels, for enabling debugging output.  Valid debug level values
+are the @samp{scanner}, @samp{parser}, @samp{eval}, @samp{netlink},
+@samp{mnl}, @samp{proto-ctx}, @samp{segtree} or @samp{all} symbols.")
+  (ruleset
+   (file-like %default-nftables-ruleset)
+   "A file-like object containing the complete nftables ruleset.  The default
+ruleset rejects all incoming connections except those to TCP port 22, with
+connections from the loopback interface are allowed."))
 
 (define (nftables-shepherd-service config)
   (match-record config <nftables-configuration>
-    (package ruleset)
+                (package debug-levels ruleset)
     (let ((nft (file-append package "/sbin/nft")))
       (shepherd-service
        (documentation "Packet filtering and classification")
        (actions (list (shepherd-configuration-action ruleset)))
        (provision '(nftables))
        (start #~(lambda _
-                  (invoke #$nft "--file" #$ruleset)))
+                  (invoke #$nft
+                          #$@(if (maybe-value-set? debug-levels)
+                                 (list (format #f "--debug=~{~a~^,~}"
+                                               debug-levels))
+                                 #~())
+                          "--file" #$ruleset)))
        (stop #~(lambda _
                  (invoke #$nft "flush" "ruleset")))))))
 
diff --git a/gnu/tests/networking.scm b/gnu/tests/networking.scm
index 7d54ebba50e..d3966d5f0da 100644
--- a/gnu/tests/networking.scm
+++ b/gnu/tests/networking.scm
@@ -5,6 +5,7 @@
 ;;; Copyright © 2018 Arun Isaac <arunisaac <at> systemreboot.net>
 ;;; Copyright © 2021 Maxime Devos <maximedevos <at> telenet.be>
 ;;; Copyright © 2021, 2023-2024 Ludovic Courtès <ludo <at> gnu.org>
+;;; Copyright © 2025 Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -28,6 +29,7 @@ (define-module (gnu tests networking)
   #:use-module (gnu services)
   #:use-module (gnu services base)
   #:use-module (gnu services networking)
+  #:use-module (gnu services ssh)
   #:use-module (guix gexp)
   #:use-module (guix store)
   #:use-module (guix monads)
@@ -48,6 +50,7 @@ (define-module (gnu tests networking)
             %test-dhcpcd
             %test-tor
             %test-iptables
+            %test-nftables
             %test-ipfs))
 
 
@@ -870,6 +873,8 @@ (define %test-tor
    (description "Test a running Tor daemon configuration.")
    (value (run-tor-test))))
 
+(define %inetd-echo-port 7)
+
 (define* (run-iptables-test)
   "Run tests of 'iptables-service-type'."
   (define iptables-rules
@@ -890,8 +895,6 @@ (define* (run-iptables-test)
 COMMIT
 ")
 
-  (define inetd-echo-port 7)
-
   (define os
     (marionette-operating-system
      (simple-operating-system
@@ -967,7 +970,8 @@ (define* (run-iptables-test)
 
           (test-error "iptables firewall blocks access to inetd echo service"
                       'misc-error
-                      (wait-for-tcp-port inetd-echo-port marionette #:timeout 5))
+                      (wait-for-tcp-port #$%inetd-echo-port marionette
+                                         #:timeout 5))
 
           ;; TODO: This test freezes up at the login prompt without any
           ;; relevant messages on the console. Perhaps it is waiting for some
@@ -979,7 +983,7 @@ (define* (run-iptables-test)
           ;;         (use-modules (gnu services herd))
           ;;         (stop-service 'iptables))
           ;;      marionette)
-          ;;     (wait-for-tcp-port inetd-echo-port marionette #:timeout 5)))
+          ;;     (wait-for-tcp-port #$%inetd-echo-port marionette #:timeout 5)))
 
           (test-end))))
 
@@ -991,6 +995,107 @@ (define %test-iptables
    (description "Test a running iptables daemon.")
    (value (run-iptables-test))))
 
+
+;;;
+;;; nftables.
+;;;
+
+(define (make-nftables-os ruleset)
+  (simple-operating-system
+   (service dhcp-client-service-type)
+   (service inetd-service-type
+            (inetd-configuration
+             (entries (list
+                       (inetd-entry
+                        (name "echo")
+                        (socket-type 'stream)
+                        (protocol "tcp")
+                        (wait? #f)
+                        (user "root"))))))
+   (service openssh-service-type)
+   (service nftables-service-type
+            (nftables-configuration
+             (debug-levels '(all))
+             (ruleset ruleset)))))
+
+(define %default-nftables-ruleset-for-tests
+  ;; This is like the %default-nftables-ruleset, but without allowing any
+  ;; connections from the loopback interface.
+  (plain-file "nftables.conf" "\
+table inet filter {
+  chain input {
+    type filter hook input priority 0; policy drop;
+
+    # early drop of invalid connections
+    ct state invalid drop
+
+    # allow established/related connections
+    ct state { established, related } accept
+
+    # allow from loopback
+    # iif lo accept   # COMMENTED OUT FOR TESTS
+    # drop connections to lo not coming from lo
+    iif != lo ip daddr 127.0.0.1/8 drop
+    iif != lo ip6 daddr ::1/128 drop
+
+    # allow icmp
+    ip protocol icmp accept
+    ip6 nexthdr icmpv6 accept
+
+    # allow ssh
+    tcp dport ssh accept
+
+    # reject everything else
+    reject with icmpx type port-unreachable
+  }
+  chain forward {
+    type filter hook forward priority 0; policy drop;
+  }
+  chain output {
+    type filter hook output priority 0; policy accept;
+  }
+}"))
+
+(define %nftables-os
+  (make-nftables-os %default-nftables-ruleset-for-tests))
+
+(define (run-nftables-test)
+  (define os
+    (marionette-operating-system
+     %nftables-os
+     #:imported-modules '((gnu services herd))
+     #:requirements '(inetd nftables ssh)))
+
+  (define test
+    (with-imported-modules '((gnu build marionette))
+      #~(begin
+          (use-modules (gnu build marionette)
+                       (srfi srfi-64))
+          (define marionette
+            (make-marionette (list #$(virtual-machine os))))
+
+          (test-runner-current (system-test-runner #$output))
+          (test-begin "nftables")
+
+          (test-error "nftables blocks access to inetd echo service"
+                      'misc-error
+                      (wait-for-tcp-port #$%inetd-echo-port marionette
+                                         #:timeout 5))
+
+          (test-assert "nftables allows access to SSH TCP port 22"
+            (wait-for-tcp-port 22 marionette))
+
+          (test-end))))
+
+  (gexp->derivation "nftables-test" test))
+
+(define %test-nftables
+  (system-test
+   (name "nftables")
+   (description "Tests that an error is raised when attempting build an OS
+with an invalid nftables ruleset file.")
+   (value (run-nftables-test))))
+
 
 ;;;
 ;;; IPFS service

base-commit: f348d2be2e019fcda44af8ab81073e2f04697a38
-- 
2.49.0





Information forwarded to guix-patches <at> gnu.org:
bug#78352; Package guix-patches. (Mon, 12 May 2025 12:01:02 GMT) Full text and rfc822 format available.

Message #8 received at 78352 <at> debbugs.gnu.org (full text, mbox):

From: Gabriel Wicki <gabriel <at> erlikon.ch>
To: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
Cc: Ludovic Court??s <ludo <at> gnu.org>, 78352 <at> debbugs.gnu.org
Subject: Re: [bug#78352] [PATCH] services: Modernize and test nftables service.
Date: Mon, 12 May 2025 13:59:51 +0200
Hello Maxim

This is the first time I read code from / for gnu/tests but AFAICT this
looks good.  Not sure what it takes for QA to apply your patch and
assure the quality - maybe you do?

Thanks for your time and effort.
gabber




Information forwarded to guix-patches <at> gnu.org:
bug#78352; Package guix-patches. (Mon, 12 May 2025 12:56:02 GMT) Full text and rfc822 format available.

Message #11 received at 78352 <at> debbugs.gnu.org (full text, mbox):

From: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
To: Gabriel Wicki <gabriel <at> erlikon.ch>
Cc: Ludovic Court??s <ludo <at> gnu.org>, 78352 <at> debbugs.gnu.org
Subject: Re: [bug#78352] [PATCH] services: Modernize and test nftables service.
Date: Mon, 12 May 2025 21:55:33 +0900
Hi Gabriel,

Gabriel Wicki <gabriel <at> erlikon.ch> writes:

> Hello Maxim
>
> This is the first time I read code from / for gnu/tests but AFAICT this
> looks good.  Not sure what it takes for QA to apply your patch and
> assure the quality - maybe you do?

I'm not sure too.  I know that Bayfront is having disk space
issues at the moment, so that could be that.

-- 
Thanks,
Maxim




Information forwarded to guix-patches <at> gnu.org:
bug#78352; Package guix-patches. (Mon, 12 May 2025 19:36:02 GMT) Full text and rfc822 format available.

Message #14 received at 78352 <at> debbugs.gnu.org (full text, mbox):

From: Gabriel Wicki <gabriel <at> erlikon.ch>
To: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
Cc: Ludovic Court??s <ludo <at> gnu.org>, 78352 <at> debbugs.gnu.org
Subject: Re: [bug#78352] [PATCH] services: Modernize and test nftables service.
Date: Mon, 12 May 2025 21:34:57 +0200
for whatever reason QA uses the wrong commit as base:
f348d2be2e019fcda44af8ab81073e2f04697a38
vs.
e923c73403b6e0dc888c12c2eaaef450bcdbb632

no idea why that happens, though..




Information forwarded to guix-patches <at> gnu.org:
bug#78352; Package guix-patches. (Tue, 13 May 2025 13:06:01 GMT) Full text and rfc822 format available.

Message #17 received at 78352 <at> debbugs.gnu.org (full text, mbox):

From: Gabriel Wicki <gabriel <at> erlikon.ch>
To: maxim.cournoyer <at> gmail.com, 78352 <at> debbugs.gnu.org
Subject: Patch broken
Date: Tue, 13 May 2025 15:05:35 +0200
Hi Maxim

After some pointer on our IRC i figured it only took a simple rebase to
re-trigger the build of this patch but i seem to be unable to apply it
locally, either.  My attempt fails the same way as QA:

> 128 git … apply -- /home/gabriel/g/my-patches/mcourn-nftables.patch                              
> error: patch fragment without header at line 21: @@ -2345,6 +2345,16 @@                          
> (define-configuration/no-serialization nftables-configuration 

Would you mind sending in an updated patch?

TIA
gabber




This bug report was last modified 2 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.