summaryrefslogtreecommitdiffstats
path: root/nixos/tests/doas.nix
blob: 7ba4164d9b727bb6547668d4d320add57fa197e8 (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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# Some tests to ensure doas is working properly.
{ lib, ... }:
{
  name = "doas";
  meta.maintainers = with lib.maintainers; [ cole-h ];

  nodes.machine =
    { ... }:
    {
      users.groups = {
        foobar = { };
        barfoo = { };
        baz = {
          gid = 1337;
        };
      };
      users.users = {
        test0 = {
          isNormalUser = true;
          extraGroups = [ "wheel" ];
        };
        test1 = {
          isNormalUser = true;
        };
        test2 = {
          isNormalUser = true;
          extraGroups = [ "foobar" ];
        };
        test3 = {
          isNormalUser = true;
          extraGroups = [ "barfoo" ];
        };
        test4 = {
          isNormalUser = true;
          extraGroups = [ "baz" ];
        };
        test5 = {
          isNormalUser = true;
        };
        test6 = {
          isNormalUser = true;
        };
        test7 = {
          isNormalUser = true;
        };
      };

      security.doas = {
        enable = true;
        wheelNeedsPassword = false;

        extraRules = [
          {
            users = [ "test1" ];
            groups = [ "foobar" ];
          }
          {
            users = [ "test2" ];
            noPass = true;
            setEnv = [
              "CORRECT"
              "HORSE=BATTERY"
            ];
          }
          {
            groups = [
              "barfoo"
              1337
            ];
            noPass = true;
          }
          {
            users = [ "test5" ];
            noPass = true;
            keepEnv = true;
            runAs = "test1";
          }
          {
            users = [ "test6" ];
            noPass = true;
            keepEnv = true;
            setEnv = [ "-STAPLE" ];
          }
          {
            users = [ "test7" ];
            noPass = true;
            setEnv = [ "-SSH_AUTH_SOCK" ];
          }
        ];
      };
    };

  testScript = ''
    with subtest("users in wheel group should have passwordless doas"):
        machine.succeed('su - test0 -c "doas -u root true"')

    with subtest("test1 user should not be able to use doas without password"):
        machine.fail('su - test1 -c "doas -n -u root true"')

    with subtest("test2 user should be able to keep some env"):
        if "CORRECT=1" not in machine.succeed('su - test2 -c "CORRECT=1 doas env"'):
            raise Exception("failed to keep CORRECT")

        if "HORSE=BATTERY" not in machine.succeed('su - test2 -c "doas env"'):
            raise Exception("failed to setenv HORSE=BATTERY")

    with subtest("users in group 'barfoo' shouldn't require password"):
        machine.succeed("doas -u test3 doas -n -u root true")

    with subtest("users in group 'baz' (GID 1337) shouldn't require password"):
        machine.succeed("doas -u test4 doas -n -u root echo true")

    with subtest("test5 user should be able to run commands under test1"):
        machine.succeed("doas -u test5 doas -n -u test1 true")

    with subtest("test5 user should not be able to run commands under root"):
        machine.fail("doas -u test5 doas -n -u root true")

    with subtest("test6 user should be able to keepenv"):
        envs = ["BATTERY=HORSE", "CORRECT=false"]
        out = machine.succeed(
            'su - test6 -c "BATTERY=HORSE CORRECT=false STAPLE=Tr0ub4dor doas env"'
        )

        if not all(env in out for env in envs):
            raise Exception("failed to keep BATTERY or CORRECT")
        if "STAPLE=Tr0ub4dor" in out:
            raise Exception("failed to exclude STAPLE")

    with subtest("test7 should not have access to SSH_AUTH_SOCK"):
        if "SSH_AUTH_SOCK=HOLEY" in machine.succeed(
            'su - test7 -c "SSH_AUTH_SOCK=HOLEY doas env"'
        ):
            raise Exception("failed to exclude SSH_AUTH_SOCK")

    # Test that the doas setuid wrapper precedes the unwrapped version in PATH after
    # calling doas.
    # The PATH set by doas is defined in
    # ../../pkgs/tools/security/doas/0001-add-NixOS-specific-dirs-to-safe-PATH.patch
    with subtest("recursive calls to doas from subprocesses should succeed"):
        machine.succeed('doas -u test0 sh -c "doas -u test0 true"')

    with subtest("test0 should inherit TERMINFO_DIRS from the user environment"):
        dirs = machine.succeed(
             "su - test0 -c 'doas -u root $SHELL -c \"echo \$TERMINFO_DIRS\"'"
        )

        if not "test0" in dirs:
           raise Exception(f"user profile TERMINFO_DIRS is not preserved: {dirs}")
  '';
}