diff --git a/manuscript/CHANGELOG.md b/manuscript/CHANGELOG.md index 3949b02..d4f4f65 100644 --- a/manuscript/CHANGELOG.md +++ b/manuscript/CHANGELOG.md @@ -15,6 +15,7 @@ ## Recently added recipes +* Added [KeyCloak](/recipes/keycloak), an open source identity and access management solution which backends neatly into [OpenLDAP](/recipes/openldap/) (among other providers), providing true SSO (_13 Dec 2018_) * Added [OpenLDAP](/recipes/openldap/), a 20-year old project which [refuses to die](https://www.youtube.com/watch?v=cnQEo4bazIo), underpinning many of today's authentication platforms, and providing a single authentication backend for multiple recipes (_9 Dec 2018_) * Added [Wetty](/recipes/wetty/), a remote terminal client in your web browser (_22 Nov 2018_) * Added [PrivateBin](/recipes/privatebin/), a self-hosted pastebin alternative (_5 Nov 2018_) diff --git a/manuscript/images/keycloak.png b/manuscript/images/keycloak.png new file mode 100644 index 0000000..2e6a6c9 Binary files /dev/null and b/manuscript/images/keycloak.png differ diff --git a/manuscript/recipes/sso-stack/keycloak.md b/manuscript/recipes/keycloak.md similarity index 57% rename from manuscript/recipes/sso-stack/keycloak.md rename to manuscript/recipes/keycloak.md index 3f75015..35547ff 100644 --- a/manuscript/recipes/sso-stack/keycloak.md +++ b/manuscript/recipes/keycloak.md @@ -1,7 +1,11 @@ # KeyCloak -!!! warning - While this could stand on its own as a standalone recipe, it's a component of the [sso-stack](/recipes/sso-stack/) "_uber-recipe_", and is written in the expectation that the entire SSO stack is being deployed. +[KeyCloak](https://www.keycloak.org/) is "an open source identity and access management solution." Using a local database, or a variety of backends (_think [OpenLDAP](/recipes/openldap/)_), you can provide Single Sign-On (SSO) using OpenID, OAuth 2.0, and SAML. + +!!! important + Development of this recipe is sponsored by [The Common Observatory](https://www.observe.global/). Thanks guys! + + [![Common Observatory](../images/common_observatory.png)](https://www.observe.global/) ![KeyCloak Screenshot](../images/keycloak.png) @@ -9,7 +13,7 @@ 1. [Docker swarm cluster](/ha-docker-swarm/design/) with [persistent shared storage](/ha-docker-swarm/shared-storage-ceph.md) 2. [Traefik](/ha-docker-swarm/traefik_public) configured per design -3. DNS entry for the hostname you intend to use, pointed to your [keepalived](ha-docker-swarm/keepalived/) IP +3. DNS entry for the hostname (_i.e. "keycloak.your-domain.com"_) you intend to use for LDAP Account Manager, pointed to your [keepalived](ha-docker-swarm/keepalived/) IP ## Preparation @@ -24,12 +28,13 @@ mkdir /var/data/keycloak/database-dump ### Prepare environment -Create /var/data/keycloak/keycloak.env, and populate with the following variables, customized for your own domain struction. Take care with LDAP_DOMAIN, this is core to the rest of the [sso-stack](/recipes/sso-stack/), and can't easily be changed later. +Create ```/var/data/keycloak/keycloak.env```, and populate with the following variables, customized for your own domain structure. + ``` # Technically, this could be auto-detected, but we prefer to be prescriptive DB_VENDOR=postgres DB_DATABASE=keycloak -DB_ADDR=db +DB_ADDR=keycloak-db DB_USER=keycloak DB_PASSWORD=myuberpassword KEYCLOAK_USER=admin @@ -39,7 +44,7 @@ KEYCLOAK_PASSWORD=ilovepasswords PROXY_ADDRESS_FORWARDING=true # What's our hostname? -KEYCLOAK_HOSTNAME=cloud.example.com +KEYCLOAK_HOSTNAME=keycloak.batcave.com # Tell Postgress what user/password to create POSTGRES_USER=keycloak @@ -49,7 +54,7 @@ POSTGRES_PASSWORD=myuberpassword Create /var/data/keycloak/keycloak-backup.env, and populate with the following, so that your database can be backed up to the filesystem, daily: ``` -PGHOST=db +PGHOST=keycloak-db PGUSER=keycloak PGPASSWORD=myuberpassword BACKUP_NUM_KEEP=7 @@ -69,28 +74,31 @@ services: keycloak: image: jboss/keycloak env_file: /var/data/config/keycloak/keycloak.env + volumes: + - /etc/localtime:/etc/localtime:ro networks: - traefik_public deploy: labels: - - traefik.frontend.rule=Host:keycloak.cloud.example.com + - traefik.frontend.rule=Host:keycloak.batcave.com - traefik.port=8080 - traefik.docker.network=traefik_public - db: + keycloak-db: env_file: /var/data/config/keycloak/keycloak.env image: postgres:10.1 volumes: - /var/data/runtime/keycloak/database:/var/lib/postgresql/data + - /etc/localtime:/etc/localtime:ro networks: - traefik_public - db-backup: + keycloak-db-backup: image: postgres:10.1 env_file: /var/data/config/keycloak/keycloak-backup.env volumes: - /var/data/keycloak/database-dump:/dump -# - /etc/localtime:/etc/localtime:ro + - /etc/localtime:/etc/localtime:ro entrypoint: | bash -c 'bash -s <_internal network. This avoids IP/gateway conflicts which can otherwise occur when you're creating/removing stacks a lot. See [my list](/reference/networks/) here. + + However, KeyCloak's JBOSS startup script assumes a single interface, and will crash in a ball of 🔥 if you try to assign multiple interfaces to the container. This means that we can't use a "keycloak_internal" network for our supporting containers. This is why unlike our other recipes, all the supporting services are prefixed with "keycloak-". ## Serving -### Launch OpenLDAP stack +### Launch KeyCloak stack Launch the OpenLDAP stack by running ```docker stack deploy keycloak -c ``` Log into your new instance at https://**YOUR-FQDN**, and login with the user/password you defined in keycloak.env. +### Integrating into OpenLDAP + +KeyCloak gets really sexy when you integrate it into your [OpenLDAP](/recipes/openldap/) stack (_also, it's great not to have to play with ugly LDAP tree UIs_). + +You'll need to have completed the [OpenLDAP](/recipes/openldap/) recipe + You start in the "Master" realm - but mouseover the realm name, to a dropdown box allowing you add an new realm: ![KeyCloak Add Realm Screenshot](/images/sso-stack-keycloak-1.png) @@ -139,16 +150,16 @@ Once in the desired realm, click on **User Federation**, and click **Add Provide * **Edit Mode** : Writeable * **Vendor** : Other * **Connection URL** : ldap://openldap -* **Users DN** : ou=People, +* **Users DN** : ou=People, * **Authentication Type** : simple -* **Bind DN** : cn=admin, -* **Bind Credential** : +* **Bind DN** : cn=admin, +* **Bind Credential** : Save your changes, and then navigate back to "User Federation" > Your LDAP name > Mappers: ![KeyCloak Add Realm Screenshot](/images/sso-stack-keycloak-3.png) -For each of the following names, click the name, and set the "_Read Only_" flag to "_Off_" (_this enables 2-way sync between KeyCloak and OpenLD_AP) +For each of the following mappers, click the name, and set the "_Read Only_" flag to "_Off_" (_this enables 2-way sync between KeyCloak and OpenLDAP_) * last name * username @@ -157,11 +168,15 @@ For each of the following names, click the name, and set the "_Read Only_" flag ![KeyCloak Add Realm Screenshot](/images/sso-stack-keycloak-4.png) -Proceed to setting up [Email](/recipes/sso-stack/docker-mailserver/)... +!!! important + Development of this recipe is sponsored by [The Common Observatory](https://www.observe.global/). Thanks guys! + + [![Common Observatory](../images/common_observatory.png)](https://www.observe.global/) + ## Chef's Notes -1. I wanted to be able to add multiple networks to KeyCloak (i.e., a dedicated overlay network for LDAP authentication), but the entrypoint used by the container produces an error when more than one network is configured. This could theoretically be corrected in future, with a PR. +1. I wanted to be able to add multiple networks to KeyCloak (_i.e., a dedicated overlay network for LDAP authentication_), but the entrypoint used by the container produces an error when more than one network is configured. This could theoretically be corrected in future, with a PR, but the [GitHub repo](https://github.com/jboss-dockerfiles/keycloak) has no issues enabled, so I wasn't sure where to start. ### Tip your waiter (donate) 👏 diff --git a/manuscript/recipes/munin.md b/manuscript/recipes/munin.md index 6172d2b..be55f96 100644 --- a/manuscript/recipes/munin.md +++ b/manuscript/recipes/munin.md @@ -94,7 +94,7 @@ services: - /var/data/munin/cache:/var/cache/munin proxy: - image: a5huynh/oauth2_proxy + image: funkypenguin/oauth2_proxy env_file: /var/data/config/munin/munin.env networks: - traefik_public diff --git a/manuscript/recipes/openldap.md b/manuscript/recipes/openldap.md index 4b28826..7315d22 100644 --- a/manuscript/recipes/openldap.md +++ b/manuscript/recipes/openldap.md @@ -45,7 +45,7 @@ mkdir /var/data/runtime/openldap/ ### Prepare environment -Create /var/data/openldap/openldap.env, and populate with the following variables, customized for your own domain structure. Take care with LDAP_DOMAIN, this is core to your directory strucutre, and can't easily be changed later. +Create /var/data/openldap/openldap.env, and populate with the following variables, customized for your own domain structure. Take care with LDAP_DOMAIN, this is core to your directory structure, and can't easily be changed later. ``` LDAP_DOMAIN=batcave.gotham @@ -53,12 +53,15 @@ LDAP_ORGANISATION=BatCave Inc LDAP_ADMIN_PASSWORD=supermansucks LDAP_TLS=false -# For the oauth2_proxy elements used to protect LAM +# Use these if you plan to protect the LDAP Account Manager webUI with an oauth_proxy OAUTH2_PROXY_CLIENT_ID= OAUTH2_PROXY_CLIENT_SECRET= OAUTH2_PROXY_COOKIE_SECRET= ``` +!!! note + I use an [OAuth proxy](/reference/oauth_proxy/) to protect access to the web UI, when the sensitivity of the protected data (i.e. my authentication store) warrants it, or if I don't necessarily trust the security of the webUI. + Create ```authenticated-emails.txt```, and populate with the email addresses (_matched to GitHub user accounts, in my case_) to which you want grant access, using OAuth2. ### Create config.cfg @@ -329,7 +332,7 @@ Create yours profile (_you chose a default profile in config.cfg above, remember ### Setup Docker Swarm -Create a docker swarm config file in docker-compose syntax (v3), something like this: +Create a docker swarm config file in docker-compose syntax (v3), something like this, at (```/var/data/config/openldap/openldap.yml```) !!! tip I share (_with my [patreon patrons](https://www.patreon.com/funkypenguin)_) a private "_premix_" git repository, which includes necessary docker-compose and env files for all published recipes. This means that patrons can launch any recipe with just a ```git pull``` and a ```docker stack deploy``` 👍 @@ -341,7 +344,8 @@ services: image: osixia/openldap env_file: /var/data/config/openldap/openldap.env networks: - - internal + - traefik_public + - auth_internal volumes: - /var/data/runtime/openldap/:/var/lib/ldap - /var/data/openldap/openldap/:/etc/ldap/slapd.d @@ -349,46 +353,75 @@ services: lam: image: jacksgt/ldap-account-manager networks: - - internal + - auth_internal volumes: - /var/data/openldap/lam/config/config.cfg:/var/www/html/config/config.cfg - /var/data/openldap/lam/config/batcave.conf:/var/www/html/config/batcave.conf - proxy: + lam-proxy: image: funkypenguin/oauth2_proxy env_file: /var/data/config/openldap/openldap.env networks: - traefik_public - - internal + - auth_internal deploy: labels: - - traefik.frontend.rule=Host:lam.batcave.gotham + - traefik.frontend.rule=Host:lam.batcave.com + - traefik.docker.network=traefik_public - traefik.port=4180 - volumes: - - /var/data/config/openldap/authenticated-emails.txt:/authenticated-emails.txt command: | -cookie-secure=false - -upstream=http://lam:8080 - -redirect-url=https://lam.batcave.gotham + -upstream=http://lam:80 + -redirect-url=https://lam.batcave.com -http-address=http://0.0.0.0:4180 - -email-domain=example.com + -email-domain=batcave.com -provider=github - -authenticated-emails-file=/authenticated-emails.txt networks: + # Used to expose lam-proxy to external access, and openldap to keycloak traefik_public: external: true + + # Used to expose openldap to other apps which want to talk to LDAP, including LAM + auth_internal: + external: true ``` -!!! note - Setup unique static subnets for every stack you deploy. This avoids IP/gateway conflicts which can otherwise occur when you're creating/removing stacks a lot. See [my list](/reference/networks/) here. +!!! warning + **Normally**, we set unique static subnets for every stack you deploy, and put the non-public facing components (like databases) in an dedicated _internal network. This avoids IP/gateway conflicts which can otherwise occur when you're creating/removing stacks a lot. See [my list](/reference/networks/) here. + + However, you're likely to want to use OpenLdap with KeyCloak, whose JBOSS startup script assumes a single interface, and will crash in a ball of 🔥 if you try to assign multiple interfaces to the container. + + Since we're going to want KeyCloak to be able to talk to OpenLDAP, we have no choice but to leave the OpenLDAP container on the "traefik_public" network. We can, however, create **another** overlay network (_auth_internal, see below_), add it to the openldap container, and use it to provide OpenLDAP access to our other stacks. + +Create **another** stack config file (```/var/data/config/openldap/auth.yml```) containing just the auth_internal network, and a dummy container: + +``` +version: '3' + +services: + helloworld: + image: hello-world + networks: + - internal + +networks: + internal: + driver: overlay + ipam: + config: + - subnet: 172.16.39.0/24 +``` + + + ## Serving ### Launch OpenLDAP stack -Launch the OpenLDAP stack by running ```docker stack deploy openldap -c ``` +Create the auth_internal overlay network, by running ```docker stack deploy auth -c /var/data/config/openldap/auth.yml`, then launch the OpenLDAP stack by running ```docker stack deploy openldap -c /var/data/config/openldap/openldap.yml``` Log into your new LAM instance at https://**YOUR-FQDN**. @@ -406,7 +439,7 @@ Create your users using the "**New User**" button. ## Chef's Notes -1. An upcoming recipe for [KeyCloak](https://www.keycloak.org/) will illustrate how to integrate KeyCloak with your LDAP directory. +1. The KeyCloak](/recipes/keycloak/) recipe illustrates how to integrate KeyCloak with your LDAP directory, giving you a cleaner interface to manage users, and a raft of SSO / OAuth features. ### Tip your waiter (donate) 👏 diff --git a/manuscript/reference/networks.md b/manuscript/reference/networks.md index ec29f3e..f73c05f 100644 --- a/manuscript/reference/networks.md +++ b/manuscript/reference/networks.md @@ -38,7 +38,7 @@ Network | Range [ElkarBackup](https://geek-cookbook.funkypenguin.co.nz/recipes/elkarbackp/) | 172.16.36.0/24 [Mayan EDMS](https://geek-cookbook.funkypenguin.co.nz/recipes/realms/) | 172.16.37.0/24 [Shaarli](https://geek-cookbook.funkypenguin.co.nz/recipes/shaarli/) | 172.16.38.0/24 -[KeyCloud](https://geek-cookbook.funkypenguin.co.nz/recipes/keycloak/) | 172.16.39.0/24 +[OpenLDAP](https://geek-cookbook.funkypenguin.co.nz/recipes/openldap/) | 172.16.39.0/24 [MatterMost](https://geek-cookbook.funkypenguin.co.nz/recipes/mattermost/) | 172.16.40.0/24 [PrivateBin](https://geek-cookbook.funkypenguin.co.nz/recipes/privatebin/) | 172.16.41.0/24 [Mayan EDMS](https://geek-cookbook.funkypenguin.co.nz/recipes/mayan-edms/) | 172.16.42.0/24 @@ -46,7 +46,6 @@ Network | Range [FlightAirMap](https://geek-cookbook.funkypenguin.co.nz/recipes/flightairmap/) |172.16.44.0/24 [Wetty](https://geek-cookbook.funkypenguin.co.nz/recipes/wetty/) | 172.16.45.0/24 [FileBrowser](https://geek-cookbook.funkypenguin.co.nz/recipes/filebrowser/) | 172.16.46.0/24 -[OpenLDAP](https://geek-cookbook.funkypenguin.co.nz/recipes/openldap/) | 172.16.47.0/24 ## Chef's Notes diff --git a/mkdocs.yml b/mkdocs.yml index 2c7be7e..65154a8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -85,6 +85,7 @@ pages: - GitLab Runner: recipes/gitlab-runner.md - Gollum: recipes/gollum.md - InstaPy: recipes/instapy.md + - KeyCloak: recipes/keycloak.md - OpenLDAP: recipes/openldap.md - Piwik: recipes/piwik.md - Portainer: recipes/portainer.md