gnu: services: Add iptables service.
* gnu/services/networking.scm (<iptables-configuration>): New record type. (iptables-service-type): New variable. * gnu/tests/networking.scm (run-iptables-test): New procedure. (%test-iptables): New variable. * doc/guix.texi (Networking Services): Document it.
This commit is contained in:
		
							parent
							
								
									3e63a83c0f
								
							
						
					
					
						commit
						9926b8f809
					
				
					 3 changed files with 231 additions and 2 deletions
				
			
		|  | @ -11612,6 +11612,54 @@ Thus, it can be instantiated like this: | |||
| @end lisp | ||||
| @end defvr | ||||
| 
 | ||||
| @cindex iptables | ||||
| @defvr {Scheme Variable} iptables-service-type | ||||
| This is the service type to set up an iptables configuration. iptables is a | ||||
| packet filtering framework supported by the Linux kernel.  This service | ||||
| supports configuring iptables for both IPv4 and IPv6.  A simple example | ||||
| configuration rejecting all incoming connections except those to the ssh port | ||||
| 22 is shown below. | ||||
| 
 | ||||
| @lisp | ||||
| (service iptables-service-type | ||||
|          (iptables-configuration | ||||
|           (ipv4-rules (plain-file "iptables.rules" "*filter | ||||
| :INPUT ACCEPT | ||||
| :FORWARD ACCEPT | ||||
| :OUTPUT ACCEPT | ||||
| -A INPUT -p tcp --dport 22 -j ACCEPT | ||||
| -A INPUT -j REJECT --reject-with icmp-port-unreachable | ||||
| COMMIT | ||||
| ")) | ||||
|           (ipv6-rules (plain-file "ip6tables.rules" "*filter | ||||
| :INPUT ACCEPT | ||||
| :FORWARD ACCEPT | ||||
| :OUTPUT ACCEPT | ||||
| -A INPUT -p tcp --dport 22 -j ACCEPT | ||||
| -A INPUT -j REJECT --reject-with icmp6-port-unreachable | ||||
| COMMIT | ||||
| ")))) | ||||
| @end lisp | ||||
| @end defvr | ||||
| 
 | ||||
| @deftp {Data Type} iptables-configuration | ||||
| The data type representing the configuration of iptables. | ||||
| 
 | ||||
| @table @asis | ||||
| @item @code{iptables} (default: @code{iptables}) | ||||
| The iptables package that provides @code{iptables-restore} and | ||||
| @code{ip6tables-restore}. | ||||
| @item @code{ipv4-rules} (default: @code{%iptables-accept-all-rules}) | ||||
| The iptables rules to use.  It will be passed to @code{iptables-restore}. | ||||
| This may be any ``file-like'' object (@pxref{G-Expressions, file-like | ||||
| objects}). | ||||
| @item @code{ipv6-rules} (default: @code{%iptables-accept-all-rules}) | ||||
| The ip6tables rules to use.  It will be passed to @code{ip6tables-restore}. | ||||
| This may be any ``file-like'' object (@pxref{G-Expressions, file-like | ||||
| objects}). | ||||
| @end table | ||||
| @end deftp | ||||
| 
 | ||||
| @cindex NTP | ||||
| @cindex real time clock | ||||
| @deffn {Scheme Procedure} ntp-service [#:ntp @var{ntp}] @ | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
| ;;; Copyright © 2017 Marius Bakke <mbakke@fastmail.com> | ||||
| ;;; Copyright © 2018 Tobias Geerinckx-Rice <me@tobias.gr> | ||||
| ;;; Copyright © 2018 Chris Marusich <cmmarusich@gmail.com> | ||||
| ;;; Copyright © 2018 Arun Isaac <arunisaac@systemreboot.net> | ||||
| ;;; | ||||
| ;;; This file is part of GNU Guix. | ||||
| ;;; | ||||
|  | @ -103,7 +104,14 @@ | |||
|             wpa-supplicant-service-type | ||||
| 
 | ||||
|             openvswitch-service-type | ||||
|             openvswitch-configuration)) | ||||
|             openvswitch-configuration | ||||
| 
 | ||||
|             iptables-configuration | ||||
|             iptables-configuration? | ||||
|             iptables-configuration-iptables | ||||
|             iptables-configuration-ipv4-rules | ||||
|             iptables-configuration-ipv6-rules | ||||
|             iptables-service-type)) | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| ;;; | ||||
|  | @ -1108,4 +1116,50 @@ networking.")))) | |||
| switch designed to enable massive network automation through programmatic | ||||
| extension."))) | ||||
| 
 | ||||
| ;;; | ||||
| ;;; iptables | ||||
| ;;; | ||||
| 
 | ||||
| (define %iptables-accept-all-rules | ||||
|   (plain-file "iptables-accept-all.rules" | ||||
|               "*filter | ||||
| :INPUT ACCEPT | ||||
| :FORWARD ACCEPT | ||||
| :OUTPUT ACCEPT | ||||
| COMMIT | ||||
| ")) | ||||
| 
 | ||||
| (define-record-type* <iptables-configuration> | ||||
|   iptables-configuration make-iptables-configuration iptables-configuration? | ||||
|   (iptables iptables-configuration-iptables | ||||
|             (default iptables)) | ||||
|   (ipv4-rules iptables-configuration-ipv4-rules | ||||
|               (default %iptables-accept-all-rules)) | ||||
|   (ipv6-rules iptables-configuration-ipv6-rules | ||||
|               (default %iptables-accept-all-rules))) | ||||
| 
 | ||||
| (define iptables-shepherd-service | ||||
|   (match-lambda | ||||
|     (($ <iptables-configuration> iptables ipv4-rules ipv6-rules) | ||||
|      (let ((iptables-restore (file-append iptables "/sbin/iptables-restore")) | ||||
|            (ip6tables-restore (file-append iptables "/sbin/ip6tables-restore"))) | ||||
|        (shepherd-service | ||||
|         (documentation "Packet filtering framework") | ||||
|         (provision '(iptables)) | ||||
|         (start #~(lambda _ | ||||
|                    (invoke #$iptables-restore #$ipv4-rules) | ||||
|                    (invoke #$ip6tables-restore #$ipv6-rules))) | ||||
|         (stop #~(lambda _ | ||||
|                   (invoke #$iptables-restore #$%iptables-accept-all-rules) | ||||
|                   (invoke #$ip6tables-restore #$%iptables-accept-all-rules)))))))) | ||||
| 
 | ||||
| (define iptables-service-type | ||||
|   (service-type | ||||
|    (name 'iptables) | ||||
|    (description | ||||
|     "Run @command{iptables-restore}, setting up the specified rules.") | ||||
|    (extensions | ||||
|     (list (service-extension shepherd-root-service-type | ||||
|                              (compose list iptables-shepherd-service)))))) | ||||
| 
 | ||||
| ;;; networking.scm ends here | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| ;;; Copyright © 2017 Thomas Danckaert <post@thomasdanckaert.be> | ||||
| ;;; Copyright © 2017 Marius Bakke <mbakke@fastmail.com> | ||||
| ;;; Copyright © 2018 Chris Marusich <cmmarusich@gmail.com> | ||||
| ;;; Copyright © 2018 Arun Isaac <arunisaac@systemreboot.net> | ||||
| ;;; | ||||
| ;;; This file is part of GNU Guix. | ||||
| ;;; | ||||
|  | @ -29,9 +30,11 @@ | |||
|   #:use-module (guix store) | ||||
|   #:use-module (guix monads) | ||||
|   #:use-module (gnu packages bash) | ||||
|   #:use-module (gnu packages linux) | ||||
|   #:use-module (gnu packages networking) | ||||
|   #:use-module (gnu services shepherd) | ||||
|   #:export (%test-inetd %test-openvswitch %test-dhcpd %test-tor)) | ||||
|   #:use-module (ice-9 match) | ||||
|   #:export (%test-inetd %test-openvswitch %test-dhcpd %test-tor %test-iptables)) | ||||
| 
 | ||||
| (define %inetd-os | ||||
|   ;; Operating system with 2 inetd services. | ||||
|  | @ -434,3 +437,127 @@ subnet 192.168.1.0 netmask 255.255.255.0 { | |||
|    (name "tor") | ||||
|    (description "Test a running Tor daemon configuration.") | ||||
|    (value (run-tor-test)))) | ||||
| 
 | ||||
| (define* (run-iptables-test) | ||||
|   "Run tests of 'iptables-service-type'." | ||||
|   (define iptables-rules | ||||
|     "*filter | ||||
| :INPUT ACCEPT | ||||
| :FORWARD ACCEPT | ||||
| :OUTPUT ACCEPT | ||||
| -A INPUT -p tcp -m tcp --dport 7 -j REJECT --reject-with icmp-port-unreachable | ||||
| COMMIT | ||||
| ") | ||||
| 
 | ||||
|   (define ip6tables-rules | ||||
|     "*filter | ||||
| :INPUT ACCEPT | ||||
| :FORWARD ACCEPT | ||||
| :OUTPUT ACCEPT | ||||
| -A INPUT -p tcp -m tcp --dport 7 -j REJECT --reject-with icmp6-port-unreachable | ||||
| COMMIT | ||||
| ") | ||||
| 
 | ||||
|   (define inetd-echo-port 7) | ||||
| 
 | ||||
|   (define os | ||||
|     (marionette-operating-system | ||||
|      (simple-operating-system | ||||
|       (dhcp-client-service) | ||||
|       (service inetd-service-type | ||||
|                (inetd-configuration | ||||
|                 (entries (list | ||||
|                           (inetd-entry | ||||
|                            (name "echo") | ||||
|                            (socket-type 'stream) | ||||
|                            (protocol "tcp") | ||||
|                            (wait? #f) | ||||
|                            (user "root")))))) | ||||
|       (service iptables-service-type | ||||
|                (iptables-configuration | ||||
|                 (ipv4-rules (plain-file "iptables.rules" iptables-rules)) | ||||
|                 (ipv6-rules (plain-file "ip6tables.rules" ip6tables-rules))))) | ||||
|      #:imported-modules '((gnu services herd)) | ||||
|      #:requirements '(inetd iptables))) | ||||
| 
 | ||||
|   (define test | ||||
|     (with-imported-modules '((gnu build marionette)) | ||||
|       #~(begin | ||||
|           (use-modules (srfi srfi-64) | ||||
|                        (gnu build marionette)) | ||||
|           (define marionette | ||||
|             (make-marionette (list #$(virtual-machine os)))) | ||||
| 
 | ||||
|           (define (dump-iptables iptables-save marionette) | ||||
|             (marionette-eval | ||||
|              `(begin | ||||
|                 (use-modules (ice-9 popen) | ||||
|                              (ice-9 rdelim) | ||||
|                              (ice-9 regex)) | ||||
|                 (call-with-output-string | ||||
|                   (lambda (out) | ||||
|                     (call-with-port | ||||
|                      (open-pipe* OPEN_READ ,iptables-save) | ||||
|                      (lambda (in) | ||||
|                        (let loop ((line (read-line in))) | ||||
|                          ;; iptables-save does not output rules in the exact | ||||
|                          ;; same format we loaded using iptables-restore. It | ||||
|                          ;; adds comments, packet counters, etc. We remove | ||||
|                          ;; these additions. | ||||
|                          (unless (eof-object? line) | ||||
|                            (cond | ||||
|                             ;; Remove comments | ||||
|                             ((string-match "^#" line) #t) | ||||
|                             ;; Remove packet counters | ||||
|                             ((string-match "^:([A-Z]*) ([A-Z]*) .*" line) | ||||
|                              => (lambda (match-record) | ||||
|                                   (format out ":~a ~a~%" | ||||
|                                           (match:substring match-record 1) | ||||
|                                           (match:substring match-record 2)))) | ||||
|                             ;; Pass other lines without modification | ||||
|                             (else (display line out) | ||||
|                                   (newline out))) | ||||
|                            (loop (read-line in))))))))) | ||||
|              marionette)) | ||||
| 
 | ||||
|           (mkdir #$output) | ||||
|           (chdir #$output) | ||||
| 
 | ||||
|           (test-begin "iptables") | ||||
| 
 | ||||
|           (test-equal "iptables-save dumps the same rules that were loaded" | ||||
|             (dump-iptables #$(file-append iptables "/sbin/iptables-save") | ||||
|                            marionette) | ||||
|             #$iptables-rules) | ||||
| 
 | ||||
|           (test-equal "ip6tables-save dumps the same rules that were loaded" | ||||
|             (dump-iptables #$(file-append iptables "/sbin/ip6tables-save") | ||||
|                            marionette) | ||||
|             #$ip6tables-rules) | ||||
| 
 | ||||
|           (test-error "iptables firewall blocks access to inetd echo service" | ||||
|                       'misc-error | ||||
|                       (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 | ||||
|           ;; timeout. Find and fix this issue. | ||||
|           ;; (test-assert "inetd echo service is accessible after iptables firewall is stopped" | ||||
|           ;;   (begin | ||||
|           ;;     (marionette-eval | ||||
|           ;;      '(begin | ||||
|           ;;         (use-modules (gnu services herd)) | ||||
|           ;;         (stop-service 'iptables)) | ||||
|           ;;      marionette) | ||||
|           ;;     (wait-for-tcp-port inetd-echo-port marionette #:timeout 5))) | ||||
| 
 | ||||
|           (test-end) | ||||
|           (exit (= (test-runner-fail-count (test-runner-current)) 0))))) | ||||
| 
 | ||||
|   (gexp->derivation "iptables" test)) | ||||
| 
 | ||||
| (define %test-iptables | ||||
|   (system-test | ||||
|    (name "iptables") | ||||
|    (description "Test a running iptables daemon.") | ||||
|    (value (run-iptables-test)))) | ||||
|  |  | |||
		Reference in a new issue