at 18.09-beta 7.2 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.security.sudo; 8 9 inherit (pkgs) sudo; 10 11 toUserString = user: if (isInt user) then "#${toString user}" else "${user}"; 12 toGroupString = group: if (isInt group) then "%#${toString group}" else "%${group}"; 13 14 toCommandOptionsString = options: 15 "${concatStringsSep ":" options}${optionalString (length options != 0) ":"} "; 16 17 toCommandsString = commands: 18 concatStringsSep ", " ( 19 map (command: 20 if (isString command) then 21 command 22 else 23 "${toCommandOptionsString command.options}${command.command}" 24 ) commands 25 ); 26 27in 28 29{ 30 31 ###### interface 32 33 options = { 34 35 security.sudo.enable = mkOption { 36 type = types.bool; 37 default = true; 38 description = 39 '' 40 Whether to enable the <command>sudo</command> command, which 41 allows non-root users to execute commands as root. 42 ''; 43 }; 44 45 security.sudo.wheelNeedsPassword = mkOption { 46 type = types.bool; 47 default = true; 48 description = 49 '' 50 Whether users of the <code>wheel</code> group must 51 provide a password to run commands as super user via <command>sudo</command>. 52 ''; 53 }; 54 55 security.sudo.configFile = mkOption { 56 type = types.lines; 57 # Note: if syntax errors are detected in this file, the NixOS 58 # configuration will fail to build. 59 description = 60 '' 61 This string contains the contents of the 62 <filename>sudoers</filename> file. 63 ''; 64 }; 65 66 security.sudo.extraRules = mkOption { 67 description = '' 68 Define specific rules to be in the <filename>sudoers</filename> file. 69 More specific rules should come after more general ones in order to 70 yield the expected behavior. You can use mkBefore/mkAfter to ensure 71 this is the case when configuration options are merged. 72 ''; 73 default = []; 74 example = [ 75 # Allow execution of any command by all users in group sudo, 76 # requiring a password. 77 { groups = [ "sudo" ]; commands = [ "ALL" ]; } 78 79 # Allow execution of "/home/root/secret.sh" by user `backup`, `database` 80 # and the group with GID `1006` without a password. 81 { users = [ "backup" "database" ]; groups = [ 1006 ]; 82 commands = [ { command = "/home/root/secret.sh"; options = [ "SETENV" "NOPASSWD" ]; } ]; } 83 84 # Allow all users of group `bar` to run two executables as user `foo` 85 # with arguments being pre-set. 86 { groups = [ "bar" ]; runAs = "foo"; 87 commands = 88 [ "/home/baz/cmd1.sh hello-sudo" 89 { command = ''/home/baz/cmd2.sh ""''; options = [ "SETENV" ]; } ]; } 90 ]; 91 type = with types; listOf (submodule { 92 options = { 93 users = mkOption { 94 type = with types; listOf (either string int); 95 description = '' 96 The usernames / UIDs this rule should apply for. 97 ''; 98 default = []; 99 }; 100 101 groups = mkOption { 102 type = with types; listOf (either string int); 103 description = '' 104 The groups / GIDs this rule should apply for. 105 ''; 106 default = []; 107 }; 108 109 host = mkOption { 110 type = types.string; 111 default = "ALL"; 112 description = '' 113 For what host this rule should apply. 114 ''; 115 }; 116 117 runAs = mkOption { 118 type = with types; string; 119 default = "ALL:ALL"; 120 description = '' 121 Under which user/group the specified command is allowed to run. 122 123 A user can be specified using just the username: <code>"foo"</code>. 124 It is also possible to specify a user/group combination using <code>"foo:bar"</code> 125 or to only allow running as a specific group with <code>":bar"</code>. 126 ''; 127 }; 128 129 commands = mkOption { 130 description = '' 131 The commands for which the rule should apply. 132 ''; 133 type = with types; listOf (either string (submodule { 134 135 options = { 136 command = mkOption { 137 type = with types; string; 138 description = '' 139 A command being either just a path to a binary to allow any arguments, 140 the full command with arguments pre-set or with <code>""</code> used as the argument, 141 not allowing arguments to the command at all. 142 ''; 143 }; 144 145 options = mkOption { 146 type = with types; listOf (enum [ "NOPASSWD" "PASSWD" "NOEXEC" "EXEC" "SETENV" "NOSETENV" "LOG_INPUT" "NOLOG_INPUT" "LOG_OUTPUT" "NOLOG_OUTPUT" ]); 147 description = '' 148 Options for running the command. Refer to the <a href="https://www.sudo.ws/man/1.7.10/sudoers.man.html">sudo manual</a>. 149 ''; 150 default = []; 151 }; 152 }; 153 154 })); 155 }; 156 }; 157 }); 158 }; 159 160 security.sudo.extraConfig = mkOption { 161 type = types.lines; 162 default = ""; 163 description = '' 164 Extra configuration text appended to <filename>sudoers</filename>. 165 ''; 166 }; 167 }; 168 169 170 ###### implementation 171 172 config = mkIf cfg.enable { 173 174 security.sudo.extraRules = [ 175 { groups = [ "wheel" ]; 176 commands = [ { command = "ALL"; options = (if cfg.wheelNeedsPassword then [ "SETENV" ] else [ "NOPASSWD" "SETENV" ]); } ]; 177 } 178 ]; 179 180 security.sudo.configFile = 181 '' 182 # Don't edit this file. Set the NixOS options security.sudo.configFile 183 # or security.sudo.extraRules instead. 184 185 # Keep SSH_AUTH_SOCK so that pam_ssh_agent_auth.so can do its magic. 186 Defaults env_keep+=SSH_AUTH_SOCK 187 188 # "root" is allowed to do anything. 189 root ALL=(ALL:ALL) SETENV: ALL 190 191 # extraRules 192 ${concatStringsSep "\n" ( 193 lists.flatten ( 194 map ( 195 rule: if (length rule.commands != 0) then [ 196 (map (user: "${toUserString user} ${rule.host}=(${rule.runAs}) ${toCommandsString rule.commands}") rule.users) 197 (map (group: "${toGroupString group} ${rule.host}=(${rule.runAs}) ${toCommandsString rule.commands}") rule.groups) 198 ] else [] 199 ) cfg.extraRules 200 ) 201 )} 202 203 ${cfg.extraConfig} 204 ''; 205 206 security.wrappers = { 207 sudo.source = "${pkgs.sudo.out}/bin/sudo"; 208 sudoedit.source = "${pkgs.sudo.out}/bin/sudoedit"; 209 }; 210 211 environment.systemPackages = [ sudo ]; 212 213 security.pam.services.sudo = { sshAgentAuth = true; }; 214 215 environment.etc = singleton 216 { source = 217 pkgs.runCommand "sudoers" 218 { src = pkgs.writeText "sudoers-in" cfg.configFile; } 219 # Make sure that the sudoers file is syntactically valid. 220 # (currently disabled - NIXOS-66) 221 "${pkgs.buildPackages.sudo}/sbin/visudo -f $src -c && cp $src $out"; 222 target = "sudoers"; 223 mode = "0440"; 224 }; 225 226 }; 227 228}