about summary refs log tree commit diff
path: root/nixos/modules/services/networking/firewall.nix
diff options
context:
space:
mode:
authorWilliam A. Kennington III <william@wkennington.com>2014-09-16 01:15:10 -0700
committerWilliam A. Kennington III <william@wkennington.com>2014-09-16 15:51:57 -0700
commitec9c4143a7142e28851b9b158a32274675114f2b (patch)
tree234a9295b864b15f979d1f7cb3d7859cf64a6e55 /nixos/modules/services/networking/firewall.nix
parente0b6da1708f6ded9344d3ee33eca812027acbe2a (diff)
downloadnixlib-ec9c4143a7142e28851b9b158a32274675114f2b.tar
nixlib-ec9c4143a7142e28851b9b158a32274675114f2b.tar.gz
nixlib-ec9c4143a7142e28851b9b158a32274675114f2b.tar.bz2
nixlib-ec9c4143a7142e28851b9b158a32274675114f2b.tar.lz
nixlib-ec9c4143a7142e28851b9b158a32274675114f2b.tar.xz
nixlib-ec9c4143a7142e28851b9b158a32274675114f2b.tar.zst
nixlib-ec9c4143a7142e28851b9b158a32274675114f2b.zip
nixos/firewall: Cleanup in case reload fails
Diffstat (limited to 'nixos/modules/services/networking/firewall.nix')
-rw-r--r--nixos/modules/services/networking/firewall.nix365
1 files changed, 195 insertions, 170 deletions
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
index eaf48b9c6e5b..68aac3d30de1 100644
--- a/nixos/modules/services/networking/firewall.nix
+++ b/nixos/modules/services/networking/firewall.nix
@@ -37,6 +37,180 @@ let
       }
     '';
 
