Allow supervisord and launchd to initialize as root by using nixproc-chainload-user to change identities

This commit is contained in:
Sander van der Burg 2021-02-16 20:02:16 +01:00 committed by Sander van der Burg
parent aa3900919c
commit 704eb98a42
9 changed files with 90 additions and 7 deletions

View File

@ -2,6 +2,7 @@
, stdenv
, writeTextFile
, runtimeDir ? "/var/run"
, forceDisableUserChange
}:
{ name
@ -30,10 +31,14 @@ let
inherit stdenv writeTextFile;
};
chainLoadUser = if initialize == "" || forceDisableUserChange then null
else user;
Program = if foregroundProcess != null then
if initialize == "" then foregroundProcess
else generateForegroundProxy ({
wrapDaemon = false;
user = chainLoadUser;
executable = foregroundProcess;
inherit name initialize runtimeDir stdenv;
} // stdenv.lib.optionalAttrs (instanceName != null) {
@ -43,6 +48,7 @@ let
})
else generateForegroundProxy ({
wrapDaemon = true;
user = chainLoadUser;
executable = daemon;
inherit name initialize runtimeDir stdenv;
} // stdenv.lib.optionalAttrs (instanceName != null) {
@ -66,7 +72,7 @@ let
Umask = umask;
} // stdenv.lib.optionalAttrs (nice != null) {
Nice = nice;
} // stdenv.lib.optionalAttrs (user != null) {
} // stdenv.lib.optionalAttrs (user != null && chainLoadUser == null) {
UserName = user;
};

View File

@ -1,4 +1,4 @@
{ createSupervisordProgram, stdenv, writeTextFile, runtimeDir }:
{ createSupervisordProgram, stdenv, writeTextFile, runtimeDir, forceDisableUserChange }:
{ name
, description
@ -26,10 +26,14 @@ let
inherit stdenv writeTextFile;
};
chainLoadUser = if initialize == "" || forceDisableUserChange then null
else user;
command = if foregroundProcess != null then
(if initialize == ""
then foregroundProcess
else generateForegroundProxy ({
user = chainLoadUser;
wrapDaemon = false;
executable = foregroundProcess;
inherit name initialize runtimeDir stdenv;
@ -40,6 +44,7 @@ let
})) + " ${stdenv.lib.escapeShellArgs foregroundProcessArgs}"
else (generateForegroundProxy ({
wrapDaemon = true;
user = chainLoadUser;
executable = daemon;
inherit name initialize runtimeDir stdenv;
} // stdenv.lib.optionalAttrs (instanceName != null) {
@ -56,7 +61,7 @@ let
inherit nice;
} // stdenv.lib.optionalAttrs (pidFile != null) {
inherit pidFile;
} // stdenv.lib.optionalAttrs (user != null) {
} // stdenv.lib.optionalAttrs (user != null && chainLoadUser == null) {
inherit user;
};

View File

@ -1,4 +1,5 @@
{stdenv, writeTextFile}:
{ name
, wrapDaemon
, initialize
@ -7,9 +8,12 @@
, runtimeDir
, instanceName ? null
, pidFile ? (if instanceName == null then null else "${runtimeDir}/${instanceName}.pid")
, user ? null
}:
let
chainload-user = (import ../../../tools {}).chainload-user;
_pidFile = if pidFile == null then "${runtimeDir}/$(basename ${executable}).pid" else pidFile;
in
writeTextFile {
@ -39,7 +43,7 @@ writeTextFile {
trap _interrupt SIGINT
# Start process in the background as a daemon
${executable} "$@"
${stdenv.lib.optionalString (user != null) "${chainload-user}/bin/nixproc-chainload-user ${user}"} ${executable} "$@"
# Wait for the PID file to become available. Useful to work with daemons that don't behave well enough.
count=1 # Start with 1, because 0 returns a non-zero exit status when incrementing it
@ -76,7 +80,7 @@ writeTextFile {
blocker_pid=$!
wait $blocker_pid
'' else ''
exec "${executable}" "$@"
exec ${stdenv.lib.optionalString (user != null) "${chainload-user}/bin/nixproc-chainload-user ${user} "}"${executable}" "$@"
''}
'';
executable = true;

View File

@ -13,6 +13,8 @@ result // {
auth required pam_deny.so
password sufficient pam_unix.so nullok sha512
EOF
sed -i -e "s|PATH=/bin:/usr/bin|PATH=/bin:/usr/bin:/nix/var/nix/profiles/default/bin|" /etc/login.defs
'';
contents = result.contents or [] ++ [ pkgs.su pkgs.shadow ];

View File

@ -46,7 +46,7 @@ let
};
generateSupervisordProgram = import ../../backends/supervisord/generate-supervisord-program.nix {
inherit createSupervisordProgram runtimeDir;
inherit createSupervisordProgram runtimeDir forceDisableUserChange;
inherit (pkgs) stdenv writeTextFile;
};
@ -72,7 +72,7 @@ let
generateLaunchdDaemon = import ../../backends/launchd/generate-launchd-daemon.nix {
inherit (pkgs) stdenv writeTextFile;
inherit createLaunchdDaemon runtimeDir;
inherit createLaunchdDaemon runtimeDir forceDisableUserChange;
};
createCygrunsrvParams = import ../../backends/cygrunsrv/create-cygrunsrv-params.nix {

View File

@ -0,0 +1,12 @@
CC = cc
all:
$(CC) $(CFLAGS) $(LDFLAGS) main.c -o nixproc-chainload-user
install: all
install -d -m755 $(PREFIX)/bin
install -m755 nixproc-chainload-user $(PREFIX)/bin
clean:
rm -f *.o
rm -f nixproc-chainload-user

View File

@ -0,0 +1,8 @@
{stdenv}:
stdenv.mkDerivation {
name = "nixproc-chainload-user";
src = ./.;
makeFlags = "PREFIX=$(out)";
dontStrip = true;
}

View File

@ -0,0 +1,42 @@
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <pwd.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
if(argc < 3)
{
printf("Usage: %s username command [args]\n", argv[0]);
return 1;
}
else
{
char *username = argv[1];
/* Query password entry for the user */
struct passwd *pwentry = getpwnam(username);
if(pwentry == NULL)
{
fprintf(stderr, "Cannot find password entry for user: %s\n", username);
return 1;
}
/* Change user permissions to the requested user */
if(setgid(pwentry->pw_gid) == 0 && setuid(pwentry->pw_uid) == 0)
{
/* Exec into the requested process */
char **cmd_argv = argv + 2;
execvp(cmd_argv[0], cmd_argv);
fprintf(stderr, "Cannot execute command: %s\n", cmd_argv[0]);
return 1;
}
else
{
fprintf(stderr, "Cannot change into user: %s with corresponding group!\n", username);
return 1;
}
}
}

View File

@ -3,6 +3,10 @@
}:
rec {
chainload-user = import ./chainload-user {
inherit (pkgs) stdenv;
};
common = import ./common {
inherit (pkgs) stdenv getopt;
};