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