+  writeShScript = name: text: let dir = pkgs.writeScriptBin name ''
+    #! ${pkgs.stdenv.shell} -e
+    ${text}
+  ''; in "${dir}/bin/${name}";
+
+  startScript = writeShScript "firewall-start" ''
+    ${helpers}
+
+    # Flush the old firewall rules.  !!! Ideally, updating the
+    # firewall would be atomic.  Apparently that's possible
+    # with iptables-restore.
+    ip46tables -D INPUT -j nixos-fw 2> /dev/null || true
+    for chain in nixos-fw nixos-fw-accept nixos-fw-log-refuse nixos-fw-refuse FW_REFUSE; do
+      ip46tables -F "$chain" 2> /dev/null || true
+      ip46tables -X "$chain" 2> /dev/null || true
+    done
+
+
+    # The "nixos-fw-accept" chain just accepts packets.
+    ip46tables -N nixos-fw-accept
+    ip46tables -A nixos-fw-accept -j ACCEPT
+
+
+    # The "nixos-fw-refuse" chain rejects or drops packets.
+    ip46tables -N nixos-fw-refuse
+
+    ${if cfg.rejectPackets then ''
+      # Send a reset for existing TCP connections that we've
+      # somehow forgotten about.  Send ICMP "port unreachable"
+      # for everything else.
+      ip46tables -A nixos-fw-refuse -p tcp ! --syn -j REJECT --reject-with tcp-reset
+      ip46tables -A nixos-fw-refuse -j REJECT
+    '' else ''
+      ip46tables -A nixos-fw-refuse -j DROP
+    ''}
+
+
+    # The "nixos-fw-log-refuse" chain performs logging, then
+    # jumps to the "nixos-fw-refuse" chain.
+    ip46tables -N nixos-fw-log-refuse
+
+    ${optionalString cfg.logRefusedConnections ''
+      ip46tables -A nixos-fw-log-refuse -p tcp --syn -j LOG --log-level info --log-prefix "rejected connection: "
+    ''}
+    ${optionalString (cfg.logRefusedPackets && !cfg.logRefusedUnicastsOnly) ''
+      ip46tables -A nixos-fw-log-refuse -m pkttype --pkt-type broadcast \
+        -j LOG --log-level info --log-prefix "rejected broadcast: "
+      ip46tables -A nixos-fw-log-refuse -m pkttype --pkt-type multicast \
+        -j LOG --log-level info --log-prefix "rejected multicast: "
+    ''}
+    ip46tables -A nixos-fw-log-refuse -m pkttype ! --pkt-type unicast -j nixos-fw-refuse
+    ${optionalString cfg.logRefusedPackets ''
+      ip46tables -A nixos-fw-log-refuse \
+        -j LOG --log-level info --log-prefix "rejected packet: "
+    ''}
+    ip46tables -A nixos-fw-log-refuse -j nixos-fw-refuse
+
+
+    # The "nixos-fw" chain does the actual work.
+    ip46tables -N nixos-fw
+
+    # Perform a reverse-path test to refuse spoofers
+    # For now, we just drop, as the raw table doesn't have a log-refuse yet
+    ${optionalString (kernelHasRPFilter && cfg.checkReversePath) ''
+      if ! ip46tables -A PREROUTING -t raw -m rpfilter --invert -j DROP; then
+        echo "<2>failed to initialise rpfilter support" >&2
+      fi
+    ''}
+
+    # Accept all traffic on the trusted interfaces.
+    ${flip concatMapStrings cfg.trustedInterfaces (iface: ''
+      ip46tables -A nixos-fw -i ${iface} -j nixos-fw-accept
+    '')}
+
+    # Accept packets from established or related connections.
+    ip46tables -A nixos-fw -m conntrack --ctstate ESTABLISHED,RELATED -j nixos-fw-accept
+
+    # Accept connections to the allowed TCP ports.
+    ${concatMapStrings (port:
+        ''
+          ip46tables -A nixos-fw -p tcp --dport ${toString port} -j nixos-fw-accept
+        ''
+      ) cfg.allowedTCPPorts
+    }
+
+    # Accept connections to the allowed TCP port ranges.
+    ${concatMapStrings (rangeAttr:
+        let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
+        ''
+          ip46tables -A nixos-fw -p tcp --dport ${range} -j nixos-fw-accept
+        ''
+      ) cfg.allowedTCPPortRanges
+    }
+
+    # Accept packets on the allowed UDP ports.
+    ${concatMapStrings (port:
+        ''
+          ip46tables -A nixos-fw -p udp --dport ${toString port} -j nixos-fw-accept
+        ''
+      ) cfg.allowedUDPPorts
+    }
+
+    # Accept packets on the allowed UDP port ranges.
+    ${concatMapStrings (rangeAttr:
+        let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
+        ''
+          ip46tables -A nixos-fw -p udp --dport ${range} -j nixos-fw-accept
+        ''
+      ) cfg.allowedUDPPortRanges
+    }
+
+    # Accept IPv4 multicast.  Not a big security risk since
+    # probably nobody is listening anyway.
+    #iptables -A nixos-fw -d 224.0.0.0/4 -j nixos-fw-accept
+
+    # Optionally respond to ICMPv4 pings.
+    ${optionalString cfg.allowPing ''
+      iptables -w -A nixos-fw -p icmp --icmp-type echo-request ${optionalString (cfg.pingLimit != null)
+        "-m limit ${cfg.pingLimit} "
+      }-j nixos-fw-accept
+    ''}
+
+    # Accept all ICMPv6 messages except redirects and node
+    # information queries (type 139).  See RFC 4890, section
+    # 4.4.
+    ${optionalString config.networking.enableIPv6 ''
+      ip6tables -A nixos-fw -p icmpv6 --icmpv6-type redirect -j DROP
+      ip6tables -A nixos-fw -p icmpv6 --icmpv6-type 139 -j DROP
+      ip6tables -A nixos-fw -p icmpv6 -j nixos-fw-accept
+    ''}
+
+    ${cfg.extraCommands}
+
+    # Reject/drop everything else.
+    ip46tables -A nixos-fw -j nixos-fw-log-refuse
+
+
+    # Enable the firewall.
+    ip46tables -A INPUT -j nixos-fw
+  '';
+
+  stopScript = writeShScript "firewall-stop" ''
+    ${helpers}
+
+    # Clean up in case reload fails
+    ip46tables -D INPUT -j nixos-drop 2>/dev/null || true
+
+    # Clean up after added ruleset
+    ip46tables -D INPUT -j nixos-fw 2>/dev/null || true
+
+    ${cfg.extraStopCommands}
+  '';
+
+  reloadScript = writeShScript "firewall-reload" ''
+    ${helpers}
+
+    # Create a unique drop rule
+    ip46tables -D INPUT -j nixos-drop 2>/dev/null || true
+    ip46tables -F nixos-drop 2>/dev/null || true
+    ip46tables -X nixos-drop 2>/dev/null || true
+    ip46tables -N nixos-drop
+    ip46tables -A nixos-drop -j DROP
+
+    # Don't allow traffic to leak out until the script has completed
+    ip46tables -A INPUT -j nixos-drop
+    if ${startScript}; then
+      ip46tables -D INPUT -j nixos-drop 2>/dev/null || true
+    else
+      echo "Failed to reload firewall... Stopping"
+      ${stopScript}
+      exit 1
+    fi
+  '';
+
   kernelPackages = config.boot.kernelPackages;
 
   kernelHasRPFilter = kernelPackages.kernel.features.netfilterRPFilter or false;
