Skip to content

Commit b9352f9

Browse files
authored
Qtodo OIDC refactor (#118)
* Refactor qtodo chart for integration with additional OIDCs Signed-off-by: Manuel Lorenzo <mlorenzofr@redhat.com> * Add docs/entraid.md Signed-off-by: Manuel Lorenzo <mlorenzofr@redhat.com> * Add suggestions from 1st revision Signed-off-by: Manuel Lorenzo <mlorenzofr@redhat.com> * Remove pending components in entraid documentation Signed-off-by: Manuel Lorenzo <mlorenzofr@redhat.com> * Add fallback warning message to RHBK Signed-off-by: Manuel Lorenzo <mlorenzofr@redhat.com> --------- Signed-off-by: Manuel Lorenzo <mlorenzofr@redhat.com>
1 parent 414a66a commit b9352f9

7 files changed

Lines changed: 119 additions & 29 deletions

File tree

charts/qtodo/templates/_helpers.tpl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,25 @@ quay.io/ztvp/qtodo) so no VP --set override is needed.
1616
{{- printf "%s:%s" $name (tpl .value.version .context) -}}
1717
{{- end -}}
1818
{{- end -}}
19+
20+
{{/*
21+
Generate the URL of the OIDC service
22+
*/}}
23+
{{- define "qtodo.oidc.url" }}
24+
{{- if not .Values.app.oidc.authServerUrl }}
25+
{{- printf "https://keycloak.%s/realms/%s" .Values.global.localClusterDomain .Values.app.oidc.realm }}
26+
{{- else }}
27+
{{- print .Values.app.oidc.authServerUrl }}
28+
{{- end }}
29+
{{- end }}
30+
31+
{{/*
32+
Generate the JWT Audience
33+
*/}}
34+
{{- define "qtodo.jwt.audience" }}
35+
{{- if not .Values.app.vault.audience }}
36+
{{- printf "https://keycloak.%s/realms/%s" .Values.global.localClusterDomain .Values.app.oidc.realm }}
37+
{{- else }}
38+
{{- print .Values.app.vault.audience }}
39+
{{- end }}
40+
{{- end }}

charts/qtodo/templates/app-config-env.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ metadata:
66
data:
77
{{- if eq .Values.app.spire.enabled true }}
88
QUARKUS_OIDC_ENABLED: "true"
9-
QUARKUS_OIDC_AUTH_SERVER_URL: "{{ default (printf "https://keycloak.%s/realms/%s" .Values.global.localClusterDomain .Values.app.keycloak.realm) .Values.app.oidc.authServerUrl }}"
9+
QUARKUS_OIDC_AUTH_SERVER_URL: "{{ include "qtodo.oidc.url" . }}"
1010
QUARKUS_OIDC_CLIENT_ID: "{{ .Values.app.oidc.clientId }}"
1111
QUARKUS_OIDC_APPLICATION_TYPE: "{{ .Values.app.oidc.applicationType }}"
1212
QUARKUS_HTTP_AUTH_PERMISSION_AUTHENTICATED_PATHS: "{{ .Values.app.oidc.authenticatedPaths }}"
1313
QUARKUS_HTTP_AUTH_PERMISSION_AUTHENTICATED_POLICY: "{{ .Values.app.oidc.authenticatedPolicy }}"
1414
QUARKUS_OIDC_AUTHENTICATION_FORCE_REDIRECT_HTTPS_SCHEME: "true"
15-
{{- if .Values.app.oidc.clientAssertion.enabled }}
15+
{{- if and .Values.app.oidc.clientAssertion.enabled (not .Values.app.oidc.clientSecret.enabled) }}
1616
QUARKUS_OIDC_CREDENTIALS_JWT_SOURCE: "bearer"
1717
QUARKUS_OIDC_CREDENTIALS_JWT_TOKEN_PATH: "{{ .Values.app.oidc.clientAssertion.jwtTokenPath }}"
1818
{{- end }}

