mirror of
https://github.com/Dasharo/systemd.git
synced 2026-03-06 15:02:31 -08:00
Merge pull request #28207 from poettering/initrd-creds
various credential improvements (including initrd creds, creds in generators, fstab + getty creds)
This commit is contained in:
36
TODO
36
TODO
@@ -131,12 +131,6 @@ Deprecations and removals:
|
||||
|
||||
Features:
|
||||
|
||||
* use kernel 6.3's "noswap" parameter in tmpfs in place of ramfs for storing
|
||||
credentials.
|
||||
|
||||
* import-creds: allocate a non-swap-backed fs for /run/credentials/@system,
|
||||
like we do for services.
|
||||
|
||||
* new "systemd-pcrlock" component for dealing with PCR4. Design idea:
|
||||
1. define /{etc,usr,var/lib}/pcrlock.d/<component>/<version>.pcrlock
|
||||
2. these files contain list of hashes that will be measured when component is
|
||||
@@ -225,12 +219,10 @@ Features:
|
||||
support .microcode in PE add-ons, so that a microcode update can be shipped
|
||||
independently of any kernel.
|
||||
|
||||
* add clean mechanism concept for passing env/creds from initrd to host on
|
||||
switch root, so that cloud-init and similar have a clean, sane method to pass
|
||||
along the stuff they picked up, without patching any dirs. Maybe add
|
||||
SwitchRootEx() as new bus call that takes these as argument. When adding
|
||||
SwitchRootEx() we should maybe also add a flags param that allows disabling
|
||||
and enabling whether serialization is requested during switch root.
|
||||
* Maybe add SwitchRootEx() as new bus call that takes env vars to set for new
|
||||
PID 1 as argument. When adding SwitchRootEx() we should maybe also add a
|
||||
flags param that allows disabling and enabling whether serialization is
|
||||
requested during switch root.
|
||||
|
||||
* introduce a .acpitable section for early ACPI table override
|
||||
|
||||
@@ -249,10 +241,6 @@ Features:
|
||||
scenarios. Maybe insist sealing is done additionally against some keypair in
|
||||
the TPM to which access is updated on each boot, for the next, or so?
|
||||
|
||||
* open up creds for uses in generators, and document clearly that encrypted
|
||||
creds are only supported if strictly tpm bound, but not when using the host
|
||||
secret (as that is only available if /var/ is around.
|
||||
|
||||
* logind: when logging in, always take an fd to the home dir, to keep the dir
|
||||
busy, so that autofs release can never happen. (this is generally a good
|
||||
idea, and specifically works around the fact the autofs ignores busy by mount
|
||||
@@ -819,10 +807,9 @@ Features:
|
||||
* Process credentials in:
|
||||
• networkd/udevd: add a way to define additional .link, .network, .netdev files
|
||||
via the credentials logic.
|
||||
• fstab-generator: allow defining additional fstab-like mounts via
|
||||
credentials (similar: crypttab-generator, verity-generator,
|
||||
integrity-generator)
|
||||
• getty-generator: allow defining additional getty instances via a credential
|
||||
• crypttab-generator: allow defining additional crypttab-like volumes via
|
||||
credentials (similar: verity-generator, integrity-generator). Use
|
||||
fstab-generator logic as inspiration.
|
||||
• run-generator: allow defining additional commands to run via a credential
|
||||
• resolved: allow defining additional /etc/hosts entries via a credential (it
|
||||
might make sense to then synthesize a new combined /etc/hosts file in /run
|
||||
@@ -837,9 +824,6 @@ Features:
|
||||
systemd.homed.register or so with JSON user records to automatically
|
||||
register if not registered yet. Usecase: deploy a system, and add an
|
||||
account one can directly log into.
|
||||
• initialize machine ID from systemd credential picked up from the ESP via
|
||||
sd-stub, so that machine ID is stable even on systems where unified kernels
|
||||
are used, and hence kernel cmdline cannot be modified locally
|
||||
• in gpt-auto-generator: check partition uuids against such uuids supplied via
|
||||
sd-stub credentials. That way, we can support parallel OS installations with
|
||||
pre-built kernels.
|
||||
@@ -948,11 +932,6 @@ Features:
|
||||
https://0pointer.net/blog/testing-my-system-code-in-usr-without-modifying-usr.html
|
||||
https://0pointer.net/blog/running-an-container-off-the-host-usr.html
|
||||
|
||||
* add a clear concept how the initrd can make up credentials on their own to
|
||||
pass to the system when transitioning into the host OS. usecase: things like
|
||||
cloud-init/ignitation and similar can parameterize the host with data they
|
||||
acquire.
|
||||
|
||||
* sd-event: compat wd reuse in inotify code: keep a set of removed watch
|
||||
descriptors, and clear this set piecemeal when we see the IN_IGNORED event
|
||||
for it, or when read() returns EAGAIN or on IN_Q_OVERFLOW. Then, whenever we
|
||||
@@ -969,7 +948,6 @@ Features:
|
||||
- kernel-install should be able to pick up initrd sysexts automatically and
|
||||
place them next to EFI kernel, for sd-stub to pick them up.
|
||||
- systemd-fstab-generator should look for rootfs device to mount in creds
|
||||
- pid 1 should look for machine ID in creds
|
||||
- systemd-resume-generator should look for resume partition uuid in creds
|
||||
- sd-stub: automatically pick up microcode from ESP (/loader/microcode/*)
|
||||
and synthesize initrd from it, and measure it. Signing is not necessary, as
|
||||
|
||||
@@ -51,9 +51,9 @@ purpose. Specifically, the following features are provided:
|
||||
allow it, via `ramfs`.)
|
||||
|
||||
7. Credentials may be acquired from a hosting VM hypervisor (SMBIOS OEM strings
|
||||
or qemu `fw_cfg`), a hosting container manager, the kernel command line, or
|
||||
from the UEFI environment and the EFI System Partition (via
|
||||
`systemd-stub`). Such system credentials may then be propagated into
|
||||
or qemu `fw_cfg`), a hosting container manager, the kernel command line,
|
||||
from the initrd, or from the UEFI environment via the EFI System Partition
|
||||
(via `systemd-stub`). Such system credentials may then be propagated into
|
||||
individual services as needed.
|
||||
|
||||
8. Credentials are an effective way to pass parameters into services that run
|
||||
@@ -72,19 +72,19 @@ 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. `ImportCredential=` may be used to load one or more (optionally encrypted)
|
||||
credentials from disk or from the credential stores.
|
||||
|
||||
2. `SetCredential=` may be used to set a credential to a literal string encoded
|
||||
3. `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,
|
||||
e.g. public keys or certificates, but not private keys.
|
||||
|
||||
3. `LoadCredentialEncrypted=` is similar to `LoadCredential=` but will load an
|
||||
4. `LoadCredentialEncrypted=` is similar to `LoadCredential=` but will load an
|
||||
encrypted credential, and decrypt it before passing it to the service. For
|
||||
details on credential encryption, see below.
|
||||
|
||||
4. `SetCredentialEncrypted=` is similar to `SetCredential=` but expects an
|
||||
5. `SetCredentialEncrypted=` is similar to `SetCredential=` but expects an
|
||||
encrypted credential to be specified literally. Unlike `SetCredential=` it
|
||||
is thus safe to be used even for sensitive information, because even though
|
||||
unit files are world readable, the ciphertext included in them cannot be
|
||||
@@ -153,6 +153,33 @@ credentials directory. For daemons that allow passing credentials via a path
|
||||
supplied as environment variable, use the `%d` specifier in the `Environment=`
|
||||
setting to build valid paths to specific credentials.
|
||||
|
||||
Encrypted credentials are automatically decrypted/authenticated during service
|
||||
activation, so that service code only receives plaintext credentials.
|
||||
|
||||
## Programming Interface from Generator Code
|
||||
|
||||
[Generators](https://www.freedesktop.org/software/systemd/man/systemd.generator.html)
|
||||
may generate native unit files from external configuration or system
|
||||
parameters, such as system credentials. Note that they run outside of service
|
||||
context, and hence will not receive encrypted credentials in plaintext
|
||||
form. Specifically, credentials passed into the system in encrypted form will
|
||||
be placed as they are in a directory referenced by the
|
||||
`$ENCRYPTED_CREDENTIALS_DIRECTORY` environment variable, and those passed in
|
||||
plaintext form will be placed in `$CREDENTIALS_DIRECTORY`. Use a command such
|
||||
as `systemd-creds --system cat …` to access both forms of credentials, and
|
||||
decrypt them if needed (see
|
||||
[systemd-creds(1)](https://www.freedesktop.org/software/systemd/man/systemd-creds.html)
|
||||
for details.
|
||||
|
||||
Note that generators typically run very early during boot (similar to initrd
|
||||
code), earlier than the `/var/` file system is necessarily mounted (which is
|
||||
where the system's credential encryption secret is located). Thus it's a good
|
||||
idea to encrypt credentials with `systemd-creds encrypt --with-key=auto-initrd`
|
||||
if they shall be consumed by a generator, to ensure they are locked to the TPM2
|
||||
only, not the credentials secret stored below `/var/`.
|
||||
|
||||
For further details about encrypted credentials, see below.
|
||||
|
||||
## Tools
|
||||
|
||||
The
|
||||
@@ -194,6 +221,12 @@ cannot be decrypted without access to the TPM2 chip and the aforementioned key
|
||||
file `/var/lib/systemd/credential.secret`. Moreover, credentials cannot be
|
||||
prepared on a machine other than the local one.
|
||||
|
||||
Decryption generally takes place at the moment of service activation. This
|
||||
means credentials passed to the system can be either encrypted or plaintext and
|
||||
remain that way all the way while they are propagated to their consumers, until
|
||||
the moment of service activation when they are decrypted and authenticated, so
|
||||
that the service only sees plaintext credentials.
|
||||
|
||||
The `systemd-creds` tool provides the commands `encrypt` and `decrypt` to
|
||||
encrypt and decrypt/authenticate credentials. Example:
|
||||
|
||||
@@ -247,7 +280,7 @@ via `systemd` credentials. In particular, it might make sense to boot a
|
||||
system with a set of credentials that are then propagated to individual
|
||||
services where they are ultimately consumed.
|
||||
|
||||
`systemd` supports four ways to pass credentials to systems:
|
||||
`systemd` supports five ways to pass credentials to systems:
|
||||
|
||||
1. A container manager may set the `$CREDENTIALS_DIRECTORY` environment
|
||||
variable for systemd running as PID 1 in the container, the same way as
|
||||
@@ -255,8 +288,7 @@ services where they are ultimately consumed.
|
||||
invokes. [`systemd-nspawn(1)`](https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html#Credentials)'s
|
||||
`--set-credential=` and `--load-credential=` switches implement this, in
|
||||
order to pass arbitrary credentials from host to container payload. Also see
|
||||
the [Container Interface](CONTAINER_INTERFACE.md)
|
||||
documentation.
|
||||
the [Container Interface](CONTAINER_INTERFACE.md) documentation.
|
||||
|
||||
2. Quite similar, VMs can be passed credentials via SMBIOS OEM strings (example
|
||||
qemu command line switch `-smbios
|
||||
@@ -269,16 +301,17 @@ services where they are ultimately consumed.
|
||||
three of these specific switches would set credential `foo` to `bar`.)
|
||||
Passing credentials via the SMBIOS mechanism is typically preferable over
|
||||
`fw_cfg` since it is faster and less specific to the chosen VMM
|
||||
implementation. Moreover, `fw_cfg` has a 55 character limitation
|
||||
on names passed that way. So some settings may not fit.
|
||||
implementation. Moreover, `fw_cfg` has a 55 character limitation on names
|
||||
passed that way. So some settings may not fit.
|
||||
|
||||
3. Credentials can also be passed into a system via the kernel command line,
|
||||
via the `systemd.set-credential=` kernel command line option. Note though
|
||||
that any data specified here is visible to any userspace application via
|
||||
`/proc/cmdline`. This is hence typically not useful to pass sensitive
|
||||
information.
|
||||
3. Credentials may be passed from the initrd to the host during the initrd →
|
||||
host transition. Provisioning systems that run in the initrd may use this to
|
||||
install credentials on the system. All files placed in
|
||||
`/run/credentials/@initrd/` are imported into the set of file system
|
||||
credentials during the transition. The files (and their directory) are
|
||||
removed once this is completed.
|
||||
|
||||
4. Credentials may also be passed from the UEFI environment to userspace, if
|
||||
5. Credentials may also be passed from the UEFI environment to userspace, if
|
||||
the
|
||||
[`systemd-stub`](https://www.freedesktop.org/software/systemd/man/systemd-stub.html)
|
||||
UEFI kernel stub is used. This allows placing encrypted credentials in the
|
||||
@@ -288,6 +321,13 @@ services where they are ultimately consumed.
|
||||
initrds, as userspace can place credentials next to these EFI kernels, and
|
||||
be sure they can be accessed securely from initrd context.
|
||||
|
||||
4. Credentials can also be passed into a system via the kernel command line,
|
||||
via the `systemd.set_credential=` and `systemd.set_credential_binary=`
|
||||
kernel command line options (the latter takes Base64 encoded binary
|
||||
data). Note though that any data specified here is visible to all userspace
|
||||
applications (even unprivileged ones) via `/proc/cmdline`. Typically, this
|
||||
is hence not useful to pass sensitive information, and should be avoided.
|
||||
|
||||
Credentials passed to the system may be enumerated/displayed via `systemd-creds
|
||||
--system`. They may also be propagated down to services, via the
|
||||
`LoadCredential=` setting. Example:
|
||||
@@ -359,6 +399,9 @@ Various services shipped with `systemd` consume credentials for tweaking behavio
|
||||
will look for the credentials `tmpfiles.extra` with arbitrary tmpfiles.d lines.
|
||||
Can be encoded in base64 to allow easily passing it on the command line.
|
||||
|
||||
* Further well-known credentials are documented in
|
||||
[`systemd.system-credentials(7)`](https://www.freedesktop.org/software/systemd/man/systemd.system-credentials.html).
|
||||
|
||||
In future more services are likely to gain support for consuming credentials.
|
||||
|
||||
Example:
|
||||
@@ -427,6 +470,11 @@ READY=1
|
||||
From *service* perspective the runtime path to find loaded credentials in is
|
||||
provided in the `$CREDENTIALS_DIRECTORY` environment variable.
|
||||
|
||||
From *generator* perspective the runtime path to find credentials passed into
|
||||
the system in plaintext form in is provided in `$CREDENTIALS_DIRECTORY`, and
|
||||
those passed into the system in encrypted form is provided in
|
||||
`$ENCRYPTED_CREDENTIALS_DIRECTORY`.
|
||||
|
||||
At runtime, credentials passed to the *system* are placed in
|
||||
`/run/credentials/@system/` (for regular credentials, such as those passed from
|
||||
a container manager or via qemu) and `/run/credentials/@encrypted/` (for
|
||||
@@ -434,13 +482,14 @@ credentials that must be decrypted/validated before use, such as those from
|
||||
`systemd-stub`).
|
||||
|
||||
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/`,
|
||||
`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. `ImportCredential` will search
|
||||
both the non-encrypted and encrypted 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
|
||||
|
||||
@@ -49,6 +49,15 @@
|
||||
<variablelist id='environment-variables' />
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>System Credentials</title>
|
||||
|
||||
<para>System credentials understood by the system and service manager and various other
|
||||
components:</para>
|
||||
|
||||
<variablelist id='system-credentials' />
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>EFI variables</title>
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
<term><varname>systemd.setenv=</varname></term>
|
||||
<term><varname>systemd.machine_id=</varname></term>
|
||||
<term><varname>systemd.set_credential=</varname></term>
|
||||
<term><varname>systemd.set_credential_binary=</varname></term>
|
||||
<term><varname>systemd.import_credentials=</varname></term>
|
||||
<term><varname>systemd.reload_limit_interval_sec=</varname></term>
|
||||
<term><varname>systemd.reload_limit_burst=</varname></term>
|
||||
|
||||
@@ -308,10 +308,10 @@
|
||||
(see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
|
||||
details). The following credentials are used when passed in:</para>
|
||||
|
||||
<variablelist>
|
||||
<variablelist class='system-credentials'>
|
||||
<varlistentry>
|
||||
<term><literal>passwd.hashed-password.root</literal></term>
|
||||
<term><literal>passwd.plaintext-password.root</literal></term>
|
||||
<term><varname>passwd.hashed-password.root</varname></term>
|
||||
<term><varname>passwd.plaintext-password.root</varname></term>
|
||||
|
||||
<listitem><para>A hashed or plaintext version of the root password to use, in place of prompting the
|
||||
user. These credentials are equivalent to the same ones defined for the
|
||||
@@ -320,7 +320,7 @@
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><literal>passwd.shell.root</literal></term>
|
||||
<term><varname>passwd.shell.root</varname></term>
|
||||
|
||||
<listitem><para>Specifies the shell binary to use for the specified account.
|
||||
Equivalent to the credential of the same name defined for the
|
||||
@@ -329,20 +329,20 @@
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><literal>firstboot.locale</literal></term>
|
||||
<term><literal>firstboot.locale-messages</literal></term>
|
||||
<term><varname>firstboot.locale</varname></term>
|
||||
<term><varname>firstboot.locale-messages</varname></term>
|
||||
|
||||
<listitem><para>These credentials specify the locale settings to set during first boot, in place of
|
||||
prompting the user.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><literal>firstboot.keymap</literal></term>
|
||||
<term><varname>firstboot.keymap</varname></term>
|
||||
|
||||
<listitem><para>This credential specifies the keyboard setting to set during first boot, in place of
|
||||
prompting the user.</para>
|
||||
|
||||
<para>Note the relationship to the <literal>vconsole.keymap</literal> credential understood by
|
||||
<para>Note the relationship to the <varname>vconsole.keymap</varname> credential understood by
|
||||
<citerefentry><refentrytitle>systemd-vconsole-setup.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>:
|
||||
both ultimately affect the same setting, but <varname>firstboot.keymap</varname> is written into
|
||||
<filename>/etc/vconsole.conf</filename> on first boot (if not already configured), and then read from
|
||||
@@ -352,7 +352,7 @@
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><literal>firstboot.timezone</literal></term>
|
||||
<term><varname>firstboot.timezone</varname></term>
|
||||
|
||||
<listitem><para>This credential specifies the system timezone setting to set during first boot, in
|
||||
place of prompting the user.</para></listitem>
|
||||
|
||||
@@ -269,6 +269,21 @@ systemd.swap=/dev/sda2:x-systemd.makefs</programlisting>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>System Credentials</title>
|
||||
|
||||
<variablelist class='system-credentials'>
|
||||
<varlistentry>
|
||||
<term><varname>fstab.extra</varname></term>
|
||||
|
||||
<listitem><para>This credential may contain addition mounts to establish, in the same format as
|
||||
<citerefentry
|
||||
project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>, with
|
||||
one mount per line. It is read in addition to <filename>/etc/fstab</filename>.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
|
||||
@@ -85,11 +85,29 @@
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>System Credentials</title>
|
||||
|
||||
<variablelist class='system-credentials'>
|
||||
<varlistentry>
|
||||
<term><varname>getty.ttys.serial</varname></term>
|
||||
<term><varname>getty.ttys.container</varname></term>
|
||||
|
||||
<listitem><para>These system credentials may be used to spawn additional login prompts on selected
|
||||
TTYs. The two credentials should contain a newline-separated list of TTY names to spawn instances of
|
||||
<filename>serial-getty@.service</filename> (in case of <varname>getty.ttys.serial</varname>) and
|
||||
<filename>container-getty@.service</filename> (in case of <varname>getty.ttys.container</varname>)
|
||||
on.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd.system-credentials</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
|
||||
<citerefentry project='man-pages'><refentrytitle>agetty</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
@@ -407,7 +407,7 @@ search foobar.com barbar.com
|
||||
(see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
|
||||
details). The following credentials are used when passed in:</para>
|
||||
|
||||
<variablelist>
|
||||
<variablelist class='system-credentials'>
|
||||
<varlistentry>
|
||||
<term><varname>network.dns</varname></term>
|
||||
<term><varname>network.search_domains</varname></term>
|
||||
|
||||
@@ -89,9 +89,9 @@
|
||||
(see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
|
||||
details). The following credentials are used when passed in:</para>
|
||||
|
||||
<variablelist>
|
||||
<variablelist class='system-credentials'>
|
||||
<varlistentry>
|
||||
<term><literal>sysctl.extra</literal></term>
|
||||
<term><varname>sysctl.extra</varname></term>
|
||||
|
||||
<listitem><para>The contents of this credential may contain additional lines to operate on. The
|
||||
credential contents should follow the same format as any other <filename>sysctl.d/</filename> drop-in
|
||||
|
||||
@@ -143,9 +143,9 @@
|
||||
(see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
|
||||
details). The following credentials are used when passed in:</para>
|
||||
|
||||
<variablelist>
|
||||
<variablelist class='system-credentials'>
|
||||
<varlistentry>
|
||||
<term><literal>passwd.hashed-password.<replaceable>user</replaceable></literal></term>
|
||||
<term><varname>passwd.hashed-password.<replaceable>user</replaceable></varname></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
|
||||
@@ -155,7 +155,7 @@
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><literal>passwd.plaintext-password.<replaceable>user</replaceable></literal></term>
|
||||
<term><varname>passwd.plaintext-password.<replaceable>user</replaceable></varname></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
|
||||
@@ -166,13 +166,13 @@
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><literal>passwd.shell.<replaceable>user</replaceable></literal></term>
|
||||
<term><varname>passwd.shell.<replaceable>user</replaceable></varname></term>
|
||||
|
||||
<listitem><para>Specifies the shell binary to use for the specified account when creating it.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><literal>sysusers.extra</literal></term>
|
||||
<term><varname>sysusers.extra</varname></term>
|
||||
|
||||
<listitem><para>The contents of this credential may contain additional lines to operate on. The
|
||||
credential contents should follow the same format as any other <filename>sysusers.d/</filename>
|
||||
|
||||
@@ -250,9 +250,9 @@
|
||||
(see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
|
||||
details). The following credentials are used when passed in:</para>
|
||||
|
||||
<variablelist>
|
||||
<variablelist class='system-credentials'>
|
||||
<varlistentry>
|
||||
<term><literal>tmpfiles.extra</literal></term>
|
||||
<term><varname>tmpfiles.extra</varname></term>
|
||||
|
||||
<listitem><para> The contents of this credential may contain additional lines to operate on. The
|
||||
credential contents should follow the same format as any other <filename>tmpfiles.d/</filename>
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
(see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
|
||||
details). The following credentials are used when passed in:</para>
|
||||
|
||||
<variablelist>
|
||||
<variablelist class='system-credentials'>
|
||||
<varlistentry>
|
||||
<term><varname>vconsole.keymap</varname></term>
|
||||
<term><varname>vconsole.keymap_toggle</varname></term>
|
||||
|
||||
@@ -3274,18 +3274,21 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
|
||||
11) with a prefix of <literal>io.systemd.credential:</literal> or
|
||||
<literal>io.systemd.credential.binary:</literal>. In both cases a key/value pair separated by
|
||||
<literal>=</literal> is expected, in the latter case the right-hand side is Base64 decoded when
|
||||
parsed (thus permitting binary data to be passed in). Example
|
||||
<ulink url="https://www.qemu.org/docs/master/system/index.html">qemu</ulink>
|
||||
switch: <literal>-smbios
|
||||
parsed (thus permitting binary data to be passed in). Example <ulink
|
||||
url="https://www.qemu.org/docs/master/system/index.html">qemu</ulink> switch: <literal>-smbios
|
||||
type=11,value=io.systemd.credential:xx=yy</literal>, or <literal>-smbios
|
||||
type=11,value=io.systemd.credential.binary:rick=TmV2ZXIgR29ubmEgR2l2ZSBZb3UgVXA=</literal>. Alternatively,
|
||||
use the <command>qemu</command> <literal>fw_cfg</literal> node
|
||||
<literal>opt/io.systemd.credentials/</literal>. Example <command>qemu</command> switch: <literal>-fw_cfg
|
||||
name=opt/io.systemd.credentials/mycred,string=supersecret</literal>. They may also be specified on
|
||||
the kernel command line using the <literal>systemd.set_credential=</literal> switch (see
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>) and from
|
||||
the UEFI firmware environment via
|
||||
<citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
|
||||
<literal>opt/io.systemd.credentials/</literal>. Example <command>qemu</command> switch:
|
||||
<literal>-fw_cfg name=opt/io.systemd.credentials/mycred,string=supersecret</literal>. They may also
|
||||
be passed from the UEFI firmware environment via
|
||||
<citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
|
||||
from the initrd (see
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>), or be
|
||||
specified on the kernel command line using the <literal>systemd.set_credential=</literal> and
|
||||
<literal>systemd.set_credential_binary=</literal> switches (see
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> – this is
|
||||
not recommended since unprivileged userspace can read the kernel command line). </para>
|
||||
|
||||
<para>If referencing an <constant>AF_UNIX</constant> stream socket to connect to, the connection will
|
||||
originate from an abstract namespace socket, that includes information about the unit and the
|
||||
|
||||
@@ -190,6 +190,20 @@
|
||||
<varname>ConditionArchitecture=</varname> in
|
||||
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>$CREDENTIALS_DIRECTORY</varname></term>
|
||||
<term><varname>$ENCRYPTED_CREDENTIALS_DIRECTORY</varname></term>
|
||||
|
||||
<listitem><para>If set, refers to the directory system credentials have been placed in. Credentials
|
||||
passed into the system in plaintext form will be placed in <varname>$CREDENTIALS_DIRECTORY</varname>,
|
||||
and those passed in in encrypted form will be placed in
|
||||
<varname>$ENCRYPTED_CREDENTIALS_DIRECTORY</varname>. Use the
|
||||
<citerefentry><refentrytitle>systemd-creds</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
command to automatically decrypt/authenticate credentials passed in, if needed. Specifically, use the
|
||||
<command>systemd-creds --system cat</command> command.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<refsect1>
|
||||
<title>Well known system credentials</title>
|
||||
|
||||
<variablelist>
|
||||
<variablelist class='system-credentials'>
|
||||
<varlistentry>
|
||||
<term><varname>firstboot.keymap</varname></term>
|
||||
<listitem>
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>firstboot.locale</varname></term>
|
||||
<term><varname>firstboot.locale-message</varname></term>
|
||||
<term><varname>firstboot.locale-messages</varname></term>
|
||||
<listitem>
|
||||
<para>The system locale to set (e.g. <literal>de_DE.UTF-8</literal>). Read by
|
||||
<citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
@@ -186,6 +186,15 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>fstab.extra</varname></term>
|
||||
|
||||
<listitem>
|
||||
<para>Additional mounts to establish at boot. For details, see
|
||||
<citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>vconsole.keymap</varname></term>
|
||||
<term><varname>vconsole.keymap_toggle</varname></term>
|
||||
@@ -198,6 +207,14 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>getty.ttys.serial</varname></term>
|
||||
<term><varname>getty.ttys.container</varname></term>
|
||||
|
||||
<listitem><para>Used for spawning additional login prompts, see
|
||||
<citerefentry><refentrytitle>systemd-getty-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry> for details.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>vmm.notify_socket</varname></term>
|
||||
<listitem>
|
||||
@@ -208,6 +225,15 @@
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>system.machine_id</varname></term>
|
||||
<listitem>
|
||||
<para>Takes a 128bit ID to initialize the machine ID from (if it is not set yet). Interpreted by
|
||||
the service manager (PID 1). For details see
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
|
||||
@@ -932,12 +932,15 @@
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.set_credential=</varname></term>
|
||||
<term><varname>systemd.set_credential_binary=</varname></term>
|
||||
|
||||
<listitem><para>Sets a system credential, which can then be propagated to system services using the
|
||||
<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
|
||||
details. Takes a pair of credential name and value, separated by a colon. The
|
||||
<varname>systemd.set_credential=</varname> parameter expects the credential value in literal text
|
||||
form, the <varname>systemd.set_credential_binary=</varname> parameter takes binary data encoded in
|
||||
Base64. Note that the kernel command line is typically accessible by unprivileged programs in
|
||||
<filename>/proc/cmdline</filename>. Thus, this mechanism is not suitable for transferring sensitive
|
||||
data. Use it only for data that is not sensitive (e.g. public keys/certificates, rather than private
|
||||
keys), or in testing/debugging environments.</para>
|
||||
@@ -1051,9 +1054,42 @@
|
||||
<refsect1>
|
||||
<title>System credentials</title>
|
||||
|
||||
<para>The service manager when run as PID 1 reads the following system credentials:</para>
|
||||
<para>During initialization the service manager will import credentials from various sources into the
|
||||
system's set of credentials, which can then be propagated into services and consumed by
|
||||
generators:</para>
|
||||
|
||||
<variablelist>
|
||||
<itemizedlist>
|
||||
<listitem><para>When the service manager first initializes it will read system credentials from SMBIOS
|
||||
Type 11 vendor strings
|
||||
<varname>io.systemd.credential:<replaceable>name</replaceable>=<replaceable>value</replaceable></varname>,
|
||||
and
|
||||
<varname>io.systemd.credential.binary:<replaceable>name</replaceable>=<replaceable>value</replaceable></varname>.</para></listitem>
|
||||
|
||||
<listitem><para>At the same time it will import credentials from QEMU <literal>fw_cfg</literal>. (Note
|
||||
that the SMBIOS mechanism is generally preferred, because it is faster and generic.)</para></listitem>
|
||||
|
||||
<listitem><para>Credentials may be passed via the kernel command line, using the
|
||||
<varname>systemd.set-credential=</varname> parameter, see above.</para></listitem>
|
||||
|
||||
<listitem><para>Credentials may be passed from the UEFI environment via
|
||||
<citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para></listitem>
|
||||
|
||||
<listitem><para>When the service manager is invoked during the initrd → host transition it will import
|
||||
all files in <filename>/run/credentials/@initrd/</filename> as system credentials.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Invoke
|
||||
<citerefentry><refentrytitle>systemd-creds</refentrytitle><manvolnum>1</manvolnum></citerefentry> as
|
||||
follows to see the list of credentials passed into the system:</para>
|
||||
|
||||
<programlisting># systemd-creds --system list</programlisting>
|
||||
|
||||
<para>For further information see <ulink url="https://systemd.io/CREDENTIALS">System and Service
|
||||
Credentials</ulink> documentation.</para>
|
||||
|
||||
<para>The service manager when run as PID 1 consumes the following system credentials:</para>
|
||||
|
||||
<variablelist class='system-credentials'>
|
||||
<varlistentry>
|
||||
<term><varname>vmm.notify_socket</varname></term>
|
||||
<listitem>
|
||||
@@ -1069,6 +1105,16 @@
|
||||
notification via VSOCK when a virtual machine has finished booting.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>system.machine_id</varname></term>
|
||||
<listitem>
|
||||
<para>Takes a 128bit hexadecimal ID to initialize <filename>/etc/machine-id</filename> from, if the
|
||||
file is not set up yet. See
|
||||
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
|
||||
details.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
|
||||
static int run(const char *dest, const char *dest_early, const char *dest_late) {
|
||||
|
||||
if (in_initrd() > 0) {
|
||||
if (in_initrd()) {
|
||||
log_debug("Skipping generator, running in the initrd.");
|
||||
return 0;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (detect_container() > 0) {
|
||||
|
||||
@@ -3182,6 +3182,10 @@ static int acquire_credentials(
|
||||
if (dfd < 0)
|
||||
return -errno;
|
||||
|
||||
r = fd_acl_make_writable(dfd); /* Add the "w" bit, if we are reusing an already set up credentials dir where it was unset */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* First, load credentials off disk (or acquire via AF_UNIX socket) */
|
||||
HASHMAP_FOREACH(lc, context->load_credentials) {
|
||||
_cleanup_close_ int sub_fd = -EBADF;
|
||||
@@ -3313,8 +3317,9 @@ static int acquire_credentials(
|
||||
left -= add;
|
||||
}
|
||||
|
||||
if (fchmod(dfd, 0500) < 0) /* Now take away the "w" bit */
|
||||
return -errno;
|
||||
r = fd_acl_make_read_only(dfd); /* Now take away the "w" bit */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* After we created all keys with the right perms, also make sure the credential store as a whole is
|
||||
* accessible */
|
||||
@@ -3384,7 +3389,7 @@ static int setup_credentials_internal(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL);
|
||||
r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -3395,57 +3400,34 @@ static int setup_credentials_internal(
|
||||
|
||||
if (workspace_mounted < 0) {
|
||||
/* Nothing is mounted on the workspace yet, let's try to mount something now */
|
||||
for (int try = 0;; try++) {
|
||||
|
||||
if (try == 0) {
|
||||
/* Try "ramfs" first, since it's not swap backed */
|
||||
r = mount_nofollow_verbose(LOG_DEBUG, "ramfs", workspace, "ramfs", MS_NODEV|MS_NOEXEC|MS_NOSUID, "mode=0700");
|
||||
if (r >= 0) {
|
||||
workspace_mounted = true;
|
||||
break;
|
||||
}
|
||||
r = mount_credentials_fs(workspace, CREDENTIALS_TOTAL_SIZE_MAX, /* ro= */ false);
|
||||
if (r < 0) {
|
||||
/* If that didn't work, try to make a bind mount from the final to the workspace, so that we can make it writable there. */
|
||||
r = mount_nofollow_verbose(LOG_DEBUG, final, workspace, NULL, MS_BIND|MS_REC, NULL);
|
||||
if (r < 0) {
|
||||
if (!ERRNO_IS_PRIVILEGE(r)) /* Propagate anything that isn't a permission problem */
|
||||
return r;
|
||||
|
||||
} else if (try == 1) {
|
||||
_cleanup_free_ char *opts = NULL;
|
||||
if (must_mount) /* If we it's not OK to use the plain directory
|
||||
* fallback, propagate all errors too */
|
||||
return r;
|
||||
|
||||
if (asprintf(&opts, "mode=0700,nr_inodes=1024,size=%zu", (size_t) CREDENTIALS_TOTAL_SIZE_MAX) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Fall back to "tmpfs" otherwise */
|
||||
r = mount_nofollow_verbose(LOG_DEBUG, "tmpfs", workspace, "tmpfs", MS_NODEV|MS_NOEXEC|MS_NOSUID, opts);
|
||||
if (r >= 0) {
|
||||
workspace_mounted = true;
|
||||
break;
|
||||
}
|
||||
/* If we lack privileges to bind mount stuff, then let's gracefully
|
||||
* proceed for compat with container envs, and just use the final dir
|
||||
* as is. */
|
||||
|
||||
workspace_mounted = false;
|
||||
} else {
|
||||
/* If that didn't work, try to make a bind mount from the final to the workspace, so that we can make it writable there. */
|
||||
r = mount_nofollow_verbose(LOG_DEBUG, final, workspace, NULL, MS_BIND|MS_REC, NULL);
|
||||
if (r < 0) {
|
||||
if (!ERRNO_IS_PRIVILEGE(r)) /* Propagate anything that isn't a permission problem */
|
||||
return r;
|
||||
|
||||
if (must_mount) /* If we it's not OK to use the plain directory
|
||||
* fallback, propagate all errors too */
|
||||
return r;
|
||||
|
||||
/* If we lack privileges to bind mount stuff, then let's gracefully
|
||||
* proceed for compat with container envs, and just use the final dir
|
||||
* as is. */
|
||||
|
||||
workspace_mounted = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Make the new bind mount writable (i.e. drop MS_RDONLY) */
|
||||
r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL);
|
||||
r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
workspace_mounted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
workspace_mounted = true;
|
||||
}
|
||||
|
||||
assert(!must_mount || workspace_mounted > 0);
|
||||
@@ -3477,7 +3459,7 @@ static int setup_credentials_internal(
|
||||
|
||||
if (install) {
|
||||
/* Make workspace read-only now, so that any bind mount we make from it defaults to read-only too */
|
||||
r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL);
|
||||
r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ true), NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -70,21 +70,36 @@ static void import_credentials_context_free(ImportCredentialContext *c) {
|
||||
c->target_dir_fd = safe_close(c->target_dir_fd);
|
||||
}
|
||||
|
||||
static int acquire_encrypted_credential_directory(ImportCredentialContext *c) {
|
||||
static int acquire_credential_directory(ImportCredentialContext *c, const char *path, bool with_mount) {
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(path);
|
||||
|
||||
if (c->target_dir_fd >= 0)
|
||||
return c->target_dir_fd;
|
||||
|
||||
r = mkdir_safe_label(ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, 0700, 0, 0, MKDIR_WARN_MODE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create " ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY ": %m");
|
||||
r = path_is_mount_point(path, NULL, 0);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to determine if %s is a mount point: %m", path);
|
||||
|
||||
c->target_dir_fd = open(ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
r = mkdir_safe_label(path, 0700, 0, 0, MKDIR_WARN_MODE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create %s mount point: %m", path);
|
||||
|
||||
r = 0; /* Now it exists and is not a mount point */
|
||||
}
|
||||
if (r > 0)
|
||||
/* If already a mount point, then remount writable */
|
||||
(void) mount_nofollow_verbose(LOG_WARNING, NULL, path, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
|
||||
else if (with_mount)
|
||||
/* If not a mount point yet, and the credentials are not encrypted, then let's try to mount a no-swap fs there */
|
||||
(void) mount_credentials_fs(path, CREDENTIALS_TOTAL_SIZE_MAX, /* ro= */ false);
|
||||
|
||||
c->target_dir_fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (c->target_dir_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open " ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY ": %m");
|
||||
return log_error_errno(errno, "Failed to open %s: %m", path);
|
||||
|
||||
return c->target_dir_fd;
|
||||
}
|
||||
@@ -137,7 +152,7 @@ static int finalize_credentials_dir(const char *dir, const char *envvar) {
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to make '%s' a mount point, ignoring: %m", dir);
|
||||
else
|
||||
(void) mount_nofollow_verbose(LOG_WARNING, NULL, dir, NULL, MS_BIND|MS_NODEV|MS_NOEXEC|MS_NOSUID|MS_RDONLY|MS_REMOUNT, NULL);
|
||||
(void) mount_nofollow_verbose(LOG_WARNING, NULL, dir, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ true), NULL);
|
||||
|
||||
if (setenv(envvar, dir, /* overwrite= */ true) < 0)
|
||||
return log_error_errno(errno, "Failed to set $%s environment variable: %m", envvar);
|
||||
@@ -227,7 +242,7 @@ static int import_credentials_boot(void) {
|
||||
if (!credential_size_ok(&context, n, st.st_size))
|
||||
continue;
|
||||
|
||||
r = acquire_encrypted_credential_directory(&context);
|
||||
r = acquire_credential_directory(&context, ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, /* with_mount= */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -261,48 +276,23 @@ static int import_credentials_boot(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acquire_credential_directory(ImportCredentialContext *c) {
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
|
||||
if (c->target_dir_fd >= 0)
|
||||
return c->target_dir_fd;
|
||||
|
||||
r = path_is_mount_point(SYSTEM_CREDENTIALS_DIRECTORY, NULL, 0);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to determine if " SYSTEM_CREDENTIALS_DIRECTORY " is a mount point: %m");
|
||||
|
||||
r = mkdir_safe_label(SYSTEM_CREDENTIALS_DIRECTORY, 0700, 0, 0, MKDIR_WARN_MODE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create " SYSTEM_CREDENTIALS_DIRECTORY " mount point: %m");
|
||||
|
||||
r = 0; /* Now it exists and is not a mount point */
|
||||
}
|
||||
if (r == 0)
|
||||
/* If not a mountpoint yet, try to mount a ramfs there (so that this stuff isn't swapped
|
||||
* out), but if that doesn't work, let's just use the regular tmpfs it already is. */
|
||||
(void) mount_nofollow_verbose(LOG_WARNING, "ramfs", SYSTEM_CREDENTIALS_DIRECTORY, "ramfs", MS_NODEV|MS_NOEXEC|MS_NOSUID, "mode=0700");
|
||||
|
||||
c->target_dir_fd = open(SYSTEM_CREDENTIALS_DIRECTORY, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (c->target_dir_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open " SYSTEM_CREDENTIALS_DIRECTORY ": %m");
|
||||
|
||||
return c->target_dir_fd;
|
||||
}
|
||||
|
||||
static int proc_cmdline_callback(const char *key, const char *value, void *data) {
|
||||
ImportCredentialContext *c = ASSERT_PTR(data);
|
||||
_cleanup_free_ void *binary = NULL;
|
||||
_cleanup_free_ char *n = NULL;
|
||||
_cleanup_close_ int nfd = -EBADF;
|
||||
const char *colon;
|
||||
const char *colon, *d;
|
||||
bool base64;
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
|
||||
if (!proc_cmdline_key_streq(key, "systemd.set_credential"))
|
||||
if (proc_cmdline_key_streq(key, "systemd.set_credential"))
|
||||
base64 = false;
|
||||
else if (proc_cmdline_key_streq(key, "systemd.set_credential_binary"))
|
||||
base64 = true;
|
||||
else
|
||||
return 0;
|
||||
|
||||
colon = value ? strchr(value, ':') : NULL;
|
||||
@@ -321,12 +311,24 @@ static int proc_cmdline_callback(const char *key, const char *value, void *data)
|
||||
}
|
||||
|
||||
colon++;
|
||||
l = strlen(colon);
|
||||
|
||||
if (base64) {
|
||||
r = unbase64mem(colon, SIZE_MAX, &binary, &l);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to decode binary credential '%s' data, ignoring: %m", n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
d = binary;
|
||||
} else {
|
||||
d = colon;
|
||||
l = strlen(colon);
|
||||
}
|
||||
|
||||
if (!credential_size_ok(c, n, l))
|
||||
return 0;
|
||||
|
||||
r = acquire_credential_directory(c);
|
||||
r = acquire_credential_directory(c, SYSTEM_CREDENTIALS_DIRECTORY, /* with_mount= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -336,7 +338,7 @@ static int proc_cmdline_callback(const char *key, const char *value, void *data)
|
||||
if (nfd < 0)
|
||||
return nfd;
|
||||
|
||||
r = loop_write(nfd, colon, l, /* do_poll= */ false);
|
||||
r = loop_write(nfd, d, l, /* do_poll= */ false);
|
||||
if (r < 0) {
|
||||
(void) unlinkat(c->target_dir_fd, n, 0);
|
||||
return log_error_errno(r, "Failed to write credential: %m");
|
||||
@@ -433,7 +435,7 @@ static int import_credentials_qemu(ImportCredentialContext *c) {
|
||||
continue;
|
||||
}
|
||||
|
||||
r = acquire_credential_directory(c);
|
||||
r = acquire_credential_directory(c, SYSTEM_CREDENTIALS_DIRECTORY, /* with_mount= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -535,7 +537,7 @@ static int parse_smbios_strings(ImportCredentialContext *c, const char *data, si
|
||||
if (!credential_size_ok(c, cn, cdata_len))
|
||||
continue;
|
||||
|
||||
r = acquire_credential_directory(c);
|
||||
r = acquire_credential_directory(c, SYSTEM_CREDENTIALS_DIRECTORY, /* with_mount= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -611,27 +613,157 @@ static int import_credentials_smbios(ImportCredentialContext *c) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int import_credentials_initrd(ImportCredentialContext *c) {
|
||||
_cleanup_free_ DirectoryEntries *de = NULL;
|
||||
_cleanup_close_ int source_dir_fd = -EBADF;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
|
||||
/* This imports credentials from /run/credentials/@initrd/ into our credentials directory and deletes
|
||||
* the source directory afterwards. This is run once after the initrd → host transition. This is
|
||||
* supposed to establish a well-defined avenue for initrd-based host configurators to pass
|
||||
* credentials into the main system. */
|
||||
|
||||
if (in_initrd())
|
||||
return 0;
|
||||
|
||||
source_dir_fd = open("/run/credentials/@initrd", O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
|
||||
if (source_dir_fd < 0) {
|
||||
if (errno == ENOENT)
|
||||
log_debug_errno(errno, "No credentials passed from initrd.");
|
||||
else
|
||||
log_warning_errno(errno, "Failed to open '/run/credentials/@initrd', ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = readdir_all(source_dir_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to read '/run/credentials/@initrd' contents, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
FOREACH_ARRAY(entry, de->entries, de->n_entries) {
|
||||
_cleanup_close_ int cfd = -EBADF, nfd = -EBADF;
|
||||
const struct dirent *d = *entry;
|
||||
struct stat st;
|
||||
|
||||
if (!credential_name_valid(d->d_name)) {
|
||||
log_warning("Credential '%s' has invalid name, ignoring.", d->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
cfd = openat(source_dir_fd, d->d_name, O_RDONLY|O_CLOEXEC);
|
||||
if (cfd < 0) {
|
||||
log_warning_errno(errno, "Failed to open %s, ignoring: %m", d->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fstat(cfd, &st) < 0) {
|
||||
log_warning_errno(errno, "Failed to stat %s, ignoring: %m", d->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = stat_verify_regular(&st);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Credential file %s is not a regular file, ignoring: %m", d->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!credential_size_ok(c, d->d_name, st.st_size))
|
||||
continue;
|
||||
|
||||
r = acquire_credential_directory(c, SYSTEM_CREDENTIALS_DIRECTORY, /* with_mount= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
nfd = open_credential_file_for_write(c->target_dir_fd, SYSTEM_CREDENTIALS_DIRECTORY, d->d_name);
|
||||
if (nfd == -EEXIST)
|
||||
continue;
|
||||
if (nfd < 0)
|
||||
return nfd;
|
||||
|
||||
r = copy_bytes(cfd, nfd, st.st_size, 0);
|
||||
if (r < 0) {
|
||||
(void) unlinkat(c->target_dir_fd, d->d_name, 0);
|
||||
return log_error_errno(r, "Failed to create credential '%s': %m", d->d_name);
|
||||
}
|
||||
|
||||
c->size_sum += st.st_size;
|
||||
c->n_credentials++;
|
||||
|
||||
log_debug("Successfully copied initrd credential '%s'.", d->d_name);
|
||||
|
||||
(void) unlinkat(source_dir_fd, d->d_name, 0);
|
||||
}
|
||||
|
||||
source_dir_fd = safe_close(source_dir_fd);
|
||||
|
||||
if (rmdir("/run/credentials/@initrd") < 0)
|
||||
log_warning_errno(errno, "Failed to remove /run/credentials/@initrd after import, ignoring: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int import_credentials_trusted(void) {
|
||||
_cleanup_(import_credentials_context_free) ImportCredentialContext c = {
|
||||
.target_dir_fd = -EBADF,
|
||||
};
|
||||
int q, w, r;
|
||||
int q, w, r, y;
|
||||
|
||||
/* This is invoked during early boot when no credentials have been imported so far. (Specifically, if
|
||||
* the $CREDENTIALS_DIRECTORY or $ENCRYPTED_CREDENTIALS_DIRECTORY environment variables are not set
|
||||
* yet.) */
|
||||
|
||||
r = import_credentials_qemu(&c);
|
||||
w = import_credentials_smbios(&c);
|
||||
q = import_credentials_proc_cmdline(&c);
|
||||
y = import_credentials_initrd(&c);
|
||||
|
||||
if (c.n_credentials > 0) {
|
||||
int z;
|
||||
|
||||
log_debug("Imported %u credentials from kernel command line/smbios/fw_cfg.", c.n_credentials);
|
||||
log_debug("Imported %u credentials from kernel command line/smbios/fw_cfg/initrd.", c.n_credentials);
|
||||
|
||||
z = finalize_credentials_dir(SYSTEM_CREDENTIALS_DIRECTORY, "CREDENTIALS_DIRECTORY");
|
||||
if (z < 0)
|
||||
return z;
|
||||
}
|
||||
|
||||
return r < 0 ? r : w < 0 ? w : q;
|
||||
return r < 0 ? r : w < 0 ? w : q < 0 ? q : y;
|
||||
}
|
||||
|
||||
static int merge_credentials_trusted(const char *creds_dir) {
|
||||
_cleanup_(import_credentials_context_free) ImportCredentialContext c = {
|
||||
.target_dir_fd = -EBADF,
|
||||
};
|
||||
int r;
|
||||
|
||||
/* This is invoked after the initrd → host transitions, when credentials already have been imported,
|
||||
* but we might want to import some more from the initrd. */
|
||||
|
||||
if (in_initrd())
|
||||
return 0;
|
||||
|
||||
/* Do not try to merge initrd credentials into foreign credentials directories */
|
||||
if (!path_equal_ptr(creds_dir, SYSTEM_CREDENTIALS_DIRECTORY)) {
|
||||
log_debug("Not importing initrd credentials, as foreign $CREDENTIALS_DIRECTORY has been set.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = import_credentials_initrd(&c);
|
||||
|
||||
if (c.n_credentials > 0) {
|
||||
int z;
|
||||
|
||||
log_debug("Merged %u credentials from initrd.", c.n_credentials);
|
||||
|
||||
z = finalize_credentials_dir(SYSTEM_CREDENTIALS_DIRECTORY, "CREDENTIALS_DIRECTORY");
|
||||
if (z < 0)
|
||||
return z;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int symlink_credential_dir(const char *envvar, const char *path, const char *where) {
|
||||
@@ -657,6 +789,79 @@ static int symlink_credential_dir(const char *envvar, const char *path, const ch
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setenv_notify_socket(void) {
|
||||
_cleanup_free_ char *address = NULL;
|
||||
int r;
|
||||
|
||||
r = read_credential_with_decryption("vmm.notify_socket", (void **)&address, /* ret_size= */ NULL);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to read 'vmm.notify_socket' credential, ignoring: %m");
|
||||
|
||||
if (isempty(address))
|
||||
return 0;
|
||||
|
||||
if (setenv("NOTIFY_SOCKET", address, /* replace= */ 1) < 0)
|
||||
return log_warning_errno(errno, "Failed to set $NOTIFY_SOCKET environment variable, ignoring: %m");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int report_credentials_per_func(const char *title, int (*get_directory_func)(const char **ret)) {
|
||||
_cleanup_free_ DirectoryEntries *de = NULL;
|
||||
_cleanup_close_ int dir_fd = -EBADF;
|
||||
_cleanup_free_ char *ll = NULL;
|
||||
const char *d = NULL;
|
||||
int r, c = 0;
|
||||
|
||||
assert(title);
|
||||
assert(get_directory_func);
|
||||
|
||||
r = get_directory_func(&d);
|
||||
if (r < 0) {
|
||||
if (r == -ENXIO) /* Env var not set */
|
||||
return 0;
|
||||
|
||||
return log_warning_errno(r, "Failed to determine %s directory: %m", title);
|
||||
}
|
||||
|
||||
dir_fd = open(d, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (dir_fd < 0)
|
||||
return log_warning_errno(errno, "Failed to open credentials directory %s: %m", d);
|
||||
|
||||
r = readdir_all(dir_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to enumerate credentials directory %s: %m", d);
|
||||
|
||||
FOREACH_ARRAY(entry, de->entries, de->n_entries) {
|
||||
const struct dirent *e = *entry;
|
||||
|
||||
if (!credential_name_valid(e->d_name))
|
||||
continue;
|
||||
|
||||
if (!strextend_with_separator(&ll, ", ", e->d_name))
|
||||
return log_oom();
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
if (ll)
|
||||
log_info("Received %s: %s", title, ll);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static void report_credentials(void) {
|
||||
int p, q;
|
||||
|
||||
p = report_credentials_per_func("regular credentials", get_credentials_dir);
|
||||
q = report_credentials_per_func("untrusted credentials", get_encrypted_credentials_dir);
|
||||
|
||||
log_full(p > 0 || q > 0 ? LOG_INFO : LOG_DEBUG,
|
||||
"Acquired %i regular credentials, %i untrusted credentials.",
|
||||
p > 0 ? p : 0,
|
||||
q > 0 ? q : 0);
|
||||
}
|
||||
|
||||
int import_credentials(void) {
|
||||
const char *received_creds_dir = NULL, *received_encrypted_creds_dir = NULL;
|
||||
bool envvar_set = false;
|
||||
@@ -690,6 +895,10 @@ int import_credentials(void) {
|
||||
r = q;
|
||||
}
|
||||
|
||||
q = merge_credentials_trusted(received_creds_dir);
|
||||
if (r >= 0)
|
||||
r = q;
|
||||
|
||||
} else {
|
||||
_cleanup_free_ char *v = NULL;
|
||||
|
||||
@@ -713,18 +922,10 @@ int import_credentials(void) {
|
||||
r = q;
|
||||
}
|
||||
|
||||
if (r >= 0) {
|
||||
_cleanup_free_ char *address = NULL;
|
||||
report_credentials();
|
||||
|
||||
r = read_credential("vmm.notify_socket", (void **)&address, /* ret_size= */ NULL);
|
||||
if (r < 0 && !IN_SET(r, -ENOENT, -ENXIO))
|
||||
log_warning_errno(r, "Failed to read 'vmm.notify_socket' credential, ignoring: %m");
|
||||
else if (r >= 0 && !isempty(address)) {
|
||||
r = setenv("NOTIFY_SOCKET", address, /* replace= */ 1);
|
||||
if (r < 0)
|
||||
log_warning_errno(errno, "Failed to set $NOTIFY_SOCKET environment variable, ignoring: %m");
|
||||
}
|
||||
}
|
||||
/* Propagate vmm_notify_socket credential → $NOTIFY_SOCKET env var */
|
||||
(void) setenv_notify_socket();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -2224,10 +2224,15 @@ static int initialize_runtime(
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Pull credentials from various sources into a common credential directory (we do
|
||||
* this here, before setting up the machine ID, so that we can use credential info
|
||||
* for setting up the machine ID) */
|
||||
(void) import_credentials();
|
||||
|
||||
(void) os_release_status();
|
||||
(void) hostname_setup(true);
|
||||
/* Force transient machine-id on first boot. */
|
||||
machine_id_setup(NULL, /* force_transient= */ first_boot, arg_machine_id, NULL);
|
||||
machine_id_setup(/* root= */ NULL, /* force_transient= */ first_boot, arg_machine_id, /* ret_machine_id */ NULL);
|
||||
(void) loopback_setup();
|
||||
bump_unix_max_dgram_qlen();
|
||||
bump_file_max_and_nr_open();
|
||||
@@ -2306,10 +2311,6 @@ static int initialize_runtime(
|
||||
(void) bump_rlimit_nofile(saved_rlimit_nofile);
|
||||
(void) bump_rlimit_memlock(saved_rlimit_memlock);
|
||||
|
||||
/* Pull credentials from various sources into a common credential directory */
|
||||
if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && !skip_setup)
|
||||
(void) import_credentials();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user