0
0
Fork 0
mirror of https://github.com/NixOS/nixpkgs.git synced 2025-07-13 21:50:33 +03:00

Merge pull request #126289 from rnhmjoj/wrappers

nixos/security/wrappers: make well-typed
This commit is contained in:
Guillaume Girol 2021-09-18 15:28:49 +00:00 committed by GitHub
commit ceb2e6667b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 462 additions and 155 deletions

View file

@ -278,6 +278,16 @@
<section xml:id="sec-release-21.11-incompatibilities"> <section xml:id="sec-release-21.11-incompatibilities">
<title>Backward Incompatibilities</title> <title>Backward Incompatibilities</title>
<itemizedlist> <itemizedlist>
<listitem>
<para>
The <literal>security.wrappers</literal> option now requires
to always specify an owner, group and whether the
setuid/setgid bit should be set. This is motivated by the fact
that before NixOS 21.11, specifying either setuid or setgid
but not owner/group resulted in wrappers owned by
nobody/nogroup, which is unsafe.
</para>
</listitem>
<listitem> <listitem>
<para> <para>
The <literal>paperless</literal> module and package have been The <literal>paperless</literal> module and package have been

View file

@ -88,6 +88,8 @@ subsonic-compatible api. Available as [navidrome](#opt-services.navidrome.enable
## Backward Incompatibilities {#sec-release-21.11-incompatibilities} ## Backward Incompatibilities {#sec-release-21.11-incompatibilities}
- The `security.wrappers` option now requires to always specify an owner, group and whether the setuid/setgid bit should be set.
This is motivated by the fact that before NixOS 21.11, specifying either setuid or setgid but not owner/group resulted in wrappers owned by nobody/nogroup, which is unsafe.
- The `paperless` module and package have been removed. All users should migrate to the - The `paperless` module and package have been removed. All users should migrate to the
successor `paperless-ng` instead. The Paperless project [has been successor `paperless-ng` instead. The Paperless project [has been

View file

@ -22,8 +22,10 @@ in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
environment.systemPackages = with pkgs; [ bandwhich ]; environment.systemPackages = with pkgs; [ bandwhich ];
security.wrappers.bandwhich = { security.wrappers.bandwhich = {
source = "${pkgs.bandwhich}/bin/bandwhich"; owner = "root";
group = "root";
capabilities = "cap_net_raw,cap_net_admin+ep"; capabilities = "cap_net_raw,cap_net_admin+ep";
source = "${pkgs.bandwhich}/bin/bandwhich";
}; };
}; };
} }

View file

@ -105,11 +105,15 @@ in
); );
security.wrappers.udhcpc = { security.wrappers.udhcpc = {
owner = "root";
group = "root";
capabilities = "cap_net_raw+p"; capabilities = "cap_net_raw+p";
source = "${pkgs.busybox}/bin/udhcpc"; source = "${pkgs.busybox}/bin/udhcpc";
}; };
security.wrappers.captive-browser = { security.wrappers.captive-browser = {
owner = "root";
group = "root";
capabilities = "cap_net_raw+p"; capabilities = "cap_net_raw+p";
source = pkgs.writeShellScript "captive-browser" '' source = pkgs.writeShellScript "captive-browser" ''
export PREV_CONFIG_HOME="$XDG_CONFIG_HOME" export PREV_CONFIG_HOME="$XDG_CONFIG_HOME"

View file

@ -28,7 +28,9 @@ in {
# "nix-ccache --show-stats" and "nix-ccache --clear" # "nix-ccache --show-stats" and "nix-ccache --clear"
security.wrappers.nix-ccache = { security.wrappers.nix-ccache = {
owner = "nobody";
group = "nixbld"; group = "nixbld";
setuid = false;
setgid = true; setgid = true;
source = pkgs.writeScript "nix-ccache.pl" '' source = pkgs.writeScript "nix-ccache.pl" ''
#!${pkgs.perl}/bin/perl #!${pkgs.perl}/bin/perl

View file

@ -81,7 +81,12 @@ in {
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
security.wrappers.firejail.source = "${lib.getBin pkgs.firejail}/bin/firejail"; security.wrappers.firejail =
{ setuid = true;
owner = "root";
group = "root";
source = "${lib.getBin pkgs.firejail}/bin/firejail";
};
environment.systemPackages = [ pkgs.firejail ] ++ [ wrappedBins ]; environment.systemPackages = [ pkgs.firejail ] ++ [ wrappedBins ];
}; };

View file

@ -56,6 +56,8 @@ in
polkit.enable = true; polkit.enable = true;
wrappers = mkIf cfg.enableRenice { wrappers = mkIf cfg.enableRenice {
gamemoded = { gamemoded = {
owner = "root";
group = "root";
source = "${pkgs.gamemode}/bin/gamemoded"; source = "${pkgs.gamemode}/bin/gamemoded";
capabilities = "cap_sys_nice+ep"; capabilities = "cap_sys_nice+ep";
}; };

View file

@ -11,8 +11,10 @@ in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
environment.systemPackages = [ pkgs.iftop ]; environment.systemPackages = [ pkgs.iftop ];
security.wrappers.iftop = { security.wrappers.iftop = {
source = "${pkgs.iftop}/bin/iftop"; owner = "root";
group = "root";
capabilities = "cap_net_raw+p"; capabilities = "cap_net_raw+p";
source = "${pkgs.iftop}/bin/iftop";
}; };
}; };
} }

View file

@ -10,8 +10,10 @@ in {
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
security.wrappers.iotop = { security.wrappers.iotop = {
source = "${pkgs.iotop}/bin/iotop"; owner = "root";
group = "root";
capabilities = "cap_net_admin+p"; capabilities = "cap_net_admin+p";
source = "${pkgs.iotop}/bin/iotop";
}; };
}; };
} }

