Merge pull request #126289 from rnhmjoj/wrappers
nixos/security/wrappers: make well-typed
This commit is contained in:
commit
ceb2e6667b
@ -278,6 +278,16 @@
|
||||
<section xml:id="sec-release-21.11-incompatibilities">
|
||||
<title>Backward Incompatibilities</title>
|
||||
<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>
|
||||
<para>
|
||||
The <literal>paperless</literal> module and package have been
|
||||
|
@ -88,6 +88,8 @@ subsonic-compatible api. Available as [navidrome](#opt-services.navidrome.enable
|
||||
|
||||
## 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
|
||||
successor `paperless-ng` instead. The Paperless project [has been
|
||||
|
@ -22,8 +22,10 @@ in {
|
||||
config = mkIf cfg.enable {
|
||||
environment.systemPackages = with pkgs; [ bandwhich ];
|
||||
security.wrappers.bandwhich = {
|
||||
source = "${pkgs.bandwhich}/bin/bandwhich";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_net_raw,cap_net_admin+ep";
|
||||
source = "${pkgs.bandwhich}/bin/bandwhich";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -105,11 +105,15 @@ in
|
||||
);
|
||||
|
||||
security.wrappers.udhcpc = {
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_net_raw+p";
|
||||
source = "${pkgs.busybox}/bin/udhcpc";
|
||||
};
|
||||
|
||||
security.wrappers.captive-browser = {
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_net_raw+p";
|
||||
source = pkgs.writeShellScript "captive-browser" ''
|
||||
export PREV_CONFIG_HOME="$XDG_CONFIG_HOME"
|
||||
|
@ -28,7 +28,9 @@ in {
|
||||
|
||||
# "nix-ccache --show-stats" and "nix-ccache --clear"
|
||||
security.wrappers.nix-ccache = {
|
||||
owner = "nobody";
|
||||
group = "nixbld";
|
||||
setuid = false;
|
||||
setgid = true;
|
||||
source = pkgs.writeScript "nix-ccache.pl" ''
|
||||
#!${pkgs.perl}/bin/perl
|
||||
|
@ -81,7 +81,12 @@ in {
|
||||
};
|
||||
|
||||
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 ];
|
||||
};
|
||||
|
@ -56,6 +56,8 @@ in
|
||||
polkit.enable = true;
|
||||
wrappers = mkIf cfg.enableRenice {
|
||||
gamemoded = {
|
||||
owner = "root";
|
||||
group = "root";
|
||||
source = "${pkgs.gamemode}/bin/gamemoded";
|
||||
capabilities = "cap_sys_nice+ep";
|
||||
};
|
||||
|
@ -11,8 +11,10 @@ in {
|
||||
config = mkIf cfg.enable {
|
||||
environment.systemPackages = [ pkgs.iftop ];
|
||||
security.wrappers.iftop = {
|
||||
source = "${pkgs.iftop}/bin/iftop";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_net_raw+p";
|
||||
source = "${pkgs.iftop}/bin/iftop";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -10,8 +10,10 @@ in {
|
||||
};
|
||||
config = mkIf cfg.enable {
|
||||
security.wrappers.iotop = {
|
||||
source = "${pkgs.iotop}/bin/iotop";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_net_admin+p";
|
||||
source = "${pkgs.iotop}/bin/iotop";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -11,6 +11,11 @@ in
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
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";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -13,8 +13,10 @@ in {
|
||||
security.wrappers = mkMerge (map (
|
||||
exec: {
|
||||
"${exec}" = {
|
||||
source = "${pkgs.liboping}/bin/${exec}";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_net_raw+p";
|
||||
source = "${pkgs.liboping}/bin/${exec}";
|
||||
};
|
||||
}
|
||||
) [ "oping" "noping" ]);
|
||||
|
@ -78,6 +78,8 @@ in {
|
||||
source = "${pkgs.msmtp}/bin/sendmail";
|
||||
setuid = false;
|
||||
setgid = false;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
};
|
||||
|
||||
environment.etc."msmtprc".text = let
|
||||
|
@ -31,8 +31,10 @@ in {
|
||||
environment.systemPackages = with pkgs; [ cfg.package ];
|
||||
|
||||
security.wrappers.mtr-packet = {
|
||||
source = "${cfg.package}/bin/mtr-packet";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_net_raw+p";
|
||||
source = "${cfg.package}/bin/mtr-packet";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -18,8 +18,10 @@ in {
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
security.wrappers.noisetorch = {
|
||||
source = "${cfg.package}/bin/noisetorch";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_sys_resource=+ep";
|
||||
source = "${cfg.package}/bin/noisetorch";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -43,6 +43,13 @@ let
|
||||
|
||||
'';
|
||||
|
||||
mkSetuidRoot = source:
|
||||
{ setuid = true;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
inherit source;
|
||||
};
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
@ -109,14 +116,14 @@ in
|
||||
};
|
||||
|
||||
security.wrappers = {
|
||||
su.source = "${pkgs.shadow.su}/bin/su";
|
||||
sg.source = "${pkgs.shadow.out}/bin/sg";
|
||||
newgrp.source = "${pkgs.shadow.out}/bin/newgrp";
|
||||
newuidmap.source = "${pkgs.shadow.out}/bin/newuidmap";
|
||||
newgidmap.source = "${pkgs.shadow.out}/bin/newgidmap";
|
||||
su = mkSetuidRoot "${pkgs.shadow.su}/bin/su";
|
||||
sg = mkSetuidRoot "${pkgs.shadow.out}/bin/sg";
|
||||
newgrp = mkSetuidRoot "${pkgs.shadow.out}/bin/newgrp";
|
||||
newuidmap = mkSetuidRoot "${pkgs.shadow.out}/bin/newuidmap";
|
||||
newgidmap = mkSetuidRoot "${pkgs.shadow.out}/bin/newgidmap";
|
||||
} // lib.optionalAttrs config.users.mutableUsers {
|
||||
chsh.source = "${pkgs.shadow.out}/bin/chsh";
|
||||
passwd.source = "${pkgs.shadow.out}/bin/passwd";
|
||||
chsh = mkSetuidRoot "${pkgs.shadow.out}/bin/chsh";
|
||||
passwd = mkSetuidRoot "${pkgs.shadow.out}/bin/passwd";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -16,7 +16,12 @@ in {
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
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 = [
|
||||
"d /var/singularity/mnt/session 0770 root root -"
|
||||
"d /var/singularity/mnt/final 0770 root root -"
|
||||
|
@ -21,6 +21,11 @@ in
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
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";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -181,6 +181,8 @@ in
|
||||
source = "${pkgs.ssmtp}/bin/sendmail";
|
||||
setuid = false;
|
||||
setgid = false;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
};
|
||||
|
||||
};
|
||||
|
@ -19,8 +19,10 @@ in {
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
security.wrappers.traceroute = {
|
||||
source = "${pkgs.traceroute}/bin/traceroute";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_net_raw+p";
|
||||
source = "${pkgs.traceroute}/bin/traceroute";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -9,6 +9,11 @@ in {
|
||||
options.programs.udevil.enable = mkEnableOption "udevil";
|
||||
|
||||
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";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -21,8 +21,10 @@ in {
|
||||
config = mkIf cfg.enable {
|
||||
environment.systemPackages = with pkgs; [ wavemon ];
|
||||
security.wrappers.wavemon = {
|
||||
source = "${pkgs.wavemon}/bin/wavemon";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_net_admin+ep";
|
||||
source = "${pkgs.wavemon}/bin/wavemon";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -17,6 +17,11 @@ in {
|
||||
};
|
||||
|
||||
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";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -28,6 +28,11 @@ in
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
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}";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -241,8 +241,11 @@ in
|
||||
}
|
||||
];
|
||||
|
||||
security.wrappers = {
|
||||
doas.source = "${doas}/bin/doas";
|
||||
security.wrappers.doas =
|
||||
{ setuid = true;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
source = "${doas}/bin/doas";
|
||||
};
|
||||
|
||||
environment.systemPackages = [
|
||||
|
@ -186,7 +186,12 @@ in
|
||||
config = mkIf (cfg.ssh.enable || cfg.pam.enable) {
|
||||
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 = {
|
||||
login_duo = mkIf cfg.ssh.enable ''
|
||||
|
@ -869,9 +869,10 @@ in
|
||||
|
||||
security.wrappers = {
|
||||
unix_chkpwd = {
|
||||
source = "${pkgs.pam}/sbin/unix_chkpwd.orig";
|
||||
owner = "root";
|
||||
setuid = true;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
source = "${pkgs.pam}/sbin/unix_chkpwd.orig";
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -32,8 +32,18 @@ in
|
||||
|
||||
# Make sure pmount and pumount are setuid wrapped.
|
||||
security.wrappers = {
|
||||
pmount.source = "${pkgs.pmount.out}/bin/pmount";
|
||||
pumount.source = "${pkgs.pmount.out}/bin/pumount";
|
||||
pmount =
|
||||
{ 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 ];
|
||||
|
@ -83,8 +83,18 @@ in
|
||||
security.pam.services.polkit-1 = {};
|
||||
|
||||
security.wrappers = {
|
||||
pkexec.source = "${pkgs.polkit.bin}/bin/pkexec";
|
||||
polkit-agent-helper-1.source = "${pkgs.polkit.out}/lib/polkit-1/polkit-agent-helper-1";
|
||||
pkexec =
|
||||
{ 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 = [
|
||||
|
@ -5,85 +5,140 @@ let
|
||||
|
||||
parentWrapperDir = dirOf wrapperDir;
|
||||
|
||||
programs =
|
||||
(lib.mapAttrsToList
|
||||
(n: v: (if v ? program then v else v // {program=n;}))
|
||||
wrappers);
|
||||
|
||||
securityWrapper = pkgs.callPackage ./wrapper.nix {
|
||||
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
|
||||
mkSetcapProgram =
|
||||
{ program
|
||||
, capabilities
|
||||
, source
|
||||
, owner ? "nobody"
|
||||
, group ? "nogroup"
|
||||
, permissions ? "u+rx,g+x,o+x"
|
||||
, owner
|
||||
, group
|
||||
, permissions
|
||||
, ...
|
||||
}:
|
||||
assert (lib.versionAtLeast (lib.getVersion config.boot.kernelPackages.kernel) "4.3");
|
||||
''
|
||||
cp ${securityWrapper}/bin/security-wrapper $wrapperDir/${program}
|
||||
echo -n "${source}" > $wrapperDir/${program}.real
|
||||
cp ${securityWrapper}/bin/security-wrapper "$wrapperDir/${program}"
|
||||
echo -n "${source}" > "$wrapperDir/${program}.real"
|
||||
|
||||
# Prevent races
|
||||
chmod 0000 $wrapperDir/${program}
|
||||
chown ${owner}.${group} $wrapperDir/${program}
|
||||
chmod 0000 "$wrapperDir/${program}"
|
||||
chown ${owner}.${group} "$wrapperDir/${program}"
|
||||
|
||||
# Set desired capabilities on the file plus cap_setpcap so
|
||||
# the wrapper program can elevate the capabilities set on
|
||||
# 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
|
||||
chmod ${permissions} $wrapperDir/${program}
|
||||
chmod ${permissions} "$wrapperDir/${program}"
|
||||
'';
|
||||
|
||||
###### Activation script for the setuid wrappers
|
||||
mkSetuidProgram =
|
||||
{ program
|
||||
, source
|
||||
, owner ? "nobody"
|
||||
, group ? "nogroup"
|
||||
, setuid ? false
|
||||
, setgid ? false
|
||||
, permissions ? "u+rx,g+x,o+x"
|
||||
, owner
|
||||
, group
|
||||
, setuid
|
||||
, setgid
|
||||
, permissions
|
||||
, ...
|
||||
}:
|
||||
''
|
||||
cp ${securityWrapper}/bin/security-wrapper $wrapperDir/${program}
|
||||
echo -n "${source}" > $wrapperDir/${program}.real
|
||||
cp ${securityWrapper}/bin/security-wrapper "$wrapperDir/${program}"
|
||||
echo -n "${source}" > "$wrapperDir/${program}.real"
|
||||
|
||||
# Prevent races
|
||||
chmod 0000 $wrapperDir/${program}
|
||||
chown ${owner}.${group} $wrapperDir/${program}
|
||||
chmod 0000 "$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 =
|
||||
builtins.map
|
||||
(s: if (s ? capabilities)
|
||||
then mkSetcapProgram
|
||||
({ owner = "root";
|
||||
group = "root";
|
||||
} // s)
|
||||
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;
|
||||
(opts:
|
||||
if opts.capabilities != ""
|
||||
then mkSetcapProgram opts
|
||||
else mkSetuidProgram opts
|
||||
) (lib.attrValues wrappers);
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
@ -95,45 +150,42 @@ in
|
||||
|
||||
options = {
|
||||
security.wrappers = lib.mkOption {
|
||||
type = lib.types.attrs;
|
||||
type = lib.types.attrsOf wrapperType;
|
||||
default = {};
|
||||
example = lib.literalExample
|
||||
''
|
||||
{ sendmail.source = "/nix/store/.../bin/sendmail";
|
||||
ping = {
|
||||
source = "${pkgs.iputils.out}/bin/ping";
|
||||
owner = "nobody";
|
||||
group = "nogroup";
|
||||
{
|
||||
# a setuid root program
|
||||
doas =
|
||||
{ setuid = true;
|
||||
owner = "root";
|
||||
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 = ''
|
||||
This option allows the ownership and permissions on the setuid
|
||||
wrappers for specific programs to be overridden from the
|
||||
default (setuid root, but not setgid root).
|
||||
|
||||
<note>
|
||||
<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>
|
||||
This option effectively allows adding setuid/setgid bits, capabilities,
|
||||
changing file ownership and permissions of a program without directly
|
||||
modifying it. This works by creating a wrapper program under the
|
||||
<option>security.wrapperDir</option> directory, which is then added to
|
||||
the shell <literal>PATH</literal>.
|
||||
'';
|
||||
};
|
||||
|
||||
@ -151,12 +203,30 @@ in
|
||||
###### implementation
|
||||
config = {
|
||||
|
||||
security.wrappers = {
|
||||
# These are mount related wrappers that require the +s permission.
|
||||
fusermount.source = "${pkgs.fuse}/bin/fusermount";
|
||||
fusermount3.source = "${pkgs.fuse3}/bin/fusermount3";
|
||||
mount.source = "${lib.getBin pkgs.util-linux}/bin/mount";
|
||||
umount.source = "${lib.getBin pkgs.util-linux}/bin/umount";
|
||||
assertions = lib.mapAttrsToList
|
||||
(name: opts:
|
||||
{ assertion = opts.setuid || opts.setgid -> opts.capabilities == "";
|
||||
message = ''
|
||||
The security.wrappers.${name} wrapper is not valid:
|
||||
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} = {
|
||||
@ -179,19 +249,15 @@ in
|
||||
]}"
|
||||
'';
|
||||
|
||||
###### setcap activation script
|
||||
###### wrappers activation script
|
||||
system.activationScripts.wrappers =
|
||||
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}"
|
||||
|
||||
# We want to place the tmpdirs for the wrappers to the parent dir.
|
||||
wrapperDir=$(mktemp --directory --tmpdir="${parentWrapperDir}" wrappers.XXXXXXXXXX)
|
||||
chmod a+rx $wrapperDir
|
||||
chmod a+rx "$wrapperDir"
|
||||
|
||||
${lib.concatStringsSep "\n" mkWrappedPrograms}
|
||||
|
||||
@ -199,16 +265,44 @@ in
|
||||
# Atomically replace the symlink
|
||||
# See https://axialcorps.com/2013/07/03/atomically-replacing-files-and-directories/
|
||||
old=$(readlink -f ${wrapperDir})
|
||||
if [ -e ${wrapperDir}-tmp ]; then
|
||||
rm --force --recursive ${wrapperDir}-tmp
|
||||
if [ -e "${wrapperDir}-tmp" ]; then
|
||||
rm --force --recursive "${wrapperDir}-tmp"
|
||||
fi
|
||||
ln --symbolic --force --no-dereference $wrapperDir ${wrapperDir}-tmp
|
||||
mv --no-target-directory ${wrapperDir}-tmp ${wrapperDir}
|
||||
rm --force --recursive $old
|
||||
ln --symbolic --force --no-dereference "$wrapperDir" "${wrapperDir}-tmp"
|
||||
mv --no-target-directory "${wrapperDir}-tmp" "${wrapperDir}"
|
||||
rm --force --recursive "$old"
|
||||
else
|
||||
# For initial setup
|
||||
ln --symbolic $wrapperDir ${wrapperDir}
|
||||
ln --symbolic "$wrapperDir" "${wrapperDir}"
|
||||
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"
|
||||
'');
|
||||
};
|
||||
}
|
||||
|
@ -52,8 +52,10 @@ with lib;
|
||||
security.pam.services.login.enableGnomeKeyring = true;
|
||||
|
||||
security.wrappers.gnome-keyring-daemon = {
|
||||
source = "${pkgs.gnome.gnome-keyring}/bin/gnome-keyring-daemon";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_ipc_lock=ep";
|
||||
source = "${pkgs.gnome.gnome-keyring}/bin/gnome-keyring-daemon";
|
||||
};
|
||||
|
||||
};
|
||||
|
@ -104,7 +104,12 @@ in
|
||||
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 = {
|
||||
description = "Exim Mail Daemon";
|
||||
|
@ -1,4 +1,4 @@
|
||||
{ config, lib, ... }:
|
||||
{ config, options, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
@ -11,6 +11,7 @@ with lib;
|
||||
services.mail = {
|
||||
|
||||
sendmailSetuidWrapper = mkOption {
|
||||
type = types.nullOr options.security.wrappers.type.nestedTypes.elemType;
|
||||
default = null;
|
||||
internal = true;
|
||||
description = ''
|
||||
|
@ -103,12 +103,15 @@ in {
|
||||
};
|
||||
|
||||
security.wrappers.smtpctl = {
|
||||
owner = "nobody";
|
||||
group = "smtpq";
|
||||
setuid = false;
|
||||
setgid = true;
|
||||
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 = [
|
||||
"d /var/spool/smtpd 711 root - - -"
|
||||
|
@ -673,6 +673,7 @@ in
|
||||
services.mail.sendmailSetuidWrapper = mkIf config.services.postfix.setSendmail {
|
||||
program = "sendmail";
|
||||
source = "${pkgs.postfix}/bin/sendmail";
|
||||
owner = "nobody";
|
||||
group = setgidGroup;
|
||||
setuid = false;
|
||||
setgid = true;
|
||||
@ -681,6 +682,7 @@ in
|
||||
security.wrappers.mailq = {
|
||||
program = "mailq";
|
||||
source = "${pkgs.postfix}/bin/mailq";
|
||||
owner = "nobody";
|
||||
group = setgidGroup;
|
||||
setuid = false;
|
||||
setgid = true;
|
||||
@ -689,6 +691,7 @@ in
|
||||
security.wrappers.postqueue = {
|
||||
program = "postqueue";
|
||||
source = "${pkgs.postfix}/bin/postqueue";
|
||||
owner = "nobody";
|
||||
group = setgidGroup;
|
||||
setuid = false;
|
||||
setgid = true;
|
||||
@ -697,6 +700,7 @@ in
|
||||
security.wrappers.postdrop = {
|
||||
program = "postdrop";
|
||||
source = "${pkgs.postfix}/bin/postdrop";
|
||||
owner = "nobody";
|
||||
group = setgidGroup;
|
||||
setuid = false;
|
||||
setgid = true;
|
||||
|
@ -45,8 +45,10 @@ in
|
||||
environment.systemPackages = [ pkgs.mame ];
|
||||
|
||||
security.wrappers."${mame}" = {
|
||||
source = "${pkgs.mame}/bin/${mame}";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_net_admin,cap_net_raw+eip";
|
||||
source = "${pkgs.mame}/bin/${mame}";
|
||||
};
|
||||
|
||||
systemd.services.mame = {
|
||||
|
@ -52,7 +52,12 @@ in
|
||||
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;
|
||||
|
@ -71,7 +71,12 @@ in
|
||||
|
||||
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
|
||||
environment.etc."incron.d/system" = {
|
||||
|
@ -262,7 +262,12 @@ in
|
||||
};
|
||||
|
||||
security.wrappers = {
|
||||
fping.source = "${pkgs.fping}/bin/fping";
|
||||
fping =
|
||||
{ setuid = true;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
source = "${pkgs.fping}/bin/fping";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.zabbix-proxy = {
|
||||
|
@ -278,8 +278,18 @@ in
|
||||
}
|
||||
];
|
||||
security.wrappers = {
|
||||
fping.source = "${pkgs.fping}/bin/fping";
|
||||
fping6.source = "${pkgs.fping}/bin/fping6";
|
||||
fping =
|
||||
{ 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 ];
|
||||
users.users.${cfg.user} = {
|
||||
|
@ -88,12 +88,14 @@ in {
|
||||
source = "${pkgs.x2goserver}/lib/x2go/libx2go-server-db-sqlite3-wrapper.pl";
|
||||
owner = "x2go";
|
||||
group = "x2go";
|
||||
setuid = false;
|
||||
setgid = true;
|
||||
};
|
||||
security.wrappers.x2goprintWrapper = {
|
||||
source = "${pkgs.x2goserver}/bin/x2goprint";
|
||||
owner = "x2go";
|
||||
group = "x2go";
|
||||
setuid = false;
|
||||
setgid = true;
|
||||
};
|
||||
|
||||
|
@ -93,7 +93,12 @@ in
|
||||
|
||||
{ services.cron.enable = mkDefault (allFiles != []); }
|
||||
(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.etc.crontab =
|
||||
{ source = pkgs.runCommand "crontabs" { inherit allFiles; preferLocalBuild = true; }
|
||||
|
@ -136,10 +136,13 @@ in
|
||||
owner = "fcron";
|
||||
group = "fcron";
|
||||
setgid = true;
|
||||
setuid = false;
|
||||
};
|
||||
fcronsighup = {
|
||||
source = "${pkgs.fcron}/bin/fcronsighup";
|
||||
owner = "root";
|
||||
group = "fcron";
|
||||
setuid = true;
|
||||
};
|
||||
};
|
||||
systemd.services.fcron = {
|
||||
|
@ -44,8 +44,10 @@ in
|
||||
|
||||
security.wrappers = mkIf cfg.enableSysAdminCapability {
|
||||
replay-sorcery = {
|
||||
source = "${pkgs.replay-sorcery}/bin/replay-sorcery";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_sys_admin+ep";
|
||||
source = "${pkgs.replay-sorcery}/bin/replay-sorcery";
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -49,9 +49,10 @@ in {
|
||||
users.groups.mail = {};
|
||||
security.wrappers = {
|
||||
dtmail = {
|
||||
source = "${pkgs.cdesktopenv}/bin/dtmail";
|
||||
group = "mail";
|
||||
setgid = true;
|
||||
owner = "nobody";
|
||||
group = "mail";
|
||||
source = "${pkgs.cdesktopenv}/bin/dtmail";
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -65,9 +65,24 @@ in
|
||||
|
||||
# Wrappers for programs installed by enlightenment that should be setuid
|
||||
security.wrappers = {
|
||||
enlightenment_ckpasswd.source = "${pkgs.enlightenment.enlightenment}/lib/enlightenment/utils/enlightenment_ckpasswd";
|
||||
enlightenment_sys.source = "${pkgs.enlightenment.enlightenment}/lib/enlightenment/utils/enlightenment_sys";
|
||||
enlightenment_system.source = "${pkgs.enlightenment.enlightenment}/lib/enlightenment/utils/enlightenment_system";
|
||||
enlightenment_ckpasswd =
|
||||
{ setuid = true;
|
||||
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;
|
||||
|
@ -197,11 +197,23 @@ in
|
||||
};
|
||||
|
||||
security.wrappers = {
|
||||
kcheckpass.source = "${lib.getBin libsForQt5.kscreenlocker}/libexec/kcheckpass";
|
||||
start_kdeinit.source = "${lib.getBin libsForQt5.kinit}/libexec/kf5/start_kdeinit";
|
||||
kwin_wayland = {
|
||||
source = "${lib.getBin plasma5.kwin}/bin/kwin_wayland";
|
||||
kcheckpass =
|
||||
{ setuid = true;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
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";
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -7,8 +7,18 @@ with lib;
|
||||
config = mkIf (any (fs: fs == "ecryptfs") config.boot.supportedFilesystems) {
|
||||
system.fsPackages = [ pkgs.ecryptfs ];
|
||||
security.wrappers = {
|
||||
"mount.ecryptfs_private".source = "${pkgs.ecryptfs.out}/bin/mount.ecryptfs_private";
|
||||
"umount.ecryptfs_private".source = "${pkgs.ecryptfs.out}/bin/umount.ecryptfs_private";
|
||||
"mount.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";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -1133,11 +1133,16 @@ in
|
||||
# kernel because we need the ambient capability
|
||||
security.wrappers = if (versionAtLeast (getVersion config.boot.kernelPackages.kernel) "4.3") then {
|
||||
ping = {
|
||||
source = "${pkgs.iputils.out}/bin/ping";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_net_raw+p";
|
||||
source = "${pkgs.iputils.out}/bin/ping";
|
||||
};
|
||||
} 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 ''
|
||||
/run/wrappers/bin/ping {
|
||||
|
@ -183,6 +183,9 @@ in {
|
||||
};
|
||||
|
||||
security.wrappers.qemu-bridge-helper = {
|
||||
setuid = true;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
source = "/run/${dirName}/nix-helpers/qemu-bridge-helper";
|
||||
};
|
||||
|
||||
|
@ -15,8 +15,10 @@
|
||||
config = lib.mkIf config.virtualisation.spiceUSBRedirection.enable {
|
||||
environment.systemPackages = [ pkgs.spice-gtk ]; # For polkit actions
|
||||
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";
|
||||
source = "${pkgs.spice-gtk}/bin/spice-client-glib-usb-acl-helper";
|
||||
};
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user