github.com/verrazzano/verrazzano@v1.7.0/platform-operator/thirdparty/charts/keycloak/README.md (about) 1 # Keycloak-X 2 3 [Keycloak-X](http://www.keycloak.org/) is an open source identity and access management for modern applications and services. 4 5 Note that this chart is the logical successor of the Wildfly based [codecentric/keycloak](https://github.com/codecentric/helm-charts/tree/master/charts/keycloak) chart. 6 7 ## TL;DR; 8 9 ```console 10 $ cat << EOF > values.yaml 11 command: 12 - "/opt/keycloak/bin/kc.sh" 13 - "start" 14 - "--http-enabled=true" 15 - "--http-port=8080" 16 - "--hostname-strict=false" 17 - "--hostname-strict-https=false" 18 extraEnv: | 19 - name: KEYCLOAK_ADMIN 20 value: admin 21 - name: KEYCLOAK_ADMIN_PASSWORD 22 value: admin 23 - name: JAVA_OPTS_APPEND 24 value: >- 25 -Djgroups.dns.query={{ include "keycloak.fullname" . }}-headless 26 EOF 27 28 $ helm install keycloak codecentric/keycloakx --values ./values.yaml 29 ``` 30 Note that the default configuration is not suitable for production since it uses a h2 file database by default. 31 It is strongly recommended to use a dedicated database with Keycloak. 32 33 For more examples see the [examples](./examples) folder. 34 35 ## Introduction 36 37 This chart bootstraps a [Keycloak](http://www.keycloak.org/) StatefulSet on a [Kubernetes](https://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. 38 It provisions a fully featured Keycloak installation. 39 For more information on Keycloak and its capabilities, see its [documentation](http://www.keycloak.org/documentation.html). 40 41 ## Installing the Chart 42 43 To install the chart with the release name `keycloakx`: 44 45 ```console 46 $ helm install keycloak codecentric/keycloakx 47 ``` 48 49 ## Uninstalling the Chart 50 51 To uninstall the `keycloakx` deployment: 52 53 ```console 54 $ helm uninstall keycloakx 55 ``` 56 57 ## Configuration 58 59 The following table lists the configurable parameters of the Keycloak-X chart and their default values. 60 61 | Parameter | Description | Default | 62 |----------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------| 63 | `fullnameOverride` | Optionally override the fully qualified name | `""` | 64 | `nameOverride` | Optionally override the name | `""` | 65 | `replicas` | The number of replicas to create | `1` | 66 | `image.repository` | The Keycloak image repository | `quay.io/keycloak/keycloak` | 67 | `image.tag` | Overrides the Keycloak image tag whose default is the chart version | `""` | 68 | `image.pullPolicy` | The Keycloak image pull policy | `IfNotPresent` | 69 | `imagePullSecrets` | Image pull secrets for the Pod | `[]` | 70 | `hostAliases` | Mapping between IPs and hostnames that will be injected as entries in the Pod's hosts files | `[]` | 71 | `enableServiceLinks` | Indicates whether information about services should be injected into Pod's environment variables, matching the syntax of Docker links | `true` | 72 | `updateStrategy` | StatefulSet update strategy. One of `RollingUpdate` or `OnDelete` | `RollingUpdate` | 73 | `podManagementPolicy` | Pod management policy. One of `Parallel` or `OrderedReady` | `Parallel` | 74 | `restartPolicy` | Pod restart policy. One of `Always`, `OnFailure`, or `Never` | `Always` | 75 | `serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` | 76 | `serviceAccount.allowReadPods` | Specifies whether the ServiceAccount can get or list pods | `false` | 77 | `serviceAccount.name` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template | `""` | 78 | `serviceAccount.annotations` | Additional annotations for the ServiceAccount | `{}` | 79 | `serviceAccount.labels` | Additional labels for the ServiceAccount | `{}` | 80 | `serviceAccount.imagePullSecrets` | Image pull secrets that are attached to the ServiceAccount | `[]` | 81 | `serviceAccount.automountServiceAccountToken` | Automount API credentials for the Service Account | `true` | 82 | `rbac.create` | Specifies whether RBAC resources are to be created | `false` 83 | `rbac.rules` | Custom RBAC rules, e. g. for KUBE_PING | `[]` 84 | `podSecurityContext` | SecurityContext for the entire Pod. Every container running in the Pod will inherit this SecurityContext. This might be relevant when other components of the environment inject additional containers into running Pods (service meshes are the most prominent example for this) | `{"fsGroup":1000}` | 85 | `securityContext` | SecurityContext for the Keycloak container | `{"runAsNonRoot":true,"runAsUser":1000}` | 86 | `extraInitContainers` | Additional init containers, e. g. for providing custom themes | `[]` | 87 | `skipInitContainers` | Skip all init containers (to avoid issues with service meshes which require sidecar proxies for connectivity) | `false` 88 | `extraContainers` | Additional sidecar containers, e. g. for a database proxy, such as Google's cloudsql-proxy | `[]` | 89 | `lifecycleHooks` | Lifecycle hooks for the Keycloak container | `{}` | 90 | `terminationGracePeriodSeconds` | Termination grace period in seconds for Keycloak shutdown. Clusters with a large cache might need to extend this to give Infinispan more time to rebalance | `60` | 91 | `clusterDomain` | The internal Kubernetes cluster domain | `cluster.local` | 92 | `command` | Overrides the default entrypoint of the Keycloak container | `[]` | 93 | `args` | Overrides the default args for the Keycloak container | `[]` | 94 | `extraEnv` | Additional environment variables for Keycloak | `""` | 95 | `extraEnvFrom` | Additional environment variables for Keycloak mapped from a Secret or ConfigMap | `""` | 96 | `priorityClassName` | Pod priority class name | `""` | 97 | `affinity` | Pod affinity | Hard node and soft zone anti-affinity | 98 | `topologySpreadConstraints` | Topology spread constraints | Constraints used to spread pods | 99 | `nodeSelector` | Node labels for Pod assignment | `{}` | 100 | `tolerations` | Node taints to tolerate | `[]` | 101 | `podLabels` | Additional Pod labels | `{}` | 102 | `podAnnotations` | Additional Pod annotations | `{}` | 103 | `livenessProbe` | Liveness probe configuration | `{"httpGet":{"path":"{{ tpl .Values.http.relativePath $ | trimSuffix "/" }}/","port":"http"},"initialDelaySeconds":0,"timeoutSeconds":5}` | 104 | `readinessProbe` | Readiness probe configuration | `{"httpGet":{"path":"{{ tpl .Values.http.relativePath $ | trimSuffix "/" }}/realms/master","port":"http"},"initialDelaySeconds":30,"timeoutSeconds":1}` | 105 | `startupProbe` | Startup probe configuration | `{"httpGet":{"path":"{{ tpl .Values.http.relativePath $ | trimSuffix "/" }}/","port":"http"},"initialDelaySeconds":30,"timeoutSeconds":5, "failureThreshold": 60, "periodSeconds": 5}` | 106 | `resources` | Pod resource requests and limits | `{}` | 107 | `extraVolumes` | Add additional volumes, e. g. for custom themes | `""` | 108 | `extraVolumeMounts` | Add additional volumes mounts, e. g. for custom themes | `""` | 109 | `extraPorts` | Add additional ports, e. g. for admin console or exposing JGroups ports | `[]` | 110 | `podDisruptionBudget` | Pod disruption budget | `{}` | 111 | `statefulsetAnnotations` | Annotations for the StatefulSet | `{}` | 112 | `statefulsetLabels` | Additional labels for the StatefulSet | `{}` | 113 | `secrets` | Configuration for secrets that should be created | `{}` | 114 | `service.annotations` | Annotations for HTTP service | `{}` | 115 | `service.labels` | Additional labels for headless and HTTP Services | `{}` | 116 | `service.type` | The Service type | `ClusterIP` | 117 | `service.loadBalancerIP` | Optional IP for the load balancer. Used for services of type LoadBalancer only | `""` | 118 | `loadBalancerSourceRanges` | Optional List of allowed source ranges (CIDRs). Used for service of type LoadBalancer only | `[]` | 119 | `service.externalTrafficPolicy` | Optional external traffic policy. Used for services of type LoadBalancer only | `"Cluster"` | 120 | `service.httpPort` | The http Service port | `80` | 121 | `service.httpNodePort` | The HTTP Service node port if type is NodePort | `""` | 122 | `service.httpsPort` | The HTTPS Service port | `8443` | 123 | `service.httpsNodePort` | The HTTPS Service node port if type is NodePort | `""` | 124 | `service.extraPorts` | Additional Service ports, e. g. for custom admin console | `[]` | 125 | `service.sessionAffinity` | sessionAffinity for Service, e. g. "ClientIP" | `""` | 126 | `service.sessionAffinityConfig` | sessionAffinityConfig for Service | `{}` | 127 | `serviceHeadless.annotations` | Annotations for headless service | `{}` | 128 | `ingress.enabled` | If `true`, an Ingress is created | `false` | 129 | `ingress.rules` | List of Ingress Ingress rule | see below | 130 | `ingress.rules[0].host` | Host for the Ingress rule | `{{ .Release.Name }}.keycloak.example.com` | 131 | `ingress.rules[0].paths` | Paths for the Ingress rule | see below | 132 | `ingress.rules[0].paths[0].path` | Path for the Ingress rule | `/` | 133 | `ingress.rules[0].paths[0].pathType` | Path Type for the Ingress rule | `Prefix` | 134 | `ingress.servicePort` | The Service port targeted by the Ingress | `http` | 135 | `ingress.annotations` | Ingress annotations | `{}` | 136 | `ingress.ingressClassName` | The name of the Ingress Class associated with the ingress | `""` | 137 | `ingress.labels` | Additional Ingress labels | `{}` | 138 | `ingress.tls` | TLS configuration | see below | 139 | `ingress.tls[0].hosts` | List of TLS hosts | `[keycloak.example.com]` | 140 | `ingress.tls[0].secretName` | Name of the TLS secret | `""` | 141 | `ingress.console.enabled` | If `true`, an Ingress for the console is created | `false` | 142 | `ingress.console.rules` | List of Ingress Ingress rule for the console | see below | 143 | `ingress.console.rules[0].host` | Host for the Ingress rule for the console | `{{ .Release.Name }}.keycloak.example.com` | 144 | `ingress.console.rules[0].paths` | Paths for the Ingress rule for the console | see below | 145 | `ingress.console.rules[0].paths[0].path` | Path for the Ingress rule for the console | `[{{ tpl .Values.http.relativePath $ | trimSuffix "/" }}/admin]` | 146 | `ingress.console.rules[0].paths[0].pathType` | Path Type for the Ingress rule for the console | `Prefix` | 147 | `ingress.console.annotations` | Ingress annotations for the console | `{}` | 148 | `ingress.console.ingressClassName` | The name of the Ingress Class associated with the console ingress | `""` | 149 | `ingress.console.tls` | TLS configuration | see below | 150 | `ingress.console.tls[0].hosts` | List of TLS hosts | `[keycloak.example.com]` | 151 | `ingress.console.tls[0].secretName` | Name of the TLS secret | `""` | 152 | `networkPolicy.enabled` | If true, the ingress network policy is deployed | `false` 153 | `networkPolicy.extraFrom` | Allows to define allowed external ingress traffic (see Kubernetes doc for network policy `from` format) | `[]` 154 | `networkPolicy.egress` | Allows to define allowed egress from Keycloak pods (see Kubernetes doc for network policy `egress` format) | `[]` 155 | `route.enabled` | If `true`, an OpenShift Route is created | `false` | 156 | `route.path` | Path for the Route | `/` | 157 | `route.annotations` | Route annotations | `{}` | 158 | `route.labels` | Additional Route labels | `{}` | 159 | `route.host` | Host name for the Route | `""` | 160 | `route.tls.enabled` | If `true`, TLS is enabled for the Route | `true` | 161 | `route.tls.insecureEdgeTerminationPolicy` | Insecure edge termination policy of the Route. Can be `None`, `Redirect`, or `Allow` | `Redirect` | 162 | `route.tls.termination` | TLS termination of the route. Can be `edge`, `passthrough`, or `reencrypt` | `edge` | 163 | `dbchecker.image.repository` | Docker image used to check database readiness at startup | `docker.io/busybox` | 164 | `dbchecker.image.tag` | Image tag for the dbchecker image | `1.32` | 165 | `dbchecker.image.pullPolicy` | Image pull policy for the dbchecker image | `IfNotPresent` | 166 | `dbchecker.securityContext` | SecurityContext for the dbchecker container | `{"allowPrivilegeEscalation":false,"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000}` | 167 | `dbchecker.resources` | Resource requests and limits for the dbchecker container | `{"limits":{"cpu":"10m","memory":"16Mi"},"requests":{"cpu":"10m","memory":"16Mi"}}` | 168 | `database.hostname` | Database Hostname | unset | 169 | `database.port` | Database Port | unset | 170 | `database.username` | Database User | unset | 171 | `database.password` | Database Password | unset | 172 | `database.existingSecret` | Existing Secret containing database password (expects key `password`) | unset | 173 | `database.database` | Database | unset | 174 | `cache.stack` | Cache / Cluster Discovery, use `custom` to disable automatic configruation. | `default` | 175 | `proxy.enabled` | If `true`, the `KC_PROXY` env variable will be set to the configured mode | `true` | 176 | `proxy.mode` | The configured proxy mode | `edge` | 177 | `http.relativePath` | The relative http path (context-path) | `/auth` | 178 | `metrics.enabled` | If `true` then the metrics endpoint is exposed | `true` | 179 | `health.enabled` | If `true` then the health endpoint is exposed. If the `readinessProbe` is is needed `metrics.enable` must be `true`. | `true` | 180 | `serviceMonitor.enabled` | If `true`, a ServiceMonitor resource for the prometheus-operator is created | `false` | 181 | `serviceMonitor.namespace` | Optionally sets a target namespace in which to deploy the ServiceMonitor resource | `""` | 182 | `serviceMonitor.namespaceSelector` | Optionally sets a namespace selector for the ServiceMonitor | `{}` | 183 | `serviceMonitor.annotations` | Annotations for the ServiceMonitor | `{}` | 184 | `serviceMonitor.labels` | Additional labels for the ServiceMonitor | `{}` | 185 | `serviceMonitor.interval` | Interval at which Prometheus scrapes metrics | `10s` | 186 | `serviceMonitor.scrapeTimeout` | Timeout for scraping | `10s` | 187 | `serviceMonitor.path` | The path at which metrics are served | `{{ tpl .Values.http.relativePath $ | trimSuffix "/" }}/metrics` | 188 | `serviceMonitor.port` | The Service port at which metrics are served | `http` | 189 | `extraServiceMonitor.enabled` | If `true`, an additional ServiceMonitor resource for the prometheus-operator is created. Could be used for additional metrics via [Keycloak Metrics SPI](https://github.com/aerogear/keycloak-metrics-spi) | `false` | 190 | `extraServiceMonitor.namespace` | Optionally sets a target namespace in which to deploy the additional ServiceMonitor resource | `""` | 191 | `extraServiceMonitor.namespaceSelector` | Optionally sets a namespace selector for the additional ServiceMonitor | `{}` | 192 | `extraServiceMonitor.annotations` | Annotations for the additional ServiceMonitor | `{}` | 193 | `extraServiceMonitor.labels` | Additional labels for the additional ServiceMonitor | `{}` | 194 | `extraServiceMonitor.interval` | Interval at which Prometheus scrapes metrics | `10s` | 195 | `extraServiceMonitor.scrapeTimeout` | Timeout for scraping | `10s` | 196 | `extraServiceMonitor.path` | The path at which metrics are served | `{{ tpl .Values.http.relativePath $ | trimSuffix "/" }}/metrics` | 197 | `extraServiceMonitor.port` | The Service port at which metrics are served | `http` | 198 | `prometheusRule.enabled` | If `true`, a PrometheusRule resource for the prometheus-operator is created | `false` | 199 | `prometheusRule.namespace` | Optionally sets a target namespace in which to deploy the PrometheusRule resource | `""` | 200 | `prometheusRule.annotations` | Annotations for the PrometheusRule | `{}` | 201 | `prometheusRule.labels` | Additional labels for the PrometheusRule | `{}` | 202 | `prometheusRule.rules` | List of rules for Prometheus | `[]` | 203 | `autoscaling.enabled` | Enable creation of a HorizontalPodAutoscaler resource | `false` | 204 | `autoscaling.labels` | Additional labels for the HorizontalPodAutoscaler resource | `{}` | 205 | `autoscaling.minReplicas` | The minimum number of Pods when autoscaling is enabled | `3` | 206 | `autoscaling.maxReplicas` | The maximum number of Pods when autoscaling is enabled | `10` | 207 | `autoscaling.metrics` | The metrics configuration for the HorizontalPodAutoscaler | `[{"resource":{"name":"cpu","target":{"averageUtilization":80,"type":"Utilization"}},"type":"Resource"}]` | 208 | `autoscaling.behavior` | The scaling policy configuration for the HorizontalPodAutoscaler | `{"scaleDown":{"policies":[{"periodSeconds":300,"type":"Pods","value":1}],"stabilizationWindowSeconds":300}` | 209 | `test.enabled` | If `true`, test resources are created | `false` | 210 | `test.image.repository` | The image for the test Pod | `docker.io/joyzoursky/python-chromedriver` | 211 | `test.image.tag` | The tag for the test Pod image | `3.9-selenium` | 212 | `test.image.pullPolicy` | The image pull policy for the test Pod image | `IfNotPresent` | 213 | `test.podSecurityContext` | SecurityContext for the entire test Pod | `{"fsGroup":1000}` | 214 | `test.securityContext` | SecurityContext for the test container | `{"runAsNonRoot":true,"runAsUser":1000}` | 215 | `test.deletionPolicy` | `helm.sh/hook-delete-policy` for the test Pod | `before-hook-creation` | 216 217 Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example: 218 219 ```console 220 $ helm install keycloak codecentric/keycloakx -n keycloak --set replicas=1 221 ``` 222 223 Alternatively, a YAML file that specifies the values for the parameters can be provided while 224 installing the chart. For example: 225 226 ```console 227 $ helm install keycloak codecentric/keycloakx -n keycloak --values values.yaml 228 ``` 229 230 The chart offers great flexibility. 231 It can be configured to work with the official Keycloak-X Docker image but any custom image can be used as well. 232 233 For the official Docker image, please check it's configuration at https://github.com/keycloak/keycloak/tree/main/quarkus/container. 234 235 ### Usage of the `tpl` Function 236 237 The `tpl` function allows us to pass string values from `values.yaml` through the templating engine. 238 It is used for the following values: 239 240 * `extraInitContainers` 241 * `extraContainers` 242 * `extraEnv` 243 * `extraEnvFrom` 244 * `affinity` 245 * `extraVolumeMounts` 246 * `extraVolumes` 247 * `livenessProbe` 248 * `readinessProbe` 249 * `startupProbe` 250 * `topologySpreadConstraints` 251 252 Additionally, custom labels and annotations can be set on various resources the values of which being passed through `tpl` as well. 253 254 It is important that these values be configured as strings. 255 Otherwise, installation will fail. 256 See example for Google Cloud Proxy or default affinity configuration in `values.yaml`. 257 258 ### JVM Settings 259 260 Keycloak sets the following system properties by default: 261 `-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true` 262 263 You can override these by setting the `JAVA_OPTS` environment variable. 264 Make sure you configure container support. 265 This allows you to only configure memory using Kubernetes resources and the JVM will automatically adapt. 266 267 ```yaml 268 extraEnv: | 269 - name: JAVA_OPTS 270 value: >- 271 -XX:+UseContainerSupport 272 -XX:MaxRAMPercentage=50.0 273 -Djava.net.preferIPv4Stack=true 274 -Djava.awt.headless=true 275 ``` 276 277 Alternatively one can append custom JVM options by setting the `JAVA_OPTS_APPEND` environment variable. 278 279 #### Using an External Database 280 281 The Keycloak Docker image supports various database types. 282 Configuration happens in a generic manner. 283 284 ##### Using a Secret Managed by the Chart 285 286 The following examples uses a PostgreSQL database with a secret that is managed by the Helm chart. 287 288 ```yaml 289 dbchecker: 290 enabled: true 291 292 database: 293 vendor: postgres 294 hostname: mypostgres 295 port: 5432 296 username: '{{ .Values.dbUser }}' 297 password: '{{ .Values.dbPassword }}' 298 database: mydb 299 ``` 300 301 `dbUser` and `dbPassword` are custom values you'd then specify on the commandline using `--set-string`. 302 303 ##### Using an Existing Secret 304 305 The following examples uses a PostgreSQL database with an existing secret. 306 307 ```yaml 308 dbchecker: 309 enabled: true 310 311 database: 312 vendor: postgres 313 hostname: mypostgres 314 port: 5432 315 database: mydb 316 username: db-user 317 existingSecret: byo-db-creds # Password is retrieved via .password 318 ``` 319 320 ### Creating a Keycloak Admin User 321 322 The Keycloak-X Docker image supports creating an initial admin user. 323 It must be configured via environment variables: 324 325 * `KEYCLOAK_ADMIN` 326 * `KEYCLOAK_ADMIN_PASSWORD` 327 328 This can be done like so in the `values.yaml`, where the `KEYCLOAK_ADMIN` is an insecure example with the value in plaintext. 329 The `KEYCLOAK_ADMIN_PASSWORD` is referenced from already existing secret but for testing it can be set with `value` too. 330 ```yaml 331 extraEnv: | 332 - name: KEYCLOAK_ADMIN 333 value: admin 334 - name: KEYCLOAK_ADMIN_PASSWORD 335 valueFrom: 336 secretKeyRef: 337 name: keycloak-admin-password 338 key: password 339 ``` 340 341 ### High Availability and Clustering 342 343 For high availability, Keycloak must be run with multiple replicas (`replicas > 1`). 344 The chart has a helper template (`keycloak.serviceDnsName`) that creates the DNS name based on the headless service. 345 346 #### DNS_PING Service Discovery 347 348 JGroups discovery via DNS_PING is enabled by default but needs an additional JVM setting, which can be configured as follows: 349 350 ```yaml 351 extraEnv: | 352 - name: JAVA_OPTS_APPEND 353 value: >- 354 -Djgroups.dns.query={{ include "keycloak.fullname" . }}-headless 355 ``` 356 357 #### Custom Service Discovery 358 359 If a custom JGroups discovery is needed, then you can configure: 360 361 ```yaml 362 cache: 363 stack: custom 364 ``` 365 366 You can then reference your custom infinispan configuration file, e.g. `cache-custom.xml` via the `KC_CACHE_CONFIG_FILE` environment variable. 367 Note that the `cache-custom.xml` must be available via `/opt/keycloak/conf/cache-custom.xml`. 368 369 ```yaml 370 extraEnv: | 371 - name: KC_CACHE 372 value: "ispn" 373 - name: KC_CACHE_CONFIG_FILE 374 value: cache-custom.xml 375 ``` 376 377 #### Autoscaling 378 379 Due to the caches in Keycloak only replicating to a few nodes (two in the example configuration above) and the limited controls around autoscaling built into Kubernetes, it has historically been problematic to autoscale Keycloak. 380 However, in Kubernetes 1.18 [additional controls were introduced](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-configurable-scaling-behavior) which make it possible to scale down in a more controlled manner. 381 382 The example autoscaling configuration in the values file scales from three up to a maximum of ten Pods using CPU utilization as the metric. Scaling up is done as quickly as required but scaling down is done at a maximum rate of one Pod per five minutes. 383 384 Autoscaling can be enabled as follows: 385 386 ```yaml 387 autoscaling: 388 enabled: true 389 ``` 390 391 KUBE_PING service discovery seems to be the most reliable mechanism to use when enabling autoscaling, due to being faster than DNS_PING at detecting changes in the cluster. 392 393 ### Running Keycloak Behind a Reverse Proxy 394 395 When running Keycloak behind a reverse proxy, which is the case when using an ingress controller, 396 proxy address forwarding must be enabled as follows: 397 398 ```yaml 399 extraEnv: | 400 - name: KC_PROXY 401 value: "passthrough" 402 ``` 403 404 ### Providing a Custom Theme 405 406 One option is certainly to provide a custom Keycloak-X image that includes the theme. 407 However, if you prefer to stick with the official Keycloak-X image, you can use an init container as theme provider. 408 409 Create your own theme and package it up into a Docker image. 410 411 ```docker 412 FROM busybox 413 COPY mytheme /mytheme 414 ``` 415 416 In combination with an `emptyDir` that is shared with the Keycloak container, configure an init container that runs your theme image and copies the theme over to the right place where Keycloak will pick it up automatically. 417 418 ```yaml 419 extraInitContainers: | 420 - name: theme-provider 421 image: myuser/mytheme:1 422 imagePullPolicy: IfNotPresent 423 command: 424 - sh 425 args: 426 - -c 427 - | 428 echo "Copying theme..." 429 cp -R /mytheme/* /theme 430 volumeMounts: 431 - name: theme 432 mountPath: /theme 433 434 extraVolumeMounts: | 435 - name: theme 436 mountPath: /opt/keycloak/themes/mytheme 437 438 extraVolumes: | 439 - name: theme 440 emptyDir: {} 441 ``` 442 443 ### Using Google Cloud SQL Proxy 444 445 Depending on your environment you may need a local proxy to connect to the database. 446 This is, e. g., the case for Google Kubernetes Engine when using Google Cloud SQL. 447 Create the secret for the credentials as documented [here](https://cloud.google.com/sql/docs/postgres/connect-kubernetes-engine) and configure the proxy as a sidecar. 448 449 Because `extraContainers` is a string that is passed through the `tpl` function, it is possible to create custom values and use them in the string. 450 451 ```yaml 452 database: 453 vendor: postgres 454 hostname: '127.0.0.1' 455 port: 5432 456 database: postgres 457 username: myuser 458 password: mypassword 459 460 # Custom values for Google Cloud SQL 461 cloudsql: 462 project: my-project 463 region: europe-west1 464 instance: my-instance 465 466 extraContainers: | 467 - name: cloudsql-proxy 468 image: gcr.io/cloudsql-docker/gce-proxy:1.17 469 command: 470 - /cloud_sql_proxy 471 args: 472 - -instances={{ .Values.cloudsql.project }}:{{ .Values.cloudsql.region }}:{{ .Values.cloudsql.instance }}=tcp:5432 473 - -credential_file=/secrets/cloudsql/credentials.json 474 volumeMounts: 475 - name: cloudsql-creds 476 mountPath: /secrets/cloudsql 477 readOnly: true 478 479 extraVolumes: | 480 - name: cloudsql-creds 481 secret: 482 secretName: cloudsql-instance-credentials 483 ``` 484 485 ### Changing the Context Path 486 487 By default, Keycloak-X is served under context `/auth`. 488 Trailing slash is removed from path. This can be changed to another context path like `/` as follows: 489 490 ```yaml 491 http: 492 relativePath: '/' 493 ``` 494 495 Alternatively, you may supply it via CLI flag: 496 497 ```console 498 --set-string http.relativePath=/ 499 ``` 500 501 ### Prometheus Metrics Support 502 503 #### Keycloak Metrics 504 505 Keycloak-X can expose metrics via `/auth/metrics`. 506 507 Metrics are enabled by default via: 508 ```yaml 509 metrics: 510 enabled: true 511 ``` 512 513 Add a ServiceMonitor if using prometheus-operator: 514 515 ```yaml 516 serviceMonitor: 517 # If `true`, a ServiceMonitor resource for the prometheus-operator is created 518 enabled: true 519 ``` 520 521 Checkout `values.yaml` for customizing the ServiceMonitor and for adding custom Prometheus rules. 522 523 Add annotations if you don't use prometheus-operator: 524 525 ```yaml 526 service: 527 annotations: 528 prometheus.io/scrape: "true" 529 prometheus.io/port: "8080" 530 ``` 531 532 #### Keycloak Metrics SPI 533 534 Optionally, it is possible to add [Keycloak Metrics SPI](https://github.com/aerogear/keycloak-metrics-spi) via init container. 535 Note that the `keycloak-metrics-spi.jar` needs to be added to the `/opt/keycloak/providers` directory. 536 537 A separate `ServiceMonitor` can be enabled to scrape metrics from the SPI: 538 539 ```yaml 540 extraServiceMonitor: 541 # If `true`, an additional ServiceMonitor resource for the prometheus-operator is created 542 enabled: true 543 ``` 544 545 Checkout `values.yaml` for customizing this ServiceMonitor. 546 547 Note that the metrics endpoint is exposed on the HTTP port. 548 You may want to restrict access to it in your ingress controller configuration. 549 For ingress-nginx, this could be done as follows: 550 551 ```yaml 552 annotations: 553 nginx.ingress.kubernetes.io/server-snippet: | 554 location ~* /auth/realms/[^/]+/metrics { 555 return 403; 556 } 557 ``` 558 559 ## Why StatefulSet? 560 561 The headless service that governs the StatefulSet is used for DNS discovery via DNS_PING. 562 563 ## Bad Gateway and Proxy Buffer Size in Nginx 564 565 A common issue with Keycloak and nginx is that the proxy buffer may be too small for what Keycloak is trying to send. This will result in a Bad Gateway (502) error. There are [many](https://github.com/kubernetes/ingress-nginx/issues/4637) [issues](https://stackoverflow.com/questions/56126864/why-do-i-get-502-when-trying-to-authenticate) around the internet about this. The solution is to increase the buffer size of nginx. This can be done by creating an annotation in the ingress specification: 566 567 ```yaml 568 ingress: 569 annotations: 570 nginx.ingress.kubernetes.io/proxy-buffer-size: "128k" 571 ``` 572 573 ## Upgrading 574 575 Notes for upgrading from previous Keycloak chart versions. 576 577 ### From chart < 18.0.0 578 579 * Keycloak is updated to 18.0.0 580 * Added new `health.enabled` option. 581 582 Keycloak 18.0.0 allows to enable the health endpoint independently of the metrics endpoint via the `health-enabled` setting. 583 We reflect that via the new config option `health.enabled`. 584 585 Please read the additional notes about [Migrating to 18.0.0](https://www.keycloak.org/docs/latest/upgrading/index.html#migrating-to-18-0-0) in the Keycloak documentation.