|
| 1 | +# subscription-manager support |
| 2 | + |
| 3 | +Exploration of the Konflux support for subscription-manager |
| 4 | +and design for how `konflux-build-cli image build` should implement it. |
| 5 | + |
| 6 | +## Context |
| 7 | + |
| 8 | +Subscription-manager makes it possible to `dnf install` subscription-gated RPMs. |
| 9 | + |
| 10 | +### How it works |
| 11 | + |
| 12 | +* `/etc/pki/entitlement/` |
| 13 | + * contains the client certs that enable the installation to succeed |
| 14 | + * created by `subscription-manager register` |
| 15 | +* `/etc/pki/consumer` |
| 16 | + * the "identity" of a registration, enables subscription-manager to modify/revoke the registration |
| 17 | + * created by `subscription-manager register` |
| 18 | +* `/etc/rhsm/ca/` |
| 19 | + * contains the CA cert(s) of the servers that serve the subscription-gated RPMs |
| 20 | + * comes with the `subscription-manager-certificates` RPM |
| 21 | +* `/etc/yum.repos.d/redhat.repo` |
| 22 | + * the repo file that tells dnf where to look for subscription-gated content |
| 23 | + and what client/CA certs to use |
| 24 | + * synthesized from `/etc/pki/entitlement` automagically by the subscription-manager dnf plugin |
| 25 | + |
| 26 | +> [!NOTE] |
| 27 | +> You don't need to have subscription-manager (or the dnf plugin) installed for this to work. |
| 28 | +> The handling is built into librhsm (used in libdnf), so dnf and microdnf just work out of the box. |
| 29 | +> All that's needed is the entitlement certs and the CA cert. |
| 30 | +
|
| 31 | +### How it works in containers (host integration) |
| 32 | + |
| 33 | +* The **host machine** may have `/usr/share/containers/mounts.conf` |
| 34 | + (comes from the `containers-common` RPM) with the following content: |
| 35 | + |
| 36 | + ```text |
| 37 | + /usr/share/rhel/secrets:/run/secrets |
| 38 | + ``` |
| 39 | + |
| 40 | +* `/usr/share/rhel/secrets` (also from `containers-common`) is: |
| 41 | + |
| 42 | + ```text |
| 43 | + /usr/share/rhel/secrets |
| 44 | + ├── etc-pki-entitlement -> ../../../../etc/pki/entitlement |
| 45 | + ├── redhat.repo -> ../../../../etc/yum.repos.d/redhat.repo |
| 46 | + └── rhsm -> ../../../../etc/rhsm |
| 47 | + ``` |
| 48 | + |
| 49 | + Meaning that the container engine will mount: |
| 50 | + * /etc/pki/entitlement -> /run/secrets/etc-pki-entitlement |
| 51 | + * /etc/rhsm -> /run/secrets/rhsm |
| 52 | + |
| 53 | + And also the redhat.repo file, but that is less important. |
| 54 | + If `subscription-manager` is installed, it manages the repo file itself. |
| 55 | + If not, modern dnf synthesizes it from the entitlement certs anyway. |
| 56 | + |
| 57 | +* The **container image** may have (UBI usually does): |
| 58 | + |
| 59 | + ```text |
| 60 | + /etc/pki/entitlement-host -> /run/secrets/etc-pki-entitlement |
| 61 | + /etc/rhsm-host -> /run/secrets/rhsm |
| 62 | + ``` |
| 63 | + |
| 64 | +* If the `*-host` paths exist and are directories (or symlinks to directories): |
| 65 | + * subscription-manager operates in container mode, meaning: |
| 66 | + * the subscription-manager CLI always exits with an error |
| 67 | + * the dnf plugin uses the `*-host` paths instead of the regular paths |
| 68 | + * librhsm uses the `*-host` paths instead of the regular paths |
| 69 | + (relevant if subscription-manager isn't installed) |
| 70 | + |
| 71 | +### How it works in Konflux |
| 72 | + |
| 73 | +The Konflux buildah task provides two mechanisms for access to subscription-gated RPMs. |
| 74 | + |
| 75 | +#### Entitlement cert secret |
| 76 | + |
| 77 | +The user runs `subscription-manager register` on their machine, |
| 78 | +takes the `/etc/pki/entitlement` certs and stores them in a secret. |
| 79 | +The buildah task takes the certs and mounts them at `/etc/pki/entitlement` during the build. |
| 80 | + |
| 81 | +> [!NOTE] |
| 82 | +> The mount path is wrong, dnf prefers the (empty) `/etc/pki/entitlement-host` dir. |
| 83 | +> It works by coincidence, thanks to a change introduced in [build-definitions@065b74b] |
| 84 | +> (the one that disables the host integration, not the one that matches the commit message). |
| 85 | +
|
| 86 | +This approach is discouraged, because the entitlement server sometimes revokes certificates |
| 87 | +as part of regular operations. But the approach is still supported. |
| 88 | + |
| 89 | +#### Activation key secret |
| 90 | + |
| 91 | +The user obtains an activation key and organization ID and stores them in a secret. |
| 92 | +The buildah task mounts them at `/activation-key/activationkey` and `/activation-key/org`. |
| 93 | +In the containerfile, the user runs: |
| 94 | + |
| 95 | +```bash |
| 96 | +subscription-manager register \ |
| 97 | + --org="$(cat /activation-key/org)" \ |
| 98 | + --activationkey="$(cat /activation-key/activationkey)" |
| 99 | +``` |
| 100 | + |
| 101 | +The buildah task mounts empty directories over `/etc/pki/entitlement` and `/etc/pki/consumer`. |
| 102 | +This prevents the secrets created as a result of `subscription-manager register` from staying |
| 103 | +in the built image (they instead go to the mounted directories). |
| 104 | + |
| 105 | +This should not have worked because subscription-manager refuses to operate in a container. |
| 106 | +Users likely soon faced problems, resulting in one of the changes in [build-definitions@065b74b] |
| 107 | +(the one that disables host integration, making subscription-manager think it's not in a container). |
| 108 | + |
| 109 | +Optionally, the buildah task may pre-register for the user. The task makes a regex-based guess and, |
| 110 | +if it doesn't look like the containerfile runs `subscription-manager register`, then the task |
| 111 | +runs the registration itself and mounts the outputs at `/etc/pki/{entitlement,consumer}`. |
| 112 | +The task still mounts the secrets at `/activation-key`, so the build has the ability to re-register. |
| 113 | + |
| 114 | +For the pre-registration path, the task also mounts `/etc/rhsm/ca/redhat-uep.pem` into the build. |
| 115 | +The reasoning for why this happens (or why it doesn't happen for the other paths) was never explained, |
| 116 | +but the logic is likely this: If the containerfile doesn't run `subscription-manager register`, |
| 117 | +then there's a chance the `subscription-manager-certificates` RPM isn't installed in the base image, |
| 118 | +so mount the CA cert in case the image doesn't have it. The same logic would apply to the entitlement |
| 119 | +cert secret approach, where the mount doesn't happen. That's most likely a bug. |
| 120 | + |
| 121 | +## konflux-build-cli implementation |
| 122 | + |
| 123 | +### Disabling host integration |
| 124 | + |
| 125 | +Proper functioning of the subscription-manager support requires host integration to be disabled. |
| 126 | +The buildah task does it by deleting `/usr/share/rhel/secrets`, but this is a destructive operation |
| 127 | +that requires root permissions. Unacceptable for a local CLI tool. |
| 128 | + |
| 129 | +Konflux-build-cli will disable host integration by mounting a tmpfs over `/usr/share/rhel/secrets` |
| 130 | +inside an user+mount namespace (`unshare --map-root-user --mount` or equivalent). |
| 131 | +This solves both problems: |
| 132 | + |
| 133 | +* Inside the user namespace, the subprocess will run as root, allowing it to create mounts |
| 134 | + even if `konflux-build-cli` is running as non-root |
| 135 | +* The mount namespace ensures the effects are local to the subprocess and don't affect the host |
| 136 | + |
| 137 | +Konflux-build-cli already wraps the `buildah build` call in multiple levels of wrappers, |
| 138 | +e.g. `buildah unshare -- konflux-build-cli internal in-user-namespace -- buildah build ...`. |
| 139 | +The `internal in-user-namespace` command will get a new `--rhsm-disable-host-integration` flag |
| 140 | +with the effect of mounting a tmpfs over the `/usr/share/rhel/secrets` directory if it exists. |
| 141 | + |
| 142 | +Same as the buildah task, the CLI will *always* disable host integration, |
| 143 | +even if the user doesn't request any subscription-manager features. |
| 144 | +This is to avoid unexpected differences in behavior between registered and unregistered hosts. |
| 145 | +We may expose a flag to toggle the disabling in the future, if necessary. |
| 146 | + |
| 147 | +### Pre-registration |
| 148 | + |
| 149 | +The regex-based guess in the current buildah task is incredibly fragile. |
| 150 | +The CLI will not implement it. Instead, the CLI will take `--rhsm-activation-preregister=true|false`. |
| 151 | +To keep backwards compatibility, the regex-based guess will stay on the Tekton task level. |
| 152 | + |
| 153 | +Also note that pre-registration will work only when running `konflux-build-cli` as root. |
| 154 | +There was an attempt to make `subscription-manager register` work inside a user+mount namespace, |
| 155 | +but subscription-manager needs read-write access to many root-owned files, which makes this |
| 156 | +approach not viable. |
| 157 | + |
| 158 | +### RHSM CA cert |
| 159 | + |
| 160 | +The buildah task's handling of the CA cert doesn't make a lot of sense. To improve coherence, |
| 161 | +the CLI will take `--rhsm-mount-ca-cert=always|auto|never`. |
| 162 | + |
| 163 | +* `always` always mounts the cert, fails if it doesn't exist on the host |
| 164 | +* `auto` mounts the cert for the activation key self-registration path (like the current buildah task) |
| 165 | + and the entitlement cert path (unlike the current buildah task, which forgets to do this). |
| 166 | + If the cert doesn't exist on the host, logs a warning and proceeds. |
| 167 | +* `never` never mounts the cert |
| 168 | + |
| 169 | +Also, the CLI will mount the whole `/etc/rhsm/ca` directory instead of a specific file. |
| 170 | +This addresses [build-definitions#1621]. |
| 171 | + |
| 172 | +## Sources |
| 173 | + |
| 174 | +libdnf |
| 175 | + |
| 176 | +* [`dnf_content_setup_enrollments`][dnf_content_setup_enrollments]: synthesizes redhat.repo |
| 177 | + from entitlement certs if subscription-manager isn't installed |
| 178 | + |
| 179 | +librhsm |
| 180 | + |
| 181 | +* [`rhsm_context_constructed`][rhsm_context_constructed]: sets up RHSM configuration, |
| 182 | + prefers `*-host` paths over regular paths |
| 183 | + |
| 184 | +subscription-manager |
| 185 | + |
| 186 | +* [`in_container`][subman_in_container]: checks if subscription-manager is running in a container |
| 187 | + (the comment is misleading, the function returns true even if /etc/pki/entitlement-host is empty) |
| 188 | +* [`main`][subman_main]: exits immediately if running in a container |
| 189 | + |
| 190 | +buildah task |
| 191 | + |
| 192 | +* [L787-L832][buildah-task-subman-lines]: subscription-manager support code |
| 193 | +* [L890][buildah-task-disable-host-integration]: disables host integration |
| 194 | + |
| 195 | +Konflux docs |
| 196 | + |
| 197 | +* [entitlement-subscription]: describes the entitlement secret approach |
| 198 | +* [activation-keys-subscription]: describes the activation key secret approach |
| 199 | + |
| 200 | +<!-- links table --> |
| 201 | +[dnf_content_setup_enrollments]: https://github.com/rpm-software-management/libdnf/blob/c885cc5a6c34a8941cc8a447b09145bb23292a53/libdnf/dnf-context.cpp#L2190 |
| 202 | +[rhsm_context_constructed]: https://github.com/rpm-software-management/librhsm/blob/29b68abbf0bf122aeda239c69578e0503dbbe957/rhsm/rhsm-context.c#L553 |
| 203 | +[subman_in_container]: https://github.com/candlepin/subscription-manager/blob/f4e41e55039a59b1deabb39aa549b8e77f475df5/src/rhsm/config.py#L108 |
| 204 | +[subman_main]: https://github.com/candlepin/subscription-manager/blob/f4e41e55039a59b1deabb39aa549b8e77f475df5/src/subscription_manager/cli_command/cli.py#L241 |
| 205 | +[build-definitions@065b74b]: https://github.com/konflux-ci/build-definitions/commit/065b74b8cc9bc99eead722ce43b6b1f7461c6e5c |
| 206 | +[build-definitions#1621]: https://github.com/konflux-ci/build-definitions/issues/1621 |
| 207 | +[buildah-task-subman-lines]: https://github.com/konflux-ci/build-definitions/blob/0628a2a4c9dc439584237bfd5a47fa3626c237ad/task/buildah/0.9/buildah.yaml#L787-L832 |
| 208 | +[buildah-task-disable-host-integration]: https://github.com/konflux-ci/build-definitions/blob/0628a2a4c9dc439584237bfd5a47fa3626c237ad/task/buildah/0.9/buildah.yaml#L890 |
| 209 | +[entitlement-subscription]: https://konflux-ci.dev/docs/building/entitlement-subscription/ |
| 210 | +[activation-keys-subscription]: https://konflux-ci.dev/docs/building/activation-keys-subscription/ |
0 commit comments