1
0
mirror of https://github.com/funkypenguin/geek-cookbook/ synced 2025-12-13 01:36:23 +00:00

Update Traefik Forward Auth for v2 (#169)

This commit is contained in:
David Young
2021-01-29 23:09:22 +13:00
committed by GitHub
parent c72d3ae098
commit 892adb4704
75 changed files with 864 additions and 1198 deletions

View File

@@ -4,107 +4,50 @@ Now that we have Traefik deployed, automatically exposing SSL access to our Dock
..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). You can use your own [KeyCloak](/recipes/keycloak/) instance for authentication, but to lower the barrier to entry, this recipe will assume you're authenticating against your own Google account.
Some of the platforms we use on our swarm may have strong, proven security to prevent abuse. Techniques such as rate-limiting (*to defeat brute force attacks*) or even support 2-factor authentication (*tiny-tiny-rss or Wallabag support this)*.
## Ingredients
Other platforms may provide **no authentication** (Traefik's web UI for example), or minimal, un-proven UI authentication which may have been added as an afterthought.
!!! summary "Ingredients"
Existing:
Still other platforms may hold such sensitive data (*i.e., NextCloud*), that we'll feel more secure by putting an additional authentication layer in front of them.
* [X] [Docker swarm cluster](/ha-docker-swarm/design/) with [persistent shared storage](/ha-docker-swarm/shared-storage-ceph)
* [X] [Traefik](/ha-docker-swarm/traefik/) configured per design
This is the role of Traefik Forward Auth.
New:
## How does it work?
* [ ] Client ID and secret from an OpenID-Connect provider (Google, [KeyCloak](/recipes/keycloak/), Microsoft, etc..)
**Normally**, Traefik proxies web requests directly to individual web apps running in containers. The user talks directly to the webapp, and the webapp is responsible for ensuring appropriate authentication.
## Preparation
When employing Traefik Forward Auth as "[middleware](https://doc.traefik.io/traefik/middlewares/forwardauth/)", the forward-auth process sits in the middle of this transaction - traefik receives the incoming request, "checks in" with the auth server to determine whether or not further authentication is required. If the user is authenticated, the auth server returns a 200 response code, and Traefik is authorized to forward the request to the backend. If not, traefik passes the auth server response back to the user - this process will usually direct the user to an authentication provider (_GitHub, Google, etc_), so that they can perform a login.
### Obtain OAuth credentials
Illustrated below:
![Traefik Forward Auth](../images/traefik-forward-auth.png)
!!! 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 OIDC Provider](/recipes/keycloak/setup-oidc-provider/) recipe for more details!
The advantage under this design is additional security. If I'm deploying a web app which I expect only an authenticated user to require access to (*unlike something intended to be accessed publically, like [Linx](/recipes/linx/)*), I'll pass the request through Traefik Forward Auth. The overhead is negligible, and the additional layer of security is well-worth it.
Log into https://console.developers.google.com/, create a new project then search for and select "Credentials" in the search bar.
## What is AuthHost mode
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*)
Under normal OIDC auth, you have to tell your auth provider which URLs it may redirect an authenticated user back to, post-authentication. This is a security feture of the OIDC spec, preventing a malicious landing page from capturing your session and using it to impersonate you. When you're securing many URLs though, explicitly listing them can be a PITA.
!!! tip
Store your client ID and secret safely - you'll need them for the next step.
[@thomaseddon's traefik-forward-auth](https://github.com/thomseddon/traefik-forward-auth) includes an ingenious mechanism to simulate an "_auth host_" in your OIDC authentication, so that you can protect an unlimited amount of DNS names (_with a common domain suffix_), without having to manually maintain a list.
#### How does it work?
### Prepare environment
Say you're protecting **radarr.example.com**. When you first browse to **https://radarr.example.com**, Traefik forwards your session to traefik-forward-auth, to be authenticated. Traefik-forward-auth redirects you to your OIDC provider's login (_KeyCloak, in this case_), but instructs the OIDC provider to redirect a successfully authenticated session **back** to **https://auth.example.com/_oauth**, rather than to **https://radarr.example.com/_oauth**.
Create `/var/data/config/traefik-forward-auth/traefik-forward-auth.env` as follows:
When you successfully authenticate against the OIDC provider, you are redirected to the "_redirect_uri_" of https://auth.example.com. Again, your request hits Traefik, which forwards the session to traefik-forward-auth, which **knows** that you've just been authenticated (_cookies have a role to play here_). Traefik-forward-auth also knows the URL of your **original** request (_thanks to the X-Forwarded-Whatever header_). Traefik-forward-auth redirects you to your original destination, and everybody is happy.
```
GOOGLE_CLIENT_ID=<your client id>
GOOGLE_CLIENT_SECRET=<your client secret>
OIDC_ISSUER=https://accounts.google.com
SECRET=<a random string, make it up>
# 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
```
This clever workaround only works under 2 conditions:
### Prepare the docker service config
1. Your "auth host" has the same domain name as the hosts you're protecting (_i.e., auth.example.com protecting radarr.example.com_)
2. You explictly tell traefik-forward-auth to use a cookie authenticating your **whole** domain (_i.e. example.com_)
Create `/var/data/config/traefik-forward-auth/traefik-forward-auth.yml` as follows:
## Authentication Providers
```
traefik-forward-auth:
image: thomseddon/traefik-forward-auth:2.1.0
env_file: /var/data/config/traefik-forward-auth/traefik-forward-auth.env
networks:
- traefik_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
```
Traefik Forward Auth needs to authenticate an incoming user against a provider. A provider can be something as simple as a self-hosted [dex][tfa-dex] instance with a single static username/password, or as complex as a [KeyCloak][keycloak] instance backed by [OpenLDAP][openldap]. Here are some options, in increasing order of complexity...
If you're not confident that forward authentication is working, add a simple "whoami" test container to the above .yml, to help debug traefik forward auth, before attempting to add it to a more complex container.
* [Authenticate against a self-hosted Dex instance with static usernames and passwords][tfa-dex-static]
* [Authenticate against a whitelist of Google accounts][tfa-google]
* [Authenticate against a self-hosted KeyCloak instance][tfa-keycloak]
```
# This simply validates that traefik forward authentication is working
whoami:
image: containous/whoami
networks:
- traefik_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
```
--8<-- "recipe-footer.md"
--8<-- "premix-cta.md"
## Serving
### Launch
Redeploy traefik with ```docker stack deploy traefik-forward-auth -c /var/data/traefik-forward-auth/traefik-forward-auth.yml```, to launch the traefik-forward-auth stack.
### 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.
## Summary
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.
!!! summary "Summary"
Created:
* [X] Traefik-forward-auth configured to authenticate against an OIDC provider
[^1]: Traefik forward auth replaces the use of [oauth_proxy containers](/reference/oauth_proxy/) found in some of the existing recipes
[^2]: 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 can be extended to work with any OIDC provider.
--8<-- "recipe-footer.md"
[^1]: Authhost mode is specifically handy for Google authentication, since Google doesn't permit wildcard redirect_uris, like [KeyCloak][keycloak] does.

View File

@@ -0,0 +1,200 @@
# Using Traefik Forward Auth with Dex (Static)
[Traefik Forward Auth](/ha-docker-swarm/traefik-forward-auth/) is incredibly useful to secure services with an additional layer of authentication, provided by an OIDC-compatible provider. The simplest possible provider is a self-hosted instance of [CoreOS's Dex](https://github.com/dexidp/dex), configured with a static username and password. This recipe will "get you started" with Traefik Forward Auth, providing a basic authentication layer. In time, you might want to migrate to a "public" provider, like [Google][tfa-google], or GitHub, or to a [KeyCloak][keycloak] installation.
--8<-- "recipe-tfa-ingredients.md"
## Preparation
### Setup dex config
Create `/var/data/config/dex/config.yml` something like the following (*this is a bare-bones, [minimal example](https://github.com/dexidp/dex/blob/master/config.dev.yaml)*). At the very least, you want to replace all occurances of `example.com` with your own domain name. (*If you change nothing else, your ID is `foo`, your secret is `bar`, your username is `admin@yourdomain`, and your password is `password`*):
```yaml
# The base path of dex and the external name of the OpenID Connect service.
#
# This is the canonical URL that all clients MUST use to refer to dex. If a
# path is provided, dex's HTTP service will listen at a non-root URL.
issuer: https://dex.example.com
storage:
type: sqlite3
config:
file: var/sqlite/dex.db
web:
http: 0.0.0.0:5556
oauth2:
skipApprovalScreen: true
staticClients:
- id: foo
redirectURIs:
- 'https://auth.example.com/_oauth'
name: 'example.com'
secret: bar
staticPasswords:
- email: "admin@example.com"
# bcrypt hash of the string "password"
hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
username: "admin"
userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
```
### Prepare Traefik Forward Auth environment
Create `/var/data/config/traefik-forward-auth/traefik-forward-auth.env` as follows:
```
DEFAULT_PROVIDER: oidc
PROVIDERS_OIDC_CLIENT_ID: foo # This is the staticClients.id value in config.yml above
PROVIDERS_OIDC_CLIENT_SECRET: bar # This is the staticClients.secret value in config.yml above
PROVIDERS_OIDC_ISSUER_URL: https://dex.example.com # This is the issuer value in config.yml above, and it has to be reachable via a browser
SECRET: imtoosexyformyshorts # Make this up. It's not configured anywhere else
AUTH_HOST: auth.example.com # This should match the value of the traefik hosts labels in Traefik Forward Auth
COOKIE_DOMAIN: example.com # This should match your base domain
```
### Setup Docker Stack for Dex
Create a docker swarm config file in docker-compose syntax (v3), something like this:
--8<-- "premix-cta.md"
```yaml
version: '3'
services:
dex:
image: dexidp/dex
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/data/config/dex/config.yml:/config.yml:ro
networks:
- traefik_public
command: ['serve','/config.yml']
deploy:
labels:
# traefik
- traefik.enable=true
- traefik.docker.network=traefik_public
# traefikv1
- traefik.frontend.rule=Host:dex.example.com
- traefik.port=5556
- traefik.docker.network=traefik_public
# and for traefikv2:
- "traefik.http.routers.dex.rule=Host(`dex.example.com`)"
- "traefik.http.routers.dex.entrypoints=https"
- "traefik.http.services.dex.loadbalancer.server.port=5556"
networks:
traefik_public:
external: true
```
### Setup Docker Stack for Traefik Forward Auth
Now create a docker swarm config file in docker-compose syntax (v3), something like this:
```yaml
version: "3.2"
services:
traefik-forward-auth:
image: thomseddon/traefik-forward-auth:2.1.0
env_file: /var/data/config/traefik-forward-auth/traefik-forward-auth.env
volumes:
- /var/data/config/traefik-forward-auth/config.ini:/config.ini:ro
networks:
- traefik_public
deploy:
labels:
# traefikv1
- "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"
# traefikv2
- "traefik.docker.network=traefik_public"
- "traefik.http.routers.auth.rule=Host(`auth.example.com`)"
- "traefik.http.routers.auth.entrypoints=https"
- "traefik.http.routers.auth.tls=true"
- "traefik.http.routers.auth.tls.domains[0].main=example.com"
- "traefik.http.routers.auth.tls.domains[0].sans=*.example.com"
- "traefik.http.routers.auth.tls.certresolver=main"
- "traefik.http.routers.auth.service=auth@docker"
- "traefik.http.services.auth.loadbalancer.server.port=4181"
- "traefik.http.middlewares.forward-auth.forwardauth.address=http://traefik-forward-auth:4181"
- "traefik.http.middlewares.forward-auth.forwardauth.trustForwardHeader=true"
- "traefik.http.middlewares.forward-auth.forwardauth.authResponseHeaders=X-Forwarded-User"
- "traefik.http.routers.auth.middlewares=forward-auth"
# This simply validates that traefik forward authentication is working
whoami:
image: containous/whoami
networks:
- traefik_public
deploy:
labels:
# traefik
- "traefik.enable=true"
- "traefik.docker.network=traefik_public"
# traefikv1
- "traefik.frontend.rule=Host:whoami.example.com"
- "traefik.http.services.whoami.loadbalancer.server.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"
# traefikv2
- "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
- "traefik.http.routers.whoami.entrypoints=https"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
- "traefik.http.routers.whoami.middlewares=forward-auth"
networks:
traefik_public:
external: true
```
## Serving
### Launch
Deploy dex with `docker stack deploy dex -c /var/data/dex/dex.yml`, to launch dex, and then deploy Traefik Forward Auth with `docker stack deploy traefik-forward-auth -c /var/data/traefik-forward-auth/traefik-forward-auth.yml`
Once you redeploy traefik-forward-auth with the above, it **should** use dex as an OIDC provider, authenticating you against the `staticPasswords` username and hashed password described in `config.yml` above.
### 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'll be redirected to a CoreOS Dex login. Once successfully logged in, you'll be directed to the basic whoami page :thumbsup:
### Protect services
To protect any other service, ensure the service itself is exposed by Traefik (_if you were previously using an oauth_proxy for this, you may have to migrate some labels from the oauth_proxy serivce to the service itself_). Add the following label:
```yaml
- "traefik.http.routers.radarr.middlewares=forward-auth"
```
And re-deploy your services :)
## Summary
What have we achieved? By adding an additional label to any service, we can secure any service behind our (static) OIDC provider, with minimal processing / handling overhead.
!!! summary "Summary"
Created:
* [X] Traefik-forward-auth configured to authenticate against Dex (static)
[^1]: You can remove the `whoami` container once you know Traefik Forward Auth is working properly
--8<-- "recipe-footer.md"

View File

@@ -0,0 +1,130 @@
# Traefik Forward Auth using Google
[Traefik Forward Auth](/ha-docker-swarm/traefik-forward-auth/) is incredibly useful to secure services with an additional layer of authentication, provided by an OIDC-compatible provider. The simplest possible provider is a self-hosted instance of [Dex][dex], configured with a static username and password. This is not much use if you want to provide "normies" access to your services though - a better solution would be to validate their credentials against an existing trusted public source.
This recipe will illustrate how to point Traefik Forward Auth to Google, confirming that the requestor has a valid Google account (*and that said account is permitted to access your services!*)
--8<-- "recipe-tfa-ingredients.md"
## Preparation
### Obtain OAuth credentials
#### TL;DR
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*)
#### Monkey see, monkey do 🙈
Here's a [screencast I recorded](https://static.funkypenguin.co.nz/2021/screencast_2021-01-29_22-29-33.gif) of the OIDC credentias setup in Google Developer Console
!!! tip
Store your client ID and secret safely - you'll need them for the next step.
### Prepare environment
Create `/var/data/config/traefik-forward-auth/traefik-forward-auth.env` as follows:
```
PROVIDERS_GOOGLE_CLIENT_ID=<your client id>
PROVIDERS_GOOGLE_CLIENT_SECRET=<your client secret>
SECRET=<a random string, make it up>
# comment out AUTH_HOST if you'd rather use individual redirect_uris (slightly less complicated but more work)
AUTH_HOST=auth.example.com
COOKIE_DOMAINS=example.com
WHITELIST=you@yourdomain.com, me@mydomain.com
```
### Prepare the docker service config
Create `/var/data/config/traefik-forward-auth/traefik-forward-auth.yml` as follows:
```
traefik-forward-auth:
image: thomseddon/traefik-forward-auth:2.1.0
env_file: /var/data/config/traefik-forward-auth/traefik-forward-auth.env
networks:
- traefik_public
deploy:
labels # you only need these if you're using an auth host
# traefik
- traefik.enable=true
- traefik.docker.network=traefik_public
# traefikv1
- "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"
# traefikv2
- "traefik.docker.network=traefik_public"
- "traefik.http.routers.auth.rule=Host(`auth.example.com`)"
- "traefik.http.routers.auth.entrypoints=https"
- "traefik.http.routers.auth.tls=true"
- "traefik.http.routers.auth.tls.domains[0].main=example.com"
- "traefik.http.routers.auth.tls.domains[0].sans=*.example.com"
- "traefik.http.routers.auth.tls.certresolver=main"
- "traefik.http.routers.auth.service=auth@docker"
- "traefik.http.services.auth.loadbalancer.server.port=4181"
- "traefik.http.middlewares.forward-auth.forwardauth.address=http://traefik-forward-auth:4181"
- "traefik.http.middlewares.forward-auth.forwardauth.trustForwardHeader=true"
- "traefik.http.middlewares.forward-auth.forwardauth.authResponseHeaders=X-Forwarded-User"
- "traefik.http.routers.auth.middlewares=forward-auth"
```
If you're not confident that forward authentication is working, add a simple "whoami" test container to the above .yml, 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:
- traefik_public
deploy:
labels:
# traefik
- traefik.enable=true
- traefik.docker.network=traefik_public
# traefikv1
- traefik.frontend.rule=Host:whoami.example.com
- "traefik.http.services.linx.loadbalancer.server.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
# traefikv2
- "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
- "traefik.http.routers.whoami.entrypoints=https"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
- "traefik.http.routers.whoami.middlewares=forward-auth" # this line enforces traefik-forward-auth
```
--8<-- "premix-cta.md"
## Serving
### Launch
Deploy traefik-forward-auth with ```docker stack deploy traefik-forward-auth -c /var/data/traefik-forward-auth/traefik-forward-auth.yml```
### 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.
## Summary
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.
!!! summary "Summary"
Created:
* [X] Traefik-forward-auth configured to authenticate against an OIDC provider
[^1]: Be sure to populate `WHITELIST` in `traefik-forward-auth.env`, else you'll happily be granting **any** authenticated Google account access to your services!
--8<-- "recipe-footer.md"

View File

@@ -2,36 +2,10 @@
While the [Traefik Forward Auth](/ha-docker-swarm/traefik-forward-auth/) recipe demonstrated a quick way to protect a set of explicitly-specified URLs using OIDC credentials from a Google account, this recipe will illustrate how to use your own KeyCloak instance to secure **any** URLs within your DNS domain.
## Ingredients
!!! Summary
Existing:
* [X] [KeyCloak](/recipes/keycloak/) recipe deployed successfully, with a [local user](/recipes/keycloak/create-user/) and an [OIDC client](/recipes/keycloak/setup-oidc-provider/)
New:
* [ ] DNS entry for your auth host (*"auth.yourdomain.com" is a good choice*), pointed to your [keepalived](/ha-docker-swarm/keepalived/) IP
--8<-- "recipe-tfa-ingredients.md"
## Preparation
### What is AuthHost mode
Under normal OIDC auth, you have to tell your auth provider which URLs it may redirect an authenticated user back to, post-authentication. This is a security feture of the OIDC spec, preventing a malicious landing page from capturing your session and using it to impersonate you. When you're securing many URLs though, explicitly listing them can be a PITA.
[@thomaseddon's traefik-forward-auth](https://github.com/thomseddon/traefik-forward-auth) includes an ingenious mechanism to simulate an "_auth host_" in your OIDC authentication, so that you can protect an unlimited amount of DNS names (_with a common domain suffix_), without having to manually maintain a list.
#### How does it work?
Say you're protecting **radarr.example.com**. When you first browse to **https://radarr.example.com**, Traefik forwards your session to traefik-forward-auth, to be authenticated. Traefik-forward-auth redirects you to your OIDC provider's login (_KeyCloak, in this case_), but instructs the OIDC provider to redirect a successfully authenticated session **back** to **https://auth.example.com/_oauth**, rather than to **https://radarr.example.com/_oauth**.
When you successfully authenticate against the OIDC provider, you are redirected to the "_redirect_uri_" of https://auth.example.com. Again, your request hits Traefik, whichforwards the session to traefik-forward-auth, which **knows** that you've just been authenticated (_cookies have a role to play here_). Traefik-forward-auth also knows the URL of your **original** request (_thanks to the X-Forwarded-Whatever header_). Traefik-forward-auth redirects you to your original destination, and everybody is happy.
This clever workaround only works under 2 conditions:
1. Your "auth host" has the same domain name as the hosts you're protecting (_i.e., auth.example.com protecting radarr.example.com_)
2. You explictly tell traefik-forward-auth to use a cookie authenticating your **whole** domain (_i.e. example.com_)
### Setup environment
Create `/var/data/config/traefik/traefik-forward-auth.env` as follows (_change "master" if you created a different realm_):
@@ -109,7 +83,7 @@ And re-deploy your services :)
What have we achieved? By adding an additional three simple labels to any service, we can secure any service behind our KeyCloak OIDC provider, with minimal processing / handling overhead.
!!! summary "Summary"
Created:
Created:
* [X] Traefik-forward-auth configured to authenticate against KeyCloak

View File

@@ -13,15 +13,20 @@ To deal with these gaps, we need a front-end load-balancer, and in this design,
![Traefik Screenshot](../images/traefik.png)
!!! tip
In 2021, this recipe was updated for Traefik v2. There's really no reason to be using Traefikv1 anymore ;)
## Ingredients
!!! summary "You'll need"
Existing
!!! summary "Ingredients"
Already deployed:
* [X] [Docker swarm cluster](/ha-docker-swarm/design/) with [persistent shared storage](/ha-docker-swarm/shared-storage-ceph)
New
* [X] [Docker swarm cluster](/ha-docker-swarm/design/) with [persistent shared storage](/ha-docker-swarm/shared-storage-ceph.md)
* [X] [Traefik](/ha-docker-swarm/traefik) configured per design
* [X] DNS entry for the hostname you intend to use (*or a wildcard*), pointed to your [keepalived](/ha-docker-swarm/keepalived/) IP
New:
* [ ] Access to update your DNS records for manual/automated [LetsEncrypt](https://letsencrypt.org/docs/challenge-types/) DNS-01 validation, or ingress HTTP/HTTPS for HTTP-01 validation
## Preparation
@@ -30,53 +35,58 @@ To deal with these gaps, we need a front-end load-balancer, and in this design,
While it's possible to configure traefik via docker command arguments, I prefer to create a config file (`traefik.toml`). This allows me to change traefik's behaviour by simply changing the file, and keeps my docker config simple.
Create `/var/data/traefikv1/traefik.toml` as follows:
Create `/var/data/traefikv2/traefik.toml` as follows:
```
checkNewVersion = true
defaultEntryPoints = ["http", "https"]
[global]
checkNewVersion = true
# This section enable LetsEncrypt automatic certificate generation / renewal
[acme]
email = "<your LetsEncrypt email address>"
storage = "acme.json" # or "traefik/acme/account" if using KV store
entryPoint = "https"
acmeLogging = true
onDemand = true
OnHostRule = true
# Enable the Dashboard
[api]
dashboard = true
# Request wildcard certificates per https://docs.traefik.io/configuration/acme/#wildcard-domains
[[acme.domains]]
main = "*.example.com"
sans = ["example.com"]
# Write out Traefik logs
[log]
level = "INFO"
filePath = "/traefik.log"
# Redirect all HTTP to HTTPS (why wouldn't you?)
[entryPoints]
[entryPoints.http]
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
# Redirect to HTTPS (why wouldn't you?)
[entryPoints.http.http.redirections.entryPoint]
to = "https"
scheme = "https"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[entryPoints.https.http.tls]
certResolver = "main"
[web]
address = ":8080"
watch = true
# Let's Encrypt
[certificatesResolvers.main.acme]
email = "batman@example.com"
storage = "acme.json"
# uncomment to use staging CA for testing
# caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
[certificatesResolvers.main.acme.dnsChallenge]
provider = "route53"
# Uncomment to use HTTP validation, like a caveman!
# [certificatesResolvers.main.acme.httpChallenge]
# entryPoint = "http"
[docker]
endpoint = "tcp://127.0.0.1:2375"
domain = "example.com"
watch = true
swarmmode = true
# Docker Traefik provider
[providers.docker]
endpoint = "unix:///var/run/docker.sock"
swarmMode = true
watch = true
```
### Prepare the docker service config
!!! tip
"We'll want an overlay network, independent of our traefik stack, so that we can attach/detach all our other stacks (including traefik) to the overlay network. This way, we can undeploy/redepoly the traefik stack without having to bring every other stack first!" - voice of experience
"We'll want an overlay network, independent of our traefik stack, so that we can attach/detach all our other stacks (including traefik) to the overlay network. This way, we can undeploy/redepoly the traefik stack without having to bring down every other stack first!" - voice of hard-won experience
Create `/var/data/config/traefik/traefik.yml` as follows:
Create `/var/data/config/traefikv2/traefikv2.yml` as follows:
```
version: "3.2"
@@ -105,18 +115,18 @@ networks:
--8<-- "premix-cta.md"
Create `/var/data/config/traefik/traefik-app.yml` as follows:
Create `/var/data/config/traefikv2/traefikv2.yml` as follows:
```
version: "3.2"
services:
traefik:
image: traefik:v1.7.16
command: --web --docker --docker.swarmmode --docker.watch --docker.domain=example.com --logLevel=DEBUG
app:
image: traefik:v2.4
env_file: /var/data/config/traefikv2/traefikv2.env
# Note below that we use host mode to avoid source nat being applied to our ingress HTTP/HTTPS sessions
# Without host mode, all inbound sessions would have the source IP of the swarm nodes, rather than the
# original source IP, which would impact logging. If you don't care about this, you can expose ports the
# original source IP, which would impact logging. If you don't care about this, you can expose ports the
# "minimal" way instead
ports:
- target: 80
@@ -132,21 +142,30 @@ services:
protocol: tcp
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /var/data/traefikv1:/etc/traefik
- /var/data/traefikv1/traefik.log:/traefik.log
- /var/data/traefikv1/acme.json:/acme.json
- /var/data/config/traefikv2:/etc/traefik
- /var/data/traefikv2/traefik.log:/traefik.log
- /var/data/traefikv2/acme.json:/acme.json
networks:
- traefik_public
# Global mode makes an instance of traefik listen on _every_ node, so that regardless of which
# node the request arrives on, it'll be forwarded to the correct backend service.
deploy:
labels:
- "traefik.enable=false"
mode: global
labels:
- "traefik.docker.network=traefik_public"
- "traefik.http.routers.api.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.api.entrypoints=https"
- "traefik.http.routers.api.tls.domains[0].main=example.com"
- "traefik.http.routers.api.tls.domains[0].sans=*.example.com"
- "traefik.http.routers.api.tls=true"
- "traefik.http.routers.api.tls.certresolver=main"
- "traefik.http.routers.api.service=api@internal"
- "traefik.http.services.dummy.loadbalancer.server.port=9999"
# uncomment this to enable forward authentication on the traefik api/dashboard
#- "traefik.http.routers.api.middlewares=forward-auth"
placement:
constraints: [node.role == manager]
restart_policy:
condition: on-failure
networks:
traefik_public:
@@ -156,10 +175,10 @@ networks:
Docker won't start a service with a bind-mount to a non-existent file, so prepare an empty acme.json and traefik.log (_with the appropriate permissions_) by running:
```
touch /var/data/traefikv1/acme.json
touch /var/data/traefikv1/traefik.log
chmod 600 /var/data/traefikv1/acme.json
chmod 600 /var/data/traefikv1/traefik.log
touch /var/data/traefikv2/acme.json
touch /var/data/traefikv2/traefik.log
chmod 600 /var/data/traefikv2/acme.json
chmod 600 /var/data/traefikv2/traefik.log
```
!!! warning
@@ -182,26 +201,26 @@ Creating service traefik_scratch
[root@kvm ~]#
```
Now deploy the traefik appliation itself (*which will attach to the overlay network*) by running `docker stack deploy traefik-app -c /var/data/config/traefik/traefik-app.yml`
Now deploy the traefik application itself (*which will attach to the overlay network*) by running `docker stack deploy traefikv2 -c /var/data/config/traefikv2/traefikv2.yml`
```
[root@kvm ~]# docker stack deploy traefik-app -c traefik-app.yml
Creating service traefik-app_app
[root@kvm ~]# docker stack deploy traefik-app -c traefikv2.yml
Creating service traefikv2_app
[root@kvm ~]#
```
Confirm traefik is running with `docker stack ps traefik-app`:
Confirm traefik is running with `docker stack ps traefikv2`:
```
[root@kvm ~]# docker stack ps traefik-app
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
74uipz4sgasm traefik-app_app.t4vcm8siwc9s1xj4c2o4orhtx traefik:alpine kvm.funkypenguin.co.nz Running Running 33 seconds ago *:443->443/tcp,*:80->80/tcp
[root@kvm ~]#
root@raphael:~# docker stack ps traefikv2
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
lmvqcfhap08o traefikv2_app.dz178s1aahv16bapzqcnzc03p traefik:v2.4 donatello Running Running 2 minutes ago *:443->443/tcp,*:80->80/tcp
root@raphael:~#
```
### Check Traefik Dashboard
You should now be able to access[^1] your traefik instance on http://<node IP\>:8080 - It'll look a little lonely currently (*below*), but we'll populate it as we add recipes :)
You should now be able to access[^1] your traefik instance on **https://traefik.<your domain\>** (*if your LetsEncrypt certificate is working*), or **http://<node IP\>:8080** (*if it's not*)- It'll look a little lonely currently (*below*), but we'll populate it as we add recipes :grin:
![Screenshot of Traefik, post-launch](/images/traefik-post-launch.png)