├── .gitignore ├── Dockerfile ├── README.md ├── build.sh ├── compose.sh ├── docker-compose-ssl.yml ├── docker-compose.yml ├── k8s-ingress.yaml ├── k8s-keycloak.yaml ├── openshift-keycloak.yaml └── ssl.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | keycloak.jks 3 | data 4 | config -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM jboss/keycloak:6.0.1 2 | 3 | # setup SSL 4 | USER root 5 | RUN yum install xmlstarlet -y 6 | ADD keycloak.jks $JBOSS_HOME/standalone/configuration/ 7 | RUN chown jboss:jboss $JBOSS_HOME/standalone/configuration/keycloak.jks 8 | USER jboss 9 | 10 | # 1. add keycloak.jks to security realm 11 | RUN sed -i -e '0,/RE/s//&\n \n \n \n \n <\/ssl>\n <\/server-identities>\n <\/security-realm>\n/' $JBOSS_HOME/standalone/configuration/standalone-ha.xml 12 | 13 | # 2. remove https-listener 14 | RUN xmlstarlet ed -L -d "//*[local-name()='https-listener']" $JBOSS_HOME/standalone/configuration/standalone-ha.xml 15 | 16 | # 3. add https-listener with Undertow security realm 17 | RUN sed -i -e 's//&\n \n/' $JBOSS_HOME/standalone/configuration/standalone-ha.xml 18 | 19 | # 4. change host attribute to UndertowRealm 20 | RUN xmlstarlet ed -L -u "//*[local-name()='server']/*[local-name()='host']/*/@security-realm" -v "UndertowRealm" $JBOSS_HOME/standalone/configuration/standalone-ha.xml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | =keycloak-docker= 2 | ====== 3 | - Docker image for the Keycloak auth server ``6.0.1`` 4 | - Postgres support (instead of the default **h2**) 5 | - HTTPS (SSL) support, so **Keycloak** can be easily deployed to the cloud (EC2, Azure) or used locally 6 | 7 | ---- 8 | 9 | ## 1. Prerequisites 10 | - [Docker](https://gist.github.com/maslick/69291bd5ed649892fe1b) 11 | - [Docker-compose](https://gist.github.com/maslick/5f77efa8ba0f8df98548) 12 | 13 | ## 2.1. Installation (vanilla Keycloak image) 14 | SSL is achieved via using the reverse proxy (e.g. Nginx), but you should handle this yourself. This is recommended for production environments. 15 | ``` 16 | docker-compose up -d 17 | ``` 18 | 19 | ## 2.2. Installation (custom image with SSL support) 20 | ``` 21 | ./ssl.sh // self-signed certificate 22 | ./build.sh 23 | ./compose.sh 24 | ``` 25 | This will: 26 | - Generate a self-signed ssl certificate and deploy it to the keystore (see ``ssl.sh`` and [keycloak docs](https://www.keycloak.org/docs/latest/server_installation/index.html#enabling-ssl-https-for-the-keycloak-server) for more details) 27 | - Build the docker image 28 | - Run postgres and keycloak using ``docker-compose`` 29 | 30 | ## 3. Run 31 | Go to this address in your browser: 32 | ``` 33 | https://{your_host}/auth 34 | ``` 35 | Default password ``admin:admin`` can be changed in ``docker-compose.yml``: ``KEYCLOAK_USER``, ``KEYCLOAK_PASSWORD`` 36 | 37 | 38 | 39 | ## Third-party signed certificate 40 | 1. Get certificate from www.sslforfree.com 41 | ``` 42 | * ca_bundle.crt (root and intermediate certificates) 43 | * certificate.crt (public key) 44 | * private.key (private key) 45 | ``` 46 | 47 | 2. Create a java keystore (jks) from files acquired in step 1 48 | ``` 49 | // combine letsencrypt certificate with the issued certificate 50 | cat certificate.crt ca_bundle.crt > fullchain.pem 51 | 52 | // convert to PKCS12 store 53 | openssl pkcs12 -export -in fullchain.pem -inkey private.key -name auth.maslick.com -out fullchain_plus_key.p12 -password pass:secret 54 | 55 | // convert to java keystore 56 | keytool -importkeystore -deststorepass secret -destkeypass secret -destkeystore keycloak.jks -srckeystore fullchain_plus_key.p12 -srcstoretype PKCS12 -srcstorepass secret 57 | ``` 58 | 59 | 60 | ## Deployment to Openshift cluster 61 | 1. Create new project: 62 | ```zsh 63 | oc new-project test 64 | ``` 65 | 66 | 2. Create persistent database: 67 | ```zsh 68 | oc new-app -f https://raw.githubusercontent.com/openshift/origin/master/examples/db-templates/postgresql-persistent-template.json \ 69 | -p DATABASE_SERVICE_NAME=keycloak-db \ 70 | -p POSTGRESQL_USER=keycloak \ 71 | -p POSTGRESQL_PASSWORD=keycloak \ 72 | -p POSTGRESQL_DATABASE=keycloakdb 73 | ``` 74 | 75 | 3. Create a keycloak instance: 76 | ```zsh 77 | oc new-app -f openshift-keycloak.yaml \ 78 | -p KEYCLOAK_USER=admin \ 79 | -p KEYCLOAK_PASSWORD=admin \ 80 | -p NAMESPACE=test \ 81 | -p HOSTNAME_HTTP=keycloak.maslick.com 82 | ``` 83 | or directly from github: 84 | ```zsh 85 | oc new-app -f https://raw.githubusercontent.com/maslick/keycloak-docker/master/openshift-keycloak.yaml \ 86 | -p KEYCLOAK_USER=admin \ 87 | -p KEYCLOAK_PASSWORD=admin \ 88 | -p NAMESPACE=test \ 89 | -p HOSTNAME_HTTP=keycloak.maslick.com 90 | ``` 91 | 92 | P.S. ``HOSTNAME_HTTP`` is not mandatory. 93 | 94 | ## Deployment to Kubernetes cluster (GKE) 95 | 1. Follow [instructions](https://github.com/maslick/ingressario) on how to install ``Nginx-ingress`` controller and ``cert-manager`` to your GKE cluster (1-10). 96 | 97 | 2. Create db (optional) 98 | ``` 99 | k create ns keycloak 100 | helm install \ 101 | --name keycloakdb \ 102 | stable/postgresql \ 103 | --set "postgresqlUsername=keycloak" \ 104 | --set "postgresqlPassword=password" \ 105 | --set "postgresqlDatabase=keycloakdb" \ 106 | --namespace keycloak 107 | ``` 108 | 109 | 3. Deploy keycloak 110 | ``` 111 | k apply -f k8s-keycloak.yaml -n keycloak 112 | k expose deployment keycloak --target-port=8080 --type=NodePort -n keycloak 113 | k apply -f k8s-ingress.yaml -n keycloak 114 | ``` 115 | 116 | ## Deployment to k8s via helm charts 117 | * Install tiller: 118 | ``` 119 | k create serviceaccount tiller --namespace kube-system 120 | k create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller 121 | helm init --service-account tiller 122 | k get pods --namespace kube-system 123 | ``` 124 | 125 | * Install helm: 126 | ``` 127 | brew install kubernetes-helm 128 | ``` 129 | 130 | * Install [keycloak chart](https://github.com/codecentric/helm-charts/tree/master/charts/keycloak): 131 | ``` 132 | helm repo add codecentric https://codecentric.github.io/helm-charts 133 | helm install --name keycloak codecentric/keycloak \ 134 | --set keycloak.image.tag=6.0.1 \ 135 | --set keycloak.replicas=3 \ 136 | --set keycloak.username=admin \ 137 | --set keycloak.password=admin \ 138 | --set keycloak.persistence.deployPostgres=true \ 139 | --set keycloak.persistence.dbVendor=postgres \ 140 | --namespace keycloak 141 | ``` 142 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker build -t my_keycloak_image:6.0.1 . 4 | -------------------------------------------------------------------------------- /compose.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run containers 4 | docker-compose up -d keycloak -f docker-compose-my.yml 5 | docker-compose logs -f -------------------------------------------------------------------------------- /docker-compose-ssl.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | keycloak: 5 | image: my_keycloak_image:6.0.1 6 | restart: on-failure 7 | command: 8 | - "-b" 9 | - "0.0.0.0" 10 | - "-Dkeycloak.migration.action=import" 11 | - "-Dkeycloak.migration.provider=dir" 12 | - "-Dkeycloak.migration.dir=/config/" 13 | - "-Dkeycloak.migration.strategy=IGNORE_EXISTING" 14 | volumes: 15 | - ./config:/config/ 16 | environment: 17 | - KEYCLOAK_USER=admin 18 | - KEYCLOAK_PASSWORD=admin 19 | - DB_VENDOR=postgres 20 | - DB_USER=admin 21 | - DB_PASSWORD=password 22 | - DB_ADDR=keycloak-db 23 | - DB_PORT=5432 24 | - DB_DATABASE=keycloakdb 25 | ports: 26 | - "8089:8080" 27 | - "443:8443" 28 | depends_on: 29 | - keycloak-db 30 | 31 | keycloak-db: 32 | image: postgres:10 33 | environment: 34 | POSTGRES_USER: admin 35 | POSTGRES_PASSWORD: password 36 | POSTGRES_DB: keycloakdb 37 | volumes: 38 | - pgdata:/var/lib/postgresql/data 39 | 40 | volumes: 41 | pgdata: -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | keycloak: 5 | image: jboss/keycloak:6.0.1 6 | restart: on-failure 7 | command: 8 | - "-b" 9 | - "0.0.0.0" 10 | - "-Dkeycloak.migration.action=import" 11 | - "-Dkeycloak.migration.provider=dir" 12 | - "-Dkeycloak.migration.dir=/config/" 13 | - "-Dkeycloak.migration.strategy=IGNORE_EXISTING" 14 | volumes: 15 | - ./config:/config/ 16 | environment: 17 | - KEYCLOAK_USER=admin 18 | - KEYCLOAK_PASSWORD=admin 19 | - DB_VENDOR=postgres 20 | - DB_USER=admin 21 | - DB_PASSWORD=password 22 | - DB_ADDR=keycloak-db 23 | - DB_PORT=5432 24 | - DB_DATABASE=keycloakdb 25 | ports: 26 | - "8089:8080" 27 | - "443:8443" 28 | depends_on: 29 | - keycloak-db 30 | 31 | keycloak-db: 32 | image: postgres:10 33 | restart: on-failure 34 | environment: 35 | POSTGRES_USER: admin 36 | POSTGRES_PASSWORD: password 37 | POSTGRES_DB: keycloakdb 38 | volumes: 39 | - keycloak-vol:/var/lib/postgresql/data 40 | 41 | volumes: 42 | keycloak-vol: -------------------------------------------------------------------------------- /k8s-ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Ingress 3 | metadata: 4 | name: keycloak-ingress 5 | labels: 6 | project: web 7 | annotations: 8 | kubernetes.io/tls-acme: "true" 9 | kubernetes.io/ingress.class: "nginx" 10 | certmanager.k8s.io/cluster-issuer: "letsencrypt-prod" 11 | spec: 12 | rules: 13 | - host: auth.maslick.ru 14 | http: 15 | paths: 16 | - path: / 17 | backend: 18 | serviceName: keycloak 19 | servicePort: 8080 20 | - path: /auth 21 | backend: 22 | serviceName: keycloak 23 | servicePort: 8080 24 | tls: 25 | - secretName: tls-keycloak-cert 26 | hosts: 27 | - 'auth.maslick.ru' -------------------------------------------------------------------------------- /k8s-keycloak.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: keycloak 5 | labels: 6 | name: keycloak 7 | app: keycloak 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: keycloak 13 | template: 14 | metadata: 15 | name: keycloak 16 | labels: 17 | app: keycloak 18 | name: keycloak 19 | spec: 20 | restartPolicy: Always 21 | containers: 22 | - name: keycloak 23 | image: jboss/keycloak:6.0.1 24 | imagePullPolicy: IfNotPresent 25 | ports: 26 | - containerPort: 8080 27 | protocol: TCP 28 | resources: 29 | requests: 30 | cpu: 200m 31 | memory: 512Mi 32 | limits: 33 | cpu: 1 34 | memory: 1024Mi 35 | env: 36 | - name: KEYCLOAK_USER 37 | value: "admin" 38 | - name: KEYCLOAK_PASSWORD 39 | value: "admin" 40 | - name: DB_VENDOR 41 | value: "postgres" 42 | - name: DB_USER 43 | value: "keycloak" 44 | - name: DB_PASSWORD 45 | value: "password" 46 | - name: DB_ADDR 47 | value: "keycloakdb-postgresql" 48 | - name: DB_PORT 49 | value: "5432" 50 | - name: DB_DATABASE 51 | value: "keycloakdb" 52 | - name: PROXY_ADDRESS_FORWARDING 53 | value: "true" -------------------------------------------------------------------------------- /openshift-keycloak.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Template 3 | apiVersion: v1 4 | metadata: 5 | name: keycloak-https 6 | annotations: 7 | iconClass: icon-sso 8 | tags: keycloak 9 | version: 6.0.1 10 | openshift.io/display-name: Keycloak 6.0.1 11 | description: Keycloak server with HTTPS 12 | parameters: 13 | - displayName: Application Name 14 | description: The name for the application. 15 | name: APPLICATION_NAME 16 | value: keycloak 17 | required: true 18 | - displayName: Keycloak Administrator Username 19 | description: Keycloak Server administrator username 20 | name: KEYCLOAK_USER 21 | from: "[a-zA-Z0-9]{8}" 22 | generate: expression 23 | required: true 24 | - displayName: Keycloak Administrator Password 25 | description: Keycloak Server administrator password 26 | name: KEYCLOAK_PASSWORD 27 | from: "[a-zA-Z0-9]{8}" 28 | generate: expression 29 | required: true 30 | - displayName: Custom http Route Hostname 31 | description: 'Custom hostname for http service route. Leave blank for default hostname, 32 | e.g.: ..' 33 | name: HOSTNAME_HTTP 34 | required: false 35 | - displayName: Namespace used for DNS discovery 36 | description: This namespace is a part of DNS query sent to Kubernetes API. This 37 | query allows the DNS_PING protocol to extract cluster members. This parameter 38 | might be removed once https://issues.jboss.org/browse/JGRP-2292 is implemented. 39 | name: NAMESPACE 40 | required: true 41 | - displayName: Use HTTPS instead of HTTP 42 | description: Use HTTPS instead of HTTP 43 | name: KEYCLOAK_HTTPS 44 | value: 'true' 45 | - displayName: Database name 46 | description: Database name 47 | name: DB_DATABASE 48 | value: keycloakdb 49 | - displayName: Db username 50 | description: Db username 51 | name: DB_USER 52 | value: keycloak 53 | - displayName: Db password 54 | description: Db password 55 | name: DB_PASSWORD 56 | value: keycloak 57 | - displayName: Db vendor 58 | description: Db vendor 59 | name: DB_VENDOR 60 | value: POSTGRES 61 | - displayName: Db address 62 | description: Db address (will be service name e.g. keycloak-db.auth.svc) 63 | name: DB_ADDR 64 | value: keycloak-db 65 | - displayName: Db port 66 | description: Db port 67 | name: DB_PORT 68 | value: '5432' 69 | - displayName: Use reverse proxy 70 | description: Set to true if using HTTPS through reverse proxy 71 | name: PROXY_ADDRESS_FORWARDING 72 | value: 'true' 73 | objects: 74 | - kind: Service 75 | apiVersion: v1 76 | spec: 77 | ports: 78 | - protocol: TCP 79 | port: 8080 80 | targetPort: 8080 81 | nodePort: 0 82 | selector: 83 | deploymentConfig: "${APPLICATION_NAME}" 84 | type: ClusterIP 85 | sessionAffinity: None 86 | metadata: 87 | name: "${APPLICATION_NAME}" 88 | labels: 89 | application: "${APPLICATION_NAME}" 90 | annotations: 91 | description: The web server's http port. 92 | - kind: Route 93 | apiVersion: v1 94 | id: "${APPLICATION_NAME}-http" 95 | metadata: 96 | name: "${APPLICATION_NAME}" 97 | labels: 98 | application: "${APPLICATION_NAME}" 99 | annotations: 100 | description: Route for application's http service. 101 | spec: 102 | tls: 103 | termination: edge 104 | insecureEdgeTerminationPolicy: Allow 105 | host: "${HOSTNAME_HTTP}" 106 | to: 107 | kind: Service 108 | name: "${APPLICATION_NAME}" 109 | - kind: DeploymentConfig 110 | apiVersion: v1 111 | metadata: 112 | name: "${APPLICATION_NAME}" 113 | labels: 114 | application: "${APPLICATION_NAME}" 115 | spec: 116 | strategy: 117 | type: Recreate 118 | triggers: 119 | - type: ConfigChange 120 | replicas: 1 121 | selector: 122 | deploymentConfig: "${APPLICATION_NAME}" 123 | template: 124 | metadata: 125 | name: "${APPLICATION_NAME}" 126 | labels: 127 | deploymentConfig: "${APPLICATION_NAME}" 128 | application: "${APPLICATION_NAME}" 129 | spec: 130 | containers: 131 | - name: "${APPLICATION_NAME}" 132 | image: jboss/keycloak:6.0.1 133 | livenessProbe: 134 | failureThreshold: 3 135 | httpGet: 136 | path: "/auth/realms/master" 137 | port: 8080 138 | scheme: HTTP 139 | initialDelaySeconds: 60 140 | ports: 141 | - containerPort: 8080 142 | protocol: TCP 143 | readinessProbe: 144 | httpGet: 145 | path: "/auth/realms/master" 146 | port: 8080 147 | scheme: HTTP 148 | initialDelaySeconds: 30 149 | failureThreshold: 10 150 | env: 151 | - name: KEYCLOAK_HTTPS 152 | value: "${KEYCLOAK_HTTPS}" 153 | - name: KEYCLOAK_USER 154 | value: "${KEYCLOAK_USER}" 155 | - name: KEYCLOAK_PASSWORD 156 | value: "${KEYCLOAK_PASSWORD}" 157 | - name: JGROUPS_DISCOVERY_PROTOCOL 158 | value: dns.DNS_PING 159 | - name: JGROUPS_DISCOVERY_PROPERTIES 160 | value: dns_query=${APPLICATION_NAME}.${NAMESPACE}.svc.cluster.local 161 | - name: DB_DATABASE 162 | value: "${DB_DATABASE}" 163 | - name: DB_USER 164 | value: "${DB_USER}" 165 | - name: DB_PASSWORD 166 | value: "${DB_PASSWORD}" 167 | - name: DB_VENDOR 168 | value: "${DB_VENDOR}" 169 | - name: DB_ADDR 170 | value: "${DB_ADDR}" 171 | - name: DB_PORT 172 | value: "${DB_PORT}" 173 | - name: PROXY_ADDRESS_FORWARDING 174 | value: "${PROXY_ADDRESS_FORWARDING}" 175 | securityContext: 176 | privileged: false 177 | -------------------------------------------------------------------------------- /ssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -f keycloak.jks 4 | 5 | function genkey { 6 | keytool -genkey -noprompt \ 7 | -alias keycloak-$1 \ 8 | -dname "CN=$1, OU=LIIS, O=FRI, L=Pavel, S=Maslov, C=SI" \ 9 | -keyalg RSA \ 10 | -keystore keycloak.jks \ 11 | -storepass secret \ 12 | -keypass secret \ 13 | -validity 10950 14 | } 15 | 16 | genkey auth.maslick.com 17 | --------------------------------------------------------------------------------