1. The problem I’m having:
I am trying to use a wildcard DNS certificate and have forward auth via Authentik for multiple subdomains. I have previously had this working but was using a new certificate for each subdomain which was not ideal. Reverse proxying works but there is no auth happening. whoami.stalepopcorn.em works as expected but completely bypasses any auth.
2. Error messages and/or full log output:
{"level":"info","ts":1688466003.759242,"logger":"docker-proxy","msg":"Running caddy proxy server"}
{"level":"info","ts":1688466003.7601113,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
{"level":"info","ts":1688466003.7606738,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
{"level":"info","ts":1688466003.7607014,"logger":"docker-proxy","msg":"Running caddy proxy controller"}
{"level":"info","ts":1688466003.761169,"logger":"docker-proxy","msg":"Start","CaddyfilePath":"/Caddyfile","LabelPrefix":"caddy","PollingInterval":30,"ProcessCaddyfile":true,"ProxyServiceTasks":true,"IngressNetworks":"[caddy]","DockerSockets":[""],"DockerCertsPath":[""],"DockerAPIsVersion":[""]}
{"level":"info","ts":1688466003.7619762,"logger":"docker-proxy","msg":"IngressNetworksMap","ingres":"map[5d3b42ca2da1edbf57a85211658c0af54b2692ef4cfdbfa1e04827c6b1171c94:true caddy:true]"}
{"level":"info","ts":1688466003.761993,"logger":"docker-proxy","msg":"Connecting to docker events","DockerSocket":""}
{"level":"info","ts":1688466003.7672575,"logger":"docker-proxy","msg":"Swarm is available","new":false}
{"level":"info","ts":1688466003.777905,"logger":"docker-proxy","msg":"Process Caddyfile","logs":"[ERROR] Removing invalid block: ambiguous site definition: stalepopcorn.me\nstalepopcorn.me {\n\treverse_proxy 192.168.90.5:3000\n}\n\n"}
{"level":"info","ts":1688466003.7779224,"logger":"docker-proxy","msg":"New Caddyfile","caddyfile":"(auth) {\n\treverse_proxy /outpost.goauthentik.io/* http://authentik_server:9000 {\n\t\theader_up Host {http.reverse_proxy.upstream.hostport}\n\t}\n\tforward_auth http://authentik_server:9000 {\n\t\turi /outpost.goauthentik.io/auth/caddy\n\t\tcopy_headers 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 authorization\n\t\ttrusted_proxies private_ranges\n\t}\n}\n*.stalepopcorn.me, stalepopcorn.me {\n\t@whoami host whoami.stalepopcorn.me\n\ttls {\n\t\tdns cloudflare randomapikey\n\t}\n\thandle @whoami {\n\t\timport auth\n\t\treverse_proxy whoami:80\n\t}\n}\nsonarr.stalepopcorn.me {\n\timport auth\n\treverse_proxy http://sonarr:8989 {\n\t\theader_up X-Real-IP {remote_host}\n\t}\n}\nplex.stalepopcorn.me {\n\treverse_proxy http://192.168.0.69:32400\n}\nauthentik.stalepopcorn.me {\n\treverse_proxy 192.168.90.6:9000\n}\ncode.stalepopcorn.me {\n\treverse_proxy 192.168.90.7:8443\n}\ndozzle.stalepopcorn.me {\n\treverse_proxy 192.168.90.10:8080\n}\nwhoami.stalepopcorn.me {\n\treverse_proxy 192.168.90.8\n}\n"}
{"level":"info","ts":1688466003.7789881,"logger":"docker-proxy","msg":"New Config JSON","json":"{\"apps\":{\"http\":{\"servers\":{\"srv0\":{\"listen\":[\":443\"],\"routes\":[{\"match\":[{\"host\":[\"authentik.stalepopcorn.me\"]}],\"handle\":[{\"handler\":\"subroute\",\"routes\":[{\"handle\":[{\"handler\":\"reverse_proxy\",\"upstreams\":[{\"dial\":\"192.168.90.6:9000\"}]}]}]}],\"terminal\":true},{\"match\":[{\"host\":[\"sonarr.stalepopcorn.me\"]}],\"handle\":[{\"handler\":\"subroute\",\"routes\":[{\"handle\":[{\"handle_response\":[{\"match\":{\"status_code\":[2]},\"routes\":[{\"handle\":[{\"handler\":\"headers\",\"request\":{\"set\":{\"Authorization\":[\"{http.reverse_proxy.header.Authorization}\"],\"X-Authentik-Email\":[\"{http.reverse_proxy.header.X-Authentik-Email}\"],\"X-Authentik-Groups\":[\"{http.reverse_proxy.header.X-Authentik-Groups}\"],\"X-Authentik-Jwt\":[\"{http.reverse_proxy.header.X-Authentik-Jwt}\"],\"X-Authentik-Meta-App\":[\"{http.reverse_proxy.header.X-Authentik-Meta-App}\"],\"X-Authentik-Meta-Jwks\":[\"{http.reverse_proxy.header.X-Authentik-Meta-Jwks}\"],\"X-Authentik-Meta-Outpost\":[\"{http.reverse_proxy.header.X-Authentik-Meta-Outpost}\"],\"X-Authentik-Meta-Provider\":[\"{http.reverse_proxy.header.X-Authentik-Meta-Provider}\"],\"X-Authentik-Meta-Version\":[\"{http.reverse_proxy.header.X-Authentik-Meta-Version}\"],\"X-Authentik-Name\":[\"{http.reverse_proxy.header.X-Authentik-Name}\"],\"X-Authentik-Uid\":[\"{http.reverse_proxy.header.X-Authentik-Uid}\"],\"X-Authentik-Username\":[\"{http.reverse_proxy.header.X-Authentik-Username}\"]}}}]}]}],\"handler\":\"reverse_proxy\",\"headers\":{\"request\":{\"set\":{\"X-Forwarded-Method\":[\"{http.request.method}\"],\"X-Forwarded-Uri\":[\"{http.request.uri}\"]}}},\"rewrite\":{\"method\":\"GET\",\"uri\":\"/outpost.goauthentik.io/auth/caddy\"},\"trusted_proxies\":[\"192.168.0.0/16\",\"172.16.0.0/12\",\"10.0.0.0/8\",\"127.0.0.1/8\",\"fd00::/8\",\"::1\"],\"upstreams\":[{\"dial\":\"authentik_server:9000\"}]}]},{\"handle\":[{\"handler\":\"reverse_proxy\",\"headers\":{\"request\":{\"set\":{\"Host\":[\"{http.reverse_proxy.upstream.hostport}\"]}}},\"upstreams\":[{\"dial\":\"authentik_server:9000\"}]}],\"match\":[{\"path\":[\"/outpost.goauthentik.io/*\"]}]},{\"handle\":[{\"handler\":\"reverse_proxy\",\"headers\":{\"request\":{\"set\":{\"X-Real-Ip\":[\"{http.request.remote.host}\"]}}},\"upstreams\":[{\"dial\":\"sonarr:8989\"}]}]}]}],\"terminal\":true},{\"match\":[{\"host\":[\"dozzle.stalepopcorn.me\"]}],\"handle\":[{\"handler\":\"subroute\",\"routes\":[{\"handle\":[{\"handler\":\"reverse_proxy\",\"upstreams\":[{\"dial\":\"192.168.90.10:8080\"}]}]}]}],\"terminal\":true},{\"match\":[{\"host\":[\"whoami.stalepopcorn.me\"]}],\"handle\":[{\"handler\":\"subroute\",\"routes\":[{\"handle\":[{\"handler\":\"reverse_proxy\",\"upstreams\":[{\"dial\":\"192.168.90.8:80\"}]}]}]}],\"terminal\":true},{\"match\":[{\"host\":[\"plex.stalepopcorn.me\"]}],\"handle\":[{\"handler\":\"subroute\",\"routes\":[{\"handle\":[{\"handler\":\"reverse_proxy\",\"upstreams\":[{\"dial\":\"192.168.0.69:32400\"}]}]}]}],\"terminal\":true},{\"match\":[{\"host\":[\"code.stalepopcorn.me\"]}],\"handle\":[{\"handler\":\"subroute\",\"routes\":[{\"handle\":[{\"handler\":\"reverse_proxy\",\"upstreams\":[{\"dial\":\"192.168.90.7:8443\"}]}]}]}],\"terminal\":true},{\"match\":[{\"host\":[\"*.stalepopcorn.me\",\"stalepopcorn.me\"]}],\"handle\":[{\"handler\":\"subroute\",\"routes\":[{\"handle\":[{\"handler\":\"subroute\",\"routes\":[{\"handle\":[{\"handle_response\":[{\"match\":{\"status_code\":[2]},\"routes\":[{\"handle\":[{\"handler\":\"headers\",\"request\":{\"set\":{\"Authorization\":[\"{http.reverse_proxy.header.Authorization}\"],\"X-Authentik-Email\":[\"{http.reverse_proxy.header.X-Authentik-Email}\"],\"X-Authentik-Groups\":[\"{http.reverse_proxy.header.X-Authentik-Groups}\"],\"X-Authentik-Jwt\":[\"{http.reverse_proxy.header.X-Authentik-Jwt}\"],\"X-Authentik-Meta-App\":[\"{http.reverse_proxy.header.X-Authentik-Meta-App}\"],\"X-Authentik-Meta-Jwks\":[\"{http.reverse_proxy.header.X-Authentik-Meta-Jwks}\"],\"X-Authentik-Meta-Outpost\":[\"{http.reverse_proxy.header.X-Authentik-Meta-Outpost}\"],\"X-Authentik-Meta-Provider\":[\"{http.reverse_proxy.header.X-Authentik-Meta-Provider}\"],\"X-Authentik-Meta-Version\":[\"{http.reverse_proxy.header.X-Authentik-Meta-Version}\"],\"X-Authentik-Name\":[\"{http.reverse_proxy.header.X-Authentik-Name}\"],\"X-Authentik-Uid\":[\"{http.reverse_proxy.header.X-Authentik-Uid}\"],\"X-Authentik-Username\":[\"{http.reverse_proxy.header.X-Authentik-Username}\"]}}}]}]}],\"handler\":\"reverse_proxy\",\"headers\":{\"request\":{\"set\":{\"X-Forwarded-Method\":[\"{http.request.method}\"],\"X-Forwarded-Uri\":[\"{http.request.uri}\"]}}},\"rewrite\":{\"method\":\"GET\",\"uri\":\"/outpost.goauthentik.io/auth/caddy\"},\"trusted_proxies\":[\"192.168.0.0/16\",\"172.16.0.0/12\",\"10.0.0.0/8\",\"127.0.0.1/8\",\"fd00::/8\",\"::1\"],\"upstreams\":[{\"dial\":\"authentik_server:9000\"}]}]},{\"handle\":[{\"handler\":\"reverse_proxy\",\"headers\":{\"request\":{\"set\":{\"Host\":[\"{http.reverse_proxy.upstream.hostport}\"]}}},\"upstreams\":[{\"dial\":\"authentik_server:9000\"}]}],\"match\":[{\"path\":[\"/outpost.goauthentik.io/*\"]}]},{\"handle\":[{\"handler\":\"reverse_proxy\",\"upstreams\":[{\"dial\":\"whoami:80\"}]}]}]}],\"match\":[{\"host\":[\"whoami.stalepopcorn.me\"]}]}]}],\"terminal\":true}]}}},\"tls\":{\"automation\":{\"policies\":[{\"subjects\":[\"authentik.stalepopcorn.me\",\"sonarr.stalepopcorn.me\",\"dozzle.stalepopcorn.me\",\"whoami.stalepopcorn.me\",\"plex.stalepopcorn.me\",\"code.stalepopcorn.me\"]},{\"subjects\":[\"*.stalepopcorn.me\",\"stalepopcorn.me\"],\"issuers\":[{\"challenges\":{\"dns\":{\"provider\":{\"api_token\":\"randomapikey\",\"name\":\"cloudflare\"}}},\"module\":\"acme\"},{\"challenges\":{\"dns\":{\"provider\":{\"api_token\":\"randomapikey\",\"name\":\"cloudflare\"}}},\"module\":\"zerossl\"}]}]}}}}"}
{"level":"info","ts":1688466003.7790296,"logger":"docker-proxy","msg":"Sending configuration to","server":"localhost"}
{"level":"info","ts":1688466003.7795088,"logger":"admin.api","msg":"received request","method":"POST","host":"localhost:2019","uri":"/load","remote_ip":"127.0.0.1","remote_port":"51554","headers":{"Accept-Encoding":["gzip"],"Content-Length":["5348"],"Content-Type":["application/json"],"User-Agent":["Go-http-client/1.1"]}}
{"level":"info","ts":1688466003.7802045,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
{"level":"info","ts":1688466003.7804182,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc00011ad20"}
{"level":"info","ts":1688466003.78044,"logger":"http","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443}
{"level":"info","ts":1688466003.780462,"logger":"http","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
{"level":"info","ts":1688466003.7811518,"logger":"http","msg":"enabling HTTP/3 listener","addr":":443"}
{"level":"info","ts":1688466003.7811913,"msg":"failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 2048 kiB, got: 416 kiB). See https://github.com/quic-go/quic-go/wiki/UDP-Receive-Buffer-Size for details."}
{"level":"info","ts":1688466003.7812448,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
{"level":"info","ts":1688466003.7812808,"logger":"http.log","msg":"server running","name":"remaining_auto_https_redirects","protocols":["h1","h2","h3"]}
{"level":"info","ts":1688466003.7812843,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["*.stalepopcorn.me","stalepopcorn.me","authentik.stalepopcorn.me","sonarr.stalepopcorn.me","dozzle.stalepopcorn.me","whoami.stalepopcorn.me","plex.stalepopcorn.me","code.stalepopcorn.me"]}
{"level":"info","ts":1688466003.784341,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
{"level":"info","ts":1688466003.784455,"logger":"admin.api","msg":"load complete"}
{"level":"info","ts":1688466003.7845197,"logger":"tls","msg":"cleaning storage unit","description":"FileStorage:/data/caddy"}
{"level":"info","ts":1688466003.7845967,"logger":"docker-proxy","msg":"Successfully configured","server":"localhost"}
{"level":"info","ts":1688466003.7858076,"logger":"tls","msg":"finished cleaning storage units"}
{"level":"info","ts":1688466003.7887352,"logger":"admin","msg":"stopped previous server","address":"localhost:2019"}
3. Caddy version:
v2.6.4 with Caddy Docker Proxy and Cloudflare DNS
4. How I installed and ran Caddy:
Official builder image with Caddy Docker Proxy and Cloudflare DNS plugins, running in Docker
a. System environment:
Ubuntu 23.04 (GNU/Linux 6.2.0-24-generic x86_64)
Docker CE 24.0.2
b. Command:
docker compose up -d
c. Service/unit/compose file:
caddy:
image: caddy:cloudflare
#image: lucaslorentz/caddy-docker-proxy:ci-alpine
container_name: caddy
ports:
- 80:80
- 443:443
environment:
- CADDY_INGRESS_NETWORKS=caddy
- CADDY_DOCKER_CADDYFILE_PATH=/Caddyfile
networks:
- caddy
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./appdata/caddy/data:/data
- ./appdata/caddy/config:/config
- ./appdata/caddy/Caddyfile:/Caddyfile
restart: unless-stopped
d. My complete Caddy config:
(auth) {
# always forward outpost path to actual outpost
reverse_proxy /outpost.goauthentik.io/* http://authentik_server:9000 {
header_up Host {http.reverse_proxy.upstream.hostport}
}
# forward authentication to outpost
forward_auth http://authentik_server:9000 {
uri /outpost.goauthentik.io/auth/caddy
# capitalization of the headers is important, otherwise they will be empty
copy_headers 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 authorization
# optional, in this config trust all private ranges, should probably be set to the outposts IP
trusted_proxies private_ranges
}
}
*.stalepopcorn.me, stalepopcorn.me {
tls {
dns cloudflare randomapikey
}
@home host stalepopcorn.me
handle @home {
revers_proxy homepage:3000
}
@whoami host whoami.stalepopcorn.me
handle @whoami {
import auth
reverse_proxy whoami:80
}
}
Old working Caddyfile. I was using one block per service before attempting wildcard certs. I can move the auth parts to a snippet and us ‘import’ in each block and that works as well.
whoami.stalepopcorn.me {
# always forward outpost path to actual outpost
reverse_proxy /outpost.goauthentik.io/* http://authentik_server:9000 {
header_up Host {http.reverse_proxy.upstream.hostport}
}
# forward authentication to outpost
forward_auth http://authentik_server:9000 {
uri /outpost.goauthentik.io/auth/caddy
# capitalization of the headers is important, otherwise they will be empty
copy_headers 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-Provi>
# optional, in this config trust all private ranges, should probably be set to the outposts IP
trusted_proxies private_ranges
}
# actual site configuration below, for example
reverse_proxy {{upstreams}}
}
sonarr.stalepopcorn.me {
# always forward outpost path to actual outpost
reverse_proxy /outpost.goauthentik.io/* http://authentik_server:9000
# forward authentication to outpost
forward_auth http://authentik_server:9000 {
uri /outpost.goauthentik.io/auth/caddy
# capitalization of the headers is important, otherwise they will be empty
copy_headers 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-Provi>
# optional, in this config trust all private ranges, should probably be set to the outposts IP
trusted_proxies private_ranges
}