liminix

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

demo-firewall.nix (5683B)


      1 let
      2   drop = expr : "${expr} drop";
      3   accept = expr : "${expr} accept";
      4   mcast-scope = 8;
      5   allow-incoming = false;
      6 in {
      7   bogons-ip6 = {
      8     type = "filter";
      9     family = "ip6";
     10     policy = "accept";
     11     hook = "prerouting";
     12     rules = [
     13       (drop "ip6 saddr ff00::/8") # multicast saddr is illegal
     14 
     15       (drop "ip6 saddr ::/128") # unspecified address
     16       (drop "ip6 daddr ::/128")
     17       (drop "ip6 saddr 2001:db8::/32") # documentation addresses
     18       (drop "ip6 daddr 2001:db8::/32")
     19 
     20       # I think this means "check FIB for (saddr, iif) to see if we
     21       # could route a packet to that address using that interface",
     22       # and if we can't then it was an inapproppriate source address
     23       # for packets received _from_ said interface
     24       (drop "fib saddr . iif oif eq 0")
     25 
     26       (drop "icmpv6 type router-renumbering")
     27       (drop "icmpv6 type 139") # Node Information Query
     28       (drop "icmpv6 type 140") # Node Information Response
     29       (drop "icmpv6 type 100")
     30       (drop "icmpv6 type 101")
     31       (drop "icmpv6 type 200")
     32       (drop "icmpv6 type 201")
     33       (drop "icmpv6 type 127")
     34       (drop "icmpv6 type 255")
     35       (drop "icmpv6 type destination-unreachable ct state invalid,untracked")
     36     ];
     37   };
     38   forward-ip6 = {
     39     type = "filter";
     40     family = "ip6";
     41     policy = "drop";
     42     hook = "forward";
     43     rules = [
     44       (drop "ip6 saddr ::1/128") # loopback address [RFC4291]
     45       (drop "ip6 daddr ::1/128")
     46       (drop "ip6 saddr ::FFFF:0:0/96")# IPv4-mapped addresses
     47       (drop "ip6 daddr ::FFFF:0:0/96")
     48       (drop "ip6 saddr fe80::/10") # link-local unicast
     49       (drop "ip6 daddr fe80::/10")
     50       (drop "ip6 saddr fc00::/7") # unique-local addresses
     51       (drop "ip6 daddr fc00::/7")
     52       (drop "ip6 saddr 2001:10::/28") # ORCHID [RFC4843].
     53       (drop "ip6 daddr 2001:10::/28")
     54 
     55       (drop "ip6 saddr fc00::/7") # unique local source
     56       (drop "ip6 daddr fc00::/7") # and/or dst addresses [RFC4193]
     57 
     58       # multicast with wrong scopes
     59       (drop
     60         # dest addr first byte 0xff, low nibble of second byte <= scope
     61         # https://www.mankier.com/8/nft#Payload_Expressions-Raw_Payload_Expression
     62         "@nh,192,8 eq 0xff @nh,204,4 le ${toString mcast-scope}")
     63 
     64       (accept "oifname \"int\" iifname \"ppp0\" meta l4proto udp ct state established,related")
     65       (accept "iifname \"int\" oifname \"ppp0\" meta l4proto udp")
     66 
     67       (accept "meta l4proto icmpv6")
     68       (accept "meta l4proto ah")
     69       (accept "meta l4proto esp")
     70 
     71       # does this ever get used or does the preceding general udp accept
     72       # already grab anything that might get here?
     73       (accept "oifname \"ppp0\" udp dport 500") # IKE Protocol [RFC5996]. haha zyxel
     74       (accept "ip6 nexthdr 139") #  Host Identity Protocol
     75 
     76       ## FIXME no support yet for recs 27-30 Mobility Header
     77 
     78       (accept "oifname \"int\" iifname \"ppp0\" meta l4proto tcp ct state established,related")
     79       (accept "iifname \"int\" oifname \"ppp0\" meta l4proto tcp")
     80 
     81       (accept "oifname \"int\" iifname \"ppp0\" meta l4proto sctp ct state established,related")
     82       (accept "iifname \"int\" oifname \"ppp0\" meta l4proto sctp")
     83 
     84       (accept "oifname \"int\" iifname \"ppp0\" meta l4proto dccp ct state established,related")
     85       (accept "iifname \"int\" oifname \"ppp0\" meta l4proto dccp")
     86 
     87       # we can allow all reasonable inbound, or we can use an explicit
     88       # allowlist to enumerate the endpoints that are allowed to
     89       # accept inbound from the WAN
     90       (if allow-incoming
     91        then accept "oifname \"int\" iifname \"ppp0\""
     92        else "oifname \"int\" iifname \"ppp0\" jump incoming-allowed-ip6"
     93       )
     94       # allow all outbound and any inbound that's part of a
     95       # recognised (outbound-initiated) flow
     96       (accept "oifname \"int\" iifname \"ppp0\" ct state established,related")
     97       (accept "iifname \"int\" oifname \"ppp0\" ")
     98     ];
     99   };
    100   input-lan = {
    101     type = "filter";
    102     family = "ip6";
    103 
    104     rules = [
    105       (accept "udp dport 547") # dhcp, could restrict to daddr ff02::1:2
    106       (accept "tcp dport 22")
    107     ];
    108   };
    109 
    110   input-wan = {
    111     type = "filter";
    112     family = "ip6";
    113 
    114     rules = [
    115       (accept "udp dport 546") # dhcp client, needed for prefix delegation
    116     ];
    117   };
    118 
    119   input-ip6 = {
    120     type = "filter";
    121     family = "ip6";
    122     policy = "drop";
    123     hook = "input";
    124     rules = [
    125       (accept "meta l4proto icmpv6")
    126       "iifname int jump input-lan"
    127       "iifname ppp0 jump input-wan"
    128       (if allow-incoming
    129        then accept "oifname \"int\" iifname \"ppp0\""
    130        else "oifname \"int\" iifname \"ppp0\" jump incoming-allowed-ip6"
    131       )
    132       # how does this even make sense in an input chain?
    133       (accept "oifname \"int\" iifname \"ppp0\"  ct state established,related")
    134       (accept "iifname \"int\" oifname \"ppp0\" ")
    135     ];
    136   };
    137 
    138   incoming-allowed-ip6 = {
    139     type = "filter";
    140     family = "ip6";
    141     rules = [
    142       # this is where you put permitted incoming connections
    143       # "oifname \"int\" ip6 daddr 2001:8b0:de3a:40de::e9d tcp dport 22"
    144     ];
    145   };
    146 
    147   nat-tx = {
    148     type = "nat";
    149     hook = "postrouting";
    150     priority = "100";
    151     policy = "accept";
    152     family = "ip";
    153     rules = [
    154       "oifname \"ppp0\" masquerade"
    155     ];
    156   };
    157   nat-rx = {
    158     type = "nat";
    159     hook = "prerouting";
    160     priority = "-100";
    161     family = "ip";
    162     policy = "accept";
    163     rules = [
    164       # per https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/security_guide/sec-configuring_nat_using_nftables:
    165       # "Even if you do not add a rule to the prerouting chain, the
    166       # nftables framework requires this chain to match incoming
    167       # packet replies. "
    168     ];
    169   };
    170 }