goauthentik / goauthentik/authentik

How to integrate Authentik behind a homelab connected to a VPS

Open
#14,097 3 comments 0 reactions 0 assignees View on GitHub
question
Dominant language
Python
Stars
25.6k
Forks
2k
Avg merge
1d 1h
Merged PRs (30d)
644

Description

### Introduction

I have a homelab behind CGNAT. I want to expose to internet certain services. I bought a cheap 2 GB RAM VPS and installed wireguard on both servers to estabish a VPN tunnel. My VPS has the wireguard IP 10.7.0.1 and my homelab has 10.7.0.2

I installed traefik in the VPS to manage domain TLS and redirection. Because I want to save as much RAM as possible in the VPS, I installed all my services (nextcloud, minecraft server, stirling-pdf, etc), including Authentik in the Homelab.

So, basically the VPS is just a connector with just traefik and wireguard, whereas the Homelab has the services and the high consuming resources.

Now, with that in context, I have the following configuration:

--- IN THE VPS ---

**docker-compose.yml of traefik**

```docker-compose
services:
traefik:
container_name: traefik
image: traefik:latest
ports:
- "80:80"
- "443:443"
environment:
- CF_DNS_API_TOKEN=${CLOUDFLARE_API_KEY}
env_file:
- .env
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro # So that Traefik can listen to the Docker events
- ./config/traefik.yaml:/etc/traefik/traefik.yaml:ro # Static config file.
- ./config/config.yaml:/etc/traefik/config.yaml:ro # Dynamic config file.
- ./config/certs/:/etc/traefik/certs/:rw # Storage for Let's Encrypt's TLS certificates.
- ./logs/:/var/log/traefik/ # Storage for Traefik logs.
- ./config/captcha.html:/captcha.html # Crowdsec's Appsec captcha html.
- ./config/ban.html:/ban.html # Crowdsec's Appsec ban html.
restart: always
networks:
- proxy-net
networks:
proxy-net:
external: true
```

**traefik.yaml (static config)**

```yaml
global:
checkNewVersion: false
sendAnonymousUsage: true

log:
level: DEBUG
# Container's dir, not the machine dir.
filePath: /var/log/traefik/traefik.log
accessLog:
# Container's dir, not the machine dir.
filePath: /var/log/traefik/access.log

api:
dashboard: true
insecure: false

entryPoints:
web:
address: :80
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: :443
http:
tls:
certResolver: cloudflare
domains:
- main: "example.com"
sans:
- "*.example.com"
options: default

tls:
options:
default:
minVersion: VersionTLS12
sniStrict: true
curvePreferences:
- CurveP521
- CurveP384
cipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256

certificatesResolvers:
cloudflare:
acme:
email: "support@example.com"
# Container's dir, not the machine dir.
storage: /etc/traefik/certs/cloudflare-acme.json
caServer: 'https://acme-v02.api.letsencrypt.org/directory'
keyType: EC256
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
- "8.8.8.8:53"

serversTransport:
insecureSkipVerify: true

providers:
docker:
exposedByDefault: false
endpoint: 'unix:///var/run/docker.sock'
watch: true
file:
# Container's dir, not the machine dir. Dynamic config file.
filename: /etc/traefik/config.yaml
watch: true

experimental:
plugins:
crowdsec-bouncer:
moduleName: "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
version: "v1.4.2"
```

**config.yaml (traefik dynamic conf)**

```yaml
http:
routers:
to-authentik-dashboard:
rule: "Host(`auth.example.com`)"
entryPoints:
- web
- websecure
priority: 10
middlewares:
- default-headers
- crowdsec
service: authentik-dash
to-authentik-auth:
rule: "Host(`auth.example.com`) && PathPrefix(`/outpost.goauthentik.io/`)"
entryPoints:
- web
- websecure
priority: 15
middlewares:
- default-headers
- crowdsec
service: authentik-auth
to-nextcloud-apache:
rule: "Host(`cloud.example.com`)"
entryPoints:
- web
- websecure
middlewares:
- default-headers
- crowdsec
service: nextcloud-apache
to-traefik-dashboard:
rule: "Host(`traefik.example.com`)"
entryPoints:
- web
- websecure
middlewares:
- authentik
service: api@internal

middlewares:
default-headers:
headers:
frameDeny: true
browserXssFilter: true
contentTypeNosniff: true
forceSTSHeader: true
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 15552000
customFrameOptionsValue: SAMEORIGIN
referrerPolicy: "same-origin"

crowdsec:
plugin:
crowdsec-bouncer:
enabled: true
logLevel: DEBUG
LogFilePath: "/var/log/traefik/crowdsec-bouncer.log"
updateIntervalSeconds: 15
updateMaxFailure: 0
defaultDecisionSeconds: 15
httpTimeoutSeconds: 10
crowdsecMode: stream
crowdsecAppsecEnabled: true
crowdsecAppsecHost: 10.7.0.1:7422
crowdsecAppsecFailureBlock: true
crowdsecAppsecUnreachableBlock: true
crowdsecLapiKey: xxxxxxxxxxxxxxxxxxxxxxxxxxxx
crowdsecLapiHost: 10.7.0.1:8180
crowdsecLapiScheme: http
forwardedHeadersTrustedIPs:
- 10.0.10.23/32
- 10.0.20.0/24
clientTrustedIPs:
# Internal LAN IPs
- 10.20.10.0/24
- 10.20.15.0/24
- 10.20.20.0/24
- 10.20.25.0/24
- 192.168.1.0/24
# WireGuard
- 10.7.0.0/24
crowdsecLapiTLSInsecureVerify: false
crowdsecCapiScenarios:
- crowdsecurity/http-path-traversal-probing
- crowdsecurity/http-xss-probing
- crowdsecurity/http-generic-bf
captchaProvider: hcaptcha
captchaSiteKey: xxxxxxxxxxxxxxxxxxxxxx
captchaSecretKey: xxxxxxxxxxxxxxxxxxxxx
captchaGracePeriodSeconds: 1800
captchaHTMLFilePath: /captcha.html
banHTMLFilePath: /ban.html

authentik:
forwardAuth:
address: https://auth.example.com/outpost.goauthentik.io/auth/traefik
trustForwardHeader: true
authResponseHeaders:
- X-authentik-username
- X-authentik-groups
- X-authentik-email
- X-authentik-name
- X-authentik-uid
- X-authentik-jwt
- X-authentik-meta-jwks
- X-authentik-meta-outpost
- X-authentik-meta-provider
- X-authentik-meta-app
- X-authentik-meta-version

services:
authentik-dash:
loadBalancer:
servers:
- url: http://10.7.0.2:9000
authentik-auth:
loadBalancer:
servers:
- url: http://10.7.0.2:9000/outpost.goauthentik.io
nextcloud-apache:
loadBalancer:
servers:
- url: http://10.7.0.2:11000
```

