Exploring alternate user creation methods

In this exercise, you’ll explore other ways to create users and add credentials to image mode hosts.

Embedding user credentials into an image

There are many ways and many reasons why handling users would happen at any particular stage of the process, during deployment or after install via network authentication. In the previous exercises, we’ve created users and added ssh keys at install time, either via a blueprint file passed to bootc-image-builder or via kickstart to Anaconda.

In addition to adding users during the creation of a host system, via bootc-image-builder, cloud-init, or some other means, we can also create users during the creation of the bootc image. This can be useful in certain cases where a specific user and credential are used for initial system management connectivity or emergency users are needed in environments with limited connectivity.

These users and credentials would be tied to the image, which means controlling how secrets are used in the build are important to understand before going down this path. You will want to explore more before making this choice in your own environment.

For this example, we’re going to add an SSH key to root in the image, something that might be used for emergency access, and can be then rotated with an update.

Using podman secrets at build time

So far, we’ve been copying ssh keys directly into files with editors, which is fine for exercises or a very small numbers of users. We will use an additional feature of podman to inject the credentials into the Containerfile using a more modern method like an external password vault.

First, we’ll set up a drop-in file that will configure SSHd to look for user authorized keys in a path controlled by the image. This gives us a way to update credentials later. The drop-in uses the %u username match in the search path, so a key that matches the username in /usr/ssh would act just like the typical authorized_keys file in their home directory. Like the sudoers drop-in, the sshd_config.d directory is where SSHd looks in it’s config path for additional config files.

Create the expected directories we want to see in /etc on the host first. Since we’re adding things to /etc that we want to control via the host, we need to be careful to avoid local changes to these drop-ins.

mkdir -p etc/ssh/sshd_config.d

Then add the additional configuration directive for SSHd to read.

nano etc/ssh/sshd_config.d/330-auth-system.conf
AuthorizedKeysFile /usr/ssh/%u.keys .ssh/authorized_keys .ssh/authorized_keys2

The podman build command has an option to pass information into a build in a way that it doesn’t get stored in the final image. It will read a file or an environment variable, and make the contents available during the build in a file under mounted under /run/secrets on the host. The arguments passed to podman build --secret will determine if the source of the secret is a file or an environment variable. To pass more than one secret, you add an additional --secret argument.

Inside the build, we can have a RUN directive read that file and do something with it. There’s a --mount option for RUN that can be used for a lot of different things, but one of those options is a secrets mount type. There are other options for the RUN directive, but we’ll keep it as simple as we can here.

The id is the connection between the RUN directive in the Containerfile and the podman build command. The id of the secret is the filename mounted under /run/secrets, which we then can extract the contents for our ssh key. This id needs to match in the podman build and in the Containerfile. We’ll highlight those in the examples below.

You can now edit the Containerfile to match the following.

FROM registry.redhat.io/rhel9/rhel-bootc:9.5

RUN dnf install -y httpd

ADD etc/ /etc (1)

RUN mkdir -p /usr/ssh (2)
RUN --mount=type=secret,id=SSHPUBKEY cat /run/secrets/SSHPUBKEY > /usr/ssh/root.keys \ (3)
 && chmod 0600 /usr/ssh/root.keys (4)

RUN <<EOF
    set -euxo pipefail
    mv /var/www /usr/share/www
    sed -i 's-/var/www-/usr/share/www-' /etc/httpd/conf/httpd.conf
EOF

RUN echo "Hello Red Hat Summit 2025!!" > /usr/share/www/html/index.html

RUN systemctl enable httpd.service
1 ADD etc/ /etc → automatically picks up the drop-in for sshd since we added it to our local etc source
2 RUN mkdir -p /usr/ssh → create the new keys directory in /usr so it will be controlled in the image
3 RUN --mount=type=secret,id=SSHPUBKEY…​ → mounts the file located at the id, only available during this build
4 Fixes the permissions on the SSH key

When you build the image, make note of the --secret argument.

  • id → matches the RUN line so we have the file in the right place for use at build

  • src → the file that contains the secret on the host, in this case our SSH public key.

We’re also going to use a new tag for this image, like we did in the previous bootc switch exercise.

podman build --secret id=SSHPUBKEY,src=.ssh/my-guidkey.pub --file Containerfile --tag {registry_hostname}/httpd:auth

And make sure to push it to the registry:

podman push {registry_hostname}/httpd:auth

Changing the virtual machine image

The virtual machine you have been working with during this lab should still be running. You can check this with

virsh --connect qemu:///system list

And the output should contain a virtual machine called qcow-vm.

Now you can ssh into the virtual machine

ssh core@qcow-vm

We can change to our new image with the embedded root credentials

sudo bootc switch {registry_hostname}/httpd:auth
layers already present: 69; layers needed: 5 (7.2 kB)
Fetched layers: 7.06 KiB in 16 seconds (462 B/s)
  Deploying: done (4 seconds)
Queued for next boot: node.545jj.gcp.redhatworkshops.io/httpd:auth
  Version: 9.20250415.0
  Digest: sha256:45352aa6baee739183feeed562fd1cd06e40c40a224ba6d6eee9e1cb78214b2d

As usual, after the command is done you need to reboot the virtual machine for the changes to take effect. Before doing that, please make sure you are logged in to the virtual machine and not the hypervisor (the prompt should look like [core@localhost ~]$):

sudo systemctl reboot

Testing the changes

You can now login to the virtual machine with the newly added root credentials:

ssh root@qcow-vm

And check once again the status of bootc (no need to use sudo, you are root!):

bootc status
No staged image present
Current booted image: node.545jj.gcp.redhatworkshops.io/httpd:auth
    Image version: 9.20250415.0 (2025-04-16 21:14:37.616335138 UTC)
    Image digest: sha256:45352aa6baee739183feeed562fd1cd06e40c40a224ba6d6eee9e1cb78214b2d
Current rollback image: node.545jj.gcp.redhatworkshops.io/caddy-wp
    Image version: 9.20250415.0 (2025-04-16 20:42:46.356417348 UTC)
    Image digest: sha256:c15b09203ea36a342135cc2d1c061ea96c0b61f4e5c46fd38bc8afe3f6c787a0

Feel free to explore the virtual machine before moving on to the next section, remembering you are now root.

You’ve completed the final exercise in the lab. You should have a good baseline for how image mode operates and how it might be of use in your environment. You can find more information about image mode in the Links section at the top of the page. Feel free to log out of the virtual machine and revisit any modules as well.