Chapter 11. Appendix: Managing users, groups, SSH keys, and secrets in image mode for RHEL
Learn more about users, groups, SSH keys, and secrets management in image mode for RHEL.
11.1. Users and groups configuration
Image mode for RHEL is a generic operating system update and configuration mechanism. You cannot use it to configure users or groups. The only exception is the bootc install
command that has the --root-ssh-authorized-keys
option.
- Users and groups configuration for generic base images
- Usually, the distribution base images do not have any configuration. Do not encrypt passwords and SSH keys with publicly-available private keys in generic images because of security risks.
- Injecting SSH keys through
systemd
credentials -
You can use
systemd
to inject a root password or SSHauthorized_keys
file in some environments. For example, use System Management BIOS (SMBIOS) to inject SSH keys system firmware. You can configure this in local virtualization environments, such asqemu
. - Injecting users and SSH keys by using
cloud-init
-
Many Infrastructure as a service (IaaS) and virtualization systems use metadata servers that are commonly processed by software such as
cloud-init
orignition
. See AWS instance metadata. The base image you are using might includecloud-init
or Ignition, or you can install it in your own derived images. In this model, the SSH configuration is managed outside of the bootc image. - Adding users and credentials by using container or unit custom logic
-
Systems such as
cloud-init
are not privileged. You can inject any logic you want to manage credentials in the way you want to launch a container image, for example, by using asystemd
unit. To manage the credentials, you can use a custom network-hosted source, for example, FreeIPA. - Adding users and credentials statically in the container build
In package-oriented systems, you can use the derived build to inject users and credentials by using the following command:
RUN useradd someuser
You can find issues in the default
shadow-utils
implementation ofuseradd
: Users and groups IDs are allocated dynamically, and this can cause drift.- User and group home directories and
/var
directory For systems configured with persistent
/home
, any changes to/var/home /var
made in the container image after initial installation will not be applied on subsequent updates.For example, if you inject
/var/home/someuser/.ssh/authorized_keys
into a container build, existing systems do not get the updatedauthorized_keys
file.- Using DynamicUser=yes for
systemd
units Use the
systemd
DynamicUser=yes
option where possible for system users.This is significantly better than the pattern of allocating users or groups at package install time, because it avoids potential UID or GID drift.
- Using
systemd
-sysusers Use
systemd
-sysusers, for example, in your derived build. For more information, see thesystemd
-sysusers documentation.COPY mycustom-user.conf /usr/lib/sysusers.d
The
sysusers
tool makes changes to the traditional/etc/passwd
file as necessary during boot time. If/etc
is persistent, this can avoidUID
orGID
drift. It means that theUID
orGID
allocation depends on how a specific machine was upgraded over time.- Using
systemd
JSON user records -
See JSON user records
systemd
documentation. Unlikesysusers
, the canonical state for these users lives in/usr
. If a subsequent image drops a user record, then it also vanishes from the system. - Using
nss-altfiles
With
nss-altfiles
, you can remove thesystemd
JSON user records. It splits system users into/usr/lib/passwd
and/usr/lib/group
, aligning with the way the OSTree project handles the 3 way merge for/etc
as it relates to/etc/passwd
. Currently, if the/etc/passwd
file is modified in any way on the local system, then subsequent changes to/etc/passwd
in the container image are not applied.Base images built by
rpm-ostree
havenns-altfiles
enabled by default.Also, base images have a system users pre-allocated and managed by the NSS file to avoid UID or GID drift.
In a derived container build, you can also append users to
/usr/lib/passwd
, for example. Usesysusers.d
orDynamicUser=yes
.- Machine-local state for users
The filesystem layout depends on the base image.
By default, the user data is stored in both
/etc
,/etc/passwd
,/etc/shadow
andgroups
, and/home
, depending on the base image. However, the generic base images have to both be machine-local persistent state. In this model/home
is a symlink to/var/home/user
.- Injecting users and SSH keys at system provisioning time
For base images where
/etc
and/var
are configured to persist by default, you can inject users by using installers such as Anaconda or Kickstart.Typically, generic installers are designed for one time bootstrap. Then, the configuration becomes a mutable machine-local state that you can change in Day 2 operations, by using some other mechanism.
You can use the Anaconda installer to set the initial password. However, changing this initial password requires a different in-system tool, such as
passwd
.These flows work equivalently in a
bootc-compatible
system, to support users directly installing generic base images, without requiring changes to the different in-system tool.- Transient home directories
Many operating system deployments minimize persistent, mutable, and executable state. This can damage user home directories.
The
/home
directory can be set astmpfs
, to ensure that user data is cleared across reboots. This approach works especially well when combined with a transient/etc
directory.To set up the user’s home directory to, for example, inject SSH
authorized_keys
or other files, use thesystemd
tmpfiles.d
snippets:f~ /home/user/.ssh/authorized_keys 600 user user - <base64 encoded data>
SSH is embedded in the image as:
/usr/lib/tmpfiles.d/<username-keys.conf
. Another example is a service embedded in the image that can fetch keys from the network and write them. This is the pattern used bycloud-init
.- UID and GID drift
-
The
/etc/passwd
and similar files are a mapping between names and numeric identifiers. When the mapping is dynamic and mixed with "stateless" container image builds, it can cause issues. Each container image build might result in the UID changing due to RPM installation ordering or other reasons. This can be a problem if that user maintains a persistent state. To handle such cases, convert it to usesysusers.d
or useDynamicUser=yes
.
11.2. Injecting secrets in image mode for RHEL
Image mode for RHEL does not have an opinionated mechanism for secrets. You can inject container pull secrets in your system for some cases, for example:
For
bootc
to fetch updates from a registry that requires authentication, you must include a pull secret in a file. In the following example, thecreds
secret contains the registry pull secret.FROM registry.redhat.io/rhel9/bootc-image-builder:latest COPY containers-auth.conf /usr/lib/tmpfiles.d/link-podman-credentials.conf RUN --mount=type=secret,id=creds,required=true cp /run/secrets/creds /usr/lib/container-auth.json && \ chmod 0600 /usr/lib/container-auth.json && \ ln -sr /usr/lib/container-auth.json /etc/ostree/auth.json
To build it, run
podman build --secret id=creds,src=$HOME/.docker/config.json
. Use a single pull secret forbootc
and Podman by using a symlink to both locations to a common persistent file embedded in the container image, for example/usr/lib/container-auth.json
.For Podman to fetch container images, include a pull secret to
/etc/containers/auth.json
. With this configuration, the two stacks share the/usr/lib/container-auth.json
file.- Injecting secrets by embedding them in a container build
- You can include secrets in the container image if the registry server is suitably protected. In some cases, embedding only bootstrap secrets into the container image is a viable pattern, especially alongside a mechanism for having a machine authenticate to a cluster. In this pattern, a provisioning tool, whether run as part of the host system or a container image, uses the bootstrap secret to inject or update other secrets, such as SSH keys, certificates, among others.
- Injecting secrets by using cloud metadata
-
Most production Infrastructure as a Service (IaaS) systems support a metadata server or equivalent which can securely host secrets, particularly bootstrap secrets. Your container image can include tools such as
cloud-init
orignition
to fetch these secrets. - Injecting secrets by embedding them in disk images
-
You can embed
bootstrap secrets
only in disk images. For example, when you generate a cloud disk image from an input container image, such as AMI or OpenStack, the disk image can contain secrets that are effectively machine-local state. Rotating them requires an additional management tool or refreshing the disk images. - Injecting secrets by using bare metal installers
- Installer tools usually support injecting configuration through secrets.
- Injecting secrets through
systemd
credentials -
The
systemd
project has a credential concept for securely acquiring and passing credential data to systems and services, which applies in some deployment methodologies. See the systemd credentials documentation for more details.
Additional resources
11.3. Injecting pull secrets for registries and disabling TLS
You can configure container images, pull secrets, and disable TLS for a registry within a system. These actions enable containerized environments to pull images from private or insecure registries.
You can include container pull secrets and other configuration to access a registry inside the base image. However, when installing by using Anaconda, the installation environment might need a duplicate copy of "bootstrap" configuration to access the targeted registry when fetching over the network.
To perform arbitrary changes to the installation environment before the target bootc container image is fetched, you can use the Anaconda %pre
command.
See the containers-auth.json(5)
for more detailed information about format and configurations of the auth.json
file.
Procedure
Configure a pull secret:
%pre mkdir -p /etc/ostree cat > /etc/ostree/auth.json << 'EOF' { "auths": { "quay.io": { "auth": "<your secret here>" } } } EOF %end
With this configuration, the system pulls images from
quay.io
using the provided authentication credentials, which are stored in/etc/ostree/auth.json
.Disable TLS for an insecure registry:
%pre mkdir -p /etc/containers/registries.conf.d/ cat > /etc/containers/registries.conf.d/local-registry.conf << 'EOF' [[registry]] location="[IP_Address]:5000" insecure=true EOF %end
With this configuration, the system pulls container images from a registry that is not secured with TLS. You can use it in development or internal networks.
You can also use %pre
to:
-
Fetch data from the network by using binaries included in the installation environment, such as
curl
. -
Inject trusted certificate authorities into the installation environment
/etc/pki/ca-trust/source/anchors
by using theupdate-ca-trust
command.
You can configure insecure registries similarly by modifying the /etc/containers
directory.
Additional resources
11.4. Configuring container pull secrets
To be able to fetch container images, you must configure a host system with a "pull secret", which includes the host updates itself. See the appendix about Injecting secrets in image mode for RHEL documentation for more details.
You can configure the container pull secrets to an image already built. If you use an external installer such as Anaconda for bare metal, or bootc-image-builder
, you must configure the systems with any applicable pull secrets.
The host bootc updates write the configuration to the /etc/ostree/auth.json
file, which is shared with rpm-ostree
.
Podman does not have system wide credentials. Podman accepts the containers-auth
locations that are underneath the following directories:
-
/run
: The content of this directory vanishes on reboot, which is not desired. -
/root
: Part of root’s home directory, which is local mutable state by default.
To unify bootc
and Podman credentials, use a single default global pull secret for both bootc
and Podman. The following container build is an example to unify the bootc
and the Podman credentials. The example expects a secret named creds
to contain the registry pull secret to build.
Procedure
-
Create a symbolic link between
bootc
and Podman to use a single pull secret. By creating the symbolic link, you ensure that both locations are present to a common persistent file embedded in the container image. Create the
/usr/lib/container-auth.json
file.FROM quay.io/<namespace>/<image>:_<tag>_ COPY containers-auth.conf /usr/lib/tmpfiles.d/link-podman-credentials.conf RUN --mount=type=secret,id=creds,required=true cp /run/secrets/creds /usr/lib/container-auth.json && \ chmod 0600 /usr/lib/container-auth.json && \ ln -sr /usr/lib/container-auth.json /etc/ostree/auth.json
When you run the containerfile, the following actions happen:
-
The Containerfile makes
/run/containers/0/auth.json
a transient runtime file. -
It creates a symbolic link to the
/usr/lib/container-auth.json
. -
It also creates a persistent file, which is also symbolic linked from
/etc/ostree/auth.json
.
-
The Containerfile makes