]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sysusers: read passwords from the credentials logic
authorLennart Poettering <lennart@poettering.net>
Thu, 11 Mar 2021 09:34:20 +0000 (10:34 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 26 Mar 2021 11:20:52 +0000 (12:20 +0100)
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
src/sysusers/sysusers.c
units/systemd-sysusers.service

index 950a8b4499bbb9e306cbacd2bf94468929b9026c..466a4601514fffcaffef13e12c80af9f00774868 100644 (file)
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
     </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Credentials</title>
+
+    <para><command>systemd-sysusers</command> supports the service credentials logic as implemented by
+    <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
+    <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+    details). The following credentials are used when passed in:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><literal>passwd.hashed-password.<replaceable>user</replaceable></literal></term>
+        <listitem><para>A UNIX hashed password string to use for the specified user, when creating an entry
+        for it. This is particularly useful for the <literal>root</literal> 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 <filename>/etc/</filename> on every boot.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><literal>passwd.plaintext-password.<replaceable>user</replaceable></literal></term>
+
+        <listitem><para>Similar to <literal>passwd.hashed-password.<replaceable>user</replaceable></literal>
+        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.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><literal>passwd.shell.<replaceable>user</replaceable></literal></term>
+
+        <listitem><para>Specifies the shell binary to use for the the specified account when creating it.</para></listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>Note that by default the <filename>systemd-sysusers.service</filename> unit file is set up to
+    inherit the <literal>passwd.hashed-password.root</literal>,
+    <literal>passwd.plaintext-password.root</literal> and <literal>passwd.shell.root</literal> credentials
+    from the service manager. Thus, when invoking a container with an unpopulated <filename>/etc/</filename>
+    for the first time it is possible to configure the root user's password to be <literal>systemd</literal>
+    like this:</para>
+
+    <para><programlisting># systemd-nspawn --image=… --set-credential=password.hashed-password.root:'$y$j9T$yAuRJu1o5HioZAGDYPU5d.$F64ni6J2y2nNQve90M/p0ZP0ECP/qqzipNyaY9fjGpC' …</programlisting></para>
+
+    <para>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.</para>
 
+    <para>Use <citerefentry><refentrytitle>mkpasswd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    for generating UNIX password hashes from the command line.</para>
   </refsect1>
 
   <refsect1>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-      <ulink url="https://systemd.io/UIDS-GIDS">Users, Groups, UIDs and GIDs on systemd systems</ulink>
+      <ulink url="https://systemd.io/UIDS-GIDS">Users, Groups, UIDs and GIDs on systemd systems</ulink>,
+      <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>mkpasswd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index b098eb27cd46468fbeb711f2433a82d247da7ed5..9514098a336d67dfcaf1d5fe270f0c64db07f418 100644 (file)
@@ -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;
index ff5b3db82138f70bd5a2c7d45b3e8dba42603fc2..47373307b32a527bf9cb0caf9232afe11c3a4938 100644 (file)
@@ -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