@@ -248,7 +422,7 @@ in
         ''
           Additional shell commands executed as part of the firewall
           shutdown script.  These are executed just after the removal
-          of the nixos input rule.
+          of the nixos input rule, or if the service enters a failed state.
         '';
     };
 
@@ -276,177 +450,28 @@ in
                      message = "This kernel does not support disabling conntrack helpers"; }
                  ];
 
-    systemd.services.firewall = rec
-      { description = "Firewall";
-
-        wantedBy = [ "network.target" ];
-        after = [ "network-interfaces.target" "systemd-modules-load.service" ];
-
-        path = [ pkgs.iptables ];
-
-        # FIXME: this module may also try to load kernel modules, but
-        # containers don't have CAP_SYS_MODULE. So the host system had
-        # better have all necessary modules already loaded.
-        unitConfig.ConditionCapability = "CAP_NET_ADMIN";
-
-        reloadIfChanged = true;
-
-        serviceConfig = {
-          Type = "oneshot";
-          RemainAfterExit = true;
-        };
-
-        script =
-          ''
-            ${helpers}
-
-            # Flush the old firewall rules.  !!! Ideally, updating the
-            # firewall would be atomic.  Apparently that's possible
-            # with iptables-restore.
-            ip46tables -D INPUT -j nixos-fw 2> /dev/null || true
-            for chain in nixos-fw nixos-fw-accept nixos-fw-log-refuse nixos-fw-refuse FW_REFUSE; do
-              ip46tables -F "$chain" 2> /dev/null || true
-              ip46tables -X "$chain" 2> /dev/null || true
-            done
-
-
-            # The "nixos-fw-accept" chain just accepts packets.
-            ip46tables -N nixos-fw-accept
-            ip46tables -A nixos-fw-accept -j ACCEPT
-
-
-            # The "nixos-fw-refuse" chain rejects or drops packets.
-            ip46tables -N nixos-fw-refuse
-
-            ${if cfg.rejectPackets then ''
-              # Send a reset for existing TCP connections that we've
-              # somehow forgotten about.  Send ICMP "port unreachable"
-              # for everything else.
-              ip46tables -A nixos-fw-refuse -p tcp ! --syn -j REJECT --reject-with tcp-reset
-              ip46tables -A nixos-fw-refuse -j REJECT
-            '' else ''
-              ip46tables -A nixos-fw-refuse -j DROP
-            ''}
-
-
-            # The "nixos-fw-log-refuse" chain performs logging, then
-            # jumps to the "nixos-fw-refuse" chain.
-            ip46tables -N nixos-fw-log-refuse
-
-            ${optionalString cfg.logRefusedConnections ''
-              ip46tables -A nixos-fw-log-refuse -p tcp --syn -j LOG --log-level info --log-prefix "rejected connection: "
-            ''}
-            ${optionalString (cfg.logRefusedPackets && !cfg.logRefusedUnicastsOnly) ''
-              ip46tables -A nixos-fw-log-refuse -m pkttype --pkt-type broadcast \
-                -j LOG --log-level info --log-prefix "rejected broadcast: "
-              ip46tables -A nixos-fw-log-refuse -m pkttype --pkt-type multicast \
-                -j LOG --log-level info --log-prefix "rejected multicast: "
-            ''}
-            ip46tables -A nixos-fw-log-refuse -m pkttype ! --pkt-type unicast -j nixos-fw-refuse
-            ${optionalString cfg.logRefusedPackets ''
-              ip46tables -A nixos-fw-log-refuse \
-                -j LOG --log-level info --log-prefix "rejected packet: "
-            ''}
-            ip46tables -A nixos-fw-log-refuse -j nixos-fw-refuse
-
-
-            # The "nixos-fw" chain does the actual work.
-            ip46tables -N nixos-fw
-
-            # Perform a reverse-path test to refuse spoofers
-            # For now, we just drop, as the raw table doesn't have a log-refuse yet
-            ${optionalString (kernelHasRPFilter && cfg.checkReversePath) ''
-              if ! ip46tables -A PREROUTING -t raw -m rpfilter --invert -j DROP; then
-                echo "<2>failed to initialise rpfilter support" >&2
-              fi
-            ''}
-
-            # Accept all traffic on the trusted interfaces.
-            ${flip concatMapStrings cfg.trustedInterfaces (iface: ''
-              ip46tables -A nixos-fw -i ${iface} -j nixos-fw-accept
-            '')}
-
-            # Accept packets from established or related connections.
-            ip46tables -A nixos-fw -m conntrack --ctstate ESTABLISHED,RELATED -j nixos-fw-accept
-
-            # Accept connections to the allowed TCP ports.
-            ${concatMapStrings (port:
-                ''
-                  ip46tables -A nixos-fw -p tcp --dport ${toString port} -j nixos-fw-accept
-                ''
-              ) cfg.allowedTCPPorts
-            }
-
-            # Accept connections to the allowed TCP port ranges.
-            ${concatMapStrings (rangeAttr:
-                let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
-                ''
-                  ip46tables -A nixos-fw -p tcp --dport ${range} -j nixos-fw-accept
-                ''
-              ) cfg.allowedTCPPortRanges
-            }
-
-            # Accept packets on the allowed UDP ports.
-            ${concatMapStrings (port:
-                ''
-                  ip46tables -A nixos-fw -p udp --dport ${toString port} -j nixos-fw-accept
-                ''
-              ) cfg.allowedUDPPorts
-            }
-
-            # Accept packets on the allowed UDP port ranges.
-            ${concatMapStrings (rangeAttr:
-                let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
-                ''
-                  ip46tables -A nixos-fw -p udp --dport ${range} -j nixos-fw-accept
-                ''
-              ) cfg.allowedUDPPortRanges
-            }
-
-            # Accept IPv4 multicast.  Not a big security risk since
-            # probably nobody is listening anyway.
-            #iptables -A nixos-fw -d 224.0.0.0/4 -j nixos-fw-accept
-
-            # Optionally respond to ICMPv4 pings.
-            ${optionalString cfg.allowPing ''
-              iptables -w -A nixos-fw -p icmp --icmp-type echo-request ${optionalString (cfg.pingLimit != null)
-                "-m limit ${cfg.pingLimit} "
-              }-j nixos-fw-accept
-            ''}
-
-            # Accept all ICMPv6 messages except redirects and node
-            # information queries (type 139).  See RFC 4890, section
-            # 4.4.
-            ${optionalString config.networking.enableIPv6 ''
-              ip6tables -A nixos-fw -p icmpv6 --icmpv6-type redirect -j DROP
-              ip6tables -A nixos-fw -p icmpv6 --icmpv6-type 139 -j DROP
-              ip6tables -A nixos-fw -p icmpv6 -j nixos-fw-accept
-            ''}
-
-            ${cfg.extraCommands}
-
-            # Reject/drop everything else.
-            ip46tables -A nixos-fw -j nixos-fw-log-refuse
-
-
-            # Enable the firewall.
-            ip46tables -A INPUT -j nixos-fw
-          '';
-
-        reload = ''
-          ${helpers}
-          ip46tables -A INPUT -j DROP
-          ${script}
-          ip46tables -D INPUT -j DROP || true # extraCommands might delete the above rule and cause this to fail
-        '';
+    systemd.services.firewall = {
+      description = "Firewall";
+      wantedBy = [ "network.target" ];
+      after = [ "network-interfaces.target" "systemd-modules-load.service" ];
 
-        postStop =
-          ''
-            ${helpers}
-            ip46tables -D INPUT -j nixos-fw || true
-            ${cfg.extraStopCommands}
-          '';
+      path = [ pkgs.iptables ];
+
+      # FIXME: this module may also try to load kernel modules, but
+      # containers don't have CAP_SYS_MODULE. So the host system had
+      # better have all necessary modules already loaded.
+      unitConfig.ConditionCapability = "CAP_NET_ADMIN";
+
+      reloadIfChanged = true;
+
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        ExecStart = "@${startScript} firewall-start";
+        ExecReload = "@${reloadScript} firewall-reload";
+        ExecStop = "@${stopScript} firewall-stop";
       };
+    };
 
   };