-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HMS-2694 doc: Design for hostconf token / JWK
Add first draft of a design document that explains host configuration token and JWK handling. Signed-off-by: Christian Heimes <cheimes@redhat.com>
- Loading branch information
1 parent
2daaa64
commit 5a1079e
Showing
1 changed file
with
161 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
# Host configuration token | ||
|
||
**work in progress** | ||
|
||
Host configuration tokens are used to proof that a host is eligible to enroll | ||
into an identity domain. A host requests a token from the *idmscv-backend*, | ||
then presents it to the enrollment agent on an IPA server. The enrollment | ||
agent verifies the token and on success, enrolls the host. The process uses | ||
standard JSON Web Keys (JWKs) and JSON Web Signatures (JWS). | ||
|
||
The signing JWKs are generated by the *idmsvc-backend*. Enrollment agents | ||
download a JWK Set and revoked key ids, whenever they register or update | ||
domain data. This usually happens once a day. | ||
|
||
|
||
## JSON Web Keys (JWK) | ||
|
||
Host configuration tokens are signed with an asymmetric JWK. All JWKs are | ||
elliptic curve (EC) keys with curve P-256, which is a fast and FIPS approved | ||
algorithm. The key identifier (kid) is based on the SHA-256 fingerprint | ||
according [RFC7638](https://datatracker.ietf.org/doc/html/rfc7638), | ||
URL-safe base64 encoded and truncated to 8 characters. Additionally, the keys | ||
have an expiration time `exp` attribute, which has the same mean as `exp` in | ||
[RFC7519](https://datatracker.ietf.org/doc/html/rfc7519) JWT. | ||
|
||
* `kid`: 8 characters (SHA-256 fingerprint) | ||
* `kty`: `EC` | ||
* `crv`: `P-256` | ||
* `alg`: `ES256` | ||
* `use`: `sig` | ||
* `exp`: expiration time as Unix time stamp integer | ||
|
||
Example: | ||
|
||
```json | ||
{ | ||
"alg": "ES256", | ||
"crv": "P-256", | ||
"exp": 1704261209, | ||
"kid": "7lkFVyKx", | ||
"kty": "EC", | ||
"use": "sig", | ||
"x": "dGFSfEJTinH76FFXus90CVn6r5F_FGThLjWrnmMZ3Os", | ||
"y": "p4BOLD0REq9BbKpty0nJxZ95nNFeIrxDHH9S4dMsk7M" | ||
} | ||
``` | ||
|
||
### Encryption at rest | ||
|
||
Private JWKs are encrypted with standard AES-GCM AEAD algorithm with a random | ||
nonce. The nonce is pre-pended to the cipher text. The symmetric encryption | ||
key is derived from the app secret using HKDF-SHA256. | ||
|
||
|
||
### Database schema | ||
|
||
* `id` serial | ||
* `created_at` timestamp | ||
* `updated_at` timestamp | ||
* `deleted_at` timestamp | ||
* `kid` unique varchar (not NULL) | ||
* `expiration` timestamp (not NULL) | ||
* `token` text (not NULL) | ||
* `encrypted` byte array (NULL-able) | ||
|
||
The `token` field contains the serializes JSON string representation of the | ||
**public** JWK and `encrypted` is the encrypted private JWK. Revoked keys | ||
have a NULL `encrypted` field. | ||
|
||
|
||
## Signed token (JWS / JWT) | ||
|
||
The host configuration token uses | ||
[RFC 7519](https://datatracker.ietf.org/doc/html/rfc7515) JWS (JSON Web | ||
Signature) and [RFC7519](https://datatracker.ietf.org/doc/html/rfc7519) | ||
JWT (JSON Web Token) standards. The tokens are **not** JWTs in compact | ||
notation. Instead they are JWS with one or multiple issuers in JSON string | ||
notation. Multiple issuers enable smooth key rotation. Old keys are slowly | ||
phased out while a new key is introduced. | ||
|
||
**Header** | ||
- `kid`: always set | ||
- `alg`: `ES256` | ||
|
||
**Registered JWT claims** | ||
- issuer (`iss`) must be `"idmsvc/v1"` | ||
- subject (`sub`) is RHSM cert subject "CN" | ||
- audience (`aud`) must be `"join host"` | ||
- expiration (`exp`), not before (`nbf`), and issued at (`iat`) must be set. | ||
- JWT ID (`jti`) must be set (6 random bytes, URL-safe base64 encoded). The | ||
claim is included for audit logging and for future use. | ||
|
||
**Private JWT claims** | ||
- `rhorg` (string) is set to RHSM cert subject "O" | ||
- `rhinvid` (string) is host-based inventory uuid | ||
- `rhdomid` (string) HMSIDM domain id | ||
- `rhfqdn` (string) hosts' fully qualified domain name | ||
|
||
Example payload: | ||
|
||
```json | ||
{ | ||
"aud": ["join host"], | ||
"exp": 1696486077, | ||
"iat": 1696485477, | ||
"iss": "idmsvc/v1", | ||
"jti": "tQBCmPne", | ||
"nbf": 1696485477, | ||
"rhdomid": "772e9618-d0f8-4bf8-bfed-d2831f63c619", | ||
"rhfdqn": "client.ipa.test", | ||
"rhinvid": "1efd5f0e-7589-44ac-a9af-85ba5569d5c3", | ||
"rhorg": "16765486", | ||
"sub": "1ee437bc-7b65-40cc-8a02-c24c8a7f9368" | ||
} | ||
``` | ||
|
||
## JWK distribution | ||
|
||
### Key creation and rotation | ||
|
||
TODO | ||
|
||
### Key revocation | ||
|
||
TODO | ||
|
||
## Token flow | ||
|
||
### 1. Host configuration request | ||
|
||
The host makes a POST request `/host-conf/:inventory_id/:fqdn` with mTLS | ||
authentication using the RHSM cert / key pair. The *API gateway* verifies that | ||
the client cert is valid and the host is still subscribed. The gateway | ||
injects the organization id and subscription manager id of the certificate | ||
into the `X-Rh-Identity` header. | ||
|
||
### 2. Backend response to host | ||
|
||
The *idmsvc-backend* uses the host's *fqdn* and additional information from | ||
the request body to match an identity domain. Optionally, the backend also | ||
verifies the *inventory_id* and *fqdn* with *HBI*. | ||
|
||
All information (org id, cert CN, inventory id, fqdn, and domain id) are | ||
baked into a token claim and signed with the backend's JWKs. The token is | ||
returned to the host along with other information, which are needed by the | ||
client to enroll into the domain. This includes a list of hosts with an | ||
enrollment agent and a CA chain. | ||
|
||
### 3. Host enrollment request | ||
|
||
The host connects to an enrollment agent on the IPA server with mTLS | ||
authentication using the same RHSM cert / key pair. The enrollment agent | ||
can verify that the cert has been signed by the correct CA chain, but it | ||
has no means to check that the client cert has not been revoked. Instead | ||
it uses the signed token to verify the certificate. An enrollment request | ||
is refused unless the cert's organization and CN matches the values in | ||
the token. The IPA domain's domain id must also match the claim in the token. | ||
|
||
If the signature of the token and all claims are correct, then the enrollment | ||
agent creates the IPA host entry. The host now can enroll itself with | ||
Kerberos PKINIT authentication using the RHSM cert / key pair. |