liminix

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

demo-firewall.nix (7586B)


      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 
     39   forward-ip6 = {
     40     type = "filter";
     41     family = "ip6";
     42     policy = "drop";
     43     hook = "forward";
     44     rules = [
     45       (drop "ip6 saddr ::1/128") # loopback address [RFC4291]
     46       (drop "ip6 daddr ::1/128")
     47       (drop "ip6 saddr ::FFFF:0:0/96")# IPv4-mapped addresses
     48       (drop "ip6 daddr ::FFFF:0:0/96")
     49       (drop "ip6 saddr fe80::/10") # link-local unicast
     50       (drop "ip6 daddr fe80::/10")
     51       (drop "ip6 saddr fc00::/7") # unique-local addresses
     52       (drop "ip6 daddr fc00::/7")
     53       (drop "ip6 saddr 2001:10::/28") # ORCHID [RFC4843].
     54       (drop "ip6 daddr 2001:10::/28")
     55 
     56       (drop "ip6 saddr fc00::/7") # unique local source
     57       (drop "ip6 daddr fc00::/7") # and/or dst addresses [RFC4193]
     58 
     59       # multicast with wrong scopes
     60       (drop
     61         # dest addr first byte 0xff, low nibble of second byte <= scope
     62         # https://www.mankier.com/8/nft#Payload_Expressions-Raw_Payload_Expression
     63         "@nh,192,8 eq 0xff @nh,204,4 le ${toString mcast-scope}")
     64 
     65       (accept "oifname \"int\" iifname \"ppp0\" meta l4proto udp ct state established,related")
     66       (accept "iifname \"int\" oifname \"ppp0\" meta l4proto udp")
     67 
     68       (accept "meta l4proto icmpv6")
     69       (accept "meta l4proto ah")
     70       (accept "meta l4proto esp")
     71 
     72       # does this ever get used or does the preceding general udp accept
     73       # already grab anything that might get here?
     74       (accept "oifname \"ppp0\" udp dport 500") # IKE Protocol [RFC5996]. haha zyxel
     75       (accept "ip6 nexthdr 139") #  Host Identity Protocol
     76 
     77       ## FIXME no support yet for recs 27-30 Mobility Header
     78 
     79       (accept "oifname \"int\" iifname \"ppp0\" meta l4proto tcp ct state established,related")
     80       (accept "iifname \"int\" oifname \"ppp0\" meta l4proto tcp")
     81 
     82       (accept "oifname \"int\" iifname \"ppp0\" meta l4proto sctp ct state established,related")
     83       (accept "iifname \"int\" oifname \"ppp0\" meta l4proto sctp")
     84 
     85       (accept "oifname \"int\" iifname \"ppp0\" meta l4proto dccp ct state established,related")
     86       (accept "iifname \"int\" oifname \"ppp0\" meta l4proto dccp")
     87 
     88       # we can allow all reasonable inbound, or we can use an explicit
     89       # allowlist to enumerate the endpoints that are allowed to
     90       # accept inbound from the WAN
     91       (if allow-incoming
     92        then accept "oifname \"int\" iifname \"ppp0\""
     93        else "oifname \"int\" iifname \"ppp0\" jump incoming-allowed-ip6"
     94       )
     95       # allow all outbound and any inbound that's part of a
     96       # recognised (outbound-initiated) flow
     97       (accept "oifname \"int\" iifname \"ppp0\" ct state established,related")
     98       (accept "iifname \"int\" oifname \"ppp0\" ")
     99 
    100       "log prefix \"DENIED CHAIN=forward-ip6 \""
    101     ];
    102   };
    103 
    104   input-ip6-lan = {
    105     type = "filter";
    106     family = "ip6";
    107 
    108     rules = [
    109       (accept "udp dport 547") # dhcp, could restrict to daddr ff02::1:2
    110       (accept "udp dport 53") # dns
    111       (accept "tcp dport 22")
    112     ];
    113   };
    114 
    115   input-ip6-wan = {
    116     type = "filter";
    117     family = "ip6";
    118 
    119     rules = [
    120       (accept "udp dport 546") # dhcp client, needed for prefix delegation
    121     ];
    122   };
    123 
    124   input-ip6 = {
    125     type = "filter";
    126     family = "ip6";
    127     policy = "drop";
    128     hook = "input";
    129     rules = [
    130       (accept "meta l4proto icmpv6")
    131       "iifname int jump input-ip6-lan"
    132       "iifname ppp0 jump input-ip6-wan"
    133       (if allow-incoming
    134        then accept "oifname \"int\" iifname \"ppp0\""
    135        else "oifname \"int\" iifname \"ppp0\" jump incoming-allowed-ip6"
    136       )
    137       # how does this even make sense in an input chain?
    138       (accept "oifname \"int\" iifname \"ppp0\"  ct state established,related")
    139       (accept "iifname \"int\" oifname \"ppp0\" ")
    140       "log prefix \"DENIED CHAIN=input-ip6 \""
    141     ];
    142   };
    143 
    144   incoming-allowed-ip6 = {
    145     type = "filter";
    146     family = "ip6";
    147     rules = [
    148       # this is where you put permitted incoming connections
    149       # "oifname \"int\" ip6 daddr 2001:8b0:de3a:40de::e9d tcp dport 22"
    150     ];
    151   };
    152 
    153   nat-tx = {
    154     type = "nat";
    155     hook = "postrouting";
    156     priority = "100";
    157     policy = "accept";
    158     family = "ip";
    159     rules = [
    160       "oifname \"ppp0\" masquerade"
    161     ];
    162   };
    163 
    164   nat-rx = {
    165     type = "nat";
    166     hook = "prerouting";
    167     priority = "-100";
    168     family = "ip";
    169     policy = "accept";
    170     rules = [
    171       # per https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/security_guide/sec-configuring_nat_using_nftables:
    172       # "Even if you do not add a rule to the prerouting chain, the
    173       # nftables framework requires this chain to match incoming
    174       # packet replies. "
    175     ];
    176   };
    177 
    178   # these chains are for rules that have to be present for things to
    179   # basically work at all: for example, the router won't issue DHCP
    180   # unless it's allowed to receive DHCP requests. For "site policy"
    181   # rules you may prefer to use incoming-allowed-ip[46] instead
    182 
    183   input-ip4-lan = {
    184     type = "filter";
    185     family = "ip";
    186 
    187     rules = [
    188       (accept "udp dport 67")   # dhcp
    189       (accept "udp dport 53")   # dns
    190       (accept "tcp dport 22")   # ssh
    191     ];
    192   };
    193 
    194   input-ip4-wan = {
    195     type = "filter";
    196     family = "ip";
    197 
    198     rules = [
    199       (accept "udp sport 53")
    200     ];
    201   };
    202 
    203   input-ip4 = {
    204     type = "filter";
    205     family = "ip";
    206     policy = "drop";
    207     hook = "input";
    208     rules = [
    209       "iifname lo accept"
    210       "icmp type { echo-request, echo-reply } accept"
    211       "iifname int jump input-ip4-lan"
    212       "iifname ppp0 jump input-ip4-wan"
    213       "oifname \"int\" iifname \"ppp0\" jump incoming-allowed-ip4"
    214       "ct state established,related accept"
    215       "log prefix \"DENIED CHAIN=input-ip4 \""
    216     ];
    217   };
    218 
    219   forward-ip4 = {
    220     type = "filter";
    221     family = "ip";
    222     policy = "drop";
    223     hook = "forward";
    224     rules = [
    225       "iifname \"int\" accept"
    226       "ct state established,related accept"
    227       "oifname \"int\" iifname \"ppp0\" jump incoming-allowed-ip4"
    228       "log prefix \"DENIED CHAIN=forward-ip4 \""
    229     ];
    230   };
    231 
    232   incoming-allowed-ip4 = {
    233     type = "filter";
    234     family = "ip";
    235     rules = [
    236       # This is where you put permitted incoming connections. If
    237       # you're using NAT and want to forward a port from outside to
    238       # devices on the LAN, then you need a DNAT rule in nat-rx chain
    239       # *and* to accept the packet in this chain (specifying the
    240       # internal (RFC1918) address).
    241     ];
    242   };
    243 
    244 }