View file

@ -11,6 +11,11 @@ in
config = mkIf cfg.enable { config = mkIf cfg.enable {
environment.systemPackages = [ pkgs.kbdlight ]; environment.systemPackages = [ pkgs.kbdlight ];
security.wrappers.kbdlight.source = "${pkgs.kbdlight.out}/bin/kbdlight"; security.wrappers.kbdlight =
{ setuid = true;
owner = "root";
group = "root";
source = "${pkgs.kbdlight.out}/bin/kbdlight";
};
}; };
} }

View file

@ -13,8 +13,10 @@ in {
security.wrappers = mkMerge (map ( security.wrappers = mkMerge (map (
exec: { exec: {
"${exec}" = { "${exec}" = {
source = "${pkgs.liboping}/bin/${exec}"; owner = "root";
group = "root";
capabilities = "cap_net_raw+p"; capabilities = "cap_net_raw+p";
source = "${pkgs.liboping}/bin/${exec}";
}; };
} }
) [ "oping" "noping" ]); ) [ "oping" "noping" ]);

View file

@ -78,6 +78,8 @@ in {
source = "${pkgs.msmtp}/bin/sendmail"; source = "${pkgs.msmtp}/bin/sendmail";
setuid = false; setuid = false;
setgid = false; setgid = false;
owner = "root";
group = "root";
}; };
environment.etc."msmtprc".text = let environment.etc."msmtprc".text = let

View file

@ -31,8 +31,10 @@ in {
environment.systemPackages = with pkgs; [ cfg.package ]; environment.systemPackages = with pkgs; [ cfg.package ];
security.wrappers.mtr-packet = { security.wrappers.mtr-packet = {
source = "${cfg.package}/bin/mtr-packet"; owner = "root";
group = "root";
capabilities = "cap_net_raw+p"; capabilities = "cap_net_raw+p";
source = "${cfg.package}/bin/mtr-packet";
}; };
}; };
} }

View file

@ -18,8 +18,10 @@ in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
security.wrappers.noisetorch = { security.wrappers.noisetorch = {
source = "${cfg.package}/bin/noisetorch"; owner = "root";
group = "root";
capabilities = "cap_sys_resource=+ep"; capabilities = "cap_sys_resource=+ep";
source = "${cfg.package}/bin/noisetorch";
}; };
}; };
} }

View file

@ -43,6 +43,13 @@ let
''; '';
mkSetuidRoot = source:
{ setuid = true;
owner = "root";
group = "root";
inherit source;
};
in in
{ {
@ -109,14 +116,14 @@ in
}; };
security.wrappers = { security.wrappers = {
su.source = "${pkgs.shadow.su}/bin/su"; su = mkSetuidRoot "${pkgs.shadow.su}/bin/su";
sg.source = "${pkgs.shadow.out}/bin/sg"; sg = mkSetuidRoot "${pkgs.shadow.out}/bin/sg";
newgrp.source = "${pkgs.shadow.out}/bin/newgrp"; newgrp = mkSetuidRoot "${pkgs.shadow.out}/bin/newgrp";
newuidmap.source = "${pkgs.shadow.out}/bin/newuidmap"; newuidmap = mkSetuidRoot "${pkgs.shadow.out}/bin/newuidmap";
newgidmap.source = "${pkgs.shadow.out}/bin/newgidmap"; newgidmap = mkSetuidRoot "${pkgs.shadow.out}/bin/newgidmap";
} // lib.optionalAttrs config.users.mutableUsers { } // lib.optionalAttrs config.users.mutableUsers {
chsh.source = "${pkgs.shadow.out}/bin/chsh"; chsh = mkSetuidRoot "${pkgs.shadow.out}/bin/chsh";
passwd.source = "${pkgs.shadow.out}/bin/passwd"; passwd = mkSetuidRoot "${pkgs.shadow.out}/bin/passwd";
}; };
}; };
} }

View file

@ -16,7 +16,12 @@ in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
environment.systemPackages = [ singularity ]; environment.systemPackages = [ singularity ];
security.wrappers.singularity-suid.source = "${singularity}/libexec/singularity/bin/starter-suid.orig"; security.wrappers.singularity-suid =
{ setuid = true;
owner = "root";
group = "root";
source = "${singularity}/libexec/singularity/bin/starter-suid.orig";
};
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"d /var/singularity/mnt/session 0770 root root -" "d /var/singularity/mnt/session 0770 root root -"
"d /var/singularity/mnt/final 0770 root root -" "d /var/singularity/mnt/final 0770 root root -"

View file

@ -21,6 +21,11 @@ in
config = mkIf cfg.enable { config = mkIf cfg.enable {
environment.systemPackages = [ pkgs.slock ]; environment.systemPackages = [ pkgs.slock ];
security.wrappers.slock.source = "${pkgs.slock.out}/bin/slock"; security.wrappers.slock =
{ setuid = true;
owner = "root";
group = "root";
source = "${pkgs.slock.out}/bin/slock";
};
}; };
} }

View file

@ -181,6 +181,8 @@ in
source = "${pkgs.ssmtp}/bin/sendmail"; source = "${pkgs.ssmtp}/bin/sendmail";
setuid = false; setuid = false;
setgid = false; setgid = false;
owner = "root";
group = "root";
}; };
}; };

View file

