Skip to content

Commit 22ca59f

Browse files
authored
Merge pull request #103 from chmeliik/subscription-manager-doc
Add design doc for subscription-manager support
2 parents 3d68040 + ec22235 commit 22ca59f

1 file changed

Lines changed: 210 additions & 0 deletions

File tree

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
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

Comments
 (0)