From f7a6decce68b361a7572c001a51a66184c53e07c Mon Sep 17 00:00:00 2001 From: David Young Date: Fri, 10 May 2019 23:04:45 +1200 Subject: [PATCH] Add Traefik Forward Auth --- .../ha-docker-swarm/traefik-forward-auth.md | 110 ++++++++++++++++++ manuscript/ha-docker-swarm/traefik.md | 8 +- manuscript/recipes/bookstack.md | 2 +- mkdocs.yml | 3 +- 4 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 manuscript/ha-docker-swarm/traefik-forward-auth.md diff --git a/manuscript/ha-docker-swarm/traefik-forward-auth.md b/manuscript/ha-docker-swarm/traefik-forward-auth.md new file mode 100644 index 0000000..381eb85 --- /dev/null +++ b/manuscript/ha-docker-swarm/traefik-forward-auth.md @@ -0,0 +1,110 @@ +# Traefik Forward Auth + +Now that we have Traefik deployed, automatically exposing SSL access to our Docker Swarm services using LetsEncrypt wildcard certificates, let's pause to consider that we may not _want_ some services exposed directly to the internet... + +..Wait, why not? Well, Traefik doesn't provide any form of authentication, it simply secures the **transmission** of the service between Docker Swarm and the end user. If you were to deploy a service with no native security (*[Radarr](/recipes/autopirate/radarr/) or [Sonarr](/recipes/autopirate/sonarr/) come to mind*), then anybody would be able to use it! Even services which _may_ have a layer of authentication **might** not be safe to expose publically - often open source projects may be maintained by enthusiasts who happily add extra features, but just pay lip service to security, on the basis that "*it's the user's problem to secure it in their own network*". + +To give us confidence that **we** can access our services, but BadGuys(tm) cannot, we'll deploy a layer of authentication **in front** of Traefik, using [Forward Authentication](https://docs.traefik.io/configuration/entrypoints/#forward-authentication). + +## Ingredients + +1. [Docker swarm cluster](/ha-docker-swarm/design/) with [persistent shared storage](/ha-docker-swarm/shared-storage-ceph) +2. [Traefik](/ha-docker-swarm/traefik/) configured per design +3. An account with an OpenID-Connect provider (Google, [KeyCloak](/recipes/keycloak/), Microsoft, etc..) + +## Preparation + +### Create OAuth credentials + +!!! note + This recipe will demonstrate using Google OAuth for traefik forward authentication, but it's also possible to use a self-hosted KeyCloak instance - see the [KeyCloak](/recipes/keycloak/) recipe for more details! + +Log into https://console.developers.google.com/, create a new project then search for and select "Credentials" in the search bar. + +Fill out the "OAuth Consent Screen" tab, and then click, "**Create Credentials**" > "**OAuth client ID**". Select "**Web Application**", fill in the name of your app, skip "**Authorized JavaScript origins**" and fill "**Authorized redirect URIs**" with either all the domains you will allow authentication from, appended with the url-path (*e.g. https://radarr.example.com/_oauth, https://radarr.example.com/_oauth, etc*), or if you don't like frustration, use a "auth host" URL instead, like "*https://auth.example.com/_oauth*" (see below for details) + +Store your client ID and secret safely - you'll need them for the next step. + + +### Prepare environment + +Create `traefik-forward-auth.env` as follows: + +``` +CLIENT_ID= +CLIENT_SECRET= +OIDC_ISSUER=https://accounts.google.com +SECRET= +# uncomment this to use a single auth host instead of individual redirect_uris (recommended but advanced) +#AUTH_HOST=auth.example.com +COOKIE_DOMAINS=example.com +``` + +### Prepare the docker service config + +This is a small container, you can simply add the following content to the existing `traefik.yml` deployed in the previous [Traefik](/recipes/traefik/) recipe: + +``` + traefik-forward-auth: + image: thomseddon/traefik-forward-auth + env_file: /var/data/config/keycloak/traefik-forward-auth.env + networks: + - public + # Uncomment these lines if you're using auth host mode + #deploy: + # labels: + # - traefik.port=4181 + # - traefik.frontend.rule=Host:auth.example.com + # - traefik.frontend.auth.forward.address=http://traefik-forward-auth:4181 + # - traefik.frontend.auth.forward.trustForwardHeader=true +``` + +If you're not confident that forward authentication is working, add a simple "whoami" test container, to help debug traefik forward auth, before attempting to add it to a more complex container. + +``` + # This simply validates that traefik forward authentication is working + whoami: + image: containous/whoami + networks: + - public + deploy: + labels: + - traefik.frontend.rule=Host:whoami.example.com + - traefik.port=80 + - traefik.frontend.auth.forward.address=http://traefik-forward-auth:4181 + - traefik.frontend.auth.forward.authResponseHeaders=X-Forwarded-User + - traefik.frontend.auth.forward.trustForwardHeader=true +``` + +!!! 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``` 👍 + + + +## Serving + +### Launch + +Redeploy traefik with ```docker stack deploy traefik -c /var/data/traefik/docker-compose.yml```, to launch the traefik-forward-auth container. + +### Test + +Browse to https://whoami.example.com (*obviously, customized for your domain and having created a DNS record*), and all going according to plan, you should be redirected to a Google login. Once successfully logged in, you'll be directed to the basic whoami page. + +### Dessert + +What have we achieved? By adding an additional three simple labels to any service, we can secure any service behind our choice of OAuth provider, with minimal processing / handling overhead. + + +## Chef's Notes + +1. Traefik forward auth replaces the use of [oauth_proxy containers](/reference/oauth_proxy/) found in some of the existing recipes +2. [@thomaseddon's original version](https://github.com/thomseddon/traefik-forward-auth) of traefik-forward-auth only works with Google currently, but I've created a [fork](https://www.github.com/funkypenguin/traefik-forward-auth) of a [fork](https://github.com/noelcatt/traefik-forward-auth), which implements generic OIDC providers. +3. I reviewed several implementations of forward authenticators for Traefik, but found most to be rather heavy-handed, or specific to a single auth provider. @thomaseddon's go-based docker image is 7MB in size, and with the generic OIDC patch (above), it can be extended to work with any OIDC provider. +4. No, not github natively, but you can ferderate GitHub into KeyCloak, and then use KeyCloak as the OIDC provider. + +### Tip your waiter (donate) 👏 + +Did you receive excellent service? Want to make your waiter happy? (_..and support development of current and future recipes!_) See the [support](/support/) page for (_free or paid)_ ways to say thank you! 👏 + +### Your comments? 💬 diff --git a/manuscript/ha-docker-swarm/traefik.md b/manuscript/ha-docker-swarm/traefik.md index d366f6d..09f46a6 100644 --- a/manuscript/ha-docker-swarm/traefik.md +++ b/manuscript/ha-docker-swarm/traefik.md @@ -55,8 +55,10 @@ acmeLogging = true onDemand = true OnHostRule = true +# Request wildcard certificates per https://docs.traefik.io/configuration/acme/#wildcard-domains [[acme.domains]] - main = "" + main = "*.example.com" + sans = ["example.com"] # Redirect all HTTP to HTTPS (why wouldn't you?) [entryPoints] @@ -74,7 +76,7 @@ watch = true [docker] endpoint = "tcp://127.0.0.1:2375" -domain = "" +domain = "example.com" watch = true swarmmode = true ``` @@ -94,7 +96,7 @@ version: "3" services: traefik: image: traefik - command: --web --docker --docker.swarmmode --docker.watch --docker.domain=funkypenguin.co.nz --logLevel=DEBUG + command: --web --docker --docker.swarmmode --docker.watch --docker.domain=example.com --logLevel=DEBUG ports: - target: 80 published: 80 diff --git a/manuscript/recipes/bookstack.md b/manuscript/recipes/bookstack.md index 1180912..22fcc93 100644 --- a/manuscript/recipes/bookstack.md +++ b/manuscript/recipes/bookstack.md @@ -13,7 +13,7 @@ I like to protect my public-facing web UIs with an [oauth_proxy](/reference/oaut ## Ingredients 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 +2. [Traefik](/ha-docker-swarm/traefik/) configured per design 3. DNS entry for the hostname you intend to use, pointed to your [keepalived](ha-docker-swarm/keepalived/) IP ## Preparation diff --git a/mkdocs.yml b/mkdocs.yml index e8b8da8..f247c1f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -33,6 +33,7 @@ nav: - Keepalived: ha-docker-swarm/keepalived.md - Docker Swarm Mode: ha-docker-swarm/docker-swarm-mode.md - Traefik: ha-docker-swarm/traefik.md + - Traefik Forward Auth: ha-docker-swarm/traefik-forward-auth.md - Registry: ha-docker-swarm/registry.md - Mail Server: recipes/mail.md - Duplicity: recipes/duplicity.md @@ -69,6 +70,7 @@ nav: - iBeacon: recipes/homeassistant/ibeacon.md - Huginn: recipes/huginn.md - Kanboard: recipes/kanboard.md + - KeyCloak: recipes/keycloak.md - Miniflux: recipes/miniflux.md - Munin: recipes/munin.md - NextCloud: recipes/nextcloud.md @@ -104,7 +106,6 @@ nav: - GitLab Runner: recipes/gitlab-runner.md - Gollum: recipes/gollum.md - InstaPy: recipes/instapy.md - - KeyCloak: recipes/keycloak.md - Minio: recipes/minio.md - OpenLDAP: recipes/openldap.md - Piwik: recipes/piwik.md