diff --git a/charts/keycloak/Chart.yaml b/charts/keycloak/Chart.yaml new file mode 100644 index 00000000..e27c161a --- /dev/null +++ b/charts/keycloak/Chart.yaml @@ -0,0 +1,20 @@ +name: keycloak +version: 4.10.2 +appVersion: 5.0.0 +description: Open Source Identity and Access Management For Modern Applications and Services +keywords: + - sso + - idm + - openid connect + - saml + - kerberos + - ldap +home: https://www.keycloak.org/ +icon: https://www.keycloak.org/resources/images/keycloak_logo_480x108.png +sources: + - https://github.com/jboss-dockerfiles/keycloak +maintainers: + - name: unguiculus + email: unguiculus@gmail.com + - name: thomasdarimont + email: thomas.darimont+github@gmail.com diff --git a/charts/keycloak/OWNERS b/charts/keycloak/OWNERS new file mode 100644 index 00000000..2eff592a --- /dev/null +++ b/charts/keycloak/OWNERS @@ -0,0 +1,6 @@ +approvers: +- unguiculus +- thomasdarimont +reviewers: +- unguiculus +- thomasdarimont diff --git a/charts/keycloak/README.md b/charts/keycloak/README.md new file mode 100644 index 00000000..9d7fac72 --- /dev/null +++ b/charts/keycloak/README.md @@ -0,0 +1,323 @@ +# Keycloak + +[Keycloak](http://www.keycloak.org/) is an open source identity and access management for modern applications and services. + +## TL;DR; + +```console +$ helm install codecentric/keycloak +``` + +## Introduction + +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. It provisions a fully featured Keycloak installation. +For more information on Keycloak and its capabilities, see its [documentation](http://www.keycloak.org/documentation.html). + +## Prerequisites Details + +The chart has an optional dependency on the [PostgreSQL](https://github.com/kubernetes/charts/tree/master/codecentric/postgresql) chart. +By default, the PostgreSQL chart requires PV support on underlying infrastructure (may be disabled). + +## Installing the Chart + +To install the chart with the release name `keycloak`: + +```console +$ helm install --name keycloak codecentric/keycloak +``` + +## Uninstalling the Chart + +To uninstall/delete the `keycloak` deployment: + +```console +$ helm delete keycloak +``` + +## Configuration + +The following table lists the configurable parameters of the Keycloak chart and their default values. + +Parameter | Description | Default +--- | --- | --- +`init.image.repository` | Init image repository | `alpine` +`init.image.tag` | Init image tag | `3.8` +`init.image.pullPolicy` | Init image pull policy | `IfNotPresent` +`clusterDomain` | The internal Kubernetes cluster domain | `cluster.local` +`keycloak.replicas` | The number of Keycloak replicas | `1` +`keycloak.image.repository` | The Keycloak image repository | `jboss/keycloak` +`keycloak.image.tag` | The Keycloak image tag | `5.0.0` +`keycloak.image.pullPolicy` | The Keycloak image pull policy | `IfNotPresent` +`keycloak.image.pullSecrets` | Image pull secrets | `[]` +`keycloak.basepath` | Path keycloak is hosted at | `auth` +`keycloak.username` | Username for the initial Keycloak admin user | `keycloak` +`keycloak.password` | Password for the initial Keycloak admin user (if `keycloak.existingSecret=""`). If not set, a random 10 characters password is created | `""` +`keycloak.existingSecret` | Specifies an existing secret to be used for the admin password | `""` +`keycloak.existingSecretKey` | The key in `keycloak.existingSecret` that stores the admin password | `password` +`keycloak.extraInitContainers` | Additional init containers, e. g. for providing themes, etc. Passed through the `tpl` function and thus to be configured a string | `""` +`keycloak.extraContainers` | Additional sidecar containers, e. g. for a database proxy, such as Google's cloudsql-proxy. Passed through the `tpl` function and thus to be configured a string | `""` +`keycloak.extraEnv` | Allows the specification of additional environment variables for Keycloak. Passed through the `tpl` function and thus to be configured a string | `""` +`keycloak.extraVolumeMounts` | Add additional volumes mounts, e. g. for custom themes. Passed through the `tpl` function and thus to be configured a string | `""` +`keycloak.extraVolumes` | Add additional volumes, e. g. for custom themes. Passed through the `tpl` function and thus to be configured a string | `""` +`keycloak.extraPorts` | Add additional ports, e. g. for custom admin console port. Passed through the `tpl` function and thus to be configured a string | `""` +`keycloak.podDisruptionBudget` | Pod disruption budget | `{}` +`keycloak.priorityClassName` | Pod priority classname | `{}` +`keycloak.resources` | Pod resource requests and limits | `{}` +`keycloak.affinity` | Pod affinity. Passed through the `tpl` function and thus to be configured a string | `Hard node and soft zone anti-affinity` +`keycloak.nodeSelector` | Node labels for pod assignment | `{}` +`keycloak.tolerations` | Node taints to tolerate | `[]` +`keycloak.podLabels` | Extra labels to add to pod | `{}` +`keycloak.podAnnotations` | Extra annotations to add to pod | `{}` +`keycloak.hostAliases` | Mapping between IP and hostnames that will be injected as entries in the pod's hosts files | `[]` +`keycloak.securityContext` | Security context for the pod | `{runAsUser: 1000, fsGroup: 1000, runAsNonRoot: true}` +`keycloak.preStartScript` | Custom script to run before Keycloak starts up | `` +`keycloak.lifecycleHooks` | Container lifecycle hooks. Passed through the `tpl` function and thus to be configured a string | `` +`keycloak.extraArgs` | Additional arguments to the start command | `` +`keycloak.livenessProbe.initialDelaySeconds` | Liveness Probe `initialDelaySeconds` | `120` +`keycloak.livenessProbe.timeoutSeconds` | Liveness Probe `timeoutSeconds` | `5` +`keycloak.readinessProbe.initialDelaySeconds` | Readiness Probe `initialDelaySeconds` | `30` +`keycloak.readinessProbe.timeoutSeconds` | Readiness Probe `timeoutSeconds` | `1` +`keycloak.cli.nodeIdentifier` | WildFly CLI script for setting the node identifier | See `values.yaml` +`keycloak.cli.logging` | WildFly CLI script for logging configuration | See `values.yaml` +`keycloak.cli.reverseProxy` | WildFly CLI script for reverse proxy configuration | See `values.yaml` +`keycloak.cli.ha` | Settings for HA setups | See `values.yaml` +`keycloak.cli.custom` | Additional custom WildFly CLI script | `""` +`keycloak.service.annotations` | Annotations for the Keycloak service | `{}` +`keycloak.service.labels` | Additional labels for the Keycloak service | `{}` +`keycloak.service.type` | The service type | `ClusterIP` +`keycloak.service.port` | The service port | `80` +`keycloak.service.nodePort` | The node port used if the service is of type `NodePort` | `""` +`keycloak.ingress.enabled` | if `true`, an ingress is created | `false` +`keycloak.ingress.annotations` | annotations for the ingress | `{}` +`keycloak.ingress.path` | if `true`, an ingress is created | `/` +`keycloak.ingress.hosts` | a list of ingress hosts | `[keycloak.example.com]` +`keycloak.ingress.tls` | a list of [IngressTLS](https://v1-9.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.9/#ingresstls-v1beta1-extensions) items | `[]` +`keycloak.persistence.deployPostgres` | If true, the PostgreSQL chart is installed | `false` +`keycloak.persistence.existingSecret` | Name of an existing secret to be used for the database password (if `keycloak.persistence.deployPostgres=false`). Otherwise a new secret is created | `""` +`keycloak.persistence.existingSecretKey` | The key for the database password in the existing secret (if `keycloak.persistence.deployPostgres=false`) | `password` +`keycloak.persistence.dbVendor` | One of `h2`, `postgres`, `mysql`, or `mariadb` (if `deployPostgres=false`) | `h2` +`keycloak.persistence.dbName` | The name of the database to connect to (if `deployPostgres=false`) | `keycloak` +`keycloak.persistence.dbHost` | The database host name (if `deployPostgres=false`) | `mykeycloak` +`keycloak.persistence.dbPort` | The database host port (if `deployPostgres=false`) | `5432` +`keycloak.persistence.dbUser` |The database user (if `deployPostgres=false`) | `keycloak` +`keycloak.persistence.dbPassword` |The database password (if `deployPostgres=false`) | `""` +`postgresql.postgresUser` | The PostgreSQL user (if `keycloak.persistence.deployPostgres=true`) | `keycloak` +`postgresql.postgresPassword` | The PostgreSQL password (if `keycloak.persistence.deployPostgres=true`) | `""` +`postgresql.postgresDatabase` | The PostgreSQL database (if `keycloak.persistence.deployPostgres=true`) | `keycloak` +`test.enabled` | If `true`, test pods get scheduled | `true` +`test.image.repository` | Test image repository | `unguiculus/docker-python3-phantomjs-selenium` +`test.image.tag` | Test image tag | `v1` +`test.image.pullPolicy` | Test image pull policy | `IfNotPresent` +`test.securityContext` | Security context for the test pod | `{runAsUser: 1000, fsGroup: 1000, runAsNonRoot: true}` + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```bash +$ helm install --name keycloak -f values.yaml codecentric/keycloak +``` + +### Usage of the `tpl` Function + +The `tpl` function allows us to pass string values from `values.yaml` through the templating engine. It is used for the following values: + +* `keycloak.extraInitContainers` +* `keycloak.extraContainers` +* `keycloak.extraEnv` +* `keycloak.affinity` +* `keycloak.extraVolumeMounts` +* `keycloak.extraVolumes` + +It is important that these values be configured as strings. Otherwise, installation will fail. See example for Google Cloud Proxy or default affinity configuration in `values.yaml`. + +### Database Setup + +By default, Keycloak uses an embedded H2 database. +This is only suitable for testing purposes. +All data is lost when Keycloak is shut down. +Optionally, the [PostgreSQL](https://github.com/kubernetes/charts/tree/master/stable/postgresql) chart is deployed and used as database. +Please refer to this chart for additional PostgreSQL configuration options. + +#### Using an External Database + +The Keycloak Docker image supports PostgreSQL, MySQL, MariaDB, and H2. The password for the database user is read from a Kubernetes secret. It is possible to specify an existing secret that is not managed with this chart. The key in the secret the password is read from may be specified as well (defaults to `password`). + +```yaml +keycloak: + persistence: + + # Disable deployment of the PostgreSQL chart + deployPostgres: false + + # The database vendor. Can be either "postgres", "mysql", "mariadb", or "h2" + dbVendor: postgres + + ## The following values only apply if "deployPostgres" is set to "false" + + # Optionally specify an existing secret + existingSecret: "my-database-password-secret" + existingSecretKey: "password-key in-my-database-secret" + + dbName: keycloak + dbHost: mykeycloak + dbPort: 5432 # 5432 is PostgreSQL's default port. For MySQL it would be 3306 + dbUser: keycloak + + # Only used if no existing secret is specified. In this case a new secret is created + dbPassword: keycloak +``` + +See also: +* https://github.com/jboss-dockerfiles/keycloak/blob/master/server/cli/databases/postgres/change-database.cli +* https://github.com/jboss-dockerfiles/keycloak/blob/master/server/cli/databases/mysql/change-database.cli + +### Configuring Additional Environment Variables + +```yaml +keycloak: + extraEnv: | + - name: KEYCLOAK_LOGLEVEL + value: DEBUG + - name: WILDFLY_LOGLEVEL + value: DEBUG + - name: CACHE_OWNERS + value: "3" + - name: DB_QUERY_TIMEOUT + value: "60" + - name: DB_VALIDATE_ON_MATCH + value: true + - name: DB_USE_CAST_FAIL + value: false +``` + +### Providing a Custom Theme + +One option is certainly to provide a custom Keycloak image that includes the theme. However, if you prefer to stick with the official Keycloak image, you can use an init container as theme provider. + +Create your own theme and package it up into a Docker image. + +```docker +FROM busybox +COPY my_theme /my_theme +``` + +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. + +```yaml +keycloak: + extraInitContainers: | + - name: theme-provider + image: myuser/mytheme:1 + imagePullPolicy: IfNotPresent + command: + - sh + args: + - -c + - | + echo "Copying theme..." + cp -R /mytheme/* /theme + volumeMounts: + - name: theme + mountPath: /theme + + extraVolumeMounts: | + - name: theme + mountPath: /opt/jboss/keycloak/themes/mytheme + + extraVolumes: | + - name: theme + emptyDir: {} +``` + +### Setting a Custom Realm + +A realm can be added by creating a secret or configmap for the realm json file and then supplying this into the chart. +It could be mounted using `extraVolumeMounts` and then specified in `extraArgs` using `-Dkeycloak.import`. +First we could create a Secret from a json file using `kubectl create secret generic realm-secret --from-file=realm.json` which we need to reference in `values.yaml`: + +```yaml +keycloak: + extraVolumes: | + - name: realm-secret + secret: + secretName: realm-secret + + extraVolumeMounts: | + - name: realm-secret + mountPath: "/realm/" + readOnly: true + + extraArgs: -Dkeycloak.import=/realm/realm.json +``` + +Alternatively, the file could be added to a custom image (set in `keycloak.image`) and then referenced by `-Dkeycloak.import`. + +After startup the web admin console for the realm should be available on the path /auth/admin/\/console/. + +### Using Google Cloud SQL Proxy + +Depending on your environment you may need a local proxy to connect to the database. This is, e. g., the case for Google Kubernetes Engine when using Google Cloud SQL. 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. + +Because `keycloak.extraContainers` is a string that is passed through the `tpl` function, it is possible to create custom values and use them in the string. + +```yaml + +# Custom values for Google Cloud SQL +cloudsql: + project: my-project + region: europe-west1 + instance: my-instance + +keycloak: + extraContainers: | + - name: cloudsql-proxy + image: gcr.io/cloudsql-docker/gce-proxy:1.11 + command: + - /cloud_sql_proxy + args: + - -instances={{ .Values.cloudsql.project }}:{{ .Values.cloudsql.region }}:{{ .Values.cloudsql.instance }}=tcp:5432 + - -credential_file=/secrets/cloudsql/credentials.json + volumeMounts: + - name: cloudsql-creds + mountPath: /secrets/cloudsql + readOnly: true + + extraVolumes: | + - name: cloudsql-creds + secret: + secretName: cloudsql-instance-credentials + + persistence: + deployPostgres: false + dbVendor: postgres + dbName: postgres + dbHost: 127.0.0.1 + dbPort: 5432 + dbUser: myuser + dbPassword: mypassword +``` + +### WildFly Configuration + +WildFly can be configured via its [command line interface (CLI)](https://docs.jboss.org/author/display/WFLY/Command+Line+Interface). +This chart uses the official Keycloak Docker image and customizes the installation running CLI scripts at server startup. + +In order to make further customization easier, the CLI commands are separated by their concerns into smaller scripts. +Everything is in `values.yaml` and can be overridden. Additional CLI commands may be added via `keycloak.cli.custom`, which is empty by default. + +### High Availability and Clustering + +For high availability, Keycloak should be run with multiple replicas (`keycloak.replicas > 1`). +WildFly uses Infinispan for caching. These caches can be replicated across all instances forming a cluster. +If `keycloak.replicas > 1`, JGroups' DNS_PING is configured for cluster discovery and Keycloak is started with `--server-config standalone-ha.xml`. + +## Why StatefulSet? + +The chart sets node identifiers to the system property `jboss.node.name` which is in fact the pod name. +Node identifiers must not be longer than 23 characters. +This can be problematic because pod names are quite long. +We would have to truncate the chart's fullname to six characters because pods get a 17-character suffix (e. g. `-697f8b7655-mf5ht`). +Using a StatefulSet allows us to truncate to 20 characters leaving room for up to 99 replicas, which is much better. +Additionally, we get stable values for `jboss.node.name` which can be advantageous for cluster discovery. +The headless service that governs the StatefulSet is used for DNS discovery. diff --git a/charts/keycloak/ci/h2-values.yaml b/charts/keycloak/ci/h2-values.yaml new file mode 100644 index 00000000..60140bd0 --- /dev/null +++ b/charts/keycloak/ci/h2-values.yaml @@ -0,0 +1,2 @@ +keycloak: + password: keycloak diff --git a/charts/keycloak/ci/postgres-ha-values.yaml b/charts/keycloak/ci/postgres-ha-values.yaml new file mode 100644 index 00000000..a783e861 --- /dev/null +++ b/charts/keycloak/ci/postgres-ha-values.yaml @@ -0,0 +1,6 @@ +keycloak: + replicas: 2 + password: keycloak + persistence: + deployPostgres: true + dbVendor: postgres diff --git a/charts/keycloak/requirements.lock b/charts/keycloak/requirements.lock new file mode 100644 index 00000000..2d6fdf83 --- /dev/null +++ b/charts/keycloak/requirements.lock @@ -0,0 +1,6 @@ +dependencies: +- name: postgresql + repository: https://kubernetes-charts.storage.googleapis.com/ + version: 0.15.0 +digest: sha256:428d8302be9a566a3e77538af30c56b63e0bfc97dd01dd434f303f4434cb8100 +generated: 2018-07-06T08:41:15.715456938+02:00 diff --git a/charts/keycloak/requirements.yaml b/charts/keycloak/requirements.yaml new file mode 100644 index 00000000..f6909fe6 --- /dev/null +++ b/charts/keycloak/requirements.yaml @@ -0,0 +1,5 @@ +dependencies: + - name: postgresql + version: 0.15.0 + repository: https://kubernetes-charts.storage.googleapis.com/ + condition: keycloak.persistence.deployPostgres diff --git a/charts/keycloak/scripts/datasource.cli b/charts/keycloak/scripts/datasource.cli new file mode 100644 index 00000000..4499967a --- /dev/null +++ b/charts/keycloak/scripts/datasource.cli @@ -0,0 +1,8 @@ +# Configure datasource to use explicit query timeout in seconds +/subsystem=datasources/data-source=KeycloakDS/:write-attribute(name=query-timeout,value=${env.DB_QUERY_TIMEOUT:300}) + +# Configure datasource to connection before use +/subsystem=datasources/data-source=KeycloakDS/:write-attribute(name=validate-on-match,value=${env.DB_VALIDATE_ON_MATCH:true}) + +# Configure datasource to try all other connections before failing +/subsystem=datasources/data-source=KeycloakDS/:write-attribute(name=use-fast-fail,value=${env.DB_USE_CAST_FAIL:false}) diff --git a/charts/keycloak/scripts/ha.cli b/charts/keycloak/scripts/ha.cli new file mode 100644 index 00000000..0f47014e --- /dev/null +++ b/charts/keycloak/scripts/ha.cli @@ -0,0 +1,8 @@ +/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:2}) +/subsystem=infinispan/cache-container=keycloak/distributed-cache=authenticationSessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:2}) +/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:2}) +/subsystem=infinispan/cache-container=keycloak/distributed-cache=clientSessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:2}) +/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineClientSessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:2}) +/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures:write-attribute(name=owners, value=${env.CACHE_OWNERS:2}) + +/subsystem=jgroups/channel=ee:write-attribute(name=stack, value=tcp) diff --git a/charts/keycloak/scripts/logging.cli b/charts/keycloak/scripts/logging.cli new file mode 100644 index 00000000..43973bbf --- /dev/null +++ b/charts/keycloak/scripts/logging.cli @@ -0,0 +1,10 @@ +# Allow log level to be configured via environment variable +/subsystem=logging/console-handler=CONSOLE:write-attribute(name=level, value=${env.WILDFLY_LOGLEVEL:INFO}) +/subsystem=logging/root-logger=ROOT:write-attribute(name=level, value=${env.WILDFLY_LOGLEVEL:INFO}) + +# Add dedicated eventsListener config element to allow configuring elements. +/subsystem=keycloak-server/spi=eventsListener:add() +/subsystem=keycloak-server/spi=eventsListener/provider=jboss-logging:add(enabled=true) +# Propagate success events to INFO instead of DEBUG, to expose successful logins for log analysis +/subsystem=keycloak-server/spi=eventsListener/provider=jboss-logging:write-attribute(name=properties.success-level,value=info) +/subsystem=keycloak-server/spi=eventsListener/provider=jboss-logging:write-attribute(name=properties.error-level,value=warn) diff --git a/charts/keycloak/scripts/node-identifier.cli b/charts/keycloak/scripts/node-identifier.cli new file mode 100644 index 00000000..de0f9506 --- /dev/null +++ b/charts/keycloak/scripts/node-identifier.cli @@ -0,0 +1,3 @@ +## Sets the node identifier to the node name (= pod name). Node identifiers have to be unique. They can have a +## maximum length of 23 characters. Thus, the chart's fullname template truncates its length accordingly. +/subsystem=transactions:write-attribute(name=node-identifier, value=${jboss.node.name}) diff --git a/charts/keycloak/scripts/reverse-proxy.cli b/charts/keycloak/scripts/reverse-proxy.cli new file mode 100644 index 00000000..fe592078 --- /dev/null +++ b/charts/keycloak/scripts/reverse-proxy.cli @@ -0,0 +1,3 @@ +/socket-binding-group=standard-sockets/socket-binding=proxy-https:add(port=443) +/subsystem=undertow/server=default-server/http-listener=default:write-attribute(name=redirect-socket, value=proxy-https) +/subsystem=undertow/server=default-server/http-listener=default:write-attribute(name=proxy-address-forwarding, value=true) diff --git a/charts/keycloak/templates/NOTES.txt b/charts/keycloak/templates/NOTES.txt new file mode 100644 index 00000000..eb04e032 --- /dev/null +++ b/charts/keycloak/templates/NOTES.txt @@ -0,0 +1,52 @@ + +Keycloak can be accessed: + +* Within your cluster, at the following DNS name at port {{ .Values.keycloak.service.port }}: + + {{ template "keycloak.fullname" . }}-http.{{ .Release.Namespace }}.svc.cluster.local + +{{- if .Values.keycloak.ingress.enabled }} + +* From outside the cluster: + +{{- range .Values.keycloak.ingress.hosts }} + - http{{ if $.Values.keycloak.ingress.tls }}s{{ end }}://{{ . }} +{{- end }} + +{{- else }} + +* From outside the cluster, run these commands in the same shell: + +{{- if contains "NodePort" .Values.keycloak.service.type }} + + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "keycloak.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT + +{{- else if contains "LoadBalancer" .Values.keycloak.service.type }} + + NOTE: + It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ template "keycloak.fullname" . }}' + + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "keycloak.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.keycloak.service.port }} + +{{- else if contains "ClusterIP" .Values.keycloak.service.type }} + + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l app={{ template "keycloak.name" . }},release={{ .Release.Name }} -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use Keycloak" + kubectl port-forward --namespace {{ .Release.Namespace }} $POD_NAME 8080 + +{{- end }} + +{{- end }} + +{{- if .Release.IsInstall }} + +Login with the following credentials: +Username: {{ .Values.keycloak.username }} + +To retrieve the initial user password run: +kubectl get secret --namespace {{ .Release.Namespace }} {{ template "keycloak.fullname" . }}-http -o jsonpath="{.data.password}" | base64 --decode; echo +{{- end }} diff --git a/charts/keycloak/templates/_helpers.tpl b/charts/keycloak/templates/_helpers.tpl new file mode 100644 index 00000000..5519b999 --- /dev/null +++ b/charts/keycloak/templates/_helpers.tpl @@ -0,0 +1,106 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "keycloak.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate to 20 characters because this is used to set the node identifier in WildFly which is limited to +23 characters. This allows for a replica suffix for up to 99 replicas. +*/}} +{{- define "keycloak.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 20 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 20 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 20 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "keycloak.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name for the postgres requirement. +*/}} +{{- define "keycloak.postgresql.fullname" -}} +{{- $postgresContext := dict "Values" .Values.postgresql "Release" .Release "Chart" (dict "Name" "postgresql") -}} +{{ template "postgresql.fullname" $postgresContext }} +{{- end -}} + +{{/* +Create the name for the database secret. +*/}} +{{- define "keycloak.externalDbSecret" -}} +{{- if .Values.keycloak.persistence.existingSecret -}} + {{- .Values.keycloak.persistence.existingSecret -}} +{{- else -}} + {{- template "keycloak.fullname" . -}}-db +{{- end -}} +{{- end -}} + +{{/* +Create the name for the password secret key. +*/}} +{{- define "keycloak.dbPasswordKey" -}} +{{- if .Values.keycloak.persistence.existingSecret -}} + {{- .Values.keycloak.persistence.existingSecretKey -}} +{{- else -}} + password +{{- end -}} +{{- end -}} + +{{/* +Create environment variables for database configuration. +*/}} +{{- define "keycloak.dbEnvVars" -}} +{{- if .Values.keycloak.persistence.deployPostgres }} +{{- if not (eq "postgres" .Values.keycloak.persistence.dbVendor) }} +{{ fail (printf "ERROR: 'Setting keycloak.persistence.deployPostgres' to 'true' requires setting 'keycloak.persistence.dbVendor' to 'postgres' (is: '%s')!" .Values.keycloak.persistence.dbVendor) }} +{{- end }} +- name: DB_VENDOR + value: postgres +- name: DB_ADDR + value: {{ template "keycloak.postgresql.fullname" . }} +- name: DB_PORT + value: "5432" +- name: DB_DATABASE + value: {{ .Values.postgresql.postgresDatabase | quote }} +- name: DB_USER + value: {{ .Values.postgresql.postgresUser | quote }} +- name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "keycloak.postgresql.fullname" . }} + key: postgres-password +{{- else }} +- name: DB_VENDOR + value: {{ .Values.keycloak.persistence.dbVendor | quote }} +{{- if not (eq "h2" .Values.keycloak.persistence.dbVendor) }} +- name: DB_ADDR + value: {{ .Values.keycloak.persistence.dbHost | quote }} +- name: DB_PORT + value: {{ .Values.keycloak.persistence.dbPort | quote }} +- name: DB_DATABASE + value: {{ .Values.keycloak.persistence.dbName | quote }} +- name: DB_USER + value: {{ .Values.keycloak.persistence.dbUser | quote }} +- name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "keycloak.externalDbSecret" . }} + key: {{ include "keycloak.dbPasswordKey" . | quote }} +{{- end }} +{{- end }} +{{- end -}} diff --git a/charts/keycloak/templates/configmap.yaml b/charts/keycloak/templates/configmap.yaml new file mode 100644 index 00000000..437badb5 --- /dev/null +++ b/charts/keycloak/templates/configmap.yaml @@ -0,0 +1,60 @@ +{{- $highAvailability := gt (int .Values.keycloak.replicas) 1 -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "keycloak.fullname" . }} + labels: + app: {{ template "keycloak.name" . }} + chart: {{ template "keycloak.chart" . }} + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +data: + keycloak.sh: | + #!/usr/bin/env bash + + set -eu + + /opt/jboss/keycloak/bin/jboss-cli.sh --file=/scripts/keycloak.cli + + {{- with .Values.keycloak.preStartScript }} + echo 'Running custom pre-start script...' +{{ . | indent 4 }} + {{- end }} + + exec /opt/jboss/tools/docker-entrypoint.sh -b 0.0.0.0 {{ .Values.keycloak.extraArgs }}{{- if $highAvailability }} -c standalone-ha.xml{{ else }} -c standalone.xml{{ end }} + exit "$?" + + keycloak.cli: | + embed-server {{- if $highAvailability }} --server-config=standalone-ha.xml{{ end }} --std-out=echo + batch + +{{- if ne .Values.keycloak.basepath "auth" }} + # Changes the base path to be /keycloak.basepath instead of /auth + /subsystem=keycloak-server:write-attribute(name=web-context,value={{ if eq .Values.keycloak.basepath "" }}ROOT{{ else }}{{ .Values.keycloak.basepath }}{{ end }}) + {{- if eq .Values.keycloak.basepath "" }} + /subsystem=undertow/server=default-server/host=default-host:write-attribute(name=default-web-module,value=keycloak-server.war) + {{- end }} +{{ end }} + +{{- with .Values.keycloak.cli }} + +{{ tpl .nodeIdentifier $ | indent 4 }} + +{{ tpl .logging $ | indent 4 }} + +{{ tpl .reverseProxy $ | indent 4 }} + +{{ tpl .datasource $ | indent 4 }} + +{{- if $highAvailability }} +{{ tpl .ha $ | indent 4 }} +{{- end }} + +{{- with .custom }} +{{ tpl . $ | indent 4 }} +{{- end }} + +{{- end }} + + run-batch + stop-embedded-server diff --git a/charts/keycloak/templates/db-secret.yaml b/charts/keycloak/templates/db-secret.yaml new file mode 100644 index 00000000..c7e726e2 --- /dev/null +++ b/charts/keycloak/templates/db-secret.yaml @@ -0,0 +1,14 @@ +{{- if and (not .Values.keycloak.persistence.deployPostgres) (not .Values.keycloak.persistence.existingSecret) -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "keycloak.fullname" . }}-db + labels: + app: {{ template "keycloak.name" . }} + chart: {{ template "keycloak.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +type: Opaque +data: + {{ template "keycloak.dbPasswordKey" . }}: {{ .Values.keycloak.persistence.dbPassword | b64enc | quote }} +{{- end -}} diff --git a/charts/keycloak/templates/headless-service.yaml b/charts/keycloak/templates/headless-service.yaml new file mode 100644 index 00000000..a94be2d3 --- /dev/null +++ b/charts/keycloak/templates/headless-service.yaml @@ -0,0 +1,27 @@ +{{- $highAvailability := gt (int .Values.keycloak.replicas) 1 -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "keycloak.fullname" . }}-headless + labels: + app: {{ template "keycloak.name" . }} + chart: {{ template "keycloak.chart" . }} + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +spec: + type: ClusterIP + clusterIP: None + ports: + - name: http + port: {{ .Values.keycloak.service.port }} + targetPort: http + protocol: TCP + {{- if $highAvailability }} + - name: jgroups + port: {{ .Values.keycloak.service.jgroupsPort }} + targetPort: jgroups + protocol: TCP + {{- end }} + selector: + app: {{ template "keycloak.name" . }} + release: "{{ .Release.Name }}" diff --git a/charts/keycloak/templates/http-service.yaml b/charts/keycloak/templates/http-service.yaml new file mode 100644 index 00000000..f02d0c1f --- /dev/null +++ b/charts/keycloak/templates/http-service.yaml @@ -0,0 +1,30 @@ +{{- $service := .Values.keycloak.service -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "keycloak.fullname" . }}-http +{{- with $service.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} + labels: + app: {{ template "keycloak.name" . }} + chart: {{ template "keycloak.chart" . }} + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +{{- with $service.labels }} +{{ toYaml . | indent 4 }} +{{- end }} +spec: + type: {{ $service.type }} + ports: + - name: http + port: {{ $service.port }} + targetPort: http + {{- if and (eq "NodePort" $service.type) $service.nodePort }} + nodePort: {{ $service.nodePort }} + {{- end }} + protocol: TCP + selector: + app: {{ template "keycloak.name" . }} + release: "{{ .Release.Name }}" diff --git a/charts/keycloak/templates/ingress.yaml b/charts/keycloak/templates/ingress.yaml new file mode 100644 index 00000000..6d664927 --- /dev/null +++ b/charts/keycloak/templates/ingress.yaml @@ -0,0 +1,37 @@ +{{- $ingress := .Values.keycloak.ingress -}} +{{- if $ingress.enabled -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ template "keycloak.fullname" . }} +{{- with $ingress.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} + labels: + app: {{ template "keycloak.name" . }} + chart: {{ template "keycloak.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +spec: +{{- if $ingress.tls }} + tls: + {{- range $ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range $ingress.hosts }} + - host: {{ . }} + http: + paths: + - path: {{ $ingress.path }} + backend: + serviceName: {{ template "keycloak.fullname" $ }}-http + servicePort: http + {{- end }} +{{- end -}} diff --git a/charts/keycloak/templates/keycloak-secret.yaml b/charts/keycloak/templates/keycloak-secret.yaml new file mode 100644 index 00000000..4598643d --- /dev/null +++ b/charts/keycloak/templates/keycloak-secret.yaml @@ -0,0 +1,18 @@ +{{- if not .Values.keycloak.existingSecret -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "keycloak.fullname" . }}-http + labels: + app: {{ template "keycloak.name" . }} + chart: {{ template "keycloak.chart" . }} + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +type: Opaque +data: +{{- if .Values.keycloak.password }} + {{ .Values.keycloak.existingSecretKey }}: {{ .Values.keycloak.password | b64enc | quote }} +{{- else }} + {{ .Values.keycloak.existingSecretKey }}: {{ randAlphaNum 10 | b64enc | quote }} +{{- end }} +{{- end}} diff --git a/charts/keycloak/templates/poddisruptionbudget.yaml b/charts/keycloak/templates/poddisruptionbudget.yaml new file mode 100644 index 00000000..0ef3caa6 --- /dev/null +++ b/charts/keycloak/templates/poddisruptionbudget.yaml @@ -0,0 +1,17 @@ +{{- if .Values.keycloak.podDisruptionBudget -}} +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + labels: + app: {{ template "keycloak.name" . }} + chart: {{ template "keycloak.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + name: {{ template "keycloak.fullname" . }} +spec: + selector: + matchLabels: + app: {{ template "keycloak.name" . }} + release: {{ .Release.Name }} +{{ toYaml .Values.keycloak.podDisruptionBudget | indent 2 }} +{{- end -}} diff --git a/charts/keycloak/templates/statefulset.yaml b/charts/keycloak/templates/statefulset.yaml new file mode 100644 index 00000000..a0abdd99 --- /dev/null +++ b/charts/keycloak/templates/statefulset.yaml @@ -0,0 +1,160 @@ +{{- $highAvailability := gt (int .Values.keycloak.replicas) 1 -}} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ template "keycloak.fullname" . }} + annotations: + checksum/config: {{ include (print .Template.BasePath "/configmap.yaml") . | sha256sum }} + labels: + app: {{ template "keycloak.name" . }} + chart: {{ template "keycloak.chart" . }} + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +spec: + selector: + matchLabels: + app: {{ template "keycloak.name" . }} + release: "{{ .Release.Name }}" + replicas: {{ .Values.keycloak.replicas }} + serviceName: {{ template "keycloak.fullname" . }}-headless + podManagementPolicy: Parallel + updateStrategy: + type: RollingUpdate + template: + metadata: + labels: + app: {{ template "keycloak.name" . }} + release: "{{ .Release.Name }}" + {{- if .Values.keycloak.podLabels }} +{{ toYaml .Values.keycloak.podLabels | indent 8 }} + {{- end }} +{{- if .Values.keycloak.podAnnotations }} + annotations: +{{ toYaml .Values.keycloak.podAnnotations | indent 8 }} +{{- end }} + spec: +{{- with .Values.keycloak.hostAliases }} + hostAliases: +{{ toYaml . | indent 8 }} +{{- end }} + securityContext: +{{ toYaml .Values.keycloak.securityContext | indent 8 }} + {{- with .Values.keycloak.image.pullSecrets }} + imagePullSecrets: + {{- range . }} + - name: {{ . }} + {{- end }} + {{- end }} + {{- if or .Values.keycloak.persistence.deployPostgres .Values.keycloak.extraInitContainers }} + initContainers: + {{- if .Values.keycloak.persistence.deployPostgres }} + - name: wait-for-postgresql + image: "{{ .Values.init.image.repository }}:{{ .Values.init.image.tag }}" + imagePullPolicy: {{ .Values.init.image.pullPolicy }} + command: + - sh + - -c + - | + until printf "." && nc -z -w 2 {{ template "keycloak.postgresql.fullname" . }} {{ .Values.postgresql.service.port }}; do + sleep 2; + done; + + echo 'PostgreSQL OK ✓' + {{- end }} + {{- if .Values.keycloak.extraInitContainers }} +{{ tpl .Values.keycloak.extraInitContainers . | indent 8 }} + {{- end }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.keycloak.image.repository }}:{{ .Values.keycloak.image.tag }}" + imagePullPolicy: {{ .Values.keycloak.image.pullPolicy }} + command: + - /scripts/keycloak.sh + {{- if .Values.keycloak.lifecycleHooks }} + lifecycle: +{{ tpl .Values.keycloak.lifecycleHooks . | indent 12 }} + {{- end }} + env: + {{- if .Release.IsInstall }} + - name: KEYCLOAK_USER + value: {{ .Values.keycloak.username }} + - name: KEYCLOAK_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.keycloak.existingSecret }} + name: {{ .Values.keycloak.existingSecret }} + {{- else }} + name: {{ template "keycloak.fullname" . }}-http + {{- end }} + key: {{ .Values.keycloak.existingSecretKey }} + {{- end }} + {{- if $highAvailability }} + - name: JGROUPS_DISCOVERY_PROTOCOL + value: "dns.DNS_PING" + - name: JGROUPS_DISCOVERY_PROPERTIES + value: "dns_query={{ template "keycloak.fullname" . }}-headless.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }}" + {{- end }} +{{ include "keycloak.dbEnvVars" . | indent 12 }} +{{- with .Values.keycloak.extraEnv }} +{{ tpl . $ | indent 12 }} +{{- end }} + volumeMounts: + - name: scripts + mountPath: /scripts +{{- with .Values.keycloak.extraVolumeMounts }} +{{ tpl . $ | indent 12 }} +{{- end }} + ports: + - name: http + containerPort: 8080 + protocol: TCP + {{- if $highAvailability }} + - name: jgroups + containerPort: 7600 + protocol: TCP + {{- end }} +{{- with .Values.keycloak.extraPorts }} +{{ tpl . $ | indent 12 }} +{{- end }} + livenessProbe: + httpGet: + path: {{ if ne .Values.keycloak.basepath "" }}/{{ .Values.keycloak.basepath }}{{ end }}/ + port: http + initialDelaySeconds: {{ .Values.keycloak.livenessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.keycloak.livenessProbe.timeoutSeconds }} + readinessProbe: + httpGet: + path: {{ if ne .Values.keycloak.basepath "" }}/{{ .Values.keycloak.basepath }}{{ end }}/realms/master + port: http + initialDelaySeconds: {{ .Values.keycloak.readinessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.keycloak.readinessProbe.timeoutSeconds }} + resources: +{{ toYaml .Values.keycloak.resources | indent 12 }} +{{- with .Values.keycloak.extraContainers }} +{{ tpl . $ | indent 8 }} +{{- end }} + {{- with .Values.keycloak.affinity }} + affinity: +{{ tpl . $ | indent 8 }} + {{- end }} + {{- with .Values.keycloak.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.keycloak.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} +{{- if .Values.keycloak.priorityClassName }} + priorityClassName: {{ .Values.keycloak.priorityClassName }} +{{- end }} + terminationGracePeriodSeconds: 60 + volumes: + - name: scripts + configMap: + name: {{ template "keycloak.fullname" . }} + defaultMode: 0555 +{{- with .Values.keycloak.extraVolumes }} +{{ tpl . $ | indent 8 }} +{{- end }} diff --git a/charts/keycloak/templates/test/test-configmap.yaml b/charts/keycloak/templates/test/test-configmap.yaml new file mode 100644 index 00000000..ec9e2d30 --- /dev/null +++ b/charts/keycloak/templates/test/test-configmap.yaml @@ -0,0 +1,57 @@ +{{- if .Values.test.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "keycloak.fullname" . }}-test + labels: + app: {{ template "keycloak.name" . }} + chart: {{ template "keycloak.chart" . }} + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +data: + test.py: | + import os + from selenium import webdriver + from selenium.webdriver.common.by import By + from selenium.webdriver.support.ui import WebDriverWait + from selenium.webdriver.support import expected_conditions + from urllib.parse import urlparse + + print('Creating PhantomJS driver...') + driver = webdriver.PhantomJS(service_log_path='/tmp/ghostdriver.log') + + base_url = 'http://{{ template "keycloak.fullname" . }}-http{{ if ne 80 (int .Values.keycloak.service.port) }}{{ .Values.keycloak.service.port }}{{ end }}' + + print('Opening Keycloak...') + driver.get('{0}/auth/admin/'.format(base_url)) + + username = os.environ['KEYCLOAK_USER'] + password = os.environ['KEYCLOAK_PASSWORD'] + + username_input = WebDriverWait(driver, 30).until(expected_conditions.presence_of_element_located((By.ID, "username"))) + password_input = WebDriverWait(driver, 30).until(expected_conditions.presence_of_element_located((By.ID, "password"))) + login_button = WebDriverWait(driver, 30).until(expected_conditions.presence_of_element_located((By.ID, "kc-login"))) + + print('Entering username...') + username_input.send_keys(username) + + print('Entering password...') + password_input.send_keys(password) + + print('Clicking login button...') + login_button.click() + + current_url = urlparse(driver.current_url) + expected_url = urlparse('{0}/auth/admin/master/console/'.format(base_url)) + + print('Current URL: {0}'.format(current_url)) + print('Expected URL: {0}'.format(expected_url)) + + if current_url.path != expected_url.path: + print('Login failed. Current url is not expected url') + exit(1) + + print('URLs match. Login successful.') + + driver.quit() +{{- end }} diff --git a/charts/keycloak/templates/test/test-pod.yaml b/charts/keycloak/templates/test/test-pod.yaml new file mode 100644 index 00000000..c240f711 --- /dev/null +++ b/charts/keycloak/templates/test/test-pod.yaml @@ -0,0 +1,40 @@ +{{- if .Values.test.enabled }} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ template "keycloak.fullname" . }}-test-{{ randAlphaNum 5 | lower }}" + labels: + app: {{ template "keycloak.name" . }} + chart: {{ template "keycloak.chart" . }} + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" + role: test + annotations: + "helm.sh/hook": test-success +spec: + securityContext: +{{ toYaml .Values.test.securityContext | indent 8 }} + containers: + - name: {{ .Chart.Name }}-test + image: "{{ .Values.test.image.repository }}:{{ .Values.test.image.tag }}" + imagePullPolicy: {{ .Values.test.image.pullPolicy }} + command: + - python3 + - /tests/test.py + env: + - name: KEYCLOAK_USER + value: {{ .Values.keycloak.username }} + - name: KEYCLOAK_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "keycloak.fullname" . }}-http + key: password + volumeMounts: + - name: tests + mountPath: /tests + volumes: + - name: tests + configMap: + name: {{ template "keycloak.fullname" . }}-test + restartPolicy: Never +{{- end }} diff --git a/charts/keycloak/values.yaml b/charts/keycloak/values.yaml new file mode 100644 index 00000000..b466431f --- /dev/null +++ b/charts/keycloak/values.yaml @@ -0,0 +1,263 @@ +init: + image: + repository: alpine + tag: 3.8 + pullPolicy: IfNotPresent + +clusterDomain: cluster.local + +keycloak: + replicas: 1 + + image: + repository: jboss/keycloak + tag: 5.0.0 + pullPolicy: IfNotPresent + + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + pullSecrets: [] + # - myRegistrKeySecretName + + hostAliases: [] + # - ip: "1.2.3.4" + # hostnames: + # - "my.host.com" + + securityContext: + runAsUser: 1000 + fsGroup: 1000 + runAsNonRoot: true + + ## The path keycloak will be served from. To serve keycloak from the root path, use two quotes (e.g. ""). + basepath: auth + + ## Additional init containers, e. g. for providing custom themes + extraInitContainers: | + + ## Additional sidecar containers, e. g. for a database proxy, such as Google's cloudsql-proxy + extraContainers: | + + ## Custom script that is run before Keycloak is started. + preStartScript: + + ## lifecycleHooks defines the container lifecycle hooks + lifecycleHooks: | + # postStart: + # exec: + # command: ["/bin/sh", "-c", "ls"] + + ## Additional arguments to start command e.g. -Dkeycloak.import= to load a realm + extraArgs: "" + + ## Username for the initial Keycloak admin user + username: keycloak + + ## Password for the initial Keycloak admin user. Applicable only if existingSecret is not set. + ## If not set, a random 10 characters password will be used + password: "" + + # Specifies an existing secret to be used for the admin password + existingSecret: "" + + # The key in the existing secret that stores the password + existingSecretKey: password + + ## Allows the specification of additional environment variables for Keycloak + extraEnv: | + # - name: KEYCLOAK_LOGLEVEL + # value: DEBUG + # - name: WILDFLY_LOGLEVEL + # value: DEBUG + # - name: CACHE_OWNERS + # value: "2" + # - name: DB_QUERY_TIMEOUT + # value: "60" + # - name: DB_VALIDATE_ON_MATCH + # value: true + # - name: DB_USE_CAST_FAIL + # value: false + + affinity: | + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app: {{ template "keycloak.name" . }} + release: "{{ .Release.Name }}" + matchExpressions: + - key: role + operator: NotIn + values: + - test + topologyKey: kubernetes.io/hostname + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchLabels: + app: {{ template "keycloak.name" . }} + release: "{{ .Release.Name }}" + matchExpressions: + - key: role + operator: NotIn + values: + - test + topologyKey: failure-domain.beta.kubernetes.io/zone + + nodeSelector: {} + priorityClassName: "" + tolerations: [] + + ## Additional pod labels + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + podLabels: {} + + ## Extra Annotations to be added to pod + podAnnotations: {} + + livenessProbe: + initialDelaySeconds: 120 + timeoutSeconds: 5 + readinessProbe: + initialDelaySeconds: 30 + timeoutSeconds: 1 + + resources: {} + # limits: + # cpu: "100m" + # memory: "1024Mi" + # requests: + # cpu: "100m" + # memory: "1024Mi" + + ## WildFly CLI configurations. They all end up in the file 'keycloak.cli' configured in the configmap which is + ## executed on server startup. + cli: + nodeIdentifier: | + {{ .Files.Get "scripts/node-identifier.cli" }} + + logging: | + {{ .Files.Get "scripts/logging.cli" }} + + reverseProxy: | + {{ .Files.Get "scripts/reverse-proxy.cli" }} + + ha: | + {{ .Files.Get "scripts/ha.cli" }} + + datasource: | + {{ .Files.Get "scripts/datasource.cli" }} + + # Custom CLI script + custom: | + + ## Add additional volumes and mounts, e. g. for custom themes + extraVolumes: | + extraVolumeMounts: | + + ## Add additional ports, eg. for custom admin console + extraPorts: | + + podDisruptionBudget: {} + # maxUnavailable: 1 + # minAvailable: 1 + + service: + annotations: {} + # service.beta.kubernetes.io/aws-load-balancer-internal: "0.0.0.0/0" + + labels: {} + # key: value + + ## ServiceType + ## ref: https://kubernetes.io/docs/user-guide/services/#publishing-services---service-types + type: ClusterIP + + ## Optional static port assignment for service type NodePort. + # nodePort: 30000 + + port: 80 + + # Optional: jGroups port for high availability clustering + jgroupsPort: 7600 + + ## Ingress configuration. + ## ref: https://kubernetes.io/docs/user-guide/ingress/ + ingress: + enabled: false + path: / + + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + # ingress.kubernetes.io/affinity: cookie + + ## List of hosts for the ingress + hosts: + - keycloak.example.com + + ## TLS configuration + tls: [] + # - hosts: + # - keycloak.example.com + # secretName: tls-keycloak + + ## Persistence configuration + persistence: + # If true, the Postgres chart is deployed + deployPostgres: false + + # The database vendor. Can be either "postgres", "mysql", "mariadb", or "h2" + dbVendor: h2 + + ## The following values only apply if "deployPostgres" is set to "false" + + # Specifies an existing secret to be used for the database password + existingSecret: "" + + # The key in the existing secret that stores the password + existingSecretKey: password + + dbName: keycloak + dbHost: mykeycloak + dbPort: 5432 + dbUser: keycloak + + # Only used if no existing secret is specified. In this case a new secret is created + dbPassword: "" + +postgresql: + ### PostgreSQL User to create. + ## + postgresUser: keycloak + + ## PostgreSQL Password for the new user. + ## If not set, a random 10 characters password will be used. + ## + postgresPassword: "" + + ## PostgreSQL Database to create. + ## + postgresDatabase: keycloak + + ## Persistent Volume Storage configuration. + ## ref: https://kubernetes.io/docs/user-guide/persistent-volumes + ## + persistence: + ## Enable PostgreSQL persistence using Persistent Volume Claims. + ## + enabled: false + +test: + enabled: true + image: + repository: unguiculus/docker-python3-phantomjs-selenium + tag: v1 + pullPolicy: IfNotPresent + securityContext: + runAsUser: 1000 + fsGroup: 1000 + runAsNonRoot: true