Merge pull request #26059 from DaanDeMeyer/cred-glob

creds: Add ImportCredential=
This commit is contained in:
Luca Boccassi
2023-06-08 20:40:37 +01:00
committed by GitHub
31 changed files with 478 additions and 165 deletions
+8 -7
View File
@@ -138,15 +138,16 @@ manager, please consider supporting the following interfaces.
`$container_host_version_id=10`
5. systemd supports passing immutable binary data blobs with limited size and
restricted access to services via the `LoadCredential=` and `SetCredential=`
settings. The same protocol may be used to pass credentials from the
container manager to systemd itself. The credential data should be placed in
some location (ideally a read-only and non-swappable file system, like
'ramfs'), and the absolute path to this directory exported in the
restricted access to services via the `ImportCredential=`, `LoadCredential=`
and `SetCredential=` settings. The same protocol may be used to pass credentials
from the container manager to systemd itself. The credential data should be
placed in some location (ideally a read-only and non-swappable file system,
like 'ramfs'), and the absolute path to this directory exported in the
`$CREDENTIALS_DIRECTORY` environment variable. If the container managers
does this, the credentials passed to the service manager can be propagated
to services via `LoadCredential=` (see ...). The container manager can
choose any path, but `/run/host/credentials` is recommended.
to services via `LoadCredential=` or `ImportCredential=` (see ...). The
container manager can choose any path, but `/run/host/credentials` is
recommended.
## Advanced Integration
+11 -7
View File
@@ -72,6 +72,9 @@ Within unit files, there are four settings to configure service credentials.
1. `LoadCredential=` may be used to load a credential from disk, from an
`AF_UNIX` socket, or propagate them from a system credential.
2. `ImportCredential=` may be used to load one or more (encrypted) credentials
from disk or from the credential stores.
2. `SetCredential=` may be used to set a credential to a literal string encoded
in the unit file. Because unit files are world-readable (both on disk and
via D-Bus), this should only be used for credentials that aren't sensitive,
@@ -323,7 +326,7 @@ systemd-creds --system cat mycred
Or propagated to services further down:
```
systemd-run -p LoadCredential=mycred -P --wait systemd-creds cat mycred
systemd-run -p ImportCredential=mycred -P --wait systemd-creds cat mycred
```
## Well-Known Credentials
@@ -430,13 +433,14 @@ a container manager or via qemu) and `/run/credentials/@encrypted/` (for
credentials that must be decrypted/validated before use, such as those from
`systemd-stub`).
The `LoadCredential=` and `LoadCredentialEncrypted=` settings when configured
with a relative source path will search for the source file to read the
credential from automatically. Primarily, these credentials are searched among
the credentials passed into the system. If not found there, they are searched
in `/etc/credstore/`, `/run/credstore/`,
The `ImportCredential=` setting (and the `LoadCredential=` and
`LoadCredentialEncrypted=` settings when configured with a relative source path)
will search for the source file to read the credential from automatically. Primarily,
these credentials are searched among the credentials passed into the system. If
not found there, they are searched in `/etc/credstore/`, `/run/credstore/`,
`/usr/lib/credstore/`. `LoadCredentialEncrypted=` will also search
`/etc/credstore.encrypted/` and similar directories. These directories are
`/etc/credstore.encrypted/` and similar directories. `ImportCredential` will search
both the non-encrypted and encrypted directories. These directories are
hence a great place to store credentials to load on the system.
## Conditionalizing Services
+24
View File
@@ -3042,6 +3042,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ss) LoadCredentialEncrypted = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as ImportCredential = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as SupplementaryGroups = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s PAMName = '...';
@@ -3627,6 +3629,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<!--property LoadCredentialEncrypted is not documented!-->
<!--property ImportCredential is not documented!-->
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
@@ -4277,6 +4281,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
<variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@@ -5058,6 +5064,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ss) LoadCredentialEncrypted = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as ImportCredential = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as SupplementaryGroups = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s PAMName = '...';
@@ -5655,6 +5663,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<!--property LoadCredentialEncrypted is not documented!-->
<!--property ImportCredential is not documented!-->
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
@@ -6285,6 +6295,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
<variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@@ -6941,6 +6953,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ss) LoadCredentialEncrypted = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as ImportCredential = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as SupplementaryGroups = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s PAMName = '...';
@@ -7466,6 +7480,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<!--property LoadCredentialEncrypted is not documented!-->
<!--property ImportCredential is not documented!-->
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
@@ -8014,6 +8030,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
<variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@@ -8797,6 +8815,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ss) LoadCredentialEncrypted = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as ImportCredential = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as SupplementaryGroups = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s PAMName = '...';
@@ -9308,6 +9328,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<!--property LoadCredentialEncrypted is not documented!-->
<!--property ImportCredential is not documented!-->
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
@@ -9842,6 +9864,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
<variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
+2 -2
View File
@@ -141,8 +141,8 @@
<varlistentry>
<term><option>--credential=</option></term>
<listitem><para>Configure a credential to read the password from – if it exists. This may be used in
conjunction with the <varname>LoadCredential=</varname> and <varname>SetCredential=</varname>
settings in unit files. See
conjunction with the <varname>ImportCredential=</varname>, <varname>LoadCredential=</varname> and
<varname>SetCredential=</varname> settings in unit files. See
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details. If not specified, defaults to <literal>password</literal>. This option has no effect if no
credentials directory is passed to the program (i.e. <varname>$CREDENTIALS_DIRECTORY</varname> is not
+3 -3
View File
@@ -38,9 +38,9 @@
processes. They are primarily used for passing cryptographic keys (both public and private) or
certificates, user account information or identity information from the host to services.</para>
<para>Credentials are configured in unit files via the <varname>LoadCredential=</varname>,
<varname>SetCredential=</varname>, <varname>LoadCredentialEncrypted=</varname> and
<varname>SetCredentialEncrypted=</varname> settings, see
<para>Credentials are configured in unit files via the <varname>ImportCredential></varname>,
<varname>LoadCredential=</varname>, <varname>SetCredential=</varname>,
<varname>LoadCredentialEncrypted=</varname> and <varname>SetCredentialEncrypted=</varname> settings, see
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details.</para>
+2 -2
View File
@@ -304,8 +304,8 @@
<title>Credentials</title>
<para><command>systemd-firstboot</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
<varname>ImportCredential=</varname>/<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>
+2 -2
View File
@@ -403,8 +403,8 @@ search foobar.com barbar.com
<title>Credentials</title>
<para><command>systemd-resolved</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
<varname>ImportCredential=</varname>/<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>
+2 -2
View File
@@ -85,8 +85,8 @@
<title>Credentials</title>
<para><command>systemd-sysctl</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
<varname>ImportCredential=</varname>/<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>
+2 -2
View File
@@ -139,8 +139,8 @@
<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
<varname>ImportCredential=</varname><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>
+2 -2
View File
@@ -246,8 +246,8 @@
<title>Credentials</title>
<para><command>systemd-tmpfiles</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
<varname>ImportCredential=</varname>/<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>
+2 -2
View File
@@ -53,8 +53,8 @@
<title>Credentials</title>
<para><command>systemd-vconsole-setup</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
<varname>ImportCredential=</varname><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>
+31 -9
View File
@@ -3286,6 +3286,25 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
Credentials</ulink> documentation.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>ImportCredential=</varname><replaceable>GLOB</replaceable></term>
<listitem><para>Pass one or more credentials to the unit. Takes a credential name for which we'll
attempt to find a credential that the service manager itself received under the specified name —
which may be used to propagate credentials from an invoking environment (e.g. a container manager
that invoked the service manager) into a service. If the credential name is a glob, all credentials
matching the glob are passed to the unit. Matching credentials are searched for in the system
credentials, the encrypted system credentials, and under <filename>/etc/credstore/</filename>,
<filename>/run/credstore/</filename>, <filename>/usr/lib/credstore/</filename>,
<filename>/run/credstore.encrypted/</filename>, <filename>/etc/credstore.encrypted/</filename>, and
<filename>/usr/lib/credstore.encrypted/</filename> in that order. When multiple credentials of the
same name are found, the first one found is used.</para>
<para>When multiple credentials of the same name are found, credentials found by
<varname>LoadCredential=</varname> and <varname>LoadCredentialEncrypted=</varname> take priority over
credentials found by <varname>ImportCredential=</varname></para></listitem>.
</varlistentry>
<varlistentry>
<term><varname>SetCredential=</varname><replaceable>ID</replaceable>:<replaceable>VALUE</replaceable></term>
<term><varname>SetCredentialEncrypted=</varname><replaceable>ID</replaceable>:<replaceable>VALUE</replaceable></term>
@@ -3307,10 +3326,13 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
directly from plaintext credentials. For further details see
<varname>LoadCredentialEncrypted=</varname> above.</para>
<para>If a credential of the same ID is listed in both <varname>LoadCredential=</varname> and
<varname>SetCredential=</varname>, the latter will act as default if the former cannot be
retrieved. In this case not being able to retrieve the credential from the path specified in
<varname>LoadCredential=</varname> is not considered fatal.</para></listitem>
<para>When multiple credentials of the same name are found, credentials found by
<varname>LoadCredential=</varname>, <varname>LoadCredentialEncrypted=</varname> and
<varname>ImportCredential=</varname> take priority over credentials found by
<varname>SetCredential=</varname>. As such, <varname>SetCredential=</varname> will act as default if
no credentials are found by any of the former. In this case not being able to retrieve the credential
from the path specified in <varname>LoadCredential=</varname> or
<varname>LoadCredentialEncrypted=</varname> is not considered fatal.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
@@ -3492,10 +3514,10 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
<term><varname>$CREDENTIALS_DIRECTORY</varname></term>
<listitem><para>An absolute path to the per-unit directory with credentials configured via
<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>. The directory is marked
read-only and is placed in unswappable memory (if supported and permitted), and is only accessible to
the UID associated with the unit via <varname>User=</varname> or <varname>DynamicUser=</varname> (and
the superuser).</para></listitem>
<varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>.
The directory is marked read-only and is placed in unswappable memory (if supported and permitted),
and is only accessible to the UID associated with the unit via <varname>User=</varname> or
<varname>DynamicUser=</varname> (and the superuser).</para></listitem>
</varlistentry>
<varlistentry>
@@ -4184,7 +4206,7 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
<row>
<entry>243</entry>
<entry><constant>EXIT_CREDENTIALS</constant></entry>
<entry>Failed to set up unit's credentials. See <varname>LoadCredential=</varname> and <varname>SetCredential=</varname> above.</entry>
<entry>Failed to set up unit's credentials. See <varname>ImportCredential=</varname>, <varname>LoadCredential=</varname> and <varname>SetCredential=</varname> above.</entry>
</row>
<row>
<entry>245</entry>
+1 -1
View File
@@ -623,7 +623,7 @@
credential <literal><replaceable>LINK</replaceable>.link.wol.password</literal> (e.g.,
<literal>60-foo.link.wol.password</literal>), and if the credential not found, then
read from <literal>wol.password</literal>. See
<varname>LoadCredential=</varname>/<varname>SetCredential=</varname> in
<varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname> in
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for details. The password in the credential, must be 6 bytes in hex format with each
byte separated by a colon (<literal>:</literal>) like an Ethernet MAC address, e.g.,
+1 -1
View File
@@ -934,7 +934,7 @@
<term><varname>systemd.set_credential=</varname></term>
<listitem><para>Sets a system credential, which can then be propagated to system services using the
<varname>LoadCredential=</varname> setting, see
<varname>ImportCredential=</varname> or <varname>LoadCredential=</varname> setting, see
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details. Takes a pair of credential name and value, separated by a colon. Note that the kernel
command line is typically accessible by unprivileged programs in
+2 -2
View File
@@ -44,8 +44,8 @@ static int help(void) {
" --id=ID Query identifier (e.g. \"cryptsetup:/dev/sda5\")\n"
" --keyname=NAME Kernel key name for caching passwords (e.g. \"cryptsetup\")\n"
" --credential=NAME\n"
" Credential name for LoadCredential=/SetCredential=\n"
" credentials\n"
" Credential name for ImportCredential=, LoadCredential= or\n"
" SetCredential= credentials\n"
" --timeout=SEC Timeout in seconds\n"
" --echo=yes|no|masked\n"
" Control whether to show password while typing (echo)\n"
+77 -32
View File
@@ -26,6 +26,7 @@
#include "io-util.h"
#include "ioprio-util.h"
#include "journal-file.h"
#include "load-fragment.h"
#include "memstream-util.h"
#include "missing_ioprio.h"
#include "mountpoint-util.h"
@@ -928,6 +929,36 @@ static int property_get_load_credential(
return sd_bus_message_close_container(reply);
}
static int property_get_import_credential(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
ExecContext *c = ASSERT_PTR(userdata);
const char *s;
int r;
assert(bus);
assert(property);
assert(reply);
r = sd_bus_message_open_container(reply, 'a', "s");
if (r < 0)
return r;
SET_FOREACH(s, c->import_credentials) {
r = sd_bus_message_append(reply, "s", s);
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int property_get_root_hash(
sd_bus *bus,
const char *path,
@@ -1281,6 +1312,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("SetCredentialEncrypted", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LoadCredential", "a(ss)", property_get_load_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LoadCredentialEncrypted", "a(ss)", property_get_load_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ImportCredential", "as", property_get_import_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PAMName", "s", NULL, offsetof(ExecContext, pam_name), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ReadWritePaths", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -2311,39 +2343,11 @@ int bus_exec_context_set_transient_property(
isempty = false;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
_cleanup_free_ char *copy = NULL;
ExecLoadCredential *old;
bool encrypted = streq(name, "LoadCredentialEncrypted");
copy = strdup(source);
if (!copy)
return -ENOMEM;
old = hashmap_get(c->load_credentials, id);
if (old) {
free_and_replace(old->path, copy);
old->encrypted = streq(name, "LoadCredentialEncrypted");
} else {
_cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
lc = new(ExecLoadCredential, 1);
if (!lc)
return -ENOMEM;
*lc = (ExecLoadCredential) {
.id = strdup(id),
.path = TAKE_PTR(copy),
.encrypted = streq(name, "LoadCredentialEncrypted"),
};
if (!lc->id)
return -ENOMEM;
r = hashmap_ensure_put(&c->load_credentials, &exec_load_credential_hash_ops, lc->id, lc);
if (r < 0)
return r;
TAKE_PTR(lc);
}
r = hashmap_put_credential(&c->load_credentials, id, source, encrypted);
if (r < 0)
return r;
(void) unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s:%s", name, id, source);
}
@@ -2360,6 +2364,47 @@ int bus_exec_context_set_transient_property(
return 1;
} else if (streq(name, "ImportCredential")) {
bool isempty = true;
r = sd_bus_message_enter_container(message, 'a', "s");
if (r < 0)
return r;
for (;;) {
const char *s;
r = sd_bus_message_read(message, "s", &s);
if (r < 0)
return r;
if (r == 0)
break;
if (!filename_is_valid(s))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Credential name is invalid: %s", s);
isempty = false;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
r = set_put_strdup(&c->import_credentials, s);
if (r < 0)
return r;
(void) unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, s);
}
}
r = sd_bus_message_exit_container(message);
if (r < 0)
return r;
if (!UNIT_WRITE_FLAGS_NOOP(flags) && isempty) {
c->import_credentials = set_free(c->import_credentials);
(void) unit_write_settingf(u, flags, name, "%s=", name);
}
return 1;
} else if (streq(name, "SyslogLevel")) {
int32_t level;
+174 -39
View File
@@ -1544,7 +1544,8 @@ bool exec_context_has_credentials(const ExecContext *context) {
assert(context);
return !hashmap_isempty(context->set_credentials) ||
!hashmap_isempty(context->load_credentials);
!hashmap_isempty(context->load_credentials) ||
!set_isempty(context->import_credentials);
}
#if HAVE_SECCOMP
@@ -2757,19 +2758,26 @@ static int write_credential(
return 0;
}
static char **credential_search_path(
const ExecParameters *params,
bool encrypted) {
typedef enum CredentialSearchPath {
CREDENTIAL_SEARCH_PATH_TRUSTED,
CREDENTIAL_SEARCH_PATH_ENCRYPTED,
CREDENTIAL_SEARCH_PATH_ALL,
_CREDENTIAL_SEARCH_PATH_MAX,
_CREDENTIAL_SEARCH_PATH_INVALID = -EINVAL,
} CredentialSearchPath;
static char **credential_search_path(const ExecParameters *params, CredentialSearchPath path) {
_cleanup_strv_free_ char **l = NULL;
assert(params);
assert(path >= 0 && path < _CREDENTIAL_SEARCH_PATH_MAX);
/* Assemble a search path to find credentials in. We'll look in /etc/credstore/ (and similar
* directories in /usr/lib/ + /run/) for all types of credentials. If we are looking for encrypted
* credentials, also look in /etc/credstore.encrypted/ (and similar dirs). */
/* Assemble a search path to find credentials in. For non-encrypted credentials, We'll look in
* /etc/credstore/ (and similar directories in /usr/lib/ + /run/). If we're looking for encrypted
* credentials, we'll look in /etc/credstore.encrypted/ (and similar dirs). */
if (encrypted) {
if (IN_SET(path, CREDENTIAL_SEARCH_PATH_ENCRYPTED, CREDENTIAL_SEARCH_PATH_ALL)) {
if (strv_extend(&l, params->received_encrypted_credentials_directory) < 0)
return NULL;
@@ -2777,12 +2785,14 @@ static char **credential_search_path(
return NULL;
}
if (params->received_credentials_directory)
if (strv_extend(&l, params->received_credentials_directory) < 0)
return NULL;
if (IN_SET(path, CREDENTIAL_SEARCH_PATH_TRUSTED, CREDENTIAL_SEARCH_PATH_ALL)) {
if (params->received_credentials_directory)
if (strv_extend(&l, params->received_credentials_directory) < 0)
return NULL;
if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore"), /* filter_duplicates= */ true) < 0)
return NULL;
if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore"), /* filter_duplicates= */ true) < 0)
return NULL;
}
if (DEBUG_LOGGING) {
_cleanup_free_ char *t = strv_join(l, ":");
@@ -2793,6 +2803,111 @@ static char **credential_search_path(
return TAKE_PTR(l);
}
static int maybe_decrypt_and_write_credential(
int dir_fd,
const char *id,
bool encrypted,
uid_t uid,
bool ownership_ok,
const char *data,
size_t size,
uint64_t *left) {
_cleanup_free_ void *plaintext = NULL;
size_t add;
int r;
if (encrypted) {
size_t plaintext_size = 0;
r = decrypt_credential_and_warn(id, now(CLOCK_REALTIME), NULL, NULL, data, size,
&plaintext, &plaintext_size);
if (r < 0)
return r;
data = plaintext;
size = plaintext_size;
}
add = strlen(id) + size;
if (add > *left)
return -E2BIG;
r = write_credential(dir_fd, id, data, size, uid, ownership_ok);
if (r < 0)
return log_debug_errno(r, "Failed to write credential '%s': %m", id);
*left -= add;
return 0;
}
static int load_credential_glob(
const char *path,
bool encrypted,
char **search_path,
ReadFullFileFlags flags,
int write_dfd,
uid_t uid,
bool ownership_ok,
uint64_t *left) {
int r;
STRV_FOREACH(d, search_path) {
_cleanup_globfree_ glob_t pglob = {};
_cleanup_free_ char *j = NULL;
j = path_join(*d, path);
if (!j)
return -ENOMEM;
r = safe_glob(j, 0, &pglob);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
for (unsigned n = 0; n < pglob.gl_pathc; n++) {
_cleanup_free_ char *fn = NULL;
_cleanup_(erase_and_freep) char *data = NULL;
size_t size;
/* path is absolute, hence pass AT_FDCWD as nop dir fd here */
r = read_full_file_full(
AT_FDCWD,
pglob.gl_pathv[n],
UINT64_MAX,
encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX,
flags,
NULL,
&data, &size);
if (r < 0)
return log_debug_errno(r, "Failed to read credential '%s': %m",
pglob.gl_pathv[n]);
r = path_extract_filename(pglob.gl_pathv[n], &fn);
if (r < 0)
return log_debug_errno(r, "Failed to extract filename from '%s': %m",
pglob.gl_pathv[n]);
r = maybe_decrypt_and_write_credential(
write_dfd,
fn,
encrypted,
uid,
ownership_ok,
data, size,
left);
if (r == -EEXIST)
continue;
if (r < 0)
return r;
}
}
return 0;
}
static int load_credential(
const ExecContext *context,
const ExecParameters *params,
@@ -2812,7 +2927,7 @@ static int load_credential(
_cleanup_free_ char *bindname = NULL;
const char *source = NULL;
bool missing_ok = true;
size_t size, add, maxsz;
size_t size, maxsz;
int r;
assert(context);
@@ -2858,7 +2973,7 @@ static int load_credential(
* directory we received ourselves. We don't support the AF_UNIX stuff in this mode, since we
* are operating on a credential store, i.e. this is guaranteed to be regular files. */
search_path = credential_search_path(params, encrypted);
search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ALL);
if (!search_path)
return -ENOMEM;
@@ -2914,28 +3029,7 @@ static int load_credential(
if (r < 0)
return log_debug_errno(r, "Failed to read credential '%s': %m", path);
if (encrypted) {
_cleanup_free_ void *plaintext = NULL;
size_t plaintext_size = 0;
r = decrypt_credential_and_warn(id, now(CLOCK_REALTIME), NULL, NULL, data, size, &plaintext, &plaintext_size);
if (r < 0)
return r;
free_and_replace(data, plaintext);
size = plaintext_size;
}
add = strlen(id) + size;
if (add > *left)
return -E2BIG;
r = write_credential(write_dfd, id, data, size, uid, ownership_ok);
if (r < 0)
return log_debug_errno(r, "Failed to write credential '%s': %m", id);
*left -= add;
return 0;
return maybe_decrypt_and_write_credential(write_dfd, id, encrypted, uid, ownership_ok, data, size, left);
}
struct load_cred_args {
@@ -3010,6 +3104,7 @@ static int acquire_credentials(
uint64_t left = CREDENTIALS_TOTAL_SIZE_MAX;
_cleanup_close_ int dfd = -EBADF;
const char *ic;
ExecLoadCredential *lc;
ExecSetCredential *sc;
int r;
@@ -3075,8 +3170,47 @@ static int acquire_credentials(
return r;
}
/* Second, we add in literally specified credentials. If the credentials already exist, we'll not add
* them, so that they can act as a "default" if the same credential is specified multiple times. */
/* Next, look for system credentials and credentials in the credentials store. Note that these do not
* override any credentials found earlier. */
SET_FOREACH(ic, context->import_credentials) {
_cleanup_free_ char **search_path = NULL;
search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_TRUSTED);
if (!search_path)
return -ENOMEM;
r = load_credential_glob(
ic,
/* encrypted = */ false,
search_path,
READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER,
dfd,
uid,
ownership_ok,
&left);
if (r < 0)
return r;
search_path = strv_free(search_path);
search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ENCRYPTED);
if (!search_path)
return -ENOMEM;
r = load_credential_glob(
ic,
/* encrypted = */ true,
search_path,
READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER|READ_FULL_FILE_UNBASE64,
dfd,
uid,
ownership_ok,
&left);
if (r < 0)
return r;
}
/* Finally, we add in literally specified credentials. If the credentials already exist, we'll not
* add them, so that they can act as a "default" if the same credential is specified multiple times. */
HASHMAP_FOREACH(sc, context->set_credentials) {
_cleanup_(erase_and_freep) void *plaintext = NULL;
const char *data;
@@ -5874,6 +6008,7 @@ void exec_context_done(ExecContext *c) {
c->load_credentials = hashmap_free(c->load_credentials);
c->set_credentials = hashmap_free(c->set_credentials);
c->import_credentials = set_free(c->import_credentials);
c->root_image_policy = image_policy_free(c->root_image_policy);
c->mount_image_policy = image_policy_free(c->mount_image_policy);
+1
View File
@@ -361,6 +361,7 @@ struct ExecContext {
Hashmap *set_credentials; /* output id → ExecSetCredential */
Hashmap *load_credentials; /* output id → ExecLoadCredential */
Set *import_credentials;
ImagePolicy *root_image_policy, *mount_image_policy, *extension_image_policy;
};
+1
View File
@@ -151,6 +151,7 @@
{{type}}.SetCredentialEncrypted, config_parse_set_credential, 1, offsetof({{type}}, exec_context)
{{type}}.LoadCredential, config_parse_load_credential, 0, offsetof({{type}}, exec_context)
{{type}}.LoadCredentialEncrypted, config_parse_load_credential, 1, offsetof({{type}}, exec_context)
{{type}}.ImportCredential, config_parse_import_credential, 0, offsetof({{type}}, exec_context.import_credentials)
{{type}}.TimeoutCleanSec, config_parse_sec, 0, offsetof({{type}}, exec_context.timeout_clean_usec)
{% if HAVE_PAM %}
{{type}}.PAMName, config_parse_unit_string_printf, 0, offsetof({{type}}, exec_context.pam_name)
+82 -24
View File
@@ -4840,6 +4840,46 @@ int config_parse_set_credential(
return 0;
}
int hashmap_put_credential(Hashmap **h, const char *id, const char *path, bool encrypted) {
ExecLoadCredential *old;
int r;
assert(h);
assert(id);
assert(path);
old = hashmap_get(*h, id);
if (old) {
r = free_and_strdup(&old->path, path);
if (r < 0)
return r;
old->encrypted = encrypted;
} else {
_cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
lc = new(ExecLoadCredential, 1);
if (!lc)
return log_oom();
*lc = (ExecLoadCredential) {
.id = strdup(id),
.path = strdup(path),
.encrypted = encrypted,
};
if (!lc->id || !lc->path)
return -ENOMEM;
r = hashmap_ensure_put(h, &exec_load_credential_hash_ops, lc->id, lc);
if (r < 0)
return r;
TAKE_PTR(lc);
}
return 0;
}
int config_parse_load_credential(
const char *unit,
const char *filename,
@@ -4854,7 +4894,6 @@ int config_parse_load_credential(
_cleanup_free_ char *word = NULL, *k = NULL, *q = NULL;
ExecContext *context = ASSERT_PTR(data);
ExecLoadCredential *old;
bool encrypted = ltype;
Unit *u = userdata;
const char *p;
@@ -4907,35 +4946,54 @@ int config_parse_load_credential(
}
}
old = hashmap_get(context->load_credentials, k);
if (old) {
free_and_replace(old->path, q);
old->encrypted = encrypted;
} else {
_cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
r = hashmap_put_credential(&context->load_credentials, k, q, encrypted);
if (r < 0)
return log_error_errno(r, "Failed to store load credential '%s': %m", rvalue);
lc = new(ExecLoadCredential, 1);
if (!lc)
return log_oom();
return 0;
}
*lc = (ExecLoadCredential) {
.id = TAKE_PTR(k),
.path = TAKE_PTR(q),
.encrypted = encrypted,
};
int config_parse_import_credential(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
r = hashmap_ensure_put(&context->load_credentials, &exec_load_credential_hash_ops, lc->id, lc);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Duplicated credential value '%s', ignoring assignment: %s", lc->id, rvalue);
return 0;
}
_cleanup_free_ char *s = NULL;
Set** import_credentials = ASSERT_PTR(data);
Unit *u = userdata;
int r;
TAKE_PTR(lc);
assert(filename);
assert(lvalue);
assert(rvalue);
if (isempty(rvalue)) {
/* Empty assignment resets the list */
*import_credentials = set_free(*import_credentials);
return 0;
}
r = unit_cred_printf(u, rvalue, &s);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", s);
return 0;
}
if (!filename_is_valid(s)) {
log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name \"%s\" not valid, ignoring.", s);
return 0;
}
r = set_put_strdup(import_credentials, s);
if (r < 0)
return log_error_errno(r, "Failed to store credential name '%s': %m", rvalue);
return 0;
}
+3
View File
@@ -12,6 +12,8 @@ int unit_is_likely_recursive_template_dependency(Unit *u, const char *name, cons
int parse_crash_chvt(const char *value, int *data);
int parse_confirm_spawn(const char *value, char **console);
int hashmap_put_credential(Hashmap **h, const char *id, const char *path, bool encrypted);
/* Read service data from .desktop file style configuration fragments */
int unit_load_fragment(Unit *u);
@@ -105,6 +107,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_preserve_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_directories);
CONFIG_PARSER_PROTOTYPE(config_parse_set_credential);
CONFIG_PARSER_PROTOTYPE(config_parse_load_credential);
CONFIG_PARSER_PROTOTYPE(config_parse_import_credential);
CONFIG_PARSER_PROTOTYPE(config_parse_set_status);
CONFIG_PARSER_PROTOTYPE(config_parse_namespace_path_strv);
CONFIG_PARSER_PROTOTYPE(config_parse_temporary_filesystems);
+11
View File
@@ -1209,6 +1209,17 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
return 1;
}
if (streq(field, "ImportCredential")) {
if (isempty(eq))
r = sd_bus_message_append(m, "(sv)", field, "as", 0);
else
r = sd_bus_message_append(m, "(sv)", field, "as", 1, eq);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (streq(field, "LogExtraFields")) {
r = sd_bus_message_open_container(m, 'r', "sv");
if (r < 0)
+15
View File
@@ -253,6 +253,21 @@ cmp /tmp/ts54-concat <(echo -n abcd)
rm /tmp/ts54-concat
rm -rf /tmp/ts54-creds
# Check that globs work as expected
mkdir -p /run/credstore
echo -n a >/run/credstore/test.creds.first
echo -n b >/run/credstore/test.creds.second
mkdir -p /etc/credstore
echo -n c >/etc/credstore/test.creds.third
systemd-run -p "ImportCredential=test.creds.*" \
-p DynamicUser=1 \
--wait \
--pipe \
cat '${CREDENTIALS_DIRECTORY}/test.creds.first' \
'${CREDENTIALS_DIRECTORY}/test.creds.second' \
'${CREDENTIALS_DIRECTORY}/test.creds.third' >/tmp/ts54-concat
cmp /tmp/ts54-concat <(echo -n abc)
# Now test encrypted credentials (only supported when built with OpenSSL though)
if systemctl --version | grep -q -- +OPENSSL ; then
echo -n $RANDOM >/tmp/test-54-plaintext
+4 -7
View File
@@ -32,10 +32,7 @@ StandardError=tty
# Optionally, pick up basic fields from credentials 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
LoadCredential=firstboot.locale
LoadCredential=firstboot.locale-messages
LoadCredential=firstboot.keymap
LoadCredential=firstboot.timezone
ImportCredential=passwd.hashed-password.root
ImportCredential=passwd.plaintext-password.root
ImportCredential=passwd.shell.root
ImportCredential=firstboot.*
+2 -2
View File
@@ -50,8 +50,8 @@ SystemCallErrorNumber=EPERM
SystemCallFilter=@system-service
Type=notify
User=systemd-resolve
LoadCredential=network.dns
LoadCredential=network.search_domains
ImportCredential=network.dns
ImportCredential=network.search_domains
{{SERVICE_WATCHDOG}}
[Install]
+1 -1
View File
@@ -21,4 +21,4 @@ Type=oneshot
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-sysctl
TimeoutSec=90s
LoadCredential=sysctl.extra
ImportCredential=sysctl.*
+4 -4
View File
@@ -29,9 +29,9 @@ 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
ImportCredential=passwd.hashed-password.root
ImportCredential=passwd.plaintext-password.root
ImportCredential=passwd.shell.root
# Also, allow configuring extra sysusers lines via a credential
LoadCredential=sysusers.extra
ImportCredential=sysusers.*
+1 -1
View File
@@ -21,4 +21,4 @@ Type=oneshot
ExecStart=systemd-tmpfiles --clean
SuccessExitStatus=DATAERR
IOSchedulingClass=idle
LoadCredential=tmpfiles.extra
ImportCredential=tmpfiles.*
+1 -1
View File
@@ -22,4 +22,4 @@ Type=oneshot
RemainAfterExit=yes
ExecStart=systemd-tmpfiles --prefix=/dev --create --boot --graceful
SuccessExitStatus=DATAERR CANTCREAT
LoadCredential=tmpfiles.extra
ImportCredential=tmpfiles.*
+5 -5
View File
@@ -23,8 +23,8 @@ Type=oneshot
RemainAfterExit=yes
ExecStart=systemd-tmpfiles --create --remove --boot
SuccessExitStatus=DATAERR CANTCREAT
LoadCredential=tmpfiles.extra
LoadCredential=login.motd
LoadCredential=login.issue
LoadCredential=network.hosts
LoadCredential=ssh.authorized_keys.root
ImportCredential=tmpfiles.*
ImportCredential=login.motd
ImportCredential=login.issue
ImportCredential=network.hosts
ImportCredential=ssh.authorized_keys.root
+1 -5
View File
@@ -21,8 +21,4 @@ Before=initrd-switch-root.target shutdown.target
Type=oneshot
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-vconsole-setup
LoadCredential=vconsole.keymap
LoadCredential=vconsole.keymap_toggle
LoadCredential=vconsole.font
LoadCredential=vconsole.font_map
LoadCredential=vconsole.font_unimap
ImportCredential=vconsole.*