|
| 1 | +# Agent-Based Installer Authentication and Authorization - OpenShift 4.18 |
| 2 | + |
| 3 | +## Introduction |
| 4 | +Since OpenShift 4.18, the Assisted Service (AS) APIs used by the Agent-Based Installer (ABI) have been secured through authentication and authorization mechanisms. This added security ensures that only authorized services can interact with AS. This document provides an overview of how the authentication and authorization mechanisms were implemented for ABI. |
| 5 | + |
| 6 | +## Enabling Authentication and Authorization |
| 7 | + |
| 8 | +To enable authentication and authorization, the `AuthType` setting must be configured in the `assisted-service.env.template` file. Set the following value: |
| 9 | + |
| 10 | +```bash |
| 11 | +AuthType = "agent-installer-local" |
| 12 | +``` |
| 13 | + |
| 14 | +This setting ensures that the authentication mechanism required by the agent-based installer is activated. |
| 15 | + |
| 16 | +## Token Workflow: Day 1 |
| 17 | +```mermaid |
| 18 | +flowchart TD |
| 19 | + A[Create Agent ISO] --> B[Generate 3 tokens: agentAuth, userAuth, watcherAuth. Also generate public key] |
| 20 | + B --> C[Store tokens and base64 encoded public key in asset store hidden file '.openshift_state_install.json'] |
| 21 | + B --> D[Embed tokens and base64 encoded public key in /usr/local/share/assisted-service/assisted-service.env file in ISO] |
| 22 | + D --> E[Boot hosts using ISO] |
| 23 | + E --> F[Assisted service running on rendezvous host validates the token from HTTP requests coming from each node] |
| 24 | + F --> G{Are tokens valid?} |
| 25 | + G -->|Yes| H[Allow successful cluster installation] |
| 26 | + G -->|No| I[Fail cluster installation] |
| 27 | +``` |
| 28 | + |
| 29 | +## Tokens for Agent-Based Installer |
| 30 | + |
| 31 | +When creating an Agent ISO (or PXE artifacts) for installing a new cluster, ABI generates three separate authentication tokens: `agentAuth`, `userAuth`, and `watcherAuth`. These tokens serve different purposes and have varying levels of access. The tokens are the part of the ignition that gets embeded in the CoreOS base ISO along with other ABI specific configurations for a cluster. The tokens are stored in `/usr/local/share/assisted-service/assisted-service.env` in the ISO. The base64 encoded public key required to validate these tokens is also saved in the same file. Additionally, this data also gets stored in a hidden file `.openshift_state_install.json` present in the directory location mentioned when creating ISO. |
| 32 | + |
| 33 | +### Types of Tokens: |
| 34 | +1. **agentAuth**: |
| 35 | + - **Access Level**: Read-Write (RW) |
| 36 | + - **Used By**: Agent services running in the live environment when host is booted. |
| 37 | + |
| 38 | +2. **userAuth**: |
| 39 | + - **Access Level**: Read-Write (RW) |
| 40 | + - **Used By**: Systemd services (such as agent-register-infraenv.service, agent-register-cluster.service, etc) and the agent-installer-client in the Assisted Service. |
| 41 | + |
| 42 | +3. **watcherAuth**: |
| 43 | + - **Access Level**: Read-Only (RO) |
| 44 | + - **Used By**: The monitoring commands (the `wait-for` command on Day 1 and `monitor-add-nodes` command on Day 2). |
| 45 | + |
| 46 | +### Managing Access with Tokens and Security Schemes in swagger.yaml |
| 47 | +Access control in swagger.yaml is managed through a combination of tokens and endpoint annotations. Each token corresponds to a specific security scheme and dictates the level of access based on the type of operation and annotation. |
| 48 | + |
| 49 | +- agentAuth Token and Scheme: |
| 50 | + |
| 51 | +A `GET` endpoint annotated with the `agentAuth` scheme grants `read-only (RO)` access. |
| 52 | +A `POST` endpoint annotated with the same scheme enables `read-write (RW)` access. |
| 53 | + |
| 54 | +- userAuth Token and Scheme: |
| 55 | + |
| 56 | +Provides read-write (RW) access for both `GET` and `POST` endpoints. |
| 57 | +- watcherAuth Token and Scheme: |
| 58 | + |
| 59 | +Grants `read-only (RO)` access, applicable to `GET` endpoints only. |
| 60 | + |
| 61 | +By combining the appropriate token and security annotations, `swagger.yaml` effectively controls access for various user personas. |
| 62 | + |
| 63 | +### Token Expiry Behavior |
| 64 | +- **Day 1** Tokens: The tokens generated on Day 1 never expire, meaning the agent ISO remains valid indefinitely. This is useful for use cases where customers create an agent ISO on one day and boot it on a later day (e.g., in a data center). |
| 65 | +- **Day 2** Tokens: Tokens generated for nodes ISO on Day 2 expire after 48 hours. This ensures that the ISO is only valid for a short period. |
| 66 | + |
| 67 | +### Day 2 token management |
| 68 | +When a Day 2 node ISO is created, all three tokens are saved in the `openshift-config` namespace as a new secret named `agent-auth-token`. The following steps outline how token management works: |
| 69 | +1. If the `openshift-config/agent-auth-token` secret does not exist in the cluster, the secret is created with the 3 tokens mentioned above along with the public key. |
| 70 | +2. If the `openshift-config/agent-auth-token` secret already exists in the cluster, the tokens are retrieved from the secret (instead of being generated). |
| 71 | +3. All tokens expire at the same time, so only one token needs to be checked for expiry. |
| 72 | +4. If the token in the secret is older than 24 hours when creating a node ISO, the installer generates new tokens. These new tokens are saved in the asset store, and the secret in the cluster is updated with the new token values. |
| 73 | +5. If the token in the secret is not older than 24 hours, the installer retrieves the existing token from the cluster, updates the asset store with the retrieved token, and uses the retrieved token for the new node ISO. |
| 74 | +6. When a node is booted using the Day 2 `nodes.iso` to add worker nodes, the `agent-auth-token-status.service` systemd service will check if the authentication tokens have expired. If the tokens are expired, the following message will be displayed: |
| 75 | + |
| 76 | +``` |
| 77 | +The authentication token has expired. Please generate a new ISO using the "oc adm node-image create" command, then reboot the node. |
| 78 | +``` |
| 79 | + |
| 80 | +This ensures that the user is notified of expired tokens and can take appropriate action to generate a new ISO. |
| 81 | + |
| 82 | +## Token Workflow: Day 2 |
| 83 | +```mermaid |
| 84 | +flowchart TD |
| 85 | + A[Check if openshift-config/agent-auth-token exists] -->|No| B[Create secret with 3 tokens and public key] |
| 86 | + A -->|Yes| C[Retrieve tokens from existing secret] |
| 87 | + B --> D[Generate new tokens if secret does not exist] |
| 88 | + C --> D[Check if token is older than 24 hours] |
| 89 | + D -->|Yes| E[Generate new tokens, update asset store, and secret] |
| 90 | + D -->|No| F[Retrieve token, update asset store, use token for ISO] |
| 91 | + F --> G[Node boots and checks token expiry] |
| 92 | + G -->|Expired| H[Display message: 'Authentication token expired. Generate a new ISO.'] |
| 93 | + G -->|Not expired| I[Proceed with installation using retrieved token] |
| 94 | +``` |
| 95 | + |
| 96 | +## Authorization |
| 97 | + |
| 98 | +To implement authorization for ABI, a new security definition is added in the `swagger.yaml` file in AS. The new security definition is as follows: |
| 99 | + |
| 100 | +```yaml |
| 101 | +watcherAuth: |
| 102 | + type: apiKey |
| 103 | + in: header |
| 104 | + name: Watcher-Authorization |
| 105 | +``` |
| 106 | +
|
| 107 | +The required endpoints are annotated with this security definition. For example: |
| 108 | +
|
| 109 | +```yaml |
| 110 | +/v2/clusters: |
| 111 | + get: |
| 112 | + tags: |
| 113 | + - installer |
| 114 | + security: |
| 115 | + - userAuth: [admin, read-only-admin, user] |
| 116 | + - watcherAuth: [] |
| 117 | + description: Retrieves the list of OpenShift clusters. |
| 118 | + operationId: v2ListClusters |
| 119 | +``` |
| 120 | +
|
| 121 | +### Authorization Handler |
| 122 | +
|
| 123 | +The Assisted Service API includes an authorization handler implemented in the `pkg/auth/agent_local_authz_handler.go` file. The `AgentLocalAuthzHandler` is responsible for verifying that the `auth_scheme` in the token matches the required `authScheme` for the endpoint. The authorization logic works as follows: |
| 124 | + |
| 125 | +1. The `auth_scheme` claim in the JWT token is extracted. |
| 126 | +2. If the claim is malformed or missing, an error is returned. |
| 127 | +3. The `auth_scheme` claim embedded in the token specifies the type of authorization (e.g., `watcherAuth`) and is compared with the `authScheme` expected by the endpoint. This ensures that the token matches the intended purpose of the request. |
| 128 | +4. If the `auth_scheme` in the token does not match the `authScheme` required by the endpoint, or if an incorrect token header is provided, the request is rejected with a `Forbidden (403)` error. |
| 129 | + |
| 130 | +Code implementation https://github.com/openshift/assisted-service/blame/973249d13ee9c83fd8a620a94956bcd16927b352/pkg/auth/agent_local_authz_handler.go#L46-L80 |
| 131 | + |
| 132 | +For example: |
| 133 | +- Suppose the endpoint `/api/assisted-install/v2/infra-envs` requires the `watcherAuth` scheme. |
| 134 | +- A request is made with the following command, but an **incorrect token header** is sent: |
| 135 | + ```bash |
| 136 | + curl http://localhost:8090/api/assisted-install/v2/infra-envs \ |
| 137 | + -H "Authorization: eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoX3NjaGVtZSI6IndhdGNoZXJBdXRoIn0.hJ5EuBOttDQ1-ANWUJlc97H1k63d-clDCaFUZ84XSYdF03A938OfMU6RHWltv0AiWZEHNJ0gLrjLxR-1yUYqiA" |
| 138 | + ``` |
| 139 | +- Although the token specifies the correct `auth_scheme` (`watcherAuth`), the **header sent is incorrect** for this request. |
| 140 | + |
| 141 | +Expected response: |
| 142 | +```json |
| 143 | +{ |
| 144 | + "code": 403, |
| 145 | + "message": "authClaim watcherAuth is unauthorized to access" |
| 146 | +} |
| 147 | +``` |
| 148 | + |
| 149 | +**Outcome:** |
| 150 | +The system identifies that the provided header does not satisfy the endpoint’s requirements and correctly responds with an error. This confirms that tokens must not only align with the expected `authScheme` but also be passed in the correct format and header. The authorization logic ensures that only authorized requests can access protected endpoints in Assisted Service. |
| 151 | + |
| 152 | +## Authentication API (Server Side) |
| 153 | + |
| 154 | +The authentication server validates the token provided by the client using the `validateToken` function. The following steps explain the server-side validation process: |
| 155 | + |
| 156 | +1. When an agent or nodes ISO is created, a set of public and private keys is generated. The private key is used to sign the JWT tokens, while the associated public key is made available to the API server. |
| 157 | +2. The token is passed to the API server as part of the request. The API server validates the token by verifying its signature against the public key. This ensures the token was signed by the correct private key and has not been tampered with. |
| 158 | +3. If the token is invalid or expired, an error is logged and the request is rejected with a `Unauthorized` error. |
| 159 | +4. The claims in the token are extracted. |
| 160 | +5. If the `exp` (expiration) claim is found, the server checks whether the token has expired. |
| 161 | + - In the install workflow, the `exp` claim will not be present. |
| 162 | + - In the add-nodes workflow, the token expiration is checked. If expired, a new token must be generated. |
| 163 | +6. If the token is valid and not expired, the claims are returned and the request proceeds. |
0 commit comments