diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix
index cc3ff3d11b9..2e73f8f4f31 100644
--- a/nixos/modules/security/sudo.nix
+++ b/nixos/modules/security/sudo.nix
@@ -61,6 +61,17 @@ in
'';
};
+ security.sudo.execWheelOnly = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Only allow members of the wheel
group to execute sudo by
+ setting the executable's permissions accordingly.
+ This prevents users that are not members of wheel
from
+ exploiting vulnerabilities in sudo such as CVE-2021-3156.
+ '';
+ };
+
security.sudo.configFile = mkOption {
type = types.lines;
# Note: if syntax errors are detected in this file, the NixOS
@@ -216,9 +227,20 @@ in
${cfg.extraConfig}
'';
- security.wrappers = {
- sudo.source = "${cfg.package.out}/bin/sudo";
- sudoedit.source = "${cfg.package.out}/bin/sudoedit";
+ security.wrappers = let
+ owner = "root";
+ group = if cfg.execWheelOnly then "wheel" else "root";
+ setuid = true;
+ permissions = if cfg.execWheelOnly then "u+rx,g+x" else "u+rx,g+x,o+x";
+ in {
+ sudo = {
+ source = "${cfg.package.out}/bin/sudo";
+ inherit owner group setuid permissions;
+ };
+ sudoedit = {
+ source = "${cfg.package.out}/bin/sudoedit";
+ inherit owner group setuid permissions;
+ };
};
environment.systemPackages = [ sudo ];
diff --git a/nixos/tests/sudo.nix b/nixos/tests/sudo.nix
index 2a85c490665..4885d6e17b8 100644
--- a/nixos/tests/sudo.nix
+++ b/nixos/tests/sudo.nix
@@ -10,7 +10,7 @@ in
maintainers = [ lschuermann ];
};
- machine =
+ nodes.machine =
{ lib, ... }:
with lib;
{
@@ -48,6 +48,19 @@ in
};
};
+ nodes.strict = { ... }: {
+ users.users = {
+ admin = { isNormalUser = true; extraGroups = [ "wheel" ]; };
+ noadmin = { isNormalUser = true; };
+ };
+
+ security.sudo = {
+ enable = true;
+ wheelNeedsPassword = false;
+ execWheelOnly = true;
+ };
+ };
+
testScript =
''
with subtest("users in wheel group should have passwordless sudo"):
@@ -79,5 +92,11 @@ in
with subtest("users in group 'barfoo' should not be able to keep their environment"):
machine.fail("sudo -u test3 sudo -n -E -u root true")
+
+ with subtest("users in wheel should be able to run sudo despite execWheelOnly"):
+ strict.succeed('su - admin -c "sudo -u root true"')
+
+ with subtest("non-wheel users should be unable to run sudo thanks to execWheelOnly"):
+ strict.fail('su - noadmin -c "sudo --help"')
'';
})