mirror of
https://github.com/keycloak/keycloak.git
synced 2026-07-25 18:26:11 +00:00
Closes #49536 Signed-off-by: Pedro Ruivo <1492066+pruivo@users.noreply.github.com> Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.com> Co-authored-by: Pedro Ruivo <1492066+pruivo@users.noreply.github.com> Co-authored-by: Alexander Schwartz <alexander.schwartz@ibm.com>
513 lines
18 KiB
Plaintext
513 lines
18 KiB
Plaintext
<#import "/templates/guide.adoc" as tmpl>
|
|
<#import "/templates/kc.adoc" as kc>
|
|
<#import "/templates/options.adoc" as opts>
|
|
<#import "/templates/links.adoc" as links>
|
|
<#import "/templates/profile.adoc" as profile>
|
|
|
|
<@tmpl.guide
|
|
title="Basic {project_name} deployment"
|
|
summary="Install {project_name} using the Operator.">
|
|
|
|
== Performing a basic {project_name} deployment
|
|
This {section} describes how to perform a basic {project_name} Deployment on
|
|
<@profile.ifCommunity>
|
|
Kubernetes or
|
|
</@profile.ifCommunity>
|
|
OpenShift using the Operator.
|
|
|
|
=== Preparing for deployment
|
|
|
|
Once the {project_name} Operator is installed and running in the cluster namespace, you can set up the other deployment prerequisites.
|
|
|
|
* Database
|
|
* Hostname
|
|
* TLS Certificate and associated keys
|
|
|
|
==== Database
|
|
|
|
A database should be available and accessible from the cluster namespace where {project_name} is installed.
|
|
For a list of supported databases, see <@links.server id="db"/>.
|
|
The {project_name} Operator does not manage the database and you need to provision it yourself. Consider verifying your cloud provider offering or using a database operator.
|
|
|
|
For development purposes, you can use an ephemeral PostgreSQL pod installation. To provision it, follow the approach below:
|
|
|
|
Create YAML file `example-postgres.yaml`:
|
|
[source,yaml]
|
|
----
|
|
apiVersion: apps/v1
|
|
kind: StatefulSet
|
|
metadata:
|
|
name: postgresql-db
|
|
spec:
|
|
serviceName: postgresql-db-service
|
|
selector:
|
|
matchLabels:
|
|
app: postgresql-db
|
|
replicas: 1
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: postgresql-db
|
|
spec:
|
|
containers:
|
|
- name: postgresql-db
|
|
image: postgres:15
|
|
volumeMounts:
|
|
- mountPath: /data
|
|
name: cache-volume
|
|
env:
|
|
- name: POSTGRES_USER
|
|
value: testuser
|
|
- name: POSTGRES_PASSWORD
|
|
value: testpassword
|
|
- name: PGDATA
|
|
value: /data/pgdata
|
|
- name: POSTGRES_DB
|
|
value: keycloak
|
|
volumes:
|
|
- name: cache-volume
|
|
emptyDir: {}
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: postgres-db
|
|
spec:
|
|
selector:
|
|
app: postgresql-db
|
|
type: LoadBalancer
|
|
ports:
|
|
- port: 5432
|
|
targetPort: 5432
|
|
----
|
|
|
|
Apply the changes:
|
|
|
|
[source,bash]
|
|
----
|
|
kubectl apply -f example-postgres.yaml
|
|
----
|
|
|
|
==== Hostname
|
|
|
|
For a production ready installation, you need a hostname that can be used to contact {project_name}.
|
|
See <@links.server id="hostname"/> for the available configurations.
|
|
|
|
For development purposes, this {section} will use `test.keycloak.org`.
|
|
|
|
When running on OpenShift, with ingress enabled, and with the spec.ingress.classname set to openshift-default, you may leave the spec.hostname.hostname unpopulated in the Keycloak CR.
|
|
The operator will assign a default hostname to the stored version of the CR similar to what would be created by an OpenShift Route without an explicit host - that is ingress-namespace.appsDomain
|
|
If the appsDomain changes, or should you need a different hostname for any reason, then update the Keycloak CR.
|
|
|
|
NOTE: If you set the `hostname-admin`, or the deprecated `hostname-admin-url`, even if you enable ingress, no ingress will be created specifically for admin access.
|
|
Admin access via a separate hostname is generally expected to have access restrictions, which are not currently expressible via the Keycloak CR.
|
|
Also the default ingress does not prevent accessing admin endpoints, so you may not want to enable ingress handling via the Keycloak CR at all when you have a separate hostname for admin endpoints.
|
|
|
|
==== TLS Certificate and key
|
|
|
|
See your Certification Authority to obtain the certificate and the key.
|
|
|
|
For development purposes, you can enter this command to obtain a self-signed certificate:
|
|
|
|
[source,bash]
|
|
----
|
|
openssl req -subj '/CN=test.keycloak.org/O=Test Keycloak./C=US' -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem
|
|
----
|
|
|
|
You should install it in the cluster namespace as a Secret by entering this command:
|
|
|
|
[source,bash]
|
|
----
|
|
kubectl create secret tls example-tls-secret --cert certificate.pem --key key.pem
|
|
----
|
|
|
|
=== Deploying {project_name}
|
|
|
|
To deploy {project_name}, you create a Custom Resource (CR) based on the Keycloak Custom Resource Definition (CRD).
|
|
|
|
[NOTE]
|
|
====
|
|
If you are on OpenShift, you cannot use wildcard certificates on passthrough Routes with HTTP/2 enabled. The following Keycloak CR on OpenShift if you are using a wildcard certificate with the default IngressClass, creates such a Route. In this situation, modify the YAML based on "Accessing the {project_name} deployment" later in this topic to use TLS Termination or an OpenShift re-encrypt Route.
|
|
====
|
|
|
|
Consider storing the Database credentials in a separate Secret. Enter the following commands:
|
|
[source,bash]
|
|
----
|
|
kubectl create secret generic keycloak-db-secret \
|
|
--from-literal=username=[your_database_username] \
|
|
--from-literal=password=[your_database_password]
|
|
----
|
|
|
|
You can customize several fields using the Keycloak CRD. For a basic deployment, you can stick to the following approach:
|
|
|
|
Create YAML file `example-kc.yaml`:
|
|
|
|
:check-lb-forwarded-header: pass:quotes[Check your load balancer or reverse proxy configuration to determine whether it utilizes the `Forwarded` (RFC 7239) or `X-Forwarded-*` (e.g., `X-Forwarded-For`) mechanism for header propagation.]
|
|
|
|
[source,yaml]
|
|
----
|
|
apiVersion: k8s.keycloak.org/v2beta1
|
|
kind: Keycloak
|
|
metadata:
|
|
name: example-kc
|
|
spec:
|
|
instances: 1
|
|
db:
|
|
vendor: postgres
|
|
host: postgres-db
|
|
usernameSecret:
|
|
name: keycloak-db-secret
|
|
key: username
|
|
passwordSecret:
|
|
name: keycloak-db-secret
|
|
key: password
|
|
http:
|
|
tlsSecret: example-tls-secret
|
|
hostname:
|
|
hostname: test.keycloak.org
|
|
proxy:
|
|
headers: xforwarded # <1>
|
|
----
|
|
<1> {check-lb-forwarded-header}
|
|
|
|
Apply the changes:
|
|
|
|
[source,bash]
|
|
----
|
|
kubectl apply -f example-kc.yaml
|
|
----
|
|
|
|
To check that the {project_name} instance has been provisioned in the cluster, check the status of the created CR by entering the following command:
|
|
|
|
[source,bash]
|
|
----
|
|
kubectl get keycloaks/example-kc -o go-template='{{range .status.conditions}}CONDITION: {{.type}}{{"\n"}} STATUS: {{.status}}{{"\n"}} MESSAGE: {{.message}}{{"\n"}}{{end}}'
|
|
----
|
|
|
|
When the deployment is ready, look for output similar to the following:
|
|
|
|
[source,bash]
|
|
----
|
|
CONDITION: Ready
|
|
STATUS: true
|
|
MESSAGE:
|
|
CONDITION: HasErrors
|
|
STATUS: false
|
|
MESSAGE:
|
|
CONDITION: RollingUpdate
|
|
STATUS: false
|
|
MESSAGE:
|
|
----
|
|
|
|
=== Accessing the {project_name} deployment
|
|
|
|
The {project_name} deployment can be exposed through a basic Ingress accessible through the provided hostname.
|
|
|
|
On installations with multiple default IngressClass instances
|
|
or when running on OpenShift 4.12+ you should provide an ingressClassName by setting `ingress` spec with `className` property to the desired class name:
|
|
|
|
Edit YAML file `example-kc.yaml`:
|
|
|
|
[source,yaml]
|
|
----
|
|
apiVersion: k8s.keycloak.org/v2beta1
|
|
kind: Keycloak
|
|
metadata:
|
|
name: example-kc
|
|
spec:
|
|
...
|
|
ingress:
|
|
className: openshift-default
|
|
----
|
|
|
|
NOTE: The operator annotates the Ingress to match expectations for TLS passthrough or TLS termination on OpenShift with the default IngressClass.
|
|
See below for more on TLS termination.
|
|
|
|
==== TLS Termination with default Ingress
|
|
|
|
When using the operator-provided ingress (`spec.ingress.enabled: true`), there are many Keycloak CR options (like service and ingress labels and annotations) that allow you to customize your ingress behavior.
|
|
|
|
With passthrough, the client IP address is not visible to {project_name} because the default ingress controller does not support the PROXY protocol between the ingress and {project_name}.
|
|
|
|
Passthrough is the best option when you want to use mTLS (mutual TLS), as the TLS connection is established directly between the client and {project_name}, preserving the client certificate for authentication.
|
|
Edge and reencrypt do not offer mTLS between the client and the ingress via configuration options in the CRs.
|
|
|
|
With edge, the ingress controller terminates TLS and forwards the connection to {project_name} unencrypted.
|
|
If internal encryption is required, use reencrypt instead.
|
|
|
|
With edge or reencrypt, you can only trust the client IP address and other forwarded headers if you restrict access to the {project_name} HTTP port via network policies (see <@links.operator id="advanced-configuration" anchor="_network_policies" />). For example, allow connections only from the namespace where the ingress controller runs.
|
|
You must also prevent users in the {project_name} namespace from creating additional ingress or route instances, as an unauthorized ingress could bypass the trusted ingress controller and spoof client IP addresses or other forwarded headers.
|
|
Without these protections, any Pod that can connect to {project_name} directly can spoof forwarded headers.
|
|
Parsing proxy headers therefore conflicts with direct backchannel access to {project_name}, as backchannel clients bypass the ingress and could spoof those headers.
|
|
|
|
This section shows how to achieve the following TLS Termination when using OpenShift:
|
|
|
|
- <<passthrough, Passthrough>>
|
|
- <<edge_cluster_domain, Edge using cluster domain and certificate>>
|
|
- <<edge_custom_domain, Edge using custom domain and certificate>>
|
|
- <<reencrypt_cluster_domain, Reencrypt using cluster domain and certificate>>
|
|
- <<reencrypt_custom_domain, Reencrypt using custom domain and certificate>>
|
|
|
|
For other platforms or IngressControllers, you can use the provided examples as a starting point.
|
|
|
|
[[passthrough]]
|
|
===== Passthrough
|
|
|
|
It is enabled when you associate a `tlsSecret` with the `http` configuration and leave Ingress enabled without specifying a `tlsSecret` on it.
|
|
|
|
[source,yaml]
|
|
----
|
|
apiVersion: k8s.keycloak.org/v2alpha1
|
|
kind: Keycloak
|
|
metadata:
|
|
name: example-kc
|
|
spec:
|
|
http:
|
|
tlsSecret: example-tls-secret
|
|
ingress:
|
|
enabled: true
|
|
hostname:
|
|
hostname: test.keycloak.org
|
|
----
|
|
|
|
NOTE: On OpenShift you are not allowed to use wildcard certificates on passthrough Routes with HTTP/2 enabled. For wildcard certificates use edge or reencrypt termination.
|
|
|
|
===== Edge
|
|
|
|
There are two ways to achieve TLS Edge. In both it is needed to enable HTTP access.
|
|
|
|
[[edge_cluster_domain]]
|
|
*Using cluster domain and certificate*
|
|
|
|
To use cluster domain do not provide a tls secret for the `ingress` and ensure to use cluster domain as `hostname`. Since no certificate is provided to the `ingress` the cluster certificate is used.
|
|
|
|
[source,yaml]
|
|
----
|
|
apiVersion: k8s.keycloak.org/v2alpha1
|
|
kind: Keycloak
|
|
metadata:
|
|
name: example-kc
|
|
spec:
|
|
http:
|
|
httpEnabled: true
|
|
ingress:
|
|
enabled: true
|
|
hostname:
|
|
hostname: keycloak.<cluster-domain> # <1>
|
|
proxy:
|
|
headers: xforwarded # <2>
|
|
networkPolicy:
|
|
enabled: true
|
|
http: ... # <3>
|
|
----
|
|
<1> Must use the cluster domain.
|
|
<2> {check-lb-forwarded-header}
|
|
<3> Restrict access to the ingress controller to avoid spoofing of forwarding headers.
|
|
|
|
[[edge_custom_domain]]
|
|
*Using custom domain and certificate*
|
|
|
|
To use custom domain provide a tls secrets for the `ingress` and ensure that `hostname` matches the `ingress` certificate.
|
|
|
|
[source,yaml]
|
|
----
|
|
apiVersion: k8s.keycloak.org/v2beta1
|
|
kind: Keycloak
|
|
metadata:
|
|
name: example-kc
|
|
spec:
|
|
http:
|
|
httpEnabled: true
|
|
ingress:
|
|
tlsSecret: example-tls-secret
|
|
hostname:
|
|
hostname: test.keycloak.org
|
|
proxy:
|
|
headers: xforwarded # <1>
|
|
networkPolicy:
|
|
enabled: true
|
|
http: ... # <2>
|
|
----
|
|
<1> {check-lb-forwarded-header}
|
|
<2> Restrict access to the ingress controller to avoid spoofing of forwarding headers.
|
|
|
|
===== Reencrypt
|
|
|
|
There are two ways to achieve TLS Reencryption. In both it is needed to annotate the `ingress` to specify the desired termination type since operator still lacks logic to set itself the reencrypt termination.
|
|
|
|
[[reencrypt_cluster_domain]]
|
|
*Using cluster domain and certificate*
|
|
|
|
To use cluster domain do not provide a tls secret for the `ingress` and ensure to use cluster domain as `hostname`. Since no certificate is provided to the `ingress` the cluster certificate is used.
|
|
|
|
[source,yaml]
|
|
----
|
|
apiVersion: k8s.keycloak.org/v2alpha1
|
|
kind: Keycloak
|
|
metadata:
|
|
name: example-kc
|
|
spec:
|
|
http:
|
|
annotations:
|
|
service.alpha.openshift.io/serving-cert-secret-name: svc-tls-secret <1>
|
|
tlsSecret: svc-tls-secret <2>
|
|
ingress:
|
|
annotations:
|
|
route.openshift.io/termination: reencrypt <3>
|
|
enabled: true
|
|
hostname:
|
|
hostname: keycloak.<cluster-domain> <4>
|
|
proxy:
|
|
headers: xforwarded # <5>
|
|
networkPolicy:
|
|
enabled: true
|
|
https: ... # <6>
|
|
----
|
|
<1> This will add the annotation to the service generated by the operator, so cluster will create a tls secret named svc-tls-secret
|
|
<2> Name of the tls secret created by the cluster, as defined in <1>
|
|
<3> Required because the operator only has built-in OpenShift logic for passthrough and edge.
|
|
<4> Must use the cluster domain
|
|
<5> {check-lb-forwarded-header}
|
|
<6> Restrict access to the ingress controller to avoid spoofing of forwarding headers.
|
|
|
|
[[reencrypt_custom_domain]]
|
|
*Using custom domain and certificate*
|
|
|
|
To use custom domain provide distinct tls secrets for the `ingress` and `http` and ensure that `hostname` matches the `ingress` certificate.
|
|
|
|
[source,yaml]
|
|
----
|
|
apiVersion: k8s.keycloak.org/v2alpha1
|
|
kind: Keycloak
|
|
metadata:
|
|
name: example-kc
|
|
spec:
|
|
http:
|
|
annotations:
|
|
service.alpha.openshift.io/serving-cert-secret-name: svc-tls-secret <1>
|
|
tlsSecret: svc-tls-secret <2>
|
|
ingress:
|
|
annotations:
|
|
route.openshift.io/termination: reencrypt # <3>
|
|
tlsSecret: ingress-tls-secret # <4>
|
|
enabled: true
|
|
hostname:
|
|
hostname: test.keycloak.org # <5>
|
|
proxy:
|
|
headers: xforwarded # <6>
|
|
networkPolicy:
|
|
enabled: true
|
|
https: ... # <7>
|
|
----
|
|
<1> This will add the annotation to the service generated by the operator, so cluster will create a tls secret named `svc-tls-secret`.
|
|
<2> Name of the tls secret created by the cluster, as defined in callout 1.
|
|
<3> Required because the operator only has built-in OpenShift logic for passthrough and edge.
|
|
<4> Certificate to be used at the ingress/route.
|
|
<5> Domain must match the provided certificate.
|
|
<6> {check-lb-forwarded-header}
|
|
<7> Restrict access to the ingress controller to avoid spoofing of forwarding headers.
|
|
|
|
==== Custom Access
|
|
|
|
If the default ingress does not fit your use case, disable it by setting `ingress` spec with `enabled` property to `false` value:
|
|
|
|
Edit YAML file `example-kc.yaml`:
|
|
|
|
[source,yaml]
|
|
----
|
|
apiVersion: k8s.keycloak.org/v2beta1
|
|
kind: Keycloak
|
|
metadata:
|
|
name: example-kc
|
|
spec:
|
|
...
|
|
ingress:
|
|
enabled: false
|
|
networkPolicy:
|
|
enabled: true
|
|
https: ... # <1>
|
|
----
|
|
<1> Restrict access to the ingress controller to avoid spoofing of forwarding headers.
|
|
|
|
Apply the changes:
|
|
|
|
[source,bash]
|
|
----
|
|
kubectl apply -f example-kc.yaml
|
|
----
|
|
You can then provide an alternative ingress resource pointing to the service named `<keycloak-cr-name>-service` by default.
|
|
|
|
For debugging and development purposes, consider directly connecting to the {project_name} service using a port forward. For example, enter this command:
|
|
|
|
[source,bash]
|
|
----
|
|
kubectl port-forward service/example-kc-service 8443:8443
|
|
----
|
|
|
|
==== Configuring the reverse proxy settings matching your Ingress Controller
|
|
|
|
The Operator supports configuring which of the reverse proxy headers should be accepted by server, which includes
|
|
`Forwarded` and `X-Forwarded-*` headers.
|
|
|
|
If you Ingress implementation sets and overwrites either `Forwarded` or `X-Forwarded-*` headers, you can reflect that
|
|
in the Keycloak CR as follows:
|
|
[source,yaml]
|
|
----
|
|
apiVersion: k8s.keycloak.org/v2beta1
|
|
kind: Keycloak
|
|
metadata:
|
|
name: example-kc
|
|
spec:
|
|
...
|
|
proxy:
|
|
headers: forwarded|xforwarded
|
|
networkPolicy:
|
|
enabled: true
|
|
https: ... # <1>
|
|
----
|
|
<1> restrict incoming connections to the ingress controller to avoid spoofing of forwarding headers.
|
|
|
|
NOTE: If the `proxy.headers` field is not specified, the Operator falls back to legacy behavior by implicitly setting
|
|
`proxy=passthrough` by default. This results in deprecation warnings in the server log. This fallback will be removed
|
|
in a future release.
|
|
|
|
WARNING: When using the `proxy.headers` field, make sure your Ingress properly sets and overwrites the `Forwarded` or `X-Forwarded-*` headers respectively. To set these headers, consult the documentation for your Ingress Controller. Consider configuring it for
|
|
either reencrypt or edge TLS termination as passthrough TLS doesn't allow the Ingress to modify the requests headers.
|
|
Misconfiguration will leave {project_name} exposed to security vulnerabilities.
|
|
|
|
For more details refer to the <@links.server id="reverseproxy"/> guide.
|
|
|
|
=== Accessing the Admin Console
|
|
|
|
When deploying {project_name}, the operator generates an arbitrary initial admin `username` and `password` and stores those credentials as a basic-auth Secret object in the same namespace as the CR.
|
|
|
|
[WARNING]
|
|
====
|
|
Change the default admin credentials and enable MFA in {project_name} before going to production.
|
|
====
|
|
|
|
To fetch the initial admin credentials, you have to read and decode the Secret.
|
|
The Secret name is derived from the Keycloak CR name plus the fixed suffix `-initial-admin`.
|
|
To get the username and password for the `example-kc` CR, enter the following commands:
|
|
|
|
[source,bash]
|
|
----
|
|
kubectl get secret example-kc-initial-admin -o jsonpath='{.data.username}' | base64 --decode
|
|
kubectl get secret example-kc-initial-admin -o jsonpath='{.data.password}' | base64 --decode
|
|
----
|
|
|
|
You can use those credentials to access the Admin Console or the Admin REST API.
|
|
|
|
=== Security Considerations
|
|
|
|
[WARNING]
|
|
====
|
|
Anyone with the ability to create or edit Keycloak or KeycloakRealmImport CRs should be a namespace level admin.
|
|
====
|
|
|
|
Setting the Keycloak CR image requires a high degree of trust as whatever image is running will at least have access to any Secrets used for environment variables.
|
|
|
|
Similarly the unsupported podTemplate gives the ability to deploy alternative workloads which may be granted the same permissions as the operator itself - which includes the ability to access Secrets in the namespace.
|
|
|
|
</@tmpl.guide>
|