python.pkgs.pyqt5: 5.14.2 -> 5.15.0
[NixPkgs.git] / nixos / tests / systemd-confinement.nix
blobebf6d218fd68c0de82564fc07a0906301f780441
1 import ./make-test-python.nix {
2   name = "systemd-confinement";
4   machine = { pkgs, lib, ... }: let
5     testServer = pkgs.writeScript "testserver.sh" ''
6       #!${pkgs.runtimeShell}
7       export PATH=${lib.escapeShellArg "${pkgs.coreutils}/bin"}
8       ${lib.escapeShellArg pkgs.runtimeShell} 2>&1
9       echo "exit-status:$?"
10     '';
12     testClient = pkgs.writeScriptBin "chroot-exec" ''
13       #!${pkgs.runtimeShell} -e
14       output="$(echo "$@" | nc -NU "/run/test$(< /teststep).sock")"
15       ret="$(echo "$output" | sed -nre '$s/^exit-status:([0-9]+)$/\1/p')"
16       echo "$output" | head -n -1
17       exit "''${ret:-1}"
18     '';
20     mkTestStep = num: { config ? {}, testScript }: {
21       systemd.sockets."test${toString num}" = {
22         description = "Socket for Test Service ${toString num}";
23         wantedBy = [ "sockets.target" ];
24         socketConfig.ListenStream = "/run/test${toString num}.sock";
25         socketConfig.Accept = true;
26       };
28       systemd.services."test${toString num}@" = {
29         description = "Confined Test Service ${toString num}";
30         confinement = (config.confinement or {}) // { enable = true; };
31         serviceConfig = (config.serviceConfig or {}) // {
32           ExecStart = testServer;
33           StandardInput = "socket";
34         };
35       } // removeAttrs config [ "confinement" "serviceConfig" ];
37       __testSteps = lib.mkOrder num (''
38         machine.succeed("echo ${toString num} > /teststep")
39       '' + testScript);
40     };
42   in {
43     imports = lib.imap1 mkTestStep [
44       { config.confinement.mode = "chroot-only";
45         testScript = ''
46           with subtest("chroot-only confinement"):
47               machine.succeed(
48                   'test "$(chroot-exec ls -1 / | paste -sd,)" = bin,nix',
49                   'test "$(chroot-exec id -u)" = 0',
50                   "chroot-exec chown 65534 /bin",
51               )
52         '';
53       }
54       { testScript = ''
55           with subtest("full confinement with APIVFS"):
56               machine.fail(
57                   "chroot-exec ls -l /etc",
58                   "chroot-exec ls -l /run",
59                   "chroot-exec chown 65534 /bin",
60               )
61               machine.succeed(
62                   'test "$(chroot-exec id -u)" = 0', "chroot-exec chown 0 /bin",
63               )
64         '';
65       }
66       { config.serviceConfig.BindReadOnlyPaths = [ "/etc" ];
67         testScript = ''
68           with subtest("check existence of bind-mounted /etc"):
69               machine.succeed('test -n "$(chroot-exec cat /etc/passwd)"')
70         '';
71       }
72       { config.serviceConfig.User = "chroot-testuser";
73         config.serviceConfig.Group = "chroot-testgroup";
74         testScript = ''
75           with subtest("check if User/Group really runs as non-root"):
76               machine.succeed("chroot-exec ls -l /dev")
77               machine.succeed('test "$(chroot-exec id -u)" != 0')
78               machine.fail("chroot-exec touch /bin/test")
79         '';
80       }
81       (let
82         symlink = pkgs.runCommand "symlink" {
83           target = pkgs.writeText "symlink-target" "got me\n";
84         } "ln -s \"$target\" \"$out\"";
85       in {
86         config.confinement.packages = lib.singleton symlink;
87         testScript = ''
88           with subtest("check if symlinks are properly bind-mounted"):
89               machine.fail("chroot-exec test -e /etc")
90               machine.succeed(
91                   "chroot-exec cat ${symlink} >&2",
92                   'test "$(chroot-exec cat ${symlink})" = "got me"',
93               )
94         '';
95       })
96       { config.serviceConfig.User = "chroot-testuser";
97         config.serviceConfig.Group = "chroot-testgroup";
98         config.serviceConfig.StateDirectory = "testme";
99         testScript = ''
100           with subtest("check if StateDirectory works"):
101               machine.succeed("chroot-exec touch /tmp/canary")
102               machine.succeed('chroot-exec "echo works > /var/lib/testme/foo"')
103               machine.succeed('test "$(< /var/lib/testme/foo)" = works')
104               machine.succeed("test ! -e /tmp/canary")
105         '';
106       }
107       { testScript = ''
108           with subtest("check if /bin/sh works"):
109               machine.succeed(
110                   "chroot-exec test -e /bin/sh",
111                   'test "$(chroot-exec \'/bin/sh -c "echo bar"\')" = bar',
112               )
113         '';
114       }
115       { config.confinement.binSh = null;
116         testScript = ''
117           with subtest("check if suppressing /bin/sh works"):
118               machine.succeed("chroot-exec test ! -e /bin/sh")
119               machine.succeed('test "$(chroot-exec \'/bin/sh -c "echo foo"\')" != foo')
120         '';
121       }
122       { config.confinement.binSh = "${pkgs.hello}/bin/hello";
123         testScript = ''
124           with subtest("check if we can set /bin/sh to something different"):
125               machine.succeed("chroot-exec test -e /bin/sh")
126               machine.succeed('test "$(chroot-exec /bin/sh -g foo)" = foo')
127         '';
128       }
129       { config.environment.FOOBAR = pkgs.writeText "foobar" "eek\n";
130         testScript = ''
131           with subtest("check if only Exec* dependencies are included"):
132               machine.succeed('test "$(chroot-exec \'cat "$FOOBAR"\')" != eek')
133         '';
134       }
135       { config.environment.FOOBAR = pkgs.writeText "foobar" "eek\n";
136         config.confinement.fullUnit = true;
137         testScript = ''
138           with subtest("check if all unit dependencies are included"):
139               machine.succeed('test "$(chroot-exec \'cat "$FOOBAR"\')" = eek')
140         '';
141       }
142     ];
144     options.__testSteps = lib.mkOption {
145       type = lib.types.lines;
146       description = "All of the test steps combined as a single script.";
147     };
149     config.environment.systemPackages = lib.singleton testClient;
151     config.users.groups.chroot-testgroup = {};
152     config.users.users.chroot-testuser = {
153       description = "Chroot Test User";
154       group = "chroot-testgroup";
155     };
156   };
158   testScript = { nodes, ... }: ''
159     machine.wait_for_unit("multi-user.target")
160   '' + nodes.machine.config.__testSteps;