Wildcard DNS and Forward Auth

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
    }

5. Links to relevant resources:

This seems like your problem. CDP is removing the config because it’s ambiguous.

Check your /config volume, the Caddyfile CDP actually runs with will be autosaved in there. What does it look like?

I managed to get it working

docker-compose.yml

labels:
      caddy: "*.$DOMAIN, $DOMAIN"
      caddy.@whoami: "host whoami.$DOMAIN"
      caddy.handle: "@whoami"
      caddy.handle.import: auth
      caddy.handle.reverse_proxy: "{{upstreams}}"

Caddyfile

(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 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

        # optional, in this config trust all private ranges, should probably be set to the outposts IP
        trusted_proxies private_ranges
    }
}

*.$DOMAIN, $DOMAIN {
    tls {
        dns cloudflare $CF_API_KEY
    }
    @home host $DOMAIN
    handle @home {
        reverse_proxy homepage:3000
    }
}

It may not be the most elegant way, but I can choose if I want a service to have Auth or not in my docker compose file by using

caddy.handle.import: auth
1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.