1# Test the firewall module.
2
3{ lib, nftables, ... }:
4{
5 name = "firewall" + lib.optionalString nftables "-nftables";
6 meta = with lib.maintainers; {
7 maintainers = [
8 rvfg
9 garyguo
10 ];
11 };
12
13 nodes = {
14 walled =
15 { ... }:
16 {
17 networking.firewall = {
18 enable = true;
19 logRefusedPackets = true;
20 # Syntax smoke test, not actually verified otherwise
21 allowedTCPPorts = [
22 25
23 993
24 8005
25 ];
26 allowedTCPPortRanges = [
27 {
28 from = 980;
29 to = 1000;
30 }
31 {
32 from = 990;
33 to = 1010;
34 }
35 {
36 from = 8000;
37 to = 8010;
38 }
39 ];
40 interfaces.eth0 = {
41 allowedTCPPorts = [ 10003 ];
42 allowedTCPPortRanges = [
43 {
44 from = 10000;
45 to = 10005;
46 }
47 ];
48 };
49 interfaces.eth3 = {
50 allowedUDPPorts = [ 10003 ];
51 allowedUDPPortRanges = [
52 {
53 from = 10000;
54 to = 10005;
55 }
56 ];
57 };
58 };
59 networking.nftables.enable = nftables;
60 services.httpd.enable = true;
61 services.httpd.adminAddr = "foo@example.org";
62
63 specialisation.different-config.configuration = {
64 networking.firewall.rejectPackets = true;
65 };
66 };
67
68 attacker =
69 { ... }:
70 {
71 services.httpd.enable = true;
72 services.httpd.adminAddr = "foo@example.org";
73 networking.firewall.enable = false;
74 };
75 };
76
77 testScript =
78 { nodes, ... }:
79 let
80 unit = if nftables then "nftables" else "firewall";
81 in
82 ''
83 start_all()
84
85 walled.wait_for_unit("${unit}")
86 walled.wait_for_unit("httpd")
87 attacker.wait_for_unit("network.target")
88
89 # Local connections should still work.
90 walled.succeed("curl -v http://localhost/ >&2")
91
92 # Connections to the firewalled machine should fail, but ping should succeed.
93 attacker.fail("curl --fail --connect-timeout 2 http://walled/ >&2")
94 attacker.succeed("ping -c 1 walled >&2")
95
96 # Outgoing connections/pings should still work.
97 walled.succeed("curl -v http://attacker/ >&2")
98 walled.succeed("ping -c 1 attacker >&2")
99
100 # Open tcp port 80 at runtime
101 walled.succeed("nixos-firewall-tool open tcp 80")
102 attacker.succeed("curl -v http://walled/ >&2")
103
104 # Reset the firewall
105 walled.succeed("nixos-firewall-tool reset")
106 attacker.fail("curl --fail --connect-timeout 2 http://walled/ >&2")
107
108 # If we stop the firewall, then connections should succeed.
109 walled.stop_job("${unit}")
110 attacker.succeed("curl -v http://walled/ >&2")
111
112 # Check whether activation of a new configuration reloads the firewall.
113 walled.succeed(
114 "/run/booted-system/specialisation/different-config/bin/switch-to-configuration test 2>&1 | grep -qF ${unit}.service"
115 )
116 '';
117}