]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
doc: add new markdown docs for credentials
authorLennart Poettering <lennart@poettering.net>
Fri, 22 Apr 2022 13:43:11 +0000 (15:43 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 28 Apr 2022 16:12:00 +0000 (18:12 +0200)
docs/CREDENTIALS.md [new file with mode: 0644]
man/systemd-creds.xml
man/systemd.exec.xml
man/systemd.xml

diff --git a/docs/CREDENTIALS.md b/docs/CREDENTIALS.md
new file mode 100644 (file)
index 0000000..b437f24
--- /dev/null
@@ -0,0 +1,388 @@
+---
+title: Credentials
+category: Concepts
+layout: default
+SPDX-License-Identifier: LGPL-2.1-or-later
+---
+
+# System and Service Credentials
+
+The `systemd` service manager supports a "credential" concept for securely
+acquiring and passing credential data to systems and services. The precise
+nature of the credential data is up to applications, but the concept is
+intended to provide systems and services with potentially security sensitive
+cryptographic keys, certificates, passwords, identity information and similar
+types of information. It may also be used as generic infrastructure for
+parameterizing systems and services.
+
+Traditionally, data of this nature has often been provided to services via
+environment variables (which is problematic because by default they are
+inherited down the process tree, have size limitations, and issues with binary
+data) or simple, unencrypted files on disk. `systemd`'s system and service
+credentials are supposed to provide a better alternative for this
+purpose. Specifically, the following features are provided:
+
+1. Service credentials are acquired at the moment of service activation, and
+   released on service deactivation. They are immutable during the service
+   runtime.
+
+2. Service credentials are accessible to service code as regular files, the
+   path to access them is derived from the environment variable
+   `$CREDENTIALS_DIRECTORY`.
+
+3. Access to credentials is restricted to the service's user. Unlike
+   environment variables the credential data is not propagated down the process
+   tree. Instead each time a credential is accessed an access check is enforced
+   by the kernel. If the service is using file system namespacing the loaded
+   credential data is invisble to any other services.
+
+4. Service credentials may be acquired from files on disk, specified as literal
+   strings in unit files, acquired from another service dynamically via an
+   `AF_UNIX` socket, or inherited from the system credentials the system itself
+   received.
+
+5. Credentials may optionally be encrypted and authenticated, either with a key
+   derived from a local TPM2 chip, or one stored in `/var/`, or both. This
+   encryption is supposed to *just* *work*, and requires no manual setup. (That
+   is besides first encrypting relevant credentials with one simple command,
+   see below.)
+
+6. Service credentials are placed in non-swappable memory. (If permissions
+   allow it, via `ramfs`.)
+
+7. Credentials may be acquired from a hosting VM hypervisor (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 individual services as needed.
+
+8. Credentials are an effective way to pass parameters into services that run
+   with `RootImage=` or `RootDirectory=` and thus cannot read these resources
+   directly from the host directory tree. Specifically, [Portable
+   Services](https://systemd.io/PORTABLE_SERVICES) may be parameterized this
+   way securely and robustly.
+
+9. Credentials can be binary and relatively large (though currently an overall
+   size limit of 1M per service is enforced).
+
+## Configuring per-Service Credentials
+
+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. `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,
+   i.e. public keys/certificates – but not private keys.
+
+3. `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
+   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
+   decoded unless access to TPM2/encryption key is available.
+
+Each credential configured with these options carries a short name (suitable
+for inclusion in a filename) in the unit file, under which the invoked service
+code can then retrieve it. Each name should only be specified once.
+
+For details about these four settings [see the man
+page](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Credentials).
+
+It is a good idea to also enable mount namespacing for services that process
+credentials configured this way. If so, the runtime credential directory of the
+specific service is not visible to any other service. Use `PrivateMounts=` as
+minimal option to enable such namespacing. Note that many other sandboxing
+settings (e.g. `ProtectSystem=`, `ReadOnlyPaths=` and similar) imply
+`PrivateMounts=`, hence oftentimes it's not necessary to set this option
+explicitly.
+
+## Programming Interface from Service Code
+
+When a service is invoked with one or more credentials set it will have an
+environment variable `$CREDENTIALS_DIRECTORY` set. It contains an absolute path
+to a directory the credentials are placed in. In this directory for each
+configured credential one file is placed. In addition to the
+`$CREDENTIALS_DIRECTORY` environment variable passed to the service processes
+the `%d` specifier in unit files resolves to the service's credential
+directory.
+
+Example unit file:
+
+```
+…
+[Service]
+ExecStart=/usr/bin/myservice.sh
+LoadCredential=foobar:/etc/myfoobarcredential.txt
+Environment=FOOBARPATH=%d/foobar
+…
+```
+
+Associated service shell script `/usr/bin/myservice.sh`:
+
+```sh
+#!/bin/sh
+
+sha256sum $CREDENTIAL_PATH/foobar
+sha256sum $FOOBARPATH
+
+```
+
+A service defined like this will get the contents of the file
+`/etc/myfoobarcredential.txt` passed as credential `foobar`, which is hence
+accessible under `$CREDENTIALS_DIRECTORY/foobar`. Since we additionally pass
+the path to it as environment variable `$FOOBARPATH` the credential is also
+accessible as the path in that environment variable. When invoked, the service
+will hence show the same SHA256 hash value of `/etc/myfoobarcredential.txt`
+twice.
+
+In an ideal world, well-behaved service code would directly support credentials
+passed this way, i.e. look for `$CREDENTIALS_DIRECTORY` and load the credential
+data it needs from there. For daemons that do not support this but allow
+passing credentials via a path supplied over the command line use
+`${CREDENTIAL_PATH}` in the `ExecStart=` command line to reference the
+credentials directory. For daemons that allow passing credentials via a path
+supplied as environment variabe, use the `%d` specifier in the `Environment=`
+setting to build valid paths to specific credentials.
+
+## Tools
+
+The
+[`systemd-creds`](https://www.freedesktop.org/software/systemd/man/systemd-creds.html)
+tool is provided to work with system and service credentials. It may be used to
+access and enumerate system and service credentials, or to encrypt/decrypt credentials
+(for details about the latter, see below).
+
+When invoked from service context, `systemd-creds` passed without further
+parameters will list passed credentials. The `systemd-creds cat xyz` command
+may be used to write the contents of credential `xyz` to standard output. If
+these calls are combined with the `--system` switch credentials passed to the
+system as a whole are shown, instead of the those passed to the service the
+command is invoked from.
+
+Example use:
+
+```sh
+systemd-run -P --wait -p LoadCredential=abc:/etc/hosts systemd-creds cat abc
+```
+
+This will invoke a transient service with a credential `abc` sourced from the
+system's `/etc/hosts` file. This credential is then written to standard output
+via `systemd-creds cat`.
+
+## Encryption
+
+Credentials are supposed to be useful for carrying sensitive information, such
+as cryptographic key material. For this kind of data (symmetric) encryption and
+authentication is provided to make storage of the data at rest safer. The data
+may be encrypted and authenticated with AES256-GCM. The encryption key can
+either be one derived from the local TPM2 device, or one stored in
+`/var/lib/systemd/credential.secret`, or a combination of both. If a TPM2
+device is available and `/var/` resides on persistent storage the default
+behaviour is to use the combination of both for encryption, thus ensuring that
+credentials protected this way can only be decrypted and validated on the
+local hardware and OS installation. Encrypted credentials stored on disk thus
+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 another machine than the local one.
+
+The `systemd-creds` tool provides the commands `encrypt` and `decrypt` to
+encrypt and decrypt/authenticate credentials. Example:
+
+```sh
+systemd-creds encrypt plaintext.txt ciphertext.cred
+shred -u plaintext-txt
+systemd-run -P --wait -p LoadCredentialEncrypted=foobar:$(pwd)/ciphertext.cred systemd-creds cat foobar
+```
+
+This will first create an encrypted copy of the file `plaintext.txt` in the
+encrypted credential file `ciphertext.cred`. It then securely removes the
+source file. It then runs a transient service, that reads the encrypted file
+and passes it as decrypted credential `foobar` to the invoked service binary
+(which here is the `systemd-creds` tool, which just writes the data
+it received to standard output).
+
+Instead of storing the encrypted credential as a separate file on disk, it can
+also be embedded in the unit file. Example:
+
+```
+systemd-creds encrypt -p --name=foobar plaintext.txt -
+```
+
+This will output a `SetCredentialEncrypted=` line that can directly be used in
+a unit file. e.g.:
+
+```
+…
+[Service]
+ExecStart=/usr/bin/systemd-creds cat foobar
+SetCredentialEncrypted=foobar: \
+        k6iUCUh0RJCQyvL8k8q1UyAAAAABAAAADAAAABAAAAC1lFmbWAqWZ8dCCQkAAAAAgAAAA \
+        AAAAAALACMA0AAAACAAAAAAfgAg9uNpGmj8LL2nHE0ixcycvM3XkpOCaf+9rwGscwmqRJ \
+        cAEO24kB08FMtd/hfkZBX8PqoHd/yPTzRxJQBoBsvo9VqolKdy9Wkvih0HQnQ6NkTKEdP \
+        HQ08+x8sv5sr+Mkv4ubp3YT1Jvv7CIPCbNhFtag1n5y9J7bTOKt2SQwBOAAgACwAAABIA \
+        ID8H3RbsT7rIBH02CIgm/Gv1ukSXO3DMHmVQkDG0wEciABAAII6LvrmL60uEZcp5qnEkx \
+        SuhUjsDoXrJs0rfSWX4QAx5PwfdFuxPusgEfTYIiCb8a/W6RJc7cMweZVCQMbTARyIAAA \
+        AAJt7Q9F/Gz0pBv1Lc4Dpn1WpebyBBm+vQ5N/lSKW2XSm8cONwCopxpDc7wJjXg7OTR6r \
+        xGCpIvGXLt3ibwJl81woLya2RRjIvc/R2zNm/yWzZAjiOLPih4SuHthqiX98ey8PUmZJB \
+        VGXglCZFjBx+d7eCqTIdghtp5pkDGwMJT6pjw4FfyFK2nJPawFKPAqzw9DK2iYttFeXi5 \
+        19xCfLBH9NKS/idlYXrhp+XIEtsr26s4lx5y10Goyc3qDOR3RD2cuZj0gHwV35hhhhcCz \
+        JaYytef1X/YL+7fYH5kuE4rxSksoUuA/LhtjszBeGbcbIT+O8SuvBJHLKTSHxPL8FTyk3 \
+        L4FSkEHs0rYwUIkKmnGohDdsYrMJ2fjH3yDNBP16aD1+f/Nuh75cjhUnGsDLt9K4hGg== \
+…
+```
+
+## Inheritance from Container Managers, Hypervisors, Kernel Command Line, or the UEFI Boot Environment
+
+Sometimes it is useful to parameterize whole systems the same way as services,
+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:
+
+1. A container manager may set the `$CREDENTIALS_DIRECTORY` environment
+   variable for systemd running as PID 1 in the container, the same way as
+   systemd would set it for a service it
+   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](https://systemd.io/CONTAINER_INTERFACE)
+   documentation.
+
+2. Quite similar, qemu VMs can be invoked with `-fw_cfg
+   name=opt/io.systemd.credentials/foo,string=bar` to pass credentials from
+   host through the hypervisor into the VM. (This specific switch would set
+   credential `foo` to `bar`.)
+
+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.
+
+4. 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
+   EFI System Partition, which are then picked up by `systemd-stub` and passed
+   to the kernel and ultimately userpace where systemd receives them. This is
+   useful to implement secure parameterization of vendor-built and signed
+   initial RAM disks, as userspace can place credentials next to these EFI
+   kernels, and be sure they can be accessed securely from initrd context.
+
+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:
+
+```
+systemd-nspawn --set-credential=mycred:supersecret -i test.raw -b
+```
+
+or
+
+```
+qemu-system-x86_64 \
+        -machine type=q35,accel=kvm,smm=on \
+        -smp 2 \
+        -m 1G \
+        -cpu host \
+        -nographic \
+        -nodefaults \
+        -serial mon:stdio \
+        -drive if=none,id=hd,file=test.raw,format=raw \
+        -device virtio-scsi-pci,id=scsi \
+        -device scsi-hd,drive=hd,bootindex=1 \
+        -fw_cfg name=opt/io.systemd.credentials/mycred,string=supersecret
+```
+
+Either of these lines will boot a disk image `test.raw`, once as container via
+`systemd-nspawn`, and once as VM via `qemu`. In each case the credential
+`mycred` is set to `supersecret`.
+
+Inside of the system invoked that way the credential may then be viewed:
+
+```sh
+systemd-creds --system cat mycred
+```
+
+Or propagated to services further down:
+
+```
+systemd-run -p LoadCredential=mycred -P --wait systemd-creds cat mycred
+```
+
+## Well-Known Credentials
+
+Various services shipped with `systemd` consume credentials for tweaking behaviour:
+
+* [`systemd-sysusers(8)`](https://www.freedesktop.org/software/systemd/man/systemd-sysusers.html)
+  will look for the credentials `passwd.hashed-password.<username>`,
+  `passwd.plaintext-password.<username>` and `passwd.shell.<username>` to
+  configure the password (either in UNIX hashed form, or plaintext) or shell of
+  system users created. Replace `<username>` with the system user of your
+  choice, for example `root`.
+
+* [`systemd-firstboot(1)`](https://www.freedesktop.org/software/systemd/man/systemd-firstboot.html)
+  will look for the credentials `firstboot.locale`, `firstboot.locale-messages`,
+  `firstboot.keymap`, `firstboot.timezone`, that configure locale, keymap or
+  timezone settings in case the data is not yet set in `/etc/`.
+
+In future more services are likely to gain support for consuming credentials.
+
+Example:
+
+```
+systemd-nspawn -i test.raw  \
+        --set-credential=passwd.hashed-password.root:$(mkpasswd mysecret) \
+        --set-credential=firstboot.locale:C.UTF-8 \
+        -b
+```
+
+This boots the specified disk image as `systemd-nspawn` container, and passes
+the root password `mysecret`and default locale `C.UTF-8` to use to it. This
+data is then propagated by default to `systemd-sysusers.service` and
+`systemd-firstboot.service`, where it is applied. (Note that these services
+will only do so if these settings in `/etc/` are so far unset, i.e. they only
+have an effect on *unprovisioned* systems, and will never override data already
+established in `/etc/`.) A similar line for qemu is:
+
+```
+qemu-system-x86_64 \
+        -machine type=q35,accel=kvm,smm=on \
+        -smp 2 \
+        -m 1G \
+        -cpu host \
+        -nographic \
+        -nodefaults \
+        -serial mon:stdio \
+        -drive if=none,id=hd,file=test.raw,format=raw \
+        -device virtio-scsi-pci,id=scsi \
+        -device scsi-hd,drive=hd,bootindex=1 \
+        -fw_cfg name=opt/io.systemd.credentials/passwd.hashed-password.root,string=$(mkpasswd mysecret) \
+        -fw_cfg name=opt/io.systemd.credentials/firstboot.locale,string=C.UTF-8
+```
+
+## Relevant Paths
+
+From *service* perspective the runtime path to find loaded credentials in is
+provided in the `$CREDENTIALS_DIRECTORY` environment variable.
+
+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
+credentials that must be decrypted/validated before use, such as those from
+`systemd-stub`).
+
+The `LoadCredential=` and `LoadCredentialEncrypted=` settings when configured
+with a relative source path will search for the source file to read the
+credential from automatically. Primarily, these credentials are searched among
+the credentials passed into the system. If not found there, they are searched
+in `/etc/credstore/`, `/run/credstore/`,
+`/usr/lib/credstore/`. `LoadCredentialEncrypted=` will also search
+`/etc/credstore.encrypted/` and similar directories. These directories are
+hence a great place to store credentials to load on the system.
index 2427311770901a000321bd7c4a5c0cd081b143b0..2ccbb223e846283317bde2b5d7ed52b350c5751b 100644 (file)
@@ -41,6 +41,9 @@
     <varname>SetCredentialEncrypted=</varname> settings, see
     <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
     details.</para>
+
+    <para>For further information see <ulink url="https://systemd.io/CREDENTIALS">System and Service
+    Credentials</ulink> documentation.</para>
   </refsect1>
 
   <refsect1>
index 1ff079e1c707b44a19c1a7de1e1072e6d2077356..a88d9d311bc4038b8e3ebe7fcf4818cd5da296bc 100644 (file)
@@ -3118,7 +3118,10 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
         ID requested. Example: <literal>\0adf9d86b6eda275e/unit/foobar.service/credx</literal> in case the
         credential <literal>credx</literal> is requested for a unit <literal>foobar.service</literal>. This
         functionality is useful for using a single listening socket to serve credentials to multiple
-        consumers.</para></listitem>
+        consumers.</para>
+
+        <para>For further information see <ulink url="https://systemd.io/CREDENTIALS">System and Service
+        Credentials</ulink> documentation.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 7c2ab8f4641f165a594a369f30734cc88835e4b7..d7475b4277a6c3fbf8a37ce6529fb35d075486f1 100644 (file)
         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></listitem>
+        keys), or in testing/debugging environments.</para>
+
+        <para>For further information see <ulink url="https://systemd.io/CREDENTIALS">System and Service
+        Credentials</ulink> documentation.</para></listitem>
       </varlistentry>
 
       <varlistentry>