charts/qtodo/templates/app-deployment.yaml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ spec:
5555
- name: qtodo-truststore-java
5656
mountPath: /usr/local/bin
5757
{{- end }}
58+
{{- if and .Values.app.oidc.enabled (contains "keycloak" (include "qtodo.oidc.url" .)) }}
5859
- name: wait-for-keycloak
5960
image: registry.redhat.io/openshift4/ose-tools-rhel9:latest
6061
imagePullPolicy: IfNotPresent
@@ -63,7 +64,7 @@ spec:
6364
- -c
6465
- |
6566
echo "Waiting for Keycloak OIDC endpoint to be available..."
66-
KEYCLOAK_URL="{{ default (printf "https://keycloak.%s/realms/ztvp/.well-known/openid-configuration" .Values.global.localClusterDomain) .Values.app.oidc.authServerUrl }}/.well-known/openid-configuration"
67+
KEYCLOAK_URL="{{ include "qtodo.oidc.url" . }}/.well-known/openid-configuration"
6768
# Remove duplicate .well-known if authServerUrl already contains realm
6869
KEYCLOAK_URL=$(echo "$KEYCLOAK_URL" | sed 's|/.well-known/openid-configuration/.well-known/openid-configuration|/.well-known/openid-configuration|')
6970
@@ -87,6 +88,7 @@ spec:
8788
- name: ztvp-trusted-ca
8889
mountPath: /etc/pki/ca-trust/extracted/pem
8990
readOnly: true
91+
{{- end }}
9092
- name: init-spiffe-helper
9193
image: {{ template "qtodo.image" (dict "value" .Values.app.images.spiffeHelper "context" $) }}
9294
imagePullPolicy: {{ .Values.app.images.spiffeHelper.pullPolicy }}
@@ -123,7 +125,7 @@ spec:
123125
- name: CREDENTIALS_FILE
124126
value: /run/secrets/db-credentials/credentials.properties
125127
- name: JWT_TOKEN_FILE
126-
value: /svids/jwt.token
128+
value: {{ .Values.app.oidc.clientAssertion.jwtTokenPath }}
127129
- name: ZTVP_CA_BUNDLE
128130
value: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
129131
volumeMounts:
@@ -176,7 +178,7 @@ spec:
176178
- name: CREDENTIALS_FILE
177179
value: /run/secrets/db-credentials/credentials.properties
178180
- name: JWT_TOKEN_FILE
179-
value: /svids/jwt.token
181+
value: {{ .Values.app.oidc.clientAssertion.jwtTokenPath }}
180182
- name: ZTVP_CA_BUNDLE
181183
value: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
182184
volumeMounts:
@@ -234,11 +236,11 @@ spec:
234236
{{- else }}
235237
- name: QUARKUS_CONFIG_LOCATIONS
236238
value: file:/run/secrets/db-credentials/credentials.properties
237-
{{- if not .Values.app.oidc.clientAssertion.enabled }}
239+
{{- if .Values.app.oidc.clientSecret.enabled }}
238240
- name: QUARKUS_OIDC_CREDENTIALS_SECRET
239241
valueFrom:
240242
secretKeyRef:
241-
name: oidc-client-secret
243+
name: {{ .Values.app.oidc.clientSecret.name }}
242244
key: client-secret
243245
{{- end }}
244246
{{- if .Values.app.truststore.enabled }}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
{{- if .Values.app.oidcSecret.enabled }}
1+
{{- if .Values.app.oidc.clientSecret.enabled }}
22
apiVersion: "external-secrets.io/v1beta1"
33
kind: ExternalSecret
44
metadata:
5-
name: {{ .Values.app.oidcSecret.name }}
5+
name: {{ .Values.app.oidc.clientSecret.name }}
66
namespace: {{ .Release.Namespace }}
77
spec:
88
refreshInterval: 15s
99
secretStoreRef:
1010
name: {{ .Values.global.secretStore.name }}
1111
kind: {{ .Values.global.secretStore.kind }}
1212
target:
13-
name: {{ .Values.app.oidcSecret.name }}
13+
name: {{ .Values.app.oidc.clientSecret.name }}
1414
template:
1515
type: Opaque
1616
data:
17-
client-secret: "{{ `{{ .client_secret }}` }}"
17+
client-secret: "{{ `{{ .client_secret | trim }}` }}"
1818
data:
1919
- secretKey: client_secret
2020
remoteRef:
21-
key: {{ .Values.app.oidcSecret.vaultPath }}
21+
key: {{ .Values.app.oidc.clientSecret.vaultPath }}
2222
property: client-secret
2323
{{- end }}

charts/qtodo/templates/spiffe-helper-config.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
{{- if eq .Values.app.spire.enabled true }}
2-
{{- $keycloakRealmUrl := default (printf "https://keycloak.%s/realms/%s" .Values.global.localClusterDomain .Values.app.keycloak.realm) .Values.app.keycloak.realmUrl }}
32
kind: ConfigMap
43
apiVersion: v1
54
metadata:
@@ -16,6 +15,6 @@ data:
1615
svid_file_name = "svid.pem"
1716
svid_key_file_name = "svid_key.pem"
1817
svid_bundle_file_name = "svid_bundle.pem"
19-
jwt_svids = [{jwt_audience="{{ $keycloakRealmUrl }}", jwt_svid_file_name="jwt.token"}]
18+
jwt_svids = [{jwt_audience="{{ include "qtodo.jwt.audience" . }}", jwt_svid_file_name="jwt.token"}]
2019
jwt_bundle_file_name = "jwt_bundle.json"
2120
{{- end }}

