summaryrefslogtreecommitdiffstats
path: root/nixos/tests/firewall.nix
blob: 1aaddc0eb469e2d0cf07fbf61cc7d849a36c2fd1 (plain)
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
# Test the firewall module.

{ lib, backend, ... }:
{
  name = "firewall-${backend}";
  meta = with lib.maintainers; {
    maintainers = [
      prince213
      rvfg
      garyguo
    ];
  };

  nodes = {
    walled =
      { lib, ... }:
      {
        networking.firewall = {
          enable = true;
          inherit backend;
          logRefusedPackets = true;
          # Syntax smoke test, not actually verified otherwise
          allowedTCPPorts = [
            25
            993
            8005
          ];
          allowedTCPPortRanges = [
            {
              from = 980;
              to = 1000;
            }
            {
              from = 990;
              to = 1010;
            }
            {
              from = 8000;
              to = 8010;
            }
          ];
          interfaces = lib.mkIf (backend != "firewalld") {
            eth0 = {
              allowedTCPPorts = [ 10003 ];
              allowedTCPPortRanges = [
                {
                  from = 10000;
                  to = 10005;
                }
              ];
            };
            eth3 = {
              allowedUDPPorts = [ 10003 ];
              allowedUDPPortRanges = [
                {
                  from = 10000;
                  to = 10005;
                }
              ];
            };
          };
        };
        services.firewalld.enable = backend == "firewalld";
        networking.nftables.enable = backend != "iptables";
        services.httpd.enable = true;
        services.httpd.adminAddr = "foo@example.org";

        specialisation.different-config.configuration = {
          networking.firewall.rejectPackets = true;
        };
      };

    attacker =
      { ... }:
      {
        services.httpd.enable = true;
        services.httpd.adminAddr = "foo@example.org";
        networking.firewall.enable = false;
      };
  };

  testScript =
    { nodes, ... }:
    let
      unit = if backend == "iptables" then "firewall" else backend;
      openPort =
        if backend == "firewalld" then
          "firewall-cmd --add-port=80/tcp"
        else
          "nixos-firewall-tool open tcp 80";
      reset = if backend == "firewalld" then "firewall-cmd --reload" else "nixos-firewall-tool reset";
      # https://github.com/firewalld/firewalld/issues/1571
      waitForFirewalld = lib.optionalString (backend == "firewalld") ''
        walled.wait_until_succeeds("firewall-cmd --state")
      '';
    in
    ''
      start_all()

      walled.wait_for_unit("${unit}")
      walled.wait_for_unit("httpd")
      ${waitForFirewalld}
      attacker.wait_for_unit("network.target")

      # Local connections should still work.
      walled.succeed("curl -v http://localhost/ >&2")

      # Connections to the firewalled machine should fail, but ping should succeed.
      attacker.fail("curl --fail --connect-timeout 2 http://walled/ >&2")
      attacker.succeed("ping -c 1 walled >&2")

      # Outgoing connections/pings should still work.
      walled.succeed("curl -v http://attacker/ >&2")
      walled.succeed("ping -c 1 attacker >&2")

      # Open tcp port 80 at runtime
      walled.succeed("${openPort}")
      attacker.succeed("curl -v http://walled/ >&2")

      # Reset the firewall
      walled.succeed("${reset}")
      attacker.fail("curl --fail --connect-timeout 2 http://walled/ >&2")

      # Check whether activation of a new configuration reloads the firewall.
      walled.succeed(
          "/run/booted-system/specialisation/different-config/bin/switch-to-configuration test 2>&1 | grep -F ${unit}.service"
      )

      # If we stop the firewall, then connections should succeed.
      walled.stop_job("${unit}")
      attacker.succeed("curl -v http://walled/ >&2")
    '';
}