summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVivien Kraus <vivien@planete-kraus.eu>2024-01-04 18:53:45 +0100
committerVivien Kraus <vivien@planete-kraus.eu>2024-01-04 18:53:45 +0100
commit4574bcb6b8544ea509a707a760eb02986139b576 (patch)
tree378444b0b9066c607f982b503e98174b9168237b
Add the simple-firewall-service-type
-rw-r--r--.guix-channel3
-rw-r--r--guix/vkraus/services/simple-firewall.scm160
2 files changed, 163 insertions, 0 deletions
diff --git a/.guix-channel b/.guix-channel
new file mode 100644
index 0000000..8cec433
--- /dev/null
+++ b/.guix-channel
@@ -0,0 +1,3 @@
+(channel
+ (version 0)
+ (directory "guix"))
diff --git a/guix/vkraus/services/simple-firewall.scm b/guix/vkraus/services/simple-firewall.scm
new file mode 100644
index 0000000..a5848c4
--- /dev/null
+++ b/guix/vkraus/services/simple-firewall.scm
@@ -0,0 +1,160 @@
+(define-module (vkraus services simple-firewall)
+ #:use-module ((gnu services) #:select (service-type service-extension))
+ #:use-module ((gnu packages linux) #:select (nftables))
+ #:use-module (gnu services shepherd)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (srfi srfi-9)
+ #:use-module (srfi srfi-9 gnu)
+ #:use-module (ice-9 match)
+ #:declarative? #t
+ #:export
+ (
+ <simple-firewall-configuration>
+ simple-firewall-configuration
+ simple-firewall-configuration?
+ nftables-package opened-tcp-ports opened-udp-ports
+ set-nftables-package set-opened-tcp-ports set-opened-udp-ports
+ merge-firewall-configurations
+ simple-firewall-service-type
+ ))
+
+(define-immutable-record-type <simple-firewall-configuration>
+ (simple-firewall-configuration nft tcp-ports udp-ports)
+ simple-firewall-configuration?
+ (nft nftables-package set-nftables-package)
+ (tcp-ports opened-tcp-ports set-opened-tcp-ports)
+ (udp-ports opened-udp-ports set-opened-udp-ports))
+
+(define (append-if-not-present item list)
+ (if (member item list)
+ list
+ `(,@list ,item)))
+
+(define (remove-duplicates all)
+ (let append ((lst all)
+ (unique '()))
+ (match lst
+ (() unique)
+ ((x lst ...)
+ (append lst (append-if-not-present x unique))))))
+
+(define merge-firewall-configurations
+ (match-lambda*
+ (() (simple-firewall-configuration #f '() '()))
+ ((($ <simple-firewall-configuration> nft tcp udp)
+ cfg ...)
+ (match (apply merge-firewall-configurations cfg)
+ (($ <simple-firewall-configuration> nfts tcps udps)
+ (simple-firewall-configuration
+ (if (and nft nfts)
+ (error "The nftables package is overriden twice. Please choose.")
+ (or nft nfts))
+ (remove-duplicates `(,@tcp ,@tcps))
+ (remove-duplicates `(,@udp ,@udps))))))))
+
+(define port->string
+ (match-lambda
+ ((? symbol? high-level-name)
+ (with-output-to-string
+ (lambda () (display high-level-name))))
+ ((? integer? number)
+ (with-output-to-string
+ (lambda () (display number))))))
+
+(define simple-firewall-shepherd-service
+ (match-lambda
+ (($ <simple-firewall-configuration> nft tcp udp)
+ (let ((config-file
+ (plain-file
+ "simple-firewall-configuration"
+ (format #f
+ "flush ruleset
+
+table inet firewall {
+ chain inbound {
+ # By default, drop all traffic unless it meets a filter
+ # criteria specified by the rules that follow below.
+ type filter hook input priority 0; policy drop;
+
+ # Allow traffic from established and related packets.
+ ct state established,related accept
+
+ # Drop invalid packets.
+ ct state invalid drop
+
+ # Allow loopback traffic.
+ iifname lo accept
+
+ # Allow all ICMP and IGMP traffic, but enforce a rate limit
+ # to help prevent some types of flood attacks.
+ ip protocol icmp limit rate 4/second accept
+ meta l4proto ipv6-icmp limit rate 4/second accept
+ ip protocol igmp limit rate 4/second accept
+
+ # Allow DHCPv6 incoming replies
+ ip6 daddr fe80::/64 udp sport 547 udp dport 546 ct state { new, untracked } accept
+
+ # Allow TCP ports
+ ~atcp dport { ~a } accept
+
+ # Allow UDP ports
+ ~audp dport { ~a } accept
+ }
+
+ chain forward {
+ # Drop everything (assumes this device is not a router)
+ type filter hook forward priority 0; policy drop;
+
+ }
+
+ chain outbound {
+ # Allow all outbound traffic
+ type filter hook output priority 0; policy accept;
+ }
+}
+"
+ (if (null? tcp)
+ "# "
+ "")
+ (string-join
+ (map port->string tcp)
+ ", ")
+ (if (null? udp)
+ "# "
+ "")
+ (string-join
+ (map port->string udp)
+ ", "))))
+ (nft-package
+ (or nft nftables)))
+ (shepherd-service
+ (documentation
+ "Simple firewall that only allows inbound packets to specific ports")
+ (provision '(simple-firewall))
+ (start
+ #~(lambda _
+ (invoke #$(file-append nft-package "/sbin/nft")
+ "--file" #$config-file)))
+ (stop
+ #~(lambda _
+ (invoke #$(file-append nft-package "/sbin/nft")
+ "flush" "ruleset"))))))))
+
+(define simple-firewall-service-type
+ (service-type
+ (name 'simple-firewall)
+ (extensions
+ (list
+ (service-extension
+ shepherd-root-service-type
+ (lambda (cfg)
+ `(,(simple-firewall-shepherd-service cfg))))))
+ (compose
+ (lambda (extensions)
+ (apply append extensions)))
+ (extend
+ (lambda (a bs)
+ (apply merge-firewall-configurations a bs)))
+ (description
+ "A very simple firewall service with some ports opened.")))