So there’s a lot of discussion spread over a lot of PRs. It turns out that at least I misunderstood subscription-manager and the proposed workflow. Perhaps @TimoGoebel as well.
This is how I propose it should be implemented and does differ from the original design.
Roughly speaking we have 4 flows. The first 2 are both situations where the client talks directory to Foreman and there is no Smart Proxy.
Direct connection to Foreman
Vanilla Foreman
I used Mermaid JS to draw some sequence diagrams with how I think it should work.
sequenceDiagram
autonumber
Participant Foreman
Participant Client
Client->>Foreman: GET /register
activate Foreman
Foreman-->>Client: Global Registration Template
deactivate Foreman
activate Client
Client->>Foreman: POST /register
activate Foreman
Foreman-->>Client: Host Registration Template
deactivate Foreman
Client->>Foreman: POST foreman_url('built')
deactivate Client
This renders to:
In step 1 the client uses curl to retrieve a global registration template. This template is rendered and returned to the client (step 2), which then executes it. Effectively the client runs curl https://foreman.example.com/register | sh
.
Then execution starts. Within the template there’s another curl request (step 3). The goal of this is to create a host entry within Foreman. That’s POST /register
. If this is successful, the host object is created (in the state building). With that data, a Host Registration Template can be rendered and returned (step 4). This is then executed by the client. This execution is part of the original Global Registration Template and the user doesn’t have to do anything. After everything is completed, a POST to the built
URL is sent to mark the host as built (step 5).
Authentication wise the user is responsible for providing credentials for the initial GET /register
, for example by passing --user
to curl
. In the returned Global Registration Template a token is returned that allows the POST /register
to happen. This token has an expiration time; currently JWT can’t be revoked so there is a risk of replay attacks if the token is intercepted and used multiple times. If everything is retrieved over HTTPS, this should be sufficiently mitigated as long as the user doesn’t store the rendered template insecurely (in /tmp
with bad permissions for example).
I believe the HRT also generates a token for the built
URL update, but I’m uncertain about the details.
Subscription Manager (Katello)
Subscription Manager (subman) works different because there are some additional steps required. That means the workflow is on comparable at a very high level, but implementations are very different.
This implementation is only relevant for Red Hat-based workflows, at least for now.
Again, providing the diagram:
sequenceDiagram
autonumber
Participant Foreman
Participant Client
Participant SubMan
Client->>Foreman: GET /register
activate Foreman
Foreman-->>Client: Global Registration Template
deactivate Foreman
activate Client
Client->>SubMan: subscription-manager --register
activate SubMan
SubMan->>Foreman: POST to RHSM API
activate Foreman
Foreman-->>SubMan: certificates
deactivate Foreman
deactivate SubMan
Client->>Foreman: GET /templates/hrt
activate Foreman
Foreman-->>Client: Host Registration Template
deactivate Foreman
Client->>Foreman: POST foreman_url('built')
deactivate Client
The first step is still the same: user runs curl on /register
. The GRT has code to detect subman should be used and runs step 3. I’ve drawn SubMan as a separate actor, but it happens on the Client machine. Perhaps Client should be read as shell, but that’s also an implementation detail.
During step 3, subman needs to register itself. It collects facts and prepares an API request (step 4) to the RHSM API (implemented by Katello which proxies it to Candlepin). Based on the data, Foreman ends up creating the Host object. Not drawn, but Candlepin also creates client certificates which are returned to subman (step 5). It will probably not be in status building, unless some custom fact is implemented for this. I’m not too clear on the details so I’ll invite others to correct me.
Since the HRT can’t be returned via the normal way, another way must be devised. I’m suggesting a dedicated endpoint (step 6). The exact URL is not that important.
Subman authentication (step 3) happens via Activation Keys (AKs), which is already built into subman. These keys already exist today an can be reused. It should be noted that are secrets and users should treat them as such.
The HRT template endpoint (step 6) should accept client certificates and let clients identify themselves. This avoids the need for yet another token and we know exactly which host it is due to the properties on the presented certificate.
Communication with a Smart Proxy in between
I have ideas about how this should happen, but they’re based on the previous 2 proposals. That’s why I’d suggest we first agree on those and then expand on the other case.