This article provides a brief overview of systemd credentials, and it is meant as a quick guide so you can start using them on your system right away.
If you want a more in-depth explanation, you can read systemd’s article on the topic.

What are systemd credentials?

If you administer a Linux system, chances are you needed to manage sensitive credentials at some point, like a password for a database or a certificate for your web server.
Generally, you want those to only be accessible to the services that need them.

You could use environment variables, but these have limitations with size and binary data, and they are easily leaked to child processes.
Configuration files with restricted permissions are another option, but you need to spend time managing permissions and creating many users, and they aren’t encrypted.

Systemd credentials are a way to securely store those sensitive credentials, in a way that is only accessible to the services that need them, with the kernel enforcing access control.

This works by having half the encryption key stored in the TPM, and the other half stored on-disk in a file that can only be read by systemd / root.

How to use systemd credentials

As an example, let’s assume we’re making a service that needs to access a database, and we need to store the database password securely.

Warning

You should verify your system has a TPM by running systemd-analyze has-tpm2.

If you don’t have support, you can still use systemd credentials, but the encryption key will be exclusively stored on disk in /var/lib/systemd/credential.secret.
If you’re encrypting this folder with something like LUKS, this shouldn’t be a problem.

1. Create a credential

Creating a credential simply requires running systemd-creds:

# From a file
systemd-creds --name=db-password encrypt mydbpassword.txt mypassword.cred
# From user input
systemd-ask-password -n | systemd-creds encrypt --name=db-password - mypassword.cred

This will create a file containing the encrypted credential, which you can then use in your service files. The --name you choose is important, as the credential will only be accessible using that name.

2. Using the credential in a service

Using the credential in a service is a simple matter of adding a line to the service file:

[Service]
LoadCredentialEncrypted=db-password=/path/to/mypassword.cred
ExecStart=systemd-creds cat db-password

# This is important, otherwise other services could access the credential
PrivateMounts=yes

From inside the service, the credentials are accessible:

  • Via a command: systemd-creds cat db-password
  • Via a file: $CREDENTIALS_DIRECTORY/db-password
Info

In service files, you can use %d instead of $CREDENTIALS_DIRECTORY:

[Service]
Environment=DB_PASSWORD_FILE=%d/db-password
ExecStart=/usr/bin/db-client --password-file=%d/db-password

And you’re done! Now your service has access to securely-stored credentials!


Extra tips and tricks

Reading credentials from outside a service

To decrypt a credential, you can use the systemd-creds decrypt command:

sudo systemd-creds decrypt --name db-password mypassword.cred -

This will print the decrypted credential to standard output (or a file, if you specify one).

Avoiding the use of .cred files

If you want to avoid using a .cred file, it’s possible!

Simply pass the -p option to systemd-creds encrypt

systemd-creds --name=db-password encrypt -p - mydbpassword.txt -
# Or from user input
systemd-ask-password -n | sudo systemd-creds --name=db-password encrypt -p - -

The output should look like this:

SetCredentialEncrypted=db-password: \
        k6iUCUh0RJCQyvL8k8q1UyAAAAABAAAADAAAABAAAABIXRoYGRERu3TtE8kAAAAAgAAAA \
        AAAAAALACMA8AAAACAAAAAAngAgeJ2YWETGGcdo4sldYF1rf7koRvPVVAONTOohoOCLNc \
        MAECS77JVzhmpMgOQONo/bDUO37JHl34Drh2F+9BheYu8KzI01R8PHapAbY4uprgP/DH6 \
        sYBXbiY2g9ARjpaDtcqkTlxeP0nCNXGc7FDC7iRwIxFxS8vXQOFC6qcZRE7aNspYNSt+G \
        hM1TgizHOEoeoTBr9c04tvmNcjMGAE4ACAALAAAEEgAgm6XMNmsILfrgePrLtRLdrKeS4 \
        jzLt9PirIorQcWaMncAEAAgiGYodj1EGHn/80QwKr+sgTtbpGCx6j/AxaGlH3TgwJubpc \
        w2awgt+uB4+su1Et2sp5LiPMu30+KsiitBxZoydwAAAAAAIIfUhgBCALX7+25HpnQ/YYX \
        C9dahJbKImVOy8TSSlfZ+PkyanAZLJjVhGD+2IT96NkO5gjoXPA==

Now simply replace the LoadCredentialEncrypted line in your service file with the SetCredentialEncrypted one from the command’s output.

Warning

Unit files are world-readable, so anyone will be able to read the encrypted credential.
If you consider this a security risk, you should use a .cred file instead.

Using credentials before /var is available

If you want to access a credential before /var is available (for example in the initrd), then it will fail, as half of the credential’s key is stored in /var/lib/systemd/credential.secret.

To work around this, you can pass --with-key=auto-initrd when creating the credential:

systemd-creds --with-key=auto-initrd --name=db-password encrypt mydbpassword.txt mypassword.cred

This will create a credential that can be decrypted using only the TPM for encryption, and can thus be used before /var is available.

Warning

If your system doesn’t have a TPM, this will not fail, but will result in the credential being stored with no encryption.
If you want to cause creation to fail if the TPM is not available, use --with-key=tpm2 instead.

Beyond the surface

This article only focused on using systemd credentials to store sensitive credentials in an encrypted way, and only scratched the surface of what they can do.
I would once again highly recommend checking out systemd’s article on the topic for more in-depth information if you found the topic interesting.
In particular, if you ever found yourself needing to pass configuration to containers or VMs, you will most likely enjoy the section on credential inheritance.