From 99e9f896fb491d13c6d02f6f5bbfceabae833f05 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 11 Mar 2021 10:34:20 +0100 Subject: [PATCH] sysusers: read passwords from the credentials logic Let's make use of our own credentials infrastructure in our tools: let's hook up systemd-sysusers with the credentials logic, so that the root password can be provisioned this way. This is really useful when working with stateless systems, in particular nspawn's "--volatile=yes" switch, as this works now: # systemd-nspawn -i foo.raw --volatile=yes --set-credential=passwd.plaintext-password:foo For the first time we have a nice, non-interactive way to provision the root password for a fully stateless system from the container manager. Yay! --- man/systemd-sysusers.xml | 57 +++++++++++++++++++++++++++++++++- src/sysusers/sysusers.c | 47 ++++++++++++++++++++++++++++ units/systemd-sysusers.service | 7 +++++ 3 files changed, 110 insertions(+), 1 deletion(-) diff --git a/man/systemd-sysusers.xml b/man/systemd-sysusers.xml index 950a8b4499b..466a4601514 100644 --- a/man/systemd-sysusers.xml +++ b/man/systemd-sysusers.xml @@ -126,7 +126,60 @@ + + + Credentials + + systemd-sysusers supports the service credentials logic as implemented by + LoadCredential=/SetCredential= (see + systemd.exec1 for + details). The following credentials are used when passed in: + + + + passwd.hashed-password.user + A UNIX hashed password string to use for the specified user, when creating an entry + for it. This is particularly useful for the root user as it allows provisioning + the default root password to use via a unit file drop-in or from a container manager passing in this + credential. Note that setting this credential has no effect if the specified user account already + exists. This credential is hence primarily useful in first boot scenarios or systems that are fully + stateless and come up with an empty /etc/ on every boot. + + + + passwd.plaintext-password.user + + Similar to passwd.hashed-password.user + but expect a literal, plaintext password, which is then automatically hashed before used for the user + account. If both the hashed and the plaintext credential are specified for the same user the + former takes precedence. It's generally recommended to specify the hashed version; however in test + environments with weaker requirements on security it might be easier to pass passwords in plaintext + instead. + + + + passwd.shell.user + + Specifies the shell binary to use for the the specified account when creating it. + + + + Note that by default the systemd-sysusers.service unit file is set up to + inherit the passwd.hashed-password.root, + passwd.plaintext-password.root and passwd.shell.root credentials + from the service manager. Thus, when invoking a container with an unpopulated /etc/ + for the first time it is possible to configure the root user's password to be systemd + like this: + + # systemd-nspawn --image=… --set-credential=password.hashed-password.root:'$y$j9T$yAuRJu1o5HioZAGDYPU5d.$F64ni6J2y2nNQve90M/p0ZP0ECP/qqzipNyaY9fjGpC' … + + Note again that the data specified in these credentials is consulted only when creating an account + for the first time, it may not be used for changing the password or shell of an account that already + exists. + + Use mkpasswd1 + for generating UNIX password hashes from the command line. @@ -141,7 +194,9 @@ systemd1, sysusers.d5, - Users, Groups, UIDs and GIDs on systemd systems + Users, Groups, UIDs and GIDs on systemd systems, + systemd.exec1, + mkpasswd1 diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index b098eb27cd4..9514098a336 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -6,6 +6,7 @@ #include "alloc-util.h" #include "conf-files.h" #include "copy.h" +#include "creds-util.h" #include "def.h" #include "dissect-image.h" #include "fd-util.h" @@ -13,7 +14,9 @@ #include "format-util.h" #include "fs-util.h" #include "hashmap.h" +#include "libcrypt-util.h" #include "main-func.h" +#include "memory-util.h" #include "mount-util.h" #include "nscd-flush.h" #include "pager.h" @@ -429,6 +432,8 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char } ORDERED_HASHMAP_FOREACH(i, todo_uids) { + _cleanup_free_ char *creds_shell = NULL, *cn = NULL; + struct passwd n = { .pw_name = i->name, .pw_uid = i->uid, @@ -446,6 +451,17 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char .pw_shell = i->shell ?: (char*) default_shell(i->uid), }; + /* Try to pick up the shell for this account via the credentials logic */ + cn = strjoin("passwd.shell.", i->name); + if (!cn) + return -ENOMEM; + + r = read_credential(cn, (void**) &creds_shell, NULL); + if (r < 0) + log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn); + else + n.pw_shell = creds_shell; + r = putpwent_sane(&n, passwd); if (r < 0) return r; @@ -530,6 +546,9 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char } ORDERED_HASHMAP_FOREACH(i, todo_uids) { + _cleanup_(erase_and_freep) char *creds_password = NULL; + _cleanup_free_ char *cn = NULL; + struct spwd n = { .sp_namp = i->name, .sp_pwdp = (char*) "!*", /* lock this password, and make it invalid */ @@ -542,6 +561,34 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char .sp_flag = ULONG_MAX, /* this appears to be what everybody does ... */ }; + /* Try to pick up the password for this account via the credentials logic */ + cn = strjoin("passwd.hashed-password.", i->name); + if (!cn) + return -ENOMEM; + + r = read_credential(cn, (void**) &creds_password, NULL); + if (r == -ENOENT) { + _cleanup_(erase_and_freep) char *plaintext_password = NULL; + + free(cn); + cn = strjoin("passwd.plaintext-password.", i->name); + if (!cn) + return -ENOMEM; + + r = read_credential(cn, (void**) &plaintext_password, NULL); + if (r < 0) + log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn); + else { + r = hash_password(plaintext_password, &creds_password); + if (r < 0) + return log_debug_errno(r, "Failed to hash password: %m"); + } + } else if (r < 0) + log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn); + + if (creds_password) + n.sp_pwdp = creds_password; + r = putspent_sane(&n, shadow); if (r < 0) return r; diff --git a/units/systemd-sysusers.service b/units/systemd-sysusers.service index ff5b3db8213..47373307b32 100644 --- a/units/systemd-sysusers.service +++ b/units/systemd-sysusers.service @@ -21,3 +21,10 @@ Type=oneshot RemainAfterExit=yes ExecStart=systemd-sysusers TimeoutSec=90s + +# Optionally, pick up a root password and shell for the root user from a +# credential passed to the service manager. This is useful for importing this +# data from nspawn's --set-credential= switch. +LoadCredential=passwd.hashed-password.root +LoadCredential=passwd.plaintext-password.root +LoadCredential=passwd.shell.root