-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add example for using SPIRE for mTLS with Keycloak (#248)
* Add example for using Spire for mTLS with Keycloak Signed-off-by: Moritz Schmitz von Hülst <[email protected]> * Minor improvement to the README.md Signed-off-by: Moritz Schmitz von Hülst <[email protected]> * But I still need to learn GitHub Markdown format Signed-off-by: Moritz Schmitz von Hülst <[email protected]> * Make it more obvious that it works without a (correct) password Signed-off-by: Moritz Schmitz von Hülst <[email protected]> * Add warning for Kubernetes 1.29+ feature Signed-off-by: Moritz Schmitz von Hülst <[email protected]> * Move ghostunnel into an initContainer with restartPolicy=Always Signed-off-by: Moritz Schmitz von Hülst <[email protected]> * Apply suggestions from code review Co-authored-by: kfox1111 <[email protected]> Signed-off-by: Moritz Schmitz von Hülst <[email protected]> * Move java-spiffe-helper-properties into extraDeploy of the Keycloak chart and pin node version to it has a matching rancher/kubectl image Signed-off-by: Moritz Schmitz von Hülst <[email protected]> --------- Signed-off-by: Moritz Schmitz von Hülst <[email protected]> Co-authored-by: kfox1111 <[email protected]>
- Loading branch information
1 parent
cc8ec89
commit 43a72a2
Showing
4 changed files
with
311 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,67 @@ | ||
# keycloak-config-cli using spire | ||
|
||
> [!WARNING] | ||
> This example uses | ||
> the [`SidecarContainers`](https://kubernetes.io/docs/concepts/workloads/pods/sidecar-containers/#enabling-sidecar-containers) | ||
> feature. This is only enabled by default in Kubernetes 1.29+. | ||
This example shows how to leverage SPIRE in establishing an mTLS connection | ||
between [Keycloak](https://www.keycloak.org/) and [keycloak-config-cli](https://github.com/adorsys/keycloak-config-cli), | ||
a tool to configure Keycloak. | ||
|
||
## Setup | ||
|
||
1. Create a local cluster for testing | ||
|
||
```shell | ||
kind create cluster --image kindest/node:v1.29.0 | ||
``` | ||
|
||
2. Install CRDs | ||
|
||
```shell | ||
helm upgrade --install -n spire-server spire-crds ../../charts/spire-crds --create-namespace | ||
``` | ||
|
||
3. Install `spire-server` | ||
|
||
```shell | ||
helm upgrade --install -n spire-server spire ../../charts/spire --create-namespace -f spire-values.yaml | ||
``` | ||
|
||
4. Install `keycloak` (this also configures Keycloak for client certificate authentication) | ||
|
||
```shell | ||
helm upgrade --install keycloak oci://registry-1.docker.io/bitnamicharts/keycloak -f keycloak-values.yaml | ||
``` | ||
|
||
5. Install `keycloak-config-cli` | ||
|
||
```shell | ||
kubectl apply -f keycloak-config-cli.yaml | ||
``` | ||
|
||
6. Verify the realm config at the bottom of [keycloak-config-cli.yaml](./keycloak-config-cli.yaml) has been created! | ||
7. Cleanup | ||
|
||
```shell | ||
kind delete cluster | ||
``` | ||
|
||
## Notes | ||
|
||
### java-spiffe-helper as Keycloak initContainer | ||
|
||
This example uses [java-spiffe-helper](https://github.com/spiffe/java-spiffe/tree/main/java-spiffe-helper) as an | ||
initContainer for Keycloak. It fetches the certificates from the `spire-agent` and conveniently provides them to | ||
Keycloak in `pkcs12` format. | ||
|
||
> [!IMPORTANT] | ||
> Keycloak does not rotate the certificates like SPIRE does. If you want to run the `keycloak-config-cli` | ||
> job again, you need to make sure Keycloak is also restarted/provided with non-expired certificates. | ||
### Common name as username | ||
|
||
This example is configured to read the username from the common name (`CN`) from the client certificate. Keycloak has | ||
some options there, this looked like the easiest one. SPIRE joins the values from `dnsNameTemplates` in the | ||
common name section of the certificate, so make sure you can somehow extract the username from it. |
84 changes: 84 additions & 0 deletions
84
examples/keycloak-config-cli-using-spire/keycloak-config-cli.yaml
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,84 @@ | ||
--- | ||
apiVersion: batch/v1 | ||
kind: Job | ||
metadata: | ||
name: keycloak-config-cli | ||
labels: | ||
app: keycloak-config-cli | ||
spec: | ||
backoffLimit: 1 | ||
template: | ||
metadata: | ||
labels: | ||
app: keycloak-config-cli | ||
app.kubernetes.io/instance: keycloak-config-cli # This needs to match the podSelector for the SpiffeID | ||
spec: | ||
initContainers: | ||
- name: ghostunnel | ||
image: ghostunnel/ghostunnel:v1.7.3 | ||
imagePullPolicy: IfNotPresent | ||
restartPolicy: Always | ||
args: | ||
- client | ||
- --use-workload-api-addr | ||
- unix:///run/spire/agent-sockets/spire-agent.sock # The filename depends on what the spire-agent uses | ||
- --listen | ||
- localhost:8080 # Listen on local http | ||
- --target | ||
- keycloak:8443 # Tunnel via https | ||
- --status | ||
- http://0.0.0.0:6060 | ||
ports: | ||
- containerPort: 8080 | ||
name: listen | ||
protocol: TCP | ||
- containerPort: 6060 | ||
name: readiness | ||
protocol: TCP | ||
readinessProbe: | ||
httpGet: | ||
path: /_status | ||
port: readiness | ||
volumeMounts: | ||
- name: spire-sockets | ||
mountPath: /run/spire/agent-sockets | ||
readOnly: true | ||
containers: | ||
- name: keycloak-config-cli | ||
image: adorsys/keycloak-config-cli:latest | ||
imagePullPolicy: IfNotPresent | ||
env: | ||
- name: KEYCLOAK_URL | ||
value: "http://127.0.0.1:8080" | ||
- name: KEYCLOAK_USER | ||
value: "keycloak-config-cli" | ||
- name: KEYCLOAK_PASSWORD | ||
value: "doesn't matter, since we are authenticated via the client certificate" | ||
- name: KEYCLOAK_CLIENTID | ||
value: "keycloak-config-cli" # This is the client created on bootstrapping Keycloak via the keycloak-config-cli sidecar | ||
volumeMounts: | ||
- name: realm | ||
mountPath: /config | ||
restartPolicy: OnFailure | ||
volumes: | ||
- name: realm | ||
configMap: | ||
name: keycloak-config-cli | ||
- name: spire-sockets | ||
hostPath: | ||
path: /run/spire/agent-sockets # This needs to match the path mounted by the spire-agent | ||
type: DirectoryOrCreate | ||
--- | ||
apiVersion: v1 | ||
kind: ConfigMap | ||
metadata: | ||
name: keycloak-config-cli | ||
labels: | ||
app: keycloak-config-cli | ||
data: | ||
keycloak-config-cli.json: | | ||
{ | ||
"id": "keycloak-config-cli", | ||
"realm": "keycloak-config-cli", | ||
"enabled": true | ||
} |
134 changes: 134 additions & 0 deletions
134
examples/keycloak-config-cli-using-spire/keycloak-values.yaml
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,134 @@ | ||
extraDeploy: | ||
- apiVersion: v1 | ||
kind: ConfigMap | ||
metadata: | ||
name: java-spiffe-helper-properties | ||
data: | ||
java-spiffe-helper.properties: | | ||
keyStorePath=/certs/keystore.p12 | ||
keyStorePass=password | ||
keyPass=password | ||
trustStorePath=/certs/truststore.p12 | ||
trustStorePass=password | ||
keyStoreType=pkcs12 | ||
keyAlias=spiffe | ||
spiffeSocketPath=unix:/run/spire/agent-sockets/spire-agent.sock | ||
service: | ||
extraPorts: | ||
- name: https | ||
port: 8443 | ||
targetPort: 8443 | ||
extraEnvVars: | ||
- name: KC_HTTPS_CLIENT_AUTH | ||
value: "request" | ||
- name: KC_HTTPS_KEY_STORE_FILE | ||
value: "/certs/keystore.p12" | ||
- name: KC_HTTPS_KEY_STORE_PASSWORD | ||
value: "password" | ||
- name: KC_HTTPS_KEY_STORE_TYPE | ||
value: "pkcs12" | ||
- name: KC_HTTPS_TRUST_STORE_FILE | ||
value: "/certs/truststore.p12" | ||
- name: KC_HTTPS_TRUST_STORE_PASSWORD | ||
value: "password" | ||
- name: KC_HTTPS_TRUST_STORE_TYPE | ||
value: "pkcs12" | ||
initContainers: | ||
- name: java-spiffe-helper | ||
image: ghcr.io/spiffe/java-spiffe-helper:0.8.5 | ||
imagePullPolicy: IfNotPresent | ||
restartPolicy: Always | ||
readinessProbe: | ||
exec: | ||
command: | ||
- ls | ||
- /certs/truststore.p12 | ||
volumeMounts: | ||
- name: java-spiffe-helper-properties | ||
mountPath: /app/java-spiffe-helper.properties | ||
subPath: java-spiffe-helper.properties | ||
- name: spire-sockets | ||
mountPath: /run/spire/agent-sockets | ||
readOnly: true | ||
- name: certs | ||
mountPath: /certs | ||
extraVolumeMounts: | ||
- name: certs | ||
mountPath: /certs | ||
extraVolumes: | ||
- name: java-spiffe-helper-properties | ||
configMap: | ||
name: java-spiffe-helper-properties | ||
- name: spire-sockets | ||
hostPath: | ||
path: /run/spire/agent-sockets | ||
type: DirectoryOrCreate | ||
- name: certs | ||
emptyDir: {} | ||
auth: | ||
adminPassword: "password" | ||
keycloakConfigCli: | ||
enabled: true | ||
configuration: | ||
master.json: | | ||
{ | ||
"id": "master", | ||
"realm": "master", | ||
"enabled": true, | ||
"users": [ | ||
{ | ||
"username": "keycloak-config-cli", | ||
"enabled": true, | ||
"realmRoles": [ | ||
"admin" | ||
], | ||
"credentials": [ | ||
{ | ||
"type": "password", | ||
"value": "it-really-doesn't-matter-what-you-put-here" | ||
} | ||
] | ||
} | ||
], | ||
"authenticationFlows": [ | ||
{ | ||
"alias": "direct grant x509", | ||
"providerId": "basic-flow", | ||
"topLevel": true, | ||
"builtIn": false, | ||
"authenticationExecutions": [ | ||
{ | ||
"authenticatorConfig": "username", | ||
"authenticator": "direct-grant-auth-x509-username", | ||
"requirement": "REQUIRED", | ||
"priority": 0 | ||
} | ||
] | ||
} | ||
], | ||
"authenticatorConfig": [ | ||
{ | ||
"alias": "username", | ||
"config": { | ||
"x509-cert-auth.regular-expression": "CN=(keycloak-config-cli)", | ||
"x509-cert-auth.mapper-selection": "Username or Email", | ||
"x509-cert-auth.mapping-source-selection": "Match SubjectDN using regular expression" | ||
} | ||
} | ||
], | ||
"clients": [ | ||
{ | ||
"clientId": "keycloak-config-cli", | ||
"name": "keycloak-config-cli", | ||
"enabled": true, | ||
"standardFlowEnabled": false, | ||
"directAccessGrantsEnabled": true, | ||
"publicClient": true, | ||
"authenticationFlowBindingOverrides": { | ||
"direct_grant": "direct grant x509" | ||
}, | ||
"fullScopeAllowed": true, | ||
"nodeReRegistrationTimeout": 0 | ||
} | ||
] | ||
} |
26 changes: 26 additions & 0 deletions
26
examples/keycloak-config-cli-using-spire/spire-values.yaml
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,26 @@ | ||
spire-server: | ||
controllerManager: | ||
identities: | ||
clusterSPIFFEIDs: | ||
default: | ||
enabled: false | ||
keycloak: | ||
spiffeIDTemplate: spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }} | ||
namespaceSelector: | ||
matchLabels: | ||
kubernetes.io/metadata.name: default | ||
podSelector: | ||
matchLabels: | ||
app.kubernetes.io/instance: keycloak | ||
dnsNameTemplates: | ||
- keycloak | ||
keycloak-config-cli: | ||
spiffeIDTemplate: spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }} | ||
namespaceSelector: | ||
matchLabels: | ||
kubernetes.io/metadata.name: default | ||
podSelector: | ||
matchLabels: | ||
app.kubernetes.io/instance: keycloak-config-cli | ||
dnsNameTemplates: | ||
- keycloak-config-cli # This is the common name used for the certificate. In this case, the username |