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:
@@ -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
|
||||
|
||||
+74
-25
@@ -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>
|
||||
|
||||
+12
-9
@@ -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>
|
||||
|
||||
|
||||
+50
-4
@@ -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) {
|
||||
|
||||
+26
-44
@@ -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;
|
||||
|
||||
|
||||
+261
-60
@@ -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;
|
||||
}
|
||||
|
||||
+6
-5
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -334,7 +334,7 @@ static int prompt_locale(int rfd) {
|
||||
|
||||
r = read_credential("firstboot.locale-messages", (void**) &arg_locale_messages, NULL);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to read credential firstboot.locale-message, ignoring: %m");
|
||||
log_debug_errno(r, "Failed to read credential firstboot.locale-messages, ignoring: %m");
|
||||
else
|
||||
acquired_from_creds = true;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "bus-error.h"
|
||||
#include "bus-locator.h"
|
||||
#include "chase.h"
|
||||
#include "creds-util.h"
|
||||
#include "efi-loader.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
@@ -1281,6 +1282,40 @@ static int add_mounts_from_cmdline(void) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int add_mounts_from_creds(void) {
|
||||
_cleanup_free_ void *b = NULL;
|
||||
struct mntent *me;
|
||||
int r, ret = 0;
|
||||
size_t bs;
|
||||
|
||||
r = read_credential_with_decryption(
|
||||
in_initrd() ? "fstab.extra.initrd" : "fstab.extra",
|
||||
&b, &bs);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
f = fmemopen_unlocked(b, bs, "r");
|
||||
if (!f)
|
||||
return log_oom();
|
||||
|
||||
while ((me = getmntent(f))) {
|
||||
r = parse_fstab_one(
|
||||
"/run/credentials",
|
||||
me->mnt_fsname,
|
||||
me->mnt_dir,
|
||||
me->mnt_type,
|
||||
me->mnt_opts,
|
||||
me->mnt_passno,
|
||||
/* initrd = */ false,
|
||||
/* use_swap_enabled = */ true);
|
||||
if (r < 0 && ret >= 0)
|
||||
ret = r;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
|
||||
int r;
|
||||
|
||||
@@ -1466,7 +1501,7 @@ static int run_generator(void) {
|
||||
(void) determine_usr();
|
||||
|
||||
if (arg_sysroot_check) {
|
||||
r = parse_fstab(true);
|
||||
r = parse_fstab(/* initrd= */ true);
|
||||
if (r == 0)
|
||||
log_debug("Nothing interesting found, not doing daemon-reload.");
|
||||
if (r > 0)
|
||||
@@ -1496,13 +1531,13 @@ static int run_generator(void) {
|
||||
/* Honour /etc/fstab only when that's enabled */
|
||||
if (arg_fstab_enabled) {
|
||||
/* Parse the local /etc/fstab, possibly from the initrd */
|
||||
r = parse_fstab(false);
|
||||
r = parse_fstab(/* initrd= */ false);
|
||||
if (r < 0 && ret >= 0)
|
||||
ret = r;
|
||||
|
||||
/* If running in the initrd also parse the /etc/fstab from the host */
|
||||
if (in_initrd())
|
||||
r = parse_fstab(true);
|
||||
r = parse_fstab(/* initrd= */ true);
|
||||
else
|
||||
r = generator_enable_remount_fs_service(arg_dest);
|
||||
if (r < 0 && ret >= 0)
|
||||
@@ -1513,6 +1548,10 @@ static int run_generator(void) {
|
||||
if (r < 0 && ret >= 0)
|
||||
ret = r;
|
||||
|
||||
r = add_mounts_from_creds();
|
||||
if (r < 0 && ret >= 0)
|
||||
ret = r;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,16 +5,18 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "creds-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "generator.h"
|
||||
#include "initrd-util.h"
|
||||
#include "log.h"
|
||||
#include "mkdir-label.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "process-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "unit-name.h"
|
||||
@@ -24,8 +26,7 @@ static const char *arg_dest = NULL;
|
||||
static bool arg_enabled = true;
|
||||
|
||||
static int add_symlink(const char *fservice, const char *tservice) {
|
||||
char *from, *to;
|
||||
int r;
|
||||
const char *from, *to;
|
||||
|
||||
assert(fservice);
|
||||
assert(tservice);
|
||||
@@ -35,8 +36,7 @@ static int add_symlink(const char *fservice, const char *tservice) {
|
||||
|
||||
(void) mkdir_parents_label(to, 0755);
|
||||
|
||||
r = symlink(from, to);
|
||||
if (r < 0) {
|
||||
if (symlink(from, to) < 0) {
|
||||
/* In case console=hvc0 is passed this will very likely result in EEXIST */
|
||||
if (errno == EEXIST)
|
||||
return 0;
|
||||
@@ -141,6 +141,56 @@ static int run_container(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static int add_credential_gettys(void) {
|
||||
static const struct {
|
||||
const char *credential_name;
|
||||
int (*func)(const char *tty);
|
||||
} table[] = {
|
||||
{ "getty.ttys.serial", add_serial_getty },
|
||||
{ "getty.ttys.container", add_container_getty },
|
||||
};
|
||||
int r;
|
||||
|
||||
FOREACH_ARRAY(t, table, ELEMENTSOF(table)) {
|
||||
_cleanup_free_ char *b = NULL;
|
||||
size_t sz = 0;
|
||||
|
||||
r = read_credential_with_decryption(t->credential_name, (void*) &b, &sz);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
f = fmemopen_unlocked(b, sz, "r");
|
||||
if (!f)
|
||||
return log_oom();
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *tty = NULL;
|
||||
char *s;
|
||||
|
||||
r = read_line(f, PATH_MAX, &tty);
|
||||
if (r == 0)
|
||||
break;
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to parse credential %s: %m", t->credential_name);
|
||||
break;
|
||||
}
|
||||
|
||||
s = strstrip(tty);
|
||||
if (startswith(s, "#"))
|
||||
continue;
|
||||
|
||||
r = t->func(s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
|
||||
int r;
|
||||
|
||||
@@ -163,6 +213,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
|
||||
|
||||
assert_se(arg_dest = dest);
|
||||
|
||||
if (in_initrd()) {
|
||||
log_debug("Skipping generator, running in the initrd.");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
|
||||
@@ -183,6 +238,10 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = add_credential_gettys();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (detect_container() > 0)
|
||||
/* Add console shell and look at $container_ttys, but don't do add any
|
||||
* further magic if we are in a container. */
|
||||
@@ -226,7 +285,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
|
||||
|
||||
p = path_join("/sys/class/tty", j);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
return log_oom();
|
||||
if (access(p, F_OK) < 0)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "generator.h"
|
||||
#include "initrd-util.h"
|
||||
#include "log.h"
|
||||
#include "mkdir-label.h"
|
||||
#include "string-util.h"
|
||||
@@ -58,6 +59,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
|
||||
|
||||
assert_se(arg_dest = dest);
|
||||
|
||||
if (in_initrd()) {
|
||||
log_debug("Skipping generator, running in the initrd.");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (check_executable(RC_LOCAL_PATH) >= 0) {
|
||||
log_debug("Automatically adding rc-local.service.");
|
||||
|
||||
|
||||
@@ -7,10 +7,13 @@
|
||||
|
||||
#include "acl-util.h"
|
||||
#include "alloc-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "user-util.h"
|
||||
|
||||
#if HAVE_ACL
|
||||
|
||||
int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *ret_entry) {
|
||||
acl_entry_t i;
|
||||
int r;
|
||||
@@ -489,3 +492,161 @@ int fd_add_uid_acl_permission(
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd_acl_make_read_only(int fd) {
|
||||
_cleanup_(acl_freep) acl_t acl = NULL;
|
||||
bool changed = false;
|
||||
acl_entry_t i;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
/* Safely drops all W bits from all relevant ACL entries of the file, without changing entries which
|
||||
* are masked by the ACL mask */
|
||||
|
||||
acl = acl_get_fd(fd);
|
||||
if (!acl) {
|
||||
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
return -errno;
|
||||
|
||||
/* No ACLs? Then just update the regular mode_t */
|
||||
return fd_acl_make_read_only_fallback(fd);
|
||||
}
|
||||
|
||||
for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
|
||||
r > 0;
|
||||
r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
|
||||
acl_permset_t permset;
|
||||
acl_tag_t tag;
|
||||
int b;
|
||||
|
||||
if (acl_get_tag_type(i, &tag) < 0)
|
||||
return -errno;
|
||||
|
||||
/* These three control the x bits overall (as ACL_MASK affects all remaining tags) */
|
||||
if (!IN_SET(tag, ACL_USER_OBJ, ACL_MASK, ACL_OTHER))
|
||||
continue;
|
||||
|
||||
if (acl_get_permset(i, &permset) < 0)
|
||||
return -errno;
|
||||
|
||||
b = acl_get_perm(permset, ACL_WRITE);
|
||||
if (b < 0)
|
||||
return -errno;
|
||||
|
||||
if (b) {
|
||||
if (acl_delete_perm(permset, ACL_WRITE) < 0)
|
||||
return -errno;
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
if (!changed)
|
||||
return 0;
|
||||
|
||||
if (acl_set_fd(fd, acl) < 0) {
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
return -errno;
|
||||
|
||||
return fd_acl_make_read_only_fallback(fd);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fd_acl_make_writable(int fd) {
|
||||
_cleanup_(acl_freep) acl_t acl = NULL;
|
||||
acl_entry_t i;
|
||||
int r;
|
||||
|
||||
/* Safely adds the writable bit to the owner's ACL entry of this inode. (And only the owner's! – This
|
||||
* not the obvious inverse of fd_acl_make_read_only() hence!) */
|
||||
|
||||
acl = acl_get_fd(fd);
|
||||
if (!acl) {
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
return -errno;
|
||||
|
||||
/* No ACLs? Then just update the regular mode_t */
|
||||
return fd_acl_make_writable_fallback(fd);
|
||||
}
|
||||
|
||||
for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
|
||||
r > 0;
|
||||
r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
|
||||
acl_permset_t permset;
|
||||
acl_tag_t tag;
|
||||
int b;
|
||||
|
||||
if (acl_get_tag_type(i, &tag) < 0)
|
||||
return -errno;
|
||||
|
||||
if (tag != ACL_USER_OBJ)
|
||||
continue;
|
||||
|
||||
if (acl_get_permset(i, &permset) < 0)
|
||||
return -errno;
|
||||
|
||||
b = acl_get_perm(permset, ACL_WRITE);
|
||||
if (b < 0)
|
||||
return -errno;
|
||||
|
||||
if (b)
|
||||
return 0; /* Already set? Then there's nothing to do. */
|
||||
|
||||
if (acl_add_perm(permset, ACL_WRITE) < 0)
|
||||
return -errno;
|
||||
|
||||
break;
|
||||
}
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
if (acl_set_fd(fd, acl) < 0) {
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
return -errno;
|
||||
|
||||
return fd_acl_make_writable_fallback(fd);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int fd_acl_make_read_only_fallback(int fd) {
|
||||
struct stat st;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if ((st.st_mode & 0222) == 0)
|
||||
return 0;
|
||||
|
||||
if (fchmod(fd, st.st_mode & 0555) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fd_acl_make_writable_fallback(int fd) {
|
||||
struct stat st;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if ((st.st_mode & 0200) != 0) /* already set */
|
||||
return 0;
|
||||
|
||||
if (fchmod(fd, (st.st_mode & 07777) | 0200) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int fd_acl_make_read_only_fallback(int fd);
|
||||
int fd_acl_make_writable_fallback(int fd);
|
||||
|
||||
#if HAVE_ACL
|
||||
#include <acl/libacl.h>
|
||||
#include <stdbool.h>
|
||||
@@ -24,6 +27,9 @@ int parse_acl(
|
||||
int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *ret);
|
||||
int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask);
|
||||
|
||||
int fd_acl_make_read_only(int fd);
|
||||
int fd_acl_make_writable(int fd);
|
||||
|
||||
/* acl_free takes multiple argument types.
|
||||
* Multiple cleanup functions are necessary. */
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(acl_t, acl_free, NULL);
|
||||
@@ -42,4 +48,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gid_t*, acl_free_gid_tp, NULL);
|
||||
static inline int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int fd_acl_make_read_only(int fd) {
|
||||
return fd_acl_make_read_only_fallback(fd);
|
||||
}
|
||||
|
||||
static inline int fd_acl_make_writable(int fd) {
|
||||
return fd_acl_make_writable_fallback(fd);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -125,6 +125,80 @@ int read_credential(const char *name, void **ret, size_t *ret_size) {
|
||||
(char**) ret, ret_size);
|
||||
}
|
||||
|
||||
int read_credential_with_decryption(const char *name, void **ret, size_t *ret_size) {
|
||||
_cleanup_(erase_and_freep) void *data = NULL;
|
||||
_cleanup_free_ char *fn = NULL;
|
||||
size_t sz = 0;
|
||||
const char *d;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
/* Just like read_credential() but will also look for encrypted credentials. Note that services only
|
||||
* receive decrypted credentials, hence use read_credential() for those. This helper here is for
|
||||
* generators, i.e. code that runs outside of service context, and thus has no decrypted credentials
|
||||
* yet.
|
||||
*
|
||||
* Note that read_credential_harder_and_warn() logs on its own, while read_credential() does not!
|
||||
* (It's a lot more complex and error prone given its TPM2 connectivty, and is generally called from
|
||||
* generators only where logging is OK).
|
||||
*
|
||||
* Error handling is also a bit different: if we can't find a credential we'll return 0 and NULL
|
||||
* pointers/zero size, rather than -ENXIO/-ENOENT. */
|
||||
|
||||
if (!credential_name_valid(name))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid credential name: %s", name);
|
||||
|
||||
r = read_credential(name, ret, ret_size);
|
||||
if (r >= 0)
|
||||
return 1; /* found */
|
||||
if (!IN_SET(r, -ENXIO, -ENOENT))
|
||||
return log_error_errno(r, "Failed read unencrypted credential '%s': %m", name);
|
||||
|
||||
r = get_encrypted_credentials_dir(&d);
|
||||
if (r == -ENXIO)
|
||||
goto not_found;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine encrypted credentials directory: %m");
|
||||
|
||||
fn = path_join(d, name);
|
||||
if (!fn)
|
||||
return log_oom();
|
||||
|
||||
r = read_full_file_full(
|
||||
AT_FDCWD, fn,
|
||||
UINT64_MAX, SIZE_MAX,
|
||||
READ_FULL_FILE_SECURE,
|
||||
NULL,
|
||||
(char**) data, &sz);
|
||||
if (r == -ENOENT)
|
||||
goto not_found;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read encrypted credential data: %m");
|
||||
|
||||
r = decrypt_credential_and_warn(
|
||||
name,
|
||||
now(CLOCK_REALTIME),
|
||||
/* tpm2_device = */ NULL,
|
||||
/* tpm2_signature_path = */ NULL,
|
||||
data,
|
||||
sz,
|
||||
ret,
|
||||
ret_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 1; /* found */
|
||||
|
||||
not_found:
|
||||
*ret = NULL;
|
||||
|
||||
if (ret_size)
|
||||
*ret_size = 0;
|
||||
|
||||
return 0; /* not found */
|
||||
}
|
||||
|
||||
int read_credential_strings_many_internal(
|
||||
const char *first_name, char **first_value,
|
||||
...) {
|
||||
|
||||
@@ -35,7 +35,8 @@ int get_encrypted_credentials_dir(const char **ret);
|
||||
#define SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@system"
|
||||
#define ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@encrypted"
|
||||
|
||||
int read_credential(const char *name, void **ret, size_t *ret_size);
|
||||
int read_credential(const char *name, void **ret, size_t *ret_size); /* use in services! */
|
||||
int read_credential_with_decryption(const char *name, void **ret, size_t *ret_size); /* use in generators + pid1! */
|
||||
|
||||
int read_credential_strings_many_internal(const char *first_name, char **first_value, ...);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "chase.h"
|
||||
#include "creds-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "id128-util.h"
|
||||
#include "io-util.h"
|
||||
@@ -27,6 +28,24 @@
|
||||
#include "umask-util.h"
|
||||
#include "virt.h"
|
||||
|
||||
static int acquire_machine_id_from_credential(sd_id128_t *ret) {
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
int r;
|
||||
|
||||
r = read_credential_with_decryption("system.machine_id", (void**) &buf, /* ret_size= */ NULL);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to read system.machine_id credential, ignoring: %m");
|
||||
if (r == 0) /* not found */
|
||||
return -ENXIO;
|
||||
|
||||
r = sd_id128_from_string(buf, ret);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to parse system.machine_id credential, ignoring: %m");
|
||||
|
||||
log_info("Initializing machine ID from credential.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generate_machine_id(const char *root, sd_id128_t *ret) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
int r;
|
||||
@@ -41,6 +60,11 @@ static int generate_machine_id(const char *root, sd_id128_t *ret) {
|
||||
}
|
||||
|
||||
if (isempty(root) && running_in_chroot() <= 0) {
|
||||
/* Let's use a system credential for the machine ID if we can */
|
||||
r = acquire_machine_id_from_credential(ret);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
|
||||
/* If that didn't work, see if we are running in a container,
|
||||
* and a machine ID was passed in via $container_uuid the way
|
||||
* libvirt/LXC does it */
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
shared_sources = files(
|
||||
'acl-util.c',
|
||||
'acpi-fpdt.c',
|
||||
'apparmor-util.c',
|
||||
'ask-password-api.c',
|
||||
@@ -189,7 +190,6 @@ syscall_list_h = custom_target(
|
||||
|
||||
if conf.get('HAVE_ACL') == 1
|
||||
shared_sources += files(
|
||||
'acl-util.c',
|
||||
'devnode-acl.c',
|
||||
)
|
||||
endif
|
||||
|
||||
@@ -1469,3 +1469,62 @@ int trigger_automount_at(int dir_fd, const char *path) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long credentials_fs_mount_flags(bool ro) {
|
||||
/* A tight set of mount flags for credentials mounts */
|
||||
return MS_NODEV|MS_NOEXEC|MS_NOSUID|ms_nosymfollow_supported()|(ro ? MS_RDONLY : 0);
|
||||
}
|
||||
|
||||
int mount_credentials_fs(const char *path, size_t size, bool ro) {
|
||||
_cleanup_free_ char *opts = NULL;
|
||||
int r, noswap_supported;
|
||||
|
||||
/* Mounts a file system we can place credentials in, i.e. with tight access modes right from the
|
||||
* beginning, and ideally swapping turned off. In order of preference:
|
||||
*
|
||||
* 1. tmpfs if it supports "noswap"
|
||||
* 2. ramfs
|
||||
* 3. tmpfs if it doesn't support "noswap"
|
||||
*/
|
||||
|
||||
noswap_supported = mount_option_supported("tmpfs", "noswap", NULL); /* Check explicitly to avoid kmsg noise */
|
||||
if (noswap_supported > 0) {
|
||||
_cleanup_free_ char *noswap_opts = NULL;
|
||||
|
||||
if (asprintf(&noswap_opts, "mode=0700,nr_inodes=1024,size=%zu,noswap", size) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Best case: tmpfs with noswap (needs kernel >= 6.3) */
|
||||
|
||||
r = mount_nofollow_verbose(
|
||||
LOG_DEBUG,
|
||||
"tmpfs",
|
||||
path,
|
||||
"tmpfs",
|
||||
credentials_fs_mount_flags(ro),
|
||||
noswap_opts);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = mount_nofollow_verbose(
|
||||
LOG_DEBUG,
|
||||
"ramfs",
|
||||
path,
|
||||
"ramfs",
|
||||
credentials_fs_mount_flags(ro),
|
||||
"mode=0700");
|
||||
if (r >= 0)
|
||||
return r;
|
||||
|
||||
if (asprintf(&opts, "mode=0700,nr_inodes=1024,size=%zu", size) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
return mount_nofollow_verbose(
|
||||
LOG_DEBUG,
|
||||
"tmpfs",
|
||||
path,
|
||||
"tmpfs",
|
||||
credentials_fs_mount_flags(ro),
|
||||
opts);
|
||||
}
|
||||
|
||||
@@ -141,3 +141,6 @@ int make_mount_point_inode_from_stat(const struct stat *st, const char *dest, mo
|
||||
int make_mount_point_inode_from_path(const char *source, const char *dest, mode_t mode);
|
||||
|
||||
int trigger_automount_at(int dir_fd, const char *path);
|
||||
|
||||
unsigned long credentials_fs_mount_flags(bool ro);
|
||||
int mount_credentials_fs(const char *path, size_t size, bool ro);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "fs-util.h"
|
||||
#include "generator.h"
|
||||
#include "initrd-util.h"
|
||||
#include "log.h"
|
||||
#include "path-util.h"
|
||||
#include "proc-cmdline.h"
|
||||
@@ -61,6 +62,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
|
||||
|
||||
assert_se(arg_dest = dest_early);
|
||||
|
||||
if (in_initrd()) {
|
||||
log_debug("Skipping generator, running in the initrd.");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
r = generate_symlink();
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "generator.h"
|
||||
#include "hashmap.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "initrd-util.h"
|
||||
#include "install.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
@@ -899,6 +900,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
|
||||
SysvStub *service;
|
||||
int r;
|
||||
|
||||
if (in_initrd()) {
|
||||
log_debug("Skipping generator, running in the initrd.");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
assert_se(arg_dest = dest_late);
|
||||
|
||||
r = lookup_paths_init_or_warn(&lp, RUNTIME_SCOPE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL);
|
||||
|
||||
@@ -69,4 +69,62 @@ TEST_RET(add_acls_for_user) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(fd_acl_make_read_only) {
|
||||
_cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-empty.XXXXXX";
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
const char *cmd;
|
||||
struct stat st;
|
||||
|
||||
fd = mkostemp_safe(fn);
|
||||
assert_se(fd >= 0);
|
||||
|
||||
/* make it more exciting */
|
||||
(void) fd_add_uid_acl_permission(fd, 1, ACL_READ|ACL_WRITE|ACL_EXECUTE);
|
||||
|
||||
assert_se(fstat(fd, &st) >= 0);
|
||||
assert_se((st.st_mode & 0200) == 0200);
|
||||
|
||||
cmd = strjoina("getfacl -p ", fn);
|
||||
assert_se(system(cmd) == 0);
|
||||
|
||||
cmd = strjoina("stat ", fn);
|
||||
assert_se(system(cmd) == 0);
|
||||
|
||||
log_info("read-only");
|
||||
assert_se(fd_acl_make_read_only(fd));
|
||||
|
||||
assert_se(fstat(fd, &st) >= 0);
|
||||
assert_se((st.st_mode & 0222) == 0000);
|
||||
|
||||
cmd = strjoina("getfacl -p ", fn);
|
||||
assert_se(system(cmd) == 0);
|
||||
|
||||
cmd = strjoina("stat ", fn);
|
||||
assert_se(system(cmd) == 0);
|
||||
|
||||
log_info("writable");
|
||||
assert_se(fd_acl_make_writable(fd));
|
||||
|
||||
assert_se(fstat(fd, &st) >= 0);
|
||||
assert_se((st.st_mode & 0222) == 0200);
|
||||
|
||||
cmd = strjoina("getfacl -p ", fn);
|
||||
assert_se(system(cmd) == 0);
|
||||
|
||||
cmd = strjoina("stat ", fn);
|
||||
assert_se(system(cmd) == 0);
|
||||
|
||||
log_info("read-only");
|
||||
assert_se(fd_acl_make_read_only(fd));
|
||||
|
||||
assert_se(fstat(fd, &st) >= 0);
|
||||
assert_se((st.st_mode & 0222) == 0000);
|
||||
|
||||
cmd = strjoina("getfacl -p ", fn);
|
||||
assert_se(system(cmd) == 0);
|
||||
|
||||
cmd = strjoina("stat ", fn);
|
||||
assert_se(system(cmd) == 0);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
||||
|
||||
@@ -15,6 +15,8 @@ QEMU_CREDS=(
|
||||
"-smbios type=11,value=io.systemd.credential.binary:binarysmbioscredential=bWFnaWNiaW5hcnlkYXRh"
|
||||
"-smbios type=11,value=io.systemd.credential.binary:sysusers.extra=dSBjcmVkdGVzdHVzZXIK"
|
||||
"-smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=ZiAvdG1wL3NvdXJjZWRmcm9tY3JlZGVudGlhbCAtIC0gLSAtIHRtcGZpbGVzc2VjcmV0Cg=="
|
||||
"-smbios type=11,value=io.systemd.credential.binary:fstab.extra=aW5qZWN0ZWQgL2luamVjdGVkIHRtcGZzIFgtbW91bnQubWtkaXIgMCAwCg=="
|
||||
"-smbios type=11,value=io.systemd.credential:getty.ttys.container=idontexist"
|
||||
)
|
||||
QEMU_OPTIONS="${QEMU_OPTIONS:-} ${QEMU_CREDS[*]}"
|
||||
|
||||
@@ -23,6 +25,7 @@ KERNEL_CREDS=(
|
||||
"systemd.set_credential=sysctl.extra:kernel.domainname=sysctltest"
|
||||
"systemd.set_credential=login.motd:hello"
|
||||
"systemd.set_credential=login.issue:welcome"
|
||||
"systemd.set_credential_binary=waldi:d29vb29mZmZ3dWZmZnd1ZmYK"
|
||||
"rd.systemd.import_credentials=no"
|
||||
)
|
||||
KERNEL_APPEND="${KERNEL_APPEND:-} ${KERNEL_CREDS[*]}"
|
||||
@@ -38,4 +41,27 @@ test_append_files() {
|
||||
generate_module_dependencies
|
||||
}
|
||||
|
||||
run_qemu_hook() {
|
||||
local td="$WORKDIR"/initrd.extra."$RANDOM"
|
||||
mkdir -m 755 "$td"
|
||||
add_at_exit_handler "rm -rf $td"
|
||||
mkdir -m 755 "$td/etc" "$td"/etc/systemd "$td"/etc/systemd/system "$td"/etc/systemd/system/initrd.target.wants
|
||||
|
||||
cat > "$td"/etc/systemd/system/initrdcred.service <<EOF
|
||||
[Unit]
|
||||
Description=populate initrd credential dir
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=sh -c "mkdir -m 0755 -p /run/credentials && mkdir -m 0700 /run/credentials/@initrd && umask 0077 && echo guatemala > /run/credentials/@initrd/myinitrdcred"
|
||||
EOF
|
||||
ln -s ../initrdcred.service "$td"/etc/systemd/system/initrd.target.wants/initrdcred.service
|
||||
|
||||
( cd "$td" && find . | cpio -o -H newc -R root:root > "$td".cpio )
|
||||
add_at_exit_handler "rm $td.cpio"
|
||||
|
||||
INITRD_EXTRA="$td.cpio"
|
||||
}
|
||||
|
||||
do_test "$@"
|
||||
|
||||
+38
-1
@@ -428,9 +428,25 @@ qemu_min_version() {
|
||||
printf "%s\n%s\n" "$1" "$qemu_ver" | sort -V -C
|
||||
}
|
||||
|
||||
# Pads a file to multiple of 4 bytes
|
||||
pad4_file() {
|
||||
local size
|
||||
size=$(stat -c "%s" "$1")
|
||||
local padded
|
||||
padded=$((((size + 3) / 4) * 4))
|
||||
truncate -s "$padded" "$1"
|
||||
}
|
||||
|
||||
# Return 0 if qemu did run (then you must check the result state/logs for actual
|
||||
# success), or 1 if qemu is not available.
|
||||
run_qemu() {
|
||||
if declare -F run_qemu_hook >/dev/null; then
|
||||
if ! run_qemu_hook "${workspace}"; then
|
||||
derror "check_qemu_hook() returned with EC > 0"
|
||||
ret=4
|
||||
fi
|
||||
fi
|
||||
|
||||
# If the test provided its own initrd, use it (e.g. TEST-24)
|
||||
if [[ -z "$INITRD" && -f "${TESTDIR:?}/initrd.img" ]]; then
|
||||
INITRD="$TESTDIR/initrd.img"
|
||||
@@ -577,7 +593,28 @@ run_qemu() {
|
||||
fi
|
||||
|
||||
if [[ -n "$INITRD" ]]; then
|
||||
qemu_options+=(-initrd "$INITRD")
|
||||
if [[ -n "$INITRD_EXTRA" ]]; then
|
||||
# An addition initrd has been specified, let's combine it with the main one.
|
||||
local t="$WORKDIR"/initrd.combined."$RANDOM"
|
||||
|
||||
# First, show contents of additional initrd
|
||||
echo "Additional initrd contents:"
|
||||
cpio -tv < "$INITRD_EXTRA"
|
||||
|
||||
# Copy the main initrd
|
||||
zstd -d -c -f "$INITRD" > "$t"
|
||||
add_at_exit_handler "rm $t"
|
||||
# Kernel requires this to be padded to multiple of 4 bytes with zeroes
|
||||
pad4_file "$t"
|
||||
|
||||
# Copy the additional initrd
|
||||
cat "$INITRD_EXTRA" >> "$t"
|
||||
pad4_file "$t"
|
||||
|
||||
qemu_options+=(-initrd "$t")
|
||||
else
|
||||
qemu_options+=(-initrd "$INITRD")
|
||||
fi
|
||||
fi
|
||||
|
||||
# Let's use KVM if possible
|
||||
|
||||
@@ -176,6 +176,7 @@ if systemd-detect-virt -q -c ; then
|
||||
elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then
|
||||
# Verify that passing creds through kernel cmdline works
|
||||
[ "$(systemd-creds --system cat kernelcmdlinecred)" = "uff" ]
|
||||
[ "$(systemd-creds --system cat waldi)" = "woooofffwufffwuff" ]
|
||||
|
||||
# And that it also works via SMBIOS
|
||||
[ "$(systemd-creds --system cat smbioscredential)" = "magicdata" ]
|
||||
@@ -301,6 +302,18 @@ systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \
|
||||
--pipe \
|
||||
true | cmp /etc/os-release
|
||||
|
||||
if ! systemd-detect-virt -q -c ; then
|
||||
# Validate that the credential we inserted via the initrd logic arrived
|
||||
test "$(systemd-creds cat --system myinitrdcred)" = "guatemala"
|
||||
|
||||
# Check that the fstab credential logic worked
|
||||
test -d /injected
|
||||
grep -q /injected /proc/self/mountinfo
|
||||
|
||||
# Make sure the getty generator processed the credentials properly
|
||||
systemctl -P Wants show getty.target | grep -q container-getty@idontexist.service
|
||||
fi
|
||||
|
||||
systemd-analyze log-level info
|
||||
|
||||
echo OK >/testok
|
||||
|
||||
Reference in New Issue
Block a user