summary refs log tree commit diff
path: root/nixos/modules/services/networking/firewall.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services/networking/firewall.nix')
-rw-r--r--nixos/modules/services/networking/firewall.nix588
1 files changed, 0 insertions, 588 deletions
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
deleted file mode 100644
index 4c3052e9e53..00000000000
--- a/nixos/modules/services/networking/firewall.nix
+++ /dev/null
@@ -1,588 +0,0 @@
-/* This module enables a simple firewall.
-
-   The firewall can be customised in arbitrary ways by setting
-   ‘networking.firewall.extraCommands’.  For modularity, the firewall
-   uses several chains:
-
-   - ‘nixos-fw’ is the main chain for input packet processing.
-
-   - ‘nixos-fw-accept’ is called for accepted packets.  If you want
-     additional logging, or want to reject certain packets anyway, you
-     can insert rules at the start of this chain.
-
-   - ‘nixos-fw-log-refuse’ and ‘nixos-fw-refuse’ are called for
-     refused packets.  (The former jumps to the latter after logging
-     the packet.)  If you want additional logging, or want to accept
-     certain packets anyway, you can insert rules at the start of
-     this chain.
-
-   - ‘nixos-fw-rpfilter’ is used as the main chain in the raw table,
-     called from the built-in ‘PREROUTING’ chain.  If the kernel
-     supports it and `cfg.checkReversePath` is set this chain will
-     perform a reverse path filter test.
-
-   - ‘nixos-drop’ is used while reloading the firewall in order to drop
-     all traffic.  Since reloading isn't implemented in an atomic way
-     this'll prevent any traffic from leaking through while reloading
-     the firewall.  However, if the reloading fails, the ‘firewall-stop’
-     script will be called which in return will effectively disable the
-     complete firewall (in the default configuration).
-
-*/
-
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  cfg = config.networking.firewall;
-
-  inherit (config.boot.kernelPackages) kernel;
-
-  kernelHasRPFilter = ((kernel.config.isEnabled or (x: false)) "IP_NF_MATCH_RPFILTER") || (kernel.features.netfilterRPFilter or false);
-
-  helpers = import ./helpers.nix { inherit config lib; };
-
-  writeShScript = name: text: let dir = pkgs.writeScriptBin name ''
-    #! ${pkgs.runtimeShell} -e
-    ${text}
-  ''; in "${dir}/bin/${name}";
-
-  defaultInterface = { default = mapAttrs (name: value: cfg.${name}) commonOptions; };
-  allInterfaces = defaultInterface // cfg.interfaces;
-
-  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; 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 "refused connection: "
-    ''}
-    ${optionalString (cfg.logRefusedPackets && !cfg.logRefusedUnicastsOnly) ''
-      ip46tables -A nixos-fw-log-refuse -m pkttype --pkt-type broadcast \
-        -j LOG --log-level info --log-prefix "refused broadcast: "
-      ip46tables -A nixos-fw-log-refuse -m pkttype --pkt-type multicast \
-        -j LOG --log-level info --log-prefix "refused 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 "refused packet: "
-    ''}
-    ip46tables -A nixos-fw-log-refuse -j nixos-fw-refuse
-
-
-    # The "nixos-fw" chain does the actual work.
-    ip46tables -N nixos-fw
-
-    # Clean up rpfilter rules
-    ip46tables -t raw -D PREROUTING -j nixos-fw-rpfilter 2> /dev/null || true
-    ip46tables -t raw -F nixos-fw-rpfilter 2> /dev/null || true
-    ip46tables -t raw -X nixos-fw-rpfilter 2> /dev/null || true
-
-    ${optionalString (kernelHasRPFilter && (cfg.checkReversePath != false)) ''
-      # Perform a reverse-path test to refuse spoofers
-      # For now, we just drop, as the raw table doesn't have a log-refuse yet
-      ip46tables -t raw -N nixos-fw-rpfilter 2> /dev/null || true
-      ip46tables -t raw -A nixos-fw-rpfilter -m rpfilter --validmark ${optionalString (cfg.checkReversePath == "loose") "--loose"} -j RETURN
-
-      # Allows this host to act as a DHCP4 client without first having to use APIPA
-      iptables -t raw -A nixos-fw-rpfilter -p udp --sport 67 --dport 68 -j RETURN
-
-      # Allows this host to act as a DHCPv4 server
-      iptables -t raw -A nixos-fw-rpfilter -s 0.0.0.0 -d 255.255.255.255 -p udp --sport 68 --dport 67 -j RETURN
-
-      ${optionalString cfg.logReversePathDrops ''
-        ip46tables -t raw -A nixos-fw-rpfilter -j LOG --log-level info --log-prefix "rpfilter drop: "
-      ''}
-      ip46tables -t raw -A nixos-fw-rpfilter -j DROP
-
-      ip46tables -t raw -A PREROUTING -j nixos-fw-rpfilter
-    ''}
-
-    # 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.
-    ${concatStrings (mapAttrsToList (iface: cfg:
-      concatMapStrings (port:
-        ''
-          ip46tables -A nixos-fw -p tcp --dport ${toString port} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
-        ''
-      ) cfg.allowedTCPPorts
-    ) allInterfaces)}
-
-    # Accept connections to the allowed TCP port ranges.
-    ${concatStrings (mapAttrsToList (iface: cfg:
-      concatMapStrings (rangeAttr:
-        let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
-        ''
-          ip46tables -A nixos-fw -p tcp --dport ${range} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
-        ''
-      ) cfg.allowedTCPPortRanges
-    ) allInterfaces)}
-
-    # Accept packets on the allowed UDP ports.
-    ${concatStrings (mapAttrsToList (iface: cfg:
-      concatMapStrings (port:
-        ''
-          ip46tables -A nixos-fw -p udp --dport ${toString port} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
-        ''
-      ) cfg.allowedUDPPorts
-    ) allInterfaces)}
-
-    # Accept packets on the allowed UDP port ranges.
-    ${concatStrings (mapAttrsToList (iface: cfg:
-      concatMapStrings (rangeAttr:
-        let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
-        ''
-          ip46tables -A nixos-fw -p udp --dport ${range} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"}
-        ''
-      ) cfg.allowedUDPPortRanges
-    ) allInterfaces)}
-
-    # 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
-    ''}
-
-    ${optionalString config.networking.enableIPv6 ''
-      # Accept all ICMPv6 messages except redirects and node
-      # information queries (type 139).  See RFC 4890, section
-      # 4.4.
-      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
-
-      # Allow this host to act as a DHCPv6 client
-      ip6tables -A nixos-fw -d fe80::/64 -p udp --dport 546 -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
-
-    ${optionalString (kernelHasRPFilter && (cfg.checkReversePath != false)) ''
-      ip46tables -t raw -D PREROUTING -j nixos-fw-rpfilter 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
-
-    ${cfg.extraStopCommands}
-
-    if ${startScript}; then
-      ip46tables -D INPUT -j nixos-drop 2>/dev/null || true
-    else
-      echo "Failed to reload firewall... Stopping"
-      ${stopScript}
-      exit 1
-    fi
-  '';
-
-  canonicalizePortList =
-    ports: lib.unique (builtins.sort builtins.lessThan ports);
-
-  commonOptions = {
-    allowedTCPPorts = mkOption {
-      type = types.listOf types.port;
-      default = [ ];
-      apply = canonicalizePortList;
-      example = [ 22 80 ];
-      description =
-        ''
-          List of TCP ports on which incoming connections are
-          accepted.
-        '';
-    };
-
-    allowedTCPPortRanges = mkOption {
-      type = types.listOf (types.attrsOf types.port);
-      default = [ ];
-      example = [ { from = 8999; to = 9003; } ];
-      description =
-        ''
-          A range of TCP ports on which incoming connections are
-          accepted.
-        '';
-    };
-
-    allowedUDPPorts = mkOption {
-      type = types.listOf types.port;
-      default = [ ];
-      apply = canonicalizePortList;
-      example = [ 53 ];
-      description =
-        ''
-          List of open UDP ports.
-        '';
-    };
-
-    allowedUDPPortRanges = mkOption {
-      type = types.listOf (types.attrsOf types.port);
-      default = [ ];
-      example = [ { from = 60000; to = 61000; } ];
-      description =
-        ''
-          Range of open UDP ports.
-        '';
-    };
-  };
-
-in
-
-{
-
-  ###### interface
-
-  options = {
-
-    networking.firewall = {
-      enable = mkOption {
-        type = types.bool;
-        default = true;
-        description =
-          ''
-            Whether to enable the firewall.  This is a simple stateful
-            firewall that blocks connection attempts to unauthorised TCP
-            or UDP ports on this machine.  It does not affect packet
-            forwarding.
-          '';
-      };
-
-      package = mkOption {
-        type = types.package;
-        default = pkgs.iptables;
-        defaultText = literalExpression "pkgs.iptables";
-        example = literalExpression "pkgs.iptables-legacy";
-        description =
-          ''
-            The iptables package to use for running the firewall service."
-          '';
-      };
-
-      logRefusedConnections = mkOption {
-        type = types.bool;
-        default = true;
-        description =
-          ''
-            Whether to log rejected or dropped incoming connections.
-            Note: The logs are found in the kernel logs, i.e. dmesg
-            or journalctl -k.
-          '';
-      };
-
-      logRefusedPackets = mkOption {
-        type = types.bool;
-        default = false;
-        description =
-          ''
-            Whether to log all rejected or dropped incoming packets.
-            This tends to give a lot of log messages, so it's mostly
-            useful for debugging.
-            Note: The logs are found in the kernel logs, i.e. dmesg
-            or journalctl -k.
-          '';
-      };
-
-      logRefusedUnicastsOnly = mkOption {
-        type = types.bool;
-        default = true;
-        description =
-          ''
-            If <option>networking.firewall.logRefusedPackets</option>
-            and this option are enabled, then only log packets
-            specifically directed at this machine, i.e., not broadcasts
-            or multicasts.
-          '';
-      };
-
-      rejectPackets = mkOption {
-        type = types.bool;
-        default = false;
-        description =
-          ''
-            If set, refused packets are rejected rather than dropped
-            (ignored).  This means that an ICMP "port unreachable" error
-            message is sent back to the client (or a TCP RST packet in
-            case of an existing connection).  Rejecting packets makes
-            port scanning somewhat easier.
-          '';
-      };
-
-      trustedInterfaces = mkOption {
-        type = types.listOf types.str;
-        default = [ ];
-        example = [ "enp0s2" ];
-        description =
-          ''
-            Traffic coming in from these interfaces will be accepted
-            unconditionally.  Traffic from the loopback (lo) interface
-            will always be accepted.
-          '';
-      };
-
-      allowPing = mkOption {
-        type = types.bool;
-        default = true;
-        description =
-          ''
-            Whether to respond to incoming ICMPv4 echo requests
-            ("pings").  ICMPv6 pings are always allowed because the
-            larger address space of IPv6 makes network scanning much
-            less effective.
-          '';
-      };
-
-      pingLimit = mkOption {
-        type = types.nullOr (types.separatedString " ");
-        default = null;
-        example = "--limit 1/minute --limit-burst 5";
-        description =
-          ''
-            If pings are allowed, this allows setting rate limits
-            on them.  If non-null, this option should be in the form of
-            flags like "--limit 1/minute --limit-burst 5"
-          '';
-      };
-
-      checkReversePath = mkOption {
-        type = types.either types.bool (types.enum ["strict" "loose"]);
-        default = kernelHasRPFilter;
-        defaultText = literalDocBook "<literal>true</literal> if supported by the chosen kernel";
-        example = "loose";
-        description =
-          ''
-            Performs a reverse path filter test on a packet.  If a reply
-            to the packet would not be sent via the same interface that
-            the packet arrived on, it is refused.
-
-            If using asymmetric routing or other complicated routing, set
-            this option to loose mode or disable it and setup your own
-            counter-measures.
-
-            This option can be either true (or "strict"), "loose" (only
-            drop the packet if the source address is not reachable via any
-            interface) or false.  Defaults to the value of
-            kernelHasRPFilter.
-          '';
-      };
-
-      logReversePathDrops = mkOption {
-        type = types.bool;
-        default = false;
-        description =
-          ''
-            Logs dropped packets failing the reverse path filter test if
-            the option networking.firewall.checkReversePath is enabled.
-          '';
-      };
-
-      connectionTrackingModules = mkOption {
-        type = types.listOf types.str;
-        default = [ ];
-        example = [ "ftp" "irc" "sane" "sip" "tftp" "amanda" "h323" "netbios_sn" "pptp" "snmp" ];
-        description =
-          ''
-            List of connection-tracking helpers that are auto-loaded.
-            The complete list of possible values is given in the example.
-
-            As helpers can pose as a security risk, it is advised to
-            set this to an empty list and disable the setting
-            networking.firewall.autoLoadConntrackHelpers unless you
-            know what you are doing. Connection tracking is disabled
-            by default.
-
-            Loading of helpers is recommended to be done through the
-            CT target.  More info:
-            https://home.regit.org/netfilter-en/secure-use-of-helpers/
-          '';
-      };
-
-      autoLoadConntrackHelpers = mkOption {
-        type = types.bool;
-        default = false;
-        description =
-          ''
-            Whether to auto-load connection-tracking helpers.
-            See the description at networking.firewall.connectionTrackingModules
-
-            (needs kernel 3.5+)
-          '';
-      };
-
-      extraCommands = mkOption {
-        type = types.lines;
-        default = "";
-        example = "iptables -A INPUT -p icmp -j ACCEPT";
-        description =
-          ''
-            Additional shell commands executed as part of the firewall
-            initialisation script.  These are executed just before the
-            final "reject" firewall rule is added, so they can be used
-            to allow packets that would otherwise be refused.
-          '';
-      };
-
-      extraPackages = mkOption {
-        type = types.listOf types.package;
-        default = [ ];
-        example = literalExpression "[ pkgs.ipset ]";
-        description =
-          ''
-            Additional packages to be included in the environment of the system
-            as well as the path of networking.firewall.extraCommands.
-          '';
-      };
-
-      extraStopCommands = mkOption {
-        type = types.lines;
-        default = "";
-        example = "iptables -P INPUT ACCEPT";
-        description =
-          ''
-            Additional shell commands executed as part of the firewall
-            shutdown script.  These are executed just after the removal
-            of the NixOS input rule, or if the service enters a failed
-            state.
-          '';
-      };
-
-      interfaces = mkOption {
-        default = { };
-        type = with types; attrsOf (submodule [ { options = commonOptions; } ]);
-        description =
-          ''
-            Interface-specific open ports.
-          '';
-      };
-    } // commonOptions;
-
-  };
-
-
-  ###### implementation
-
-  # FIXME: Maybe if `enable' is false, the firewall should still be
-  # built but not started by default?
-  config = mkIf cfg.enable {
-
-    networking.firewall.trustedInterfaces = [ "lo" ];
-
-    environment.systemPackages = [ cfg.package ] ++ cfg.extraPackages;
-
-    boot.kernelModules = (optional cfg.autoLoadConntrackHelpers "nf_conntrack")
-      ++ map (x: "nf_conntrack_${x}") cfg.connectionTrackingModules;
-    boot.extraModprobeConfig = optionalString cfg.autoLoadConntrackHelpers ''
-      options nf_conntrack nf_conntrack_helper=1
-    '';
-
-    assertions = [
-      # This is approximately "checkReversePath -> kernelHasRPFilter",
-      # but the checkReversePath option can include non-boolean
-      # values.
-      { assertion = cfg.checkReversePath == false || kernelHasRPFilter;
-        message = "This kernel does not support rpfilter"; }
-    ];
-
-    systemd.services.firewall = {
-      description = "Firewall";
-      wantedBy = [ "sysinit.target" ];
-      wants = [ "network-pre.target" ];
-      before = [ "network-pre.target" ];
-      after = [ "systemd-modules-load.service" ];
-
-      path = [ cfg.package ] ++ cfg.extraPackages;
-
-      # 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";
-      unitConfig.DefaultDependencies = false;
-
-      reloadIfChanged = true;
-
-      serviceConfig = {
-        Type = "oneshot";
-        RemainAfterExit = true;
-        ExecStart = "@${startScript} firewall-start";
-        ExecReload = "@${reloadScript} firewall-reload";
-        ExecStop = "@${stopScript} firewall-stop";
-      };
-    };
-
-  };
-
-}