Hello, I'm looking for advices and feedback. I wrote a simple service (reusing the iptables service as a start) that I called "firewall", the purpose is to block all incoming ports and list the ports you want to allow. The point is to allow users to easily manage their firewall without knowing about to use iptables. Most of the time opening a few ports and blocking everything is enough.
However, while this works in its current state, I'm not satisfied of my code and the way it works. - it's not compatible with iptables and not extendable, should I merge this into the iptables service? - I'm defining the configuration file in a long string with map calls and conditions, it looks very ugly. I didn't write much Scheme in my life and I struggled a bit to get the pieces to form the string, this is noticeable in the result - what should happend when you stop the service? I'm currently using a default rules set that keep incoming traffic blocked on every ports but this may not be desirable. Exemple of configuration: (service firewall-service-type (firewall-configuration (udp '(53)) (tcp '(22 70 1965)))) The according iptables -L output: --------- Chain INPUT (policy DROP) target prot opt source destination ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED ACCEPT tcp -- anywhere anywhere tcp dpt:ssh ACCEPT tcp -- anywhere anywhere tcp dpt:gopher ACCEPT tcp -- anywhere anywhere tcp dpt:1965 ACCEPT udp -- anywhere anywhere udp dpt:domain Chain FORWARD (policy DROP) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination -------- Here is my code in its current state ;;; ;;; Firewall ;;; (define %firewall-accept-all-rules (plain-file "firewall-block-all.rules" "*filter :INPUT DROP :FORWARD DROP :OUTPUT ACCEPT -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT COMMIT ")) (define-record-type* <firewall-configuration> firewall-configuration make-firewall-configuration firewall-configuration? (tcp firewall-configuration-tcp (default #f)) (udp firewall-configuration-udp (default #f))) (define firewall-shepherd-service (match-lambda (($ <firewall-configuration> tcp udp) (let* ((iptables-restore (file-append iptables "/sbin/iptables-restore")) (ip6tables-restore (file-append iptables "/sbin/ip6tables-restore")) (custom-rules (plain-file "iptables-defined.rules" (format #f "*filter~%:INPUT DROP~%:FORWARD DROP~%:OUTPUT ACCEPT~%~a~%~a~%~a~%COMMIT~%" "-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT" (if tcp (string-join (map (lambda (tcp) (format #f "-A INPUT -p tcp --dport ~a -j ACCEPT" tcp)) tcp) "\n") "") (if udp (string-join (map (lambda (udp) (format #f "-A INPUT -p udp --dport ~a -j ACCEPT" udp)) udp) "\n") "")))) (ruleset (if (or udp tcp) ;; if no ports defined, use the default ruleset custom-rules %firewall-accept-all-rules))) (shepherd-service (documentation "Easy firewall management") (provision '(firewall)) (start #~(lambda _ (invoke #$iptables-restore #$ruleset) (invoke #$ip6tables-restore #$ruleset))) (stop #~(lambda _ (invoke #$iptables-restore #$%firewall-accept-all-rules) (invoke #$ip6tables-restore #$%firewall-accept-all-rules)))))))) (define firewall-service-type (service-type (name 'firewall) (description "Run @command{iptables-restore}, setting up the specified rules.") (extensions (list (service-extension shepherd-root-service-type (compose list firewall-shepherd-service))))))