mirror of
https://github.com/funkypenguin/geek-cookbook/
synced 2026-11-04 06:38:18 +00:00
Update Traefik Forward Auth for v2 (#169)
This commit is contained in:
1 parent
c72d3ae098
commit
892adb4704
75 files changed
+864
-1198
No files matched your search
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user