@ -19,8 +19,10 @@ in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
security.wrappers.traceroute = { security.wrappers.traceroute = {
source = "${pkgs.traceroute}/bin/traceroute"; owner = "root";
group = "root";
capabilities = "cap_net_raw+p"; capabilities = "cap_net_raw+p";
source = "${pkgs.traceroute}/bin/traceroute";
}; };
}; };
} }

View file

@ -9,6 +9,11 @@ in {
options.programs.udevil.enable = mkEnableOption "udevil"; options.programs.udevil.enable = mkEnableOption "udevil";
config = mkIf cfg.enable { config = mkIf cfg.enable {
security.wrappers.udevil.source = "${lib.getBin pkgs.udevil}/bin/udevil"; security.wrappers.udevil =
{ setuid = true;
owner = "root";
group = "root";
source = "${lib.getBin pkgs.udevil}/bin/udevil";
};
}; };
} }

View file

@ -21,8 +21,10 @@ in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
environment.systemPackages = with pkgs; [ wavemon ]; environment.systemPackages = with pkgs; [ wavemon ];
security.wrappers.wavemon = { security.wrappers.wavemon = {
source = "${pkgs.wavemon}/bin/wavemon"; owner = "root";
group = "root";
capabilities = "cap_net_admin+ep"; capabilities = "cap_net_admin+ep";
source = "${pkgs.wavemon}/bin/wavemon";
}; };
}; };
} }

View file

@ -17,6 +17,11 @@ in {
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
security.wrappers.wshowkeys.source = "${pkgs.wshowkeys}/bin/wshowkeys"; security.wrappers.wshowkeys =
{ setuid = true;
owner = "root";
group = "root";
source = "${pkgs.wshowkeys}/bin/wshowkeys";
};
}; };
} }

View file

@ -28,6 +28,11 @@ in
config = mkIf cfg.enable { config = mkIf cfg.enable {
environment.systemPackages = [ sandbox ]; environment.systemPackages = [ sandbox ];
security.wrappers.${sandbox.passthru.sandboxExecutableName}.source = "${sandbox}/bin/${sandbox.passthru.sandboxExecutableName}"; security.wrappers.${sandbox.passthru.sandboxExecutableName} =
{ setuid = true;
owner = "root";
group = "root";
source = "${sandbox}/bin/${sandbox.passthru.sandboxExecutableName}";
};
}; };
} }

View file

