summary refs log tree commit diff
path: root/nixos/modules/services/networking/nftables.nix
blob: 54cbba4937cc706d25cde99ba0ffe84b53498427 (plain) (blame)
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
{ config, pkgs, lib, ... }:
with lib;
let
  cfg = config.networking.nftables;
in
{
  ###### interface

  options = {
    networking.nftables.enable = mkOption {
      type = types.bool;
      default = false;
      description =
        ''
          Whether to enable nftables.  nftables is a Linux-based packet
          filtering framework intended to replace frameworks like iptables.

          This conflicts with the standard networking firewall, so make sure to
          disable it before using nftables.
        '';
    };
    networking.nftables.ruleset = mkOption {
      type = types.lines;
      default =
        ''
          table inet filter {
            # Block all IPv4/IPv6 input traffic except SSH.
            chain input {
              type filter hook input priority 0;
              ct state invalid reject
              ct state {established, related} accept
              iifname lo accept
              tcp dport 22 accept
              reject
            }

            # Allow anything in.
            chain output {
              type filter hook output priority 0;
              ct state invalid reject
              ct state {established, related} accept
              oifname lo accept
              accept
            }

            chain forward {
              type filter hook forward priority 0;
              accept
            }
          }
        '';
      example =
        ''
          # Check out http://wiki.nftables.org/ for better documentation.

          define LAN = 192.168.0.1/24

          # Handle IPv4 traffic.
          table ip filter {
            chain input {
              type filter hook input priority 0;
              # Handle existing connections.
              ct state invalid reject
              ct state {established, related} accept
              # Allow loopback for applications.
              iifname lo accept
              # Allow people to ping us on LAN.
              ip protocol icmp ip daddr $LAN accept
              # Allow SSH over LAN.
              tcp dport 22 ip daddr $LAN accept
              # Reject all other output traffic.
              reject
            }

            chain output {
              type filter hook output priority 0;
              # Handle existing connections.
              ct state invalid reject
              ct state {established, related} accept
              # Allow loopback for applications.
              oifname lo accept
              # Allow the Tor user to run its daemon,
              # but only on WAN in case of compromise.
              skuid tor ip daddr != $LAN accept
              # Allowing pinging others on LAN.
              ip protocol icmp ip daddr $LAN accept
              # Reject all other output traffic.
              reject
            }

            chain forward {
              type filter hook forward priority 0;
              reject
            }
          }

          # Block all IPv6 traffic.
          table ip6 filter {
            chain input {
              type filter hook input priority 0;
              reject
            }

            chain output {
              type filter hook output priority 0;
              reject
            }

            chain forward {
              type filter hook forward priority 0;
              reject
            }
          }
        '';
      description =
        ''
          The ruleset to be used with nftables.  Should be in a format that
          can be loaded using "/bin/nft -f".  The ruleset is updated atomically.
        '';
    };
    networking.nftables.rulesetFile = mkOption {
      type = types.path;
      default = pkgs.writeTextFile {
        name = "nftables-rules";
        text = cfg.ruleset;
      };
      description =
        ''
          The ruleset file to be used with nftables.  Should be in a format that
          can be loaded using "nft -f".  The ruleset is updated atomically.
        '';
    };
  };

  ###### implementation

  config = mkIf cfg.enable {
    assertions = [{
      assertion = config.networking.firewall.enable == false;
      message = "You can not use nftables with services.networking.firewall.";
    }];
    boot.blacklistedKernelModules = [ "ip_tables" ];
    environment.systemPackages = [ pkgs.nftables ];
    systemd.services.nftables = {
      description = "nftables firewall";
      before = [ "network-pre.target" ];
      wants = [ "network-pre.target" ];
      wantedBy = [ "multi-user.target" ];
      reloadIfChanged = true;
      serviceConfig = let
        rulesScript = pkgs.writeScript "nftables-rules" ''
          #! ${pkgs.nftables}/bin/nft -f
          flush ruleset
          include "${cfg.rulesetFile}"
        '';
        checkScript = pkgs.writeScript "nftables-check" ''
          #! ${pkgs.stdenv.shell} -e
          if $(${pkgs.kmod}/bin/lsmod | grep -q ip_tables); then
            echo "Unload ip_tables before using nftables!" 1>&2
            exit 1
          else
            ${rulesScript}
          fi
        '';
      in {
        Type = "oneshot";
        RemainAfterExit = true;
        ExecStart = checkScript;
        ExecReload = checkScript;
        ExecStop = "${pkgs.nftables}/bin/nft flush ruleset";
      };
    };
  };
}