charts/qtodo/values.yaml

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,20 +48,19 @@ app:
4848
oidc:
4949
enabled: false # Will be enabled when SPIFFE is enabled
5050
authServerUrl: ""
51-
clientId: qtodo-app
51+
realm: "ztvp"
52+
clientId: "qtodo-app"
5253
applicationType: "web-app"
5354
authenticatedPaths: "/*"
5455
authenticatedPolicy: "authenticated"
5556
# Federated client assertion using SPIFFE JWT SVID
5657
clientAssertion:
5758
enabled: true
5859
jwtTokenPath: "/svids/jwt.token"
59-
60-
# Keycloak realm configuration
61-
keycloak:
62-
realm: "ztvp"
63-
# Keycloak realm URL (auto-generated from global.localClusterDomain and realm if empty)
64-
realmUrl: ""
60+
clientSecret:
61+
enabled: false
62+
name: "oidc-client-secret"
63+
vaultPath: "secret/data/apps/qtodo/qtodo-oidc-client"
6564

6665
spire:
6766
enabled: true # Enable SPIFFE + OIDC integration by default
@@ -72,17 +71,11 @@ app:
7271
vault:
7372
url: ""
7473
role: "qtodo"
74+
# JWT Audience (auto-generated if not set)
75+
# audience: "api://client-id"
7576
# QTodo secrets path (app-level isolation)
7677
secretPath: "secret/data/apps/qtodo/qtodo-db"
7778

78-
# OIDC External Secret configuration
79-
# Disabled when using federated client assertion (no client secret needed)
80-
oidcSecret:
81-
enabled: false
82-
name: "oidc-client-secret"
83-
# QTodo OIDC secret path (app-level isolation)
84-
vaultPath: "secret/data/apps/qtodo/qtodo-oidc-client"
85-
8679
# Seed image Job: mirrors the upstream qtodo image into the configured
8780
# registry so the deployment can pull before the supply-chain pipeline runs.
8881
seedImage:

docs/entraid.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Azure Entra ID integration
2+
3+
This document describes the steps required to integrate the **Zero Trust Validated Pattern** (ZTVP) with **Azure Entra ID**, trusting this service as the Identity Provider for the following components:
4+
5+
* Qtodo demo application
6+
7+
> [!WARNING]
8+
> The integration of Azure Entra ID into the pattern is still **in progress** and does not cover all components. This document describes those that are supported. For components not supported by Entra ID, **Red Hat Build of Keycloak (RHBK)** will continue to be used as the default OIDC.
9+
10+
## Configuration
11+
12+
To configure the components we will need access to Azure Portal with permissions to create App Registrations and a Microsoft Entra ID tenant.
13+
14+
### Qtodo
15+
16+
#### Azure setup
17+
18+
1. Go to [Azure Portal](https://portal.azure.com)
19+
2. Navigate to **Microsoft Entra ID**
20+
3. Click **App registrations** in the left menu
21+
4. Click **New registration**
22+
5. Fill in the details:
23+
* **Name**: `qtodo`
24+
* **Supported account types**: Choose based on your needs
25+
* **Single tenant**: Only users in your organization
26+
* **Multi-tenant**: Users from any organization
27+
* **Redirect URI**: Add the URL of the qtodo application here (for example `https://qtodo-qtodo.apps.ztvp.example.com`)/
28+
6. Click **Register**
29+
30+
After the creation, you will see the _Overview_ page:
31+
32+
* **Application (client) ID**: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
33+
* **Directory (tenant) ID**: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
34+
35+
**Save these values** - you will need them later.
36+
37+
Let's create a new secret for our app:
38+
39+
1. Click **Certificates & secrets** in the left menu
40+
2. Click **New client secret**
41+
3. Add a description: `qtodo secret`
42+
4. Choose expiration: 6 months, 12 months, 24 months, or custom
43+
5. Click **Add**
44+
6. **IMPORTANT**: Copy the **Value** immediately - it will not be shown again
45+
46+
**Save this value securely** - We will need to add this secret to the Hashicorp Vault in the OpenShift cluster.
47+
48+
#### ZTVP setup
49+
50+
In the `values-secret.yaml` file, we add a new entry with the secret we generated in the Azure portal. For example:
51+
52+
```yaml
53+
- name: qtodo-oidc-entraid
54+
vaultPrefixes:
55+
- apps/qtodo
56+
fields:
57+
- name: client-secret
58+
path: ~/.azure/ztvp-qtodo-entraid-secret
59+
```
60+
61+
In the `values-hub.yaml file`, we add the following configuration for the qtodo application:
62+
63+
```yaml
64+
qtodo:
65+
overrides:
66+
- name: app.oidc.authServerUrl
67+
value: https://login.microsoftonline.com/<YOUR_TENANT_ID>/v2.0
68+
- name: app.oidc.clientId
69+
value: <YOUR_CLIENT_ID>
70+
- name: app.oidc.clientSecret.enabled
71+
value: true
72+
- name: app.oidc.clientSecret.vaultPath
73+
value: secret/data/apps/qtodo/qtodo-oidc-entraid
74+
```

0 commit comments

Comments
 (0)