Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update recipes to support JWT-backed cloud-init #67

Merged
merged 7 commits into from
Aug 19, 2024
Merged
63 changes: 63 additions & 0 deletions quickstart/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,69 @@ This quickstart makes a few assumptions about the target operating system and is
```


## cloud-init Server Setup

OpenCHAMI utilizes the cloud-init platform for post-boot configuration.
A custom cloud-init server container is included with this quickstart Docker Compose setup, but must be populated prior to use.

The cloud-init server provides two API endpoints, described in the sections below.
Choose the appropriate option for your needs.

### Unprotected Data

#### Setup
The first endpoint, located at `/cloud-init/`, permits access to all stored data (and should therefore not contain configuration secrets).
Storing data into this endpoint is accomplished via HTTP POST requests containing JSON-formatted cloud-init configuration details.
For example:
```bash
curl 'https://foobar.openchami.cluster/cloud-init/' \
-X POST \
-d '{"name": "IDENTIFIER", "cloud-init": {
"userdata": {
"write_files": [{"content": "hello world", "path": "/etc/hello"}]
},
"metadata": {...},
"vendordata": {...}
}}'
```
`IDENTIFIER` can be:
- A node MAC address
- A node xname
- An SMD group name
It may be easiest to add nodes to a group for testing, and upload a cloud-init configuration for that group to this server.

#### Usage
Data is retrieved via HTTP GET requests to the `meta-data`, `user-data`, and `vendor-data` endpoints.
For example, one could download all cloud-init data for a node/group via `curl 'https://foobar.openchami.cluster/cloud-init/<IDENTIFIER>/{meta-data,user-data,vendor-data}'`.

When retrieving data, `IDENTIFIER` can also be omitted entirely (e.g. `https://foobar.openchami.cluster/cloud-init/user-data`).
In this case, the cloud-init server will attempt to look up the relevant xname based on the request's source IP address.

Thus, the intended use case is to set nodes' cloud-init datasource URLs to `https://foobar.openchami.cluster/cloud-init/`, from which the cloud-init client will load its configuration data.
Note that in this case, no `IDENTIFIER` is provided, so IP-based autodetection will be performed.

### JWT-Protected Data

#### Setup
The second endpoint, located at `/cloud-init-secure/`, restricts access to its cloud-init data behind a valid bearer token (i.e. a JWT).
Storing data into this endpoint requires a valid access token, which we assume is stored in `$ACCESS_TOKEN`.
The workflow described for unprotected data can be used, with the addition of the required authorization header, via e.g. `curl`'s `-H "Authorization: Bearer $ACCESS_TOKEN"`.

#### Usage
In order to access this protected data, nodes must also supply valid JWTs.
These may be distributed to nodes at boot time by including [`tpm-manager.yml`](tpm-manager.yml) in the `docker compose` command provided above.
(It should be provided last, since it supplements service definitions from other files.)

For nodes without hardware TPMs, the TPM manager will drop a JWT into `/var/run/cloud-init-jwt`.
The token can be retrieved and included with a request to the cloud-init server, using an invocation such as:
```bash
curl 'https://foobar.openchami.cluster/cloud-init-secure/<IDENTIFIER>/{meta-data,user-data,vendor-data}' \
--create-dirs --output '/PATH/TO/DATA-DIR/#1' \
--header "Authorization: Bearer $(</var/run/cloud-init-jwt)"
```
cloud-init (i.e. on the node) can then be pointed at `file:///PATH/TO/DATA-DIR/` as its datasource URL.

For nodes *with* hardware TPMs, the token will be dropped into the TPM's secure storage with `ownerread|ownerwrite` scope, and can be retrieved using the `tpm2_nvread` command.


## What's next?
Expand Down
7 changes: 4 additions & 3 deletions quickstart/configs/haproxy.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ frontend openchami
acl PATH_opaal-idp path_beg -i /oauth2/token

acl PATH_cloud-init path_beg -i /cloud-init
acl PATH_cloud-init path_beg -i /cloud-init-secure


use_backend opaal if PATH_opaal
use_backend opaal-idp if PATH_opaal-idp
use_backend smd if PATH_smd
use_backend bss if PATH_bss
use_backend cloud-init if PATH_cloud-init

backend opaal
server opaal opaal:3333
Expand All @@ -60,6 +62,5 @@ backend bss
server bss bss:27778
http-request replace-path ^/apis/bss/(.*) /\1




backend cloud-init
server cloud-init cloud-init:27777
24 changes: 22 additions & 2 deletions quickstart/openchami-svcs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ services:
- BSS_OAUTH2_PUBLIC_BASE_URL=http://opaal:3333
- BSS_IPXE_SERVER=${SYSTEM_NAME}.${SYSTEM_DOMAIN}
- BSS_CHAIN_PROTO=https
- BSS_BOOTSCRIPT_NOTIFY_URL=http://tpm-manager:27780/Node
#ports:
# - '27778:27778'
depends_on:
Expand All @@ -111,5 +112,24 @@ services:
interval: 5s
timeout: 10s
retries: 60


###
# cloud-init Server Container
###
# cloud-init server, with the secure route disabled for now
cloud-init:
image: ghcr.io/openchami/cloud-init:v0.1.1
container_name: cloud-init
hostname: cloud-init
environment:
- LISTEN_ADDR=:27777
- SMD_URL=http://smd:27779
- OPAAL_URL=http://opaal:3333
ports:
- '27777:27777'
depends_on:
smd:
condition: service_healthy
opaal:
condition: service_healthy
networks:
- internal
32 changes: 32 additions & 0 deletions quickstart/tpm-manager.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
services:
###
# TPM-manager container, pushes cloud-init tokens into nodes' TPM storage
###
tpm-manager:
image: ghcr.io/openchami/tpm-manager:v0.2.2
container_name: tpm-manager
hostname: tpm-manager
command: ["-port", "27780", "-batch-size", "100", "-interval", "30s"]
environment:
- OPAAL_URL=http://opaal:3333
- HSM_URL=http://smd:27779
- ANSIBLE_HOST_KEY_CHECKING=False
volumes:
- type: bind
source: /root/.ssh
target: /root/.ssh
depends_on:
opaal:
condition: service_healthy
smd:
condition: service_healthy
networks:
- internal
###
# cloud-init server container, provides secured config access via JWT authorization
# NOTE: This merges with the default cloud-init config specified in openchami-svcs.yml
###
cloud-init:
environment:
# This enables the server's secure route
- JWKS_URL=http://opaal:3333/keys