@ -241,9 +241,12 @@ in
} }
]; ];
security.wrappers = { security.wrappers.doas =
doas.source = "${doas}/bin/doas"; { setuid = true;
}; owner = "root";
group = "root";
source = "${doas}/bin/doas";
};
environment.systemPackages = [ environment.systemPackages = [
doas doas

View file

@ -186,7 +186,12 @@ in
config = mkIf (cfg.ssh.enable || cfg.pam.enable) { config = mkIf (cfg.ssh.enable || cfg.pam.enable) {
environment.systemPackages = [ pkgs.duo-unix ]; environment.systemPackages = [ pkgs.duo-unix ];
security.wrappers.login_duo.source = "${pkgs.duo-unix.out}/bin/login_duo"; security.wrappers.login_duo =
{ setuid = true;
owner = "root";
group = "root";
source = "${pkgs.duo-unix.out}/bin/login_duo";
};
system.activationScripts = { system.activationScripts = {
login_duo = mkIf cfg.ssh.enable '' login_duo = mkIf cfg.ssh.enable ''

View file

@ -869,9 +869,10 @@ in
security.wrappers = { security.wrappers = {
unix_chkpwd = { unix_chkpwd = {
source = "${pkgs.pam}/sbin/unix_chkpwd.orig";
owner = "root";
setuid = true; setuid = true;
owner = "root";
group = "root";
source = "${pkgs.pam}/sbin/unix_chkpwd.orig";
}; };
}; };

View file

@ -32,8 +32,18 @@ in
# Make sure pmount and pumount are setuid wrapped. # Make sure pmount and pumount are setuid wrapped.
security.wrappers = { security.wrappers = {
pmount.source = "${pkgs.pmount.out}/bin/pmount"; pmount =
pumount.source = "${pkgs.pmount.out}/bin/pumount"; { setuid = true;
owner = "root";
group = "root";
source = "${pkgs.pmount.out}/bin/pmount";
};
pumount =
{ setuid = true;
owner = "root";
group = "root";
source = "${pkgs.pmount.out}/bin/pumount";
};
}; };
environment.systemPackages = [ pkgs.pmount ]; environment.systemPackages = [ pkgs.pmount ];

View file

@ -83,8 +83,18 @@ in
security.pam.services.polkit-1 = {}; security.pam.services.polkit-1 = {};
security.wrappers = { security.wrappers = {
pkexec.source = "${pkgs.polkit.bin}/bin/pkexec"; pkexec =
polkit-agent-helper-1.source = "${pkgs.polkit.out}/lib/polkit-1/polkit-agent-helper-1"; { setuid = true;
owner = "root";
group = "root";
source = "${pkgs.polkit.bin}/bin/pkexec";
};
polkit-agent-helper-1 =
{ setuid = true;
owner = "root";
group = "root";
source = "${pkgs.polkit.out}/lib/polkit-1/polkit-agent-helper-1";
};
}; };
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [

View file

@ -5,85 +5,140 @@ let
parentWrapperDir = dirOf wrapperDir; parentWrapperDir = dirOf wrapperDir;
programs =
(lib.mapAttrsToList
(n: v: (if v ? program then v else v // {program=n;}))
wrappers);
securityWrapper = pkgs.callPackage ./wrapper.nix { securityWrapper = pkgs.callPackage ./wrapper.nix {
inherit parentWrapperDir; inherit parentWrapperDir;
}; };
fileModeType =
let
# taken from the chmod(1) man page
symbolic = "[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=][0-7]+";
numeric = "[-+=]?[0-7]{0,4}";
mode = "((${symbolic})(,${symbolic})*)|(${numeric})";
in
lib.types.strMatching mode
// { description = "file mode string"; };
wrapperType = lib.types.submodule ({ name, config, ... }: {
options.source = lib.mkOption
{ type = lib.types.path;
description = "The absolute path to the program to be wrapped.";
};
options.program = lib.mkOption
{ type = with lib.types; nullOr str;
default = name;
description = ''
The name of the wrapper program. Defaults to the attribute name.
'';
};
options.owner = lib.mkOption
{ type = lib.types.str;
description = "The owner of the wrapper program.";
};
options.group = lib.mkOption
{ type = lib.types.str;
description = "The group of the wrapper program.";
};
options.permissions = lib.mkOption
{ type = fileModeType;
default = "u+rx,g+x,o+x";
example = "a+rx";
description = ''
The permissions of the wrapper program. The format is that of a
symbolic or numeric file mode understood by <command>chmod</command>.
'';
};
options.capabilities = lib.mkOption
{ type = lib.types.commas;
default = "";
description = ''
A comma-separated list of capabilities to be given to the wrapper
program. For capabilities supported by the system check the
<citerefentry>
<refentrytitle>capabilities</refentrytitle>
<manvolnum>7</manvolnum>
</citerefentry>
manual page.
<note><para>
<literal>cap_setpcap</literal>, which is required for the wrapper
program to be able to raise caps into the Ambient set is NOT raised
to the Ambient set so that the real program cannot modify its own
capabilities!! This may be too restrictive for cases in which the
real program needs cap_setpcap but it at least leans on the side
security paranoid vs. too relaxed.
</para></note>
'';
};
options.setuid = lib.mkOption
{ type = lib.types.bool;
default = false;
description = "Whether to add the setuid bit the wrapper program.";
};
options.setgid = lib.mkOption
{ type = lib.types.bool;
default = false;
description = "Whether to add the setgid bit the wrapper program.";
};
});
###### Activation script for the setcap wrappers ###### Activation script for the setcap wrappers
mkSetcapProgram = mkSetcapProgram =
{ program { program
, capabilities , capabilities
, source , source
, owner ? "nobody" , owner
, group ? "nogroup" , group
, permissions ? "u+rx,g+x,o+x" , permissions
, ... , ...
}: }:
assert (lib.versionAtLeast (lib.getVersion config.boot.kernelPackages.kernel) "4.3"); assert (lib.versionAtLeast (lib.getVersion config.boot.kernelPackages.kernel) "4.3");
'' ''
cp ${securityWrapper}/bin/security-wrapper $wrapperDir/${program} cp ${securityWrapper}/bin/security-wrapper "$wrapperDir/${program}"
echo -n "${source}" > $wrapperDir/${program}.real echo -n "${source}" > "$wrapperDir/${program}.real"
# Prevent races # Prevent races
chmod 0000 $wrapperDir/${program} chmod 0000 "$wrapperDir/${program}"
chown ${owner}.${group} $wrapperDir/${program} chown ${owner}.${group} "$wrapperDir/${program}"
# Set desired capabilities on the file plus cap_setpcap so # Set desired capabilities on the file plus cap_setpcap so
# the wrapper program can elevate the capabilities set on # the wrapper program can elevate the capabilities set on
# its file into the Ambient set. # its file into the Ambient set.
${pkgs.libcap.out}/bin/setcap "cap_setpcap,${capabilities}" $wrapperDir/${program} ${pkgs.libcap.out}/bin/setcap "cap_setpcap,${capabilities}" "$wrapperDir/${program}"
# Set the executable bit # Set the executable bit
chmod ${permissions} $wrapperDir/${program} chmod ${permissions} "$wrapperDir/${program}"
''; '';
###### Activation script for the setuid wrappers ###### Activation script for the setuid wrappers
mkSetuidProgram = mkSetuidProgram =
{ program { program
, source , source
, owner ? "nobody" , owner
, group ? "nogroup" , group
, setuid ? false , setuid
, setgid ? false , setgid
, permissions ? "u+rx,g+x,o+x" , permissions
, ... , ...
}: }:
'' ''
cp ${securityWrapper}/bin/security-wrapper $wrapperDir/${program} cp ${securityWrapper}/bin/security-wrapper "$wrapperDir/${program}"
echo -n "${source}" > $wrapperDir/${program}.real echo -n "${source}" > "$wrapperDir/${program}.real"
# Prevent races # Prevent races
chmod 0000 $wrapperDir/${program} chmod 0000 "$wrapperDir/${program}"
chown ${owner}.${group} $wrapperDir/${program} chown ${owner}.${group} "$wrapperDir/${program}"
chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" $wrapperDir/${program} chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" "$wrapperDir/${program}"
''; '';
mkWrappedPrograms = mkWrappedPrograms =
builtins.map builtins.map
(s: if (s ? capabilities) (opts:
then mkSetcapProgram if opts.capabilities != ""
({ owner = "root"; then mkSetcapProgram opts
group = "root"; else mkSetuidProgram opts
} // s) ) (lib.attrValues wrappers);
else if
(s ? setuid && s.setuid) ||
(s ? setgid && s.setgid) ||
(s ? permissions)
then mkSetuidProgram s
else mkSetuidProgram
({ owner = "root";
group = "root";
setuid = true;
setgid = false;
permissions = "u+rx,g+x,o+x";
} // s)
) programs;
in in
{ {
imports = [ imports = [
@ -95,45 +150,42 @@ in
options = { options = {
security.wrappers = lib.mkOption { security.wrappers = lib.mkOption {
type = lib.types.attrs; type = lib.types.attrsOf wrapperType;
default = {}; default = {};
example = lib.literalExample example = lib.literalExample
'' ''
{ sendmail.source = "/nix/store/.../bin/sendmail"; {
ping = { # a setuid root program
source = "${pkgs.iputils.out}/bin/ping"; doas =
owner = "nobody"; { setuid = true;
group = "nogroup"; owner = "root";
capabilities = "cap_net_raw+ep"; group = "root";
}; source = "''${pkgs.doas}/bin/doas";
};
# a setgid program
locate =
{ setgid = true;
owner = "root";
group = "mlocate";
source = "''${pkgs.locate}/bin/locate";
};
# a program with the CAP_NET_RAW capability
ping =
{ owner = "root";
group = "root";
capabilities = "cap_net_raw+ep";
source = "''${pkgs.iputils.out}/bin/ping";
};
} }
''; '';
description = '' description = ''
This option allows the ownership and permissions on the setuid This option effectively allows adding setuid/setgid bits, capabilities,
wrappers for specific programs to be overridden from the changing file ownership and permissions of a program without directly
default (setuid root, but not setgid root). modifying it. This works by creating a wrapper program under the
<option>security.wrapperDir</option> directory, which is then added to
<note> the shell <literal>PATH</literal>.
<para>The sub-attribute <literal>source</literal> is mandatory,
it must be the absolute path to the program to be wrapped.
</para>
<para>The sub-attribute <literal>program</literal> is optional and
can give the wrapper program a new name. The default name is the same
as the attribute name itself.</para>
<para>Additionally, this option can set capabilities on a
wrapper program that propagates those capabilities down to the
wrapped, real program.</para>
<para>NOTE: cap_setpcap, which is required for the wrapper
program to be able to raise caps into the Ambient set is NOT
raised to the Ambient set so that the real program cannot
modify its own capabilities!! This may be too restrictive for
cases in which the real program needs cap_setpcap but it at
least leans on the side security paranoid vs. too
relaxed.</para>
</note>
''; '';
}; };
@ -151,13 +203,31 @@ in
###### implementation ###### implementation
config = { config = {
security.wrappers = { assertions = lib.mapAttrsToList
# These are mount related wrappers that require the +s permission. (name: opts:
fusermount.source = "${pkgs.fuse}/bin/fusermount"; { assertion = opts.setuid || opts.setgid -> opts.capabilities == "";
fusermount3.source = "${pkgs.fuse3}/bin/fusermount3"; message = ''
mount.source = "${lib.getBin pkgs.util-linux}/bin/mount"; The security.wrappers.${name} wrapper is not valid:
umount.source = "${lib.getBin pkgs.util-linux}/bin/umount"; setuid/setgid and capabilities are mutually exclusive.
}; '';
}
) wrappers;
security.wrappers =
let
mkSetuidRoot = source:
{ setuid = true;
owner = "root";
group = "root";
inherit source;
};
in
{ # These are mount related wrappers that require the +s permission.
fusermount = mkSetuidRoot "${pkgs.fuse}/bin/fusermount";
fusermount3 = mkSetuidRoot "${pkgs.fuse3}/bin/fusermount3";
mount = mkSetuidRoot "${lib.getBin pkgs.util-linux}/bin/mount";
umount = mkSetuidRoot "${lib.getBin pkgs.util-linux}/bin/umount";
};
boot.specialFileSystems.${parentWrapperDir} = { boot.specialFileSystems.${parentWrapperDir} = {
fsType = "tmpfs"; fsType = "tmpfs";
@ -179,19 +249,15 @@ in
]}" ]}"
''; '';
###### setcap activation script ###### wrappers activation script
system.activationScripts.wrappers = system.activationScripts.wrappers =
lib.stringAfter [ "specialfs" "users" ] lib.stringAfter [ "specialfs" "users" ]
'' ''
# Look in the system path and in the default profile for
# programs to be wrapped.
WRAPPER_PATH=${config.system.path}/bin:${config.system.path}/sbin
chmod 755 "${parentWrapperDir}" chmod 755 "${parentWrapperDir}"
# We want to place the tmpdirs for the wrappers to the parent dir. # We want to place the tmpdirs for the wrappers to the parent dir.
wrapperDir=$(mktemp --directory --tmpdir="${parentWrapperDir}" wrappers.XXXXXXXXXX) wrapperDir=$(mktemp --directory --tmpdir="${parentWrapperDir}" wrappers.XXXXXXXXXX)
chmod a+rx $wrapperDir chmod a+rx "$wrapperDir"
${lib.concatStringsSep "\n" mkWrappedPrograms} ${lib.concatStringsSep "\n" mkWrappedPrograms}
@ -199,16 +265,44 @@ in
# Atomically replace the symlink # Atomically replace the symlink
# See https://axialcorps.com/2013/07/03/atomically-replacing-files-and-directories/ # See https://axialcorps.com/2013/07/03/atomically-replacing-files-and-directories/
old=$(readlink -f ${wrapperDir}) old=$(readlink -f ${wrapperDir})
if [ -e ${wrapperDir}-tmp ]; then if [ -e "${wrapperDir}-tmp" ]; then
rm --force --recursive ${wrapperDir}-tmp rm --force --recursive "${wrapperDir}-tmp"
fi fi
ln --symbolic --force --no-dereference $wrapperDir ${wrapperDir}-tmp ln --symbolic --force --no-dereference "$wrapperDir" "${wrapperDir}-tmp"
mv --no-target-directory ${wrapperDir}-tmp ${wrapperDir} mv --no-target-directory "${wrapperDir}-tmp" "${wrapperDir}"
rm --force --recursive $old rm --force --recursive "$old"
else else
# For initial setup # For initial setup
ln --symbolic $wrapperDir ${wrapperDir} ln --symbolic "$wrapperDir" "${wrapperDir}"
fi fi
''; '';
###### wrappers consistency checks
system.extraDependencies = lib.singleton (pkgs.runCommandLocal
"ensure-all-wrappers-paths-exist" { }
''
# make sure we produce output
mkdir -p $out
echo -n "Checking that Nix store paths of all wrapped programs exist... "
declare -A wrappers
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v:
"wrappers['${n}']='${v.source}'") wrappers)}
for name in "''${!wrappers[@]}"; do
path="''${wrappers[$name]}"
if [[ "$path" =~ /nix/store ]] && [ ! -e "$path" ]; then
test -t 1 && echo -ne '\033[1;31m'
echo "FAIL"
echo "The path $path does not exist!"
echo 'Please, check the value of `security.wrappers."'$name'".source`.'
test -t 1 && echo -ne '\033[0m'
exit 1
fi
done
echo "OK"
'');
}; };
} }

View file

@ -52,8 +52,10 @@ with lib;
security.pam.services.login.enableGnomeKeyring = true; security.pam.services.login.enableGnomeKeyring = true;
security.wrappers.gnome-keyring-daemon = { security.wrappers.gnome-keyring-daemon = {
source = "${pkgs.gnome.gnome-keyring}/bin/gnome-keyring-daemon"; owner = "root";
group = "root";
capabilities = "cap_ipc_lock=ep"; capabilities = "cap_ipc_lock=ep";
source = "${pkgs.gnome.gnome-keyring}/bin/gnome-keyring-daemon";
}; };
}; };

View file

@ -104,7 +104,12 @@ in
gid = config.ids.gids.exim; gid = config.ids.gids.exim;
}; };
security.wrappers.exim.source = "${cfg.package}/bin/exim"; security.wrappers.exim =
{ setuid = true;
owner = "root";
group = "root";
source = "${cfg.package}/bin/exim";
};
systemd.services.exim = { systemd.services.exim = {
description = "Exim Mail Daemon"; description = "Exim Mail Daemon";

View file

@ -1,4 +1,4 @@
{ config, lib, ... }: { config, options, lib, ... }:
with lib; with lib;
@ -11,6 +11,7 @@ with lib;
services.mail = { services.mail = {
sendmailSetuidWrapper = mkOption { sendmailSetuidWrapper = mkOption {
type = types.nullOr options.security.wrappers.type.nestedTypes.elemType;
default = null; default = null;
internal = true; internal = true;
description = '' description = ''

View file

@ -103,12 +103,15 @@ in {
}; };
security.wrappers.smtpctl = { security.wrappers.smtpctl = {
owner = "nobody";
group = "smtpq"; group = "smtpq";
setuid = false;
setgid = true; setgid = true;
source = "${cfg.package}/bin/smtpctl"; source = "${cfg.package}/bin/smtpctl";
}; };
services.mail.sendmailSetuidWrapper = mkIf cfg.setSendmail security.wrappers.smtpctl; services.mail.sendmailSetuidWrapper = mkIf cfg.setSendmail
security.wrappers.smtpctl // { program = "sendmail"; };
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"d /var/spool/smtpd 711 root - - -" "d /var/spool/smtpd 711 root - - -"

View file

@ -673,6 +673,7 @@ in
services.mail.sendmailSetuidWrapper = mkIf config.services.postfix.setSendmail { services.mail.sendmailSetuidWrapper = mkIf config.services.postfix.setSendmail {
program = "sendmail"; program = "sendmail";
source = "${pkgs.postfix}/bin/sendmail"; source = "${pkgs.postfix}/bin/sendmail";
owner = "nobody";
group = setgidGroup; group = setgidGroup;
setuid = false; setuid = false;
setgid = true; setgid = true;
@ -681,6 +682,7 @@ in
security.wrappers.mailq = { security.wrappers.mailq = {
program = "mailq"; program = "mailq";
source = "${pkgs.postfix}/bin/mailq"; source = "${pkgs.postfix}/bin/mailq";
owner = "nobody";
group = setgidGroup; group = setgidGroup;
setuid = false; setuid = false;
setgid = true; setgid = true;
@ -689,6 +691,7 @@ in
security.wrappers.postqueue = { security.wrappers.postqueue = {
program = "postqueue"; program = "postqueue";
source = "${pkgs.postfix}/bin/postqueue"; source = "${pkgs.postfix}/bin/postqueue";
owner = "nobody";
group = setgidGroup; group = setgidGroup;
setuid = false; setuid = false;
setgid = true; setgid = true;
@ -697,6 +700,7 @@ in
security.wrappers.postdrop = { security.wrappers.postdrop = {
program = "postdrop"; program = "postdrop";
source = "${pkgs.postfix}/bin/postdrop"; source = "${pkgs.postfix}/bin/postdrop";
owner = "nobody";
group = setgidGroup; group = setgidGroup;
setuid = false; setuid = false;
setgid = true; setgid = true;

View file

@ -45,8 +45,10 @@ in
environment.systemPackages = [ pkgs.mame ]; environment.systemPackages = [ pkgs.mame ];
security.wrappers."${mame}" = { security.wrappers."${mame}" = {
source = "${pkgs.mame}/bin/${mame}"; owner = "root";
group = "root";
capabilities = "cap_net_admin,cap_net_raw+eip"; capabilities = "cap_net_admin,cap_net_raw+eip";
source = "${pkgs.mame}/bin/${mame}";
}; };
systemd.services.mame = { systemd.services.mame = {

View file

@ -52,7 +52,12 @@ in
wants = [ "network.target" ]; wants = [ "network.target" ];
}; };
security.wrappers.screen.source = "${pkgs.screen}/bin/screen"; security.wrappers.screen =
{ setuid = true;
owner = "root";
group = "root";
source = "${pkgs.screen}/bin/screen";
};
}; };
meta.doc = ./weechat.xml; meta.doc = ./weechat.xml;

View file

@ -71,7 +71,12 @@ in
environment.systemPackages = [ pkgs.incron ]; environment.systemPackages = [ pkgs.incron ];
security.wrappers.incrontab.source = "${pkgs.incron}/bin/incrontab"; security.wrappers.incrontab =
{ setuid = true;
owner = "root";
group = "root";
source = "${pkgs.incron}/bin/incrontab";
};
# incron won't read symlinks # incron won't read symlinks
environment.etc."incron.d/system" = { environment.etc."incron.d/system" = {

View file

@ -262,7 +262,12 @@ in
}; };
security.wrappers = { security.wrappers = {
fping.source = "${pkgs.fping}/bin/fping"; fping =
{ setuid = true;
owner = "root";
group = "root";
source = "${pkgs.fping}/bin/fping";
};
}; };
systemd.services.zabbix-proxy = { systemd.services.zabbix-proxy = {

View file

@ -278,8 +278,18 @@ in
} }
]; ];
security.wrappers = { security.wrappers = {
fping.source = "${pkgs.fping}/bin/fping"; fping =
fping6.source = "${pkgs.fping}/bin/fping6"; { setuid = true;
owner = "root";
group = "root";
source = "${pkgs.fping}/bin/fping";
};
fping6 =
{ setuid = true;
owner = "root";
group = "root";
source = "${pkgs.fping}/bin/fping6";
};
}; };
environment.systemPackages = [ pkgs.fping ]; environment.systemPackages = [ pkgs.fping ];
users.users.${cfg.user} = { users.users.${cfg.user} = {

View file

@ -88,12 +88,14 @@ in {
source = "${pkgs.x2goserver}/lib/x2go/libx2go-server-db-sqlite3-wrapper.pl"; source = "${pkgs.x2goserver}/lib/x2go/libx2go-server-db-sqlite3-wrapper.pl";
owner = "x2go"; owner = "x2go";
group = "x2go"; group = "x2go";
setuid = false;
setgid = true; setgid = true;
}; };
security.wrappers.x2goprintWrapper = { security.wrappers.x2goprintWrapper = {
source = "${pkgs.x2goserver}/bin/x2goprint"; source = "${pkgs.x2goserver}/bin/x2goprint";
owner = "x2go"; owner = "x2go";
group = "x2go"; group = "x2go";
setuid = false;
setgid = true; setgid = true;
}; };

View file

@ -93,7 +93,12 @@ in
{ services.cron.enable = mkDefault (allFiles != []); } { services.cron.enable = mkDefault (allFiles != []); }
(mkIf (config.services.cron.enable) { (mkIf (config.services.cron.enable) {
security.wrappers.crontab.source = "${cronNixosPkg}/bin/crontab"; security.wrappers.crontab =
{ setuid = true;
owner = "root";
group = "root";
source = "${cronNixosPkg}/bin/crontab";
};
environment.systemPackages = [ cronNixosPkg ]; environment.systemPackages = [ cronNixosPkg ];
environment.etc.crontab = environment.etc.crontab =
{ source = pkgs.runCommand "crontabs" { inherit allFiles; preferLocalBuild = true; } { source = pkgs.runCommand "crontabs" { inherit allFiles; preferLocalBuild = true; }

View file

@ -136,10 +136,13 @@ in
owner = "fcron"; owner = "fcron";
group = "fcron"; group = "fcron";
setgid = true; setgid = true;
setuid = false;
}; };
fcronsighup = { fcronsighup = {
source = "${pkgs.fcron}/bin/fcronsighup"; source = "${pkgs.fcron}/bin/fcronsighup";
owner = "root";
group = "fcron"; group = "fcron";
setuid = true;
}; };
}; };
systemd.services.fcron = { systemd.services.fcron = {

View file

@ -44,8 +44,10 @@ in
security.wrappers = mkIf cfg.enableSysAdminCapability { security.wrappers = mkIf cfg.enableSysAdminCapability {
replay-sorcery = { replay-sorcery = {
source = "${pkgs.replay-sorcery}/bin/replay-sorcery"; owner = "root";
group = "root";
capabilities = "cap_sys_admin+ep"; capabilities = "cap_sys_admin+ep";
source = "${pkgs.replay-sorcery}/bin/replay-sorcery";
}; };
}; };

View file

@ -49,9 +49,10 @@ in {
users.groups.mail = {}; users.groups.mail = {};
security.wrappers = { security.wrappers = {
dtmail = { dtmail = {
source = "${pkgs.cdesktopenv}/bin/dtmail";
group = "mail";
setgid = true; setgid = true;
owner = "nobody";
group = "mail";
source = "${pkgs.cdesktopenv}/bin/dtmail";
}; };
}; };

View file

@ -65,9 +65,24 @@ in
# Wrappers for programs installed by enlightenment that should be setuid # Wrappers for programs installed by enlightenment that should be setuid
security.wrappers = { security.wrappers = {
enlightenment_ckpasswd.source = "${pkgs.enlightenment.enlightenment}/lib/enlightenment/utils/enlightenment_ckpasswd"; enlightenment_ckpasswd =
enlightenment_sys.source = "${pkgs.enlightenment.enlightenment}/lib/enlightenment/utils/enlightenment_sys"; { setuid = true;
enlightenment_system.source = "${pkgs.enlightenment.enlightenment}/lib/enlightenment/utils/enlightenment_system"; owner = "root";
group = "root";
source = "${pkgs.enlightenment.enlightenment}/lib/enlightenment/utils/enlightenment_ckpasswd";
};
enlightenment_sys =
{ setuid = true;
owner = "root";
group = "root";
source = "${pkgs.enlightenment.enlightenment}/lib/enlightenment/utils/enlightenment_sys";
};
enlightenment_system =
{ setuid = true;
owner = "root";
group = "root";
source = "${pkgs.enlightenment.enlightenment}/lib/enlightenment/utils/enlightenment_system";
};
}; };
environment.etc."X11/xkb".source = xcfg.xkbDir; environment.etc."X11/xkb".source = xcfg.xkbDir;

View file

@ -197,12 +197,24 @@ in
}; };
security.wrappers = { security.wrappers = {
kcheckpass.source = "${lib.getBin libsForQt5.kscreenlocker}/libexec/kcheckpass"; kcheckpass =
start_kdeinit.source = "${lib.getBin libsForQt5.kinit}/libexec/kf5/start_kdeinit"; { setuid = true;
kwin_wayland = { owner = "root";
source = "${lib.getBin plasma5.kwin}/bin/kwin_wayland"; group = "root";
capabilities = "cap_sys_nice+ep"; source = "${lib.getBin libsForQt5.kscreenlocker}/libexec/kcheckpass";
}; };
start_kdeinit =
{ setuid = true;
owner = "root";
group = "root";
source = "${lib.getBin libsForQt5.kinit}/libexec/kf5/start_kdeinit";
};
kwin_wayland =
{ owner = "root";
group = "root";
capabilities = "cap_sys_nice+ep";
source = "${lib.getBin plasma5.kwin}/bin/kwin_wayland";
};
}; };
# DDC support # DDC support

View file

@ -7,8 +7,18 @@ with lib;
config = mkIf (any (fs: fs == "ecryptfs") config.boot.supportedFilesystems) { config = mkIf (any (fs: fs == "ecryptfs") config.boot.supportedFilesystems) {
system.fsPackages = [ pkgs.ecryptfs ]; system.fsPackages = [ pkgs.ecryptfs ];
security.wrappers = { security.wrappers = {
"mount.ecryptfs_private".source = "${pkgs.ecryptfs.out}/bin/mount.ecryptfs_private"; "mount.ecryptfs_private" =
"umount.ecryptfs_private".source = "${pkgs.ecryptfs.out}/bin/umount.ecryptfs_private"; { setuid = true;
owner = "root";
group = "root";
source = "${pkgs.ecryptfs.out}/bin/mount.ecryptfs_private";
};
"umount.ecryptfs_private" =
{ setuid = true;
owner = "root";
group = "root";
source = "${pkgs.ecryptfs.out}/bin/umount.ecryptfs_private";
};
}; };
}; };
} }

View file

@ -1133,11 +1133,16 @@ in
# kernel because we need the ambient capability # kernel because we need the ambient capability
security.wrappers = if (versionAtLeast (getVersion config.boot.kernelPackages.kernel) "4.3") then { security.wrappers = if (versionAtLeast (getVersion config.boot.kernelPackages.kernel) "4.3") then {
ping = { ping = {
source = "${pkgs.iputils.out}/bin/ping"; owner = "root";
group = "root";
capabilities = "cap_net_raw+p"; capabilities = "cap_net_raw+p";
source = "${pkgs.iputils.out}/bin/ping";
}; };
} else { } else {
ping.source = "${pkgs.iputils.out}/bin/ping"; setuid = true;
owner = "root";
group = "root";
source = "${pkgs.iputils.out}/bin/ping";
}; };
security.apparmor.policies."bin.ping".profile = lib.mkIf config.security.apparmor.policies."bin.ping".enable (lib.mkAfter '' security.apparmor.policies."bin.ping".profile = lib.mkIf config.security.apparmor.policies."bin.ping".enable (lib.mkAfter ''
/run/wrappers/bin/ping { /run/wrappers/bin/ping {

View file

@ -183,6 +183,9 @@ in {
}; };
security.wrappers.qemu-bridge-helper = { security.wrappers.qemu-bridge-helper = {
setuid = true;
owner = "root";
group = "root";
source = "/run/${dirName}/nix-helpers/qemu-bridge-helper"; source = "/run/${dirName}/nix-helpers/qemu-bridge-helper";
}; };

View file

@ -14,9 +14,11 @@
config = lib.mkIf config.virtualisation.spiceUSBRedirection.enable { config = lib.mkIf config.virtualisation.spiceUSBRedirection.enable {
environment.systemPackages = [ pkgs.spice-gtk ]; # For polkit actions environment.systemPackages = [ pkgs.spice-gtk ]; # For polkit actions
security.wrappers.spice-client-glib-usb-acl-helper ={ security.wrappers.spice-client-glib-usb-acl-helper = {
source = "${pkgs.spice-gtk}/bin/spice-client-glib-usb-acl-helper"; owner = "root";
group = "root";
capabilities = "cap_fowner+ep"; capabilities = "cap_fowner+ep";
source = "${pkgs.spice-gtk}/bin/spice-client-glib-usb-acl-helper";
}; };
}; };