--- IN THE HOMELAB ---

**docker-compose.yml of authentik**

```docker
---
services:
postgresql:
container_name: authentik-postgresql
image: docker.io/library/postgres:16-alpine
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
start_period: 20s
interval: 30s
retries: 5
timeout: 5s
volumes:
- authentik-database:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: ${PG_PASS:?database password required}
POSTGRES_USER: ${PG_USER:-authentik}
POSTGRES_DB: ${PG_DB:-authentik}
env_file:
- .env
networks:
- auth-net
redis:
container_name: authentik-redis
image: docker.io/library/redis:alpine
command: --save 60 1 --loglevel warning
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
start_period: 20s
interval: 30s
retries: 5
timeout: 3s
volumes:
- authentik-redis:/data
networks:
- auth-net
server:
container_name: authentik-server
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.4}
restart: unless-stopped
command: server
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
AUTHENTIK_REDIS__HOST: authentik-redis
AUTHENTIK_POSTGRESQL__HOST: authentik-postgresql
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
volumes:
- authentik-server-media:/media
- authentik-server-custom-templates:/templates
env_file:
- .env
ports:
- "${COMPOSE_PORT_HTTP:-9000}:9000"
- "${COMPOSE_PORT_HTTPS:-9443}:9443"
depends_on:
postgresql:
condition: service_healthy
redis:
condition: service_healthy
networks:
- auth-net
worker:
container_name: authentik-worker
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.4}
restart: unless-stopped
command: worker
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
AUTHENTIK_REDIS__HOST: authentik-redis
AUTHENTIK_POSTGRESQL__HOST: authentik-postgresql
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
# `user: root` and the docker socket volume are optional.
# See more for the docker socket integration here:
# https://goauthentik.io/docs/outposts/integrations/docker
# Removing `user: root` also prevents the worker from fixing the permissions
# on the mounted folders, so when removing this make sure the folders have the correct UID/GID
# (1000:1000 by default)
user: root
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- authentik-server-media:/media
- authentik-server-certs:/certs
- authentik-server-custom-templates:/templates
env_file:
- .env
depends_on:
postgresql:
condition: service_healthy
redis:
condition: service_healthy
networks:
- auth-net

volumes:
authentik-database:
name: authentik-database
driver: local
authentik-redis:
name: authentik-redis
driver: local
authentik-server-media:
name: authentik-server-media
authentik-server-certs:
name: authentik-server-certs
authentik-server-custom-templates:
name: authentik-server-custom-templates
networks:
auth-net:
external: true
```

### The problem

See the "to-traefik-dashboard"? I want to protect the traefik dashboard just by testing, but when I access it, all what happens is "Not Found" error.

When I access just the authentik panel (authentik-dash), all works good. I completed the initual setup, and added the proxy provider with explict consent and simple forward auth pointing to the traefik.example.com. I added the application and configured the provider in the "authentik Embedded Outpost" as well.

I did the following debug without success as well:
- I disabled both "default-headers" and "crowdsec" middlewares in "to-authentik-auth" and nothing changed.
- I changed to "http://10.7.0.2:9000/outpost.goauthentik.io/auth/traefik" in the authentik middleware and clearly not worked.
- In the VPS I made ping to Homelab's http://10.7.0.2:9000/outpost.goauthentik.io/ping and successfully got "HTTP/2 204".

I am wondering it's because I am redirecting to the VPS again to enter into "to-authentik-auth" because it is not directly accesible?

Another observation is when I quit the middleware authentik to the traefik dashboard route, it displays, so traefik is not the problem...

Thank you for your time reading this.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.