summaryrefslogtreecommitdiff
path: root/guix/vkraus/services/simple-firewall.scm
blob: a5848c49c2a19b6a8df2e2fd7736b264a1afb431 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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.")))