Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 13 additions & 18 deletions docs/src/building/management-services.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,45 @@

When running a fleet of systems, it is common to use a central management service. Commonly, these services provide a client to be installed on each system which connects to the central service. Often, the management service requires the client to perform a one time registration.

The following example shows how to install the client into a bootc image and run it at startup to register the system. This example assumes the management-client handles future connections to the server, e.g. via a cron job or a separate systemd service. This example could be modified to create a persistent systemd service if that is required. The Containerfile is not optimized in order to more clarly explain each step, e.g. it's generally better to invoke RUN a single time to avoid creating multiple layers in the image.
The following example shows how to install the client into a bootc image and run it at first boot to register the system. This example assumes the management-client handles future connections to its management server, e.g. via a cron job or a separate systemd service. This example could be modified to create a persistent systemd service if that is required. The Containerfile is not optimized in order to more clearly explain each step, e.g. it's generally better to invoke RUN a single time to avoid creating multiple layers in the image.

```Dockerfile
FROM <bootc base image>

# Bake the credentials for the management service into the image.
ARG activation_key=

# Typically when using a management service, it will determine when to upgrade the system.
# So, disable bootc-fetch-apply-updates.timer if it is included in the base image.
RUN systemctl disable bootc-fetch-apply-updates.timer

# Install the client from dnf, or some other method that applies for your client
# Install the client from dnf, or some other method that applies for your client.
RUN dnf install management-client -y && dnf clean all

# Bake the credentials for the management service into the image
ARG activation_key=

# The existence of .run_next_boot acts as a flag to determine if the
# registration is required to run when booting
RUN touch /etc/management-client/.run_next_boot

COPY <<"EOT" /usr/lib/systemd/system/management-client.service
[Unit]
Description=Run management client at boot
Description=Register with management client on first boot
After=network-online.target
ConditionPathExists=/etc/management-client/.run_client_next_boot
ConditionPathExists=/etc/management-client/.register-on-first-boot

[Service]
Type=oneshot
EnvironmentFile=/etc/management-client/.credentials
ExecStartPre=/bin/rm -f /etc/management-client/.register-on-first-boot
ExecStart=/usr/bin/management-client register --activation-key ${CLIENT_ACTIVATION_KEY}
ExecStartPre=/bin/rm -f /etc/management-client/.run_next_boot
ExecStop=/bin/rm -f /etc/management-client/.credentials
Comment on lines +29 to 31
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There are a couple of issues with the current service definition that could lead to unexpected behavior:

  1. Registration retry: Using ExecStartPre to remove the condition file (.register-on-first-boot) means that if the registration in ExecStart fails (e.g., due to a transient network error), the service won't attempt to register again on the next boot. The condition file should only be removed upon successful registration.

  2. Credential cleanup: For a Type=oneshot service without RemainAfterExit=yes, ExecStop is not executed when the ExecStart command completes. This means the credentials file (/etc/management-client/.credentials) will not be removed as intended, leaving secrets on the disk.

A more robust approach is to handle both the conditional file removal and credential cleanup within a single ExecStart command. The suggested change below implements this. The trap command ensures /etc/management-client/.credentials is removed when the shell exits, regardless of success or failure. The && ensures /etc/management-client/.register-on-first-boot is only removed if the registration command is successful.

Suggested change
ExecStartPre=/bin/rm -f /etc/management-client/.register-on-first-boot
ExecStart=/usr/bin/management-client register --activation-key ${CLIENT_ACTIVATION_KEY}
ExecStartPre=/bin/rm -f /etc/management-client/.run_next_boot
ExecStop=/bin/rm -f /etc/management-client/.credentials
ExecStart=/bin/sh -c "trap '/bin/rm -f /etc/management-client/.credentials' EXIT; /usr/bin/management-client register --activation-key ${CLIENT_ACTIVATION_KEY} && /bin/rm -f /etc/management-client/.register-on-first-boot"

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feedback is valid, but I didn't want to alter the existing behavior. For an example, I believe the current version is sufficient.


[Install]
WantedBy=multi-user.target
EOT

# Link the service to run at startup
# Link the service to run at startup.
RUN ln -s /usr/lib/systemd/system/management-client.service /usr/lib/systemd/system/multi-user.target.wants/management-client.service

# Store the credentials in a file to be used by the systemd service
# Store the credentials in a file, so it can used by the systemd service.
RUN echo -e "CLIENT_ACTIVATION_KEY=${activation_key}" > /etc/management-client/.credentials

# Set the flag to enable the service to run one time
# The systemd service will remove this file after the registration completes the first time
RUN touch /etc/management-client/.run_next_boot
# This file exists as a condition flag for the management-client.service.
# It will be removed once the registration finishes.
RUN touch /etc/management-client/.register-on-first-boot
```

Loading