1. Caddy version: v2.6.2
2. How I installed, and run Caddy:
Docker swarm deploy of this service:
caddy:
image: caddy:2-alpine
ports:
- target: 443
published: 443
mode: host
- target: 80
published: 80
mode: host
volumes:
- type: bind
source: /srv
target: /srv
read_only: true
- type: bind
source: /srv/sys/caddy
target: /data/caddy
configs:
- source: Caddyfile
target: /etc/caddy/Caddyfile
- source: index.html
target: /www/index.html
- source: index.mjs
target: /www/index.mjs
- source: deergrove.png
target: /www/deergrove.png
- source: index.css
target: /www/index.css
- source: browser.html
target: /browser.html
extra_hosts:
- host.docker.internal:host-gateway
a. System environment:
Raspberry Pi 4, Ubuntu 22.04.1, Linux 5.15.0-1024-raspi, docker 20.10.23
b. Command:
docker --context deergrove stack deploy -c docker-compose.yaml --prune $(basename $(pwd))
c. Service/unit/compose file:
version: "3.8"
services:
coredns:
image: coredns/coredns
networks:
- hostnet
configs:
- source: Corefile
target: /Corefile
caddy:
image: caddy:2-alpine
ports:
- target: 443
published: 443
mode: host
- target: 80
published: 80
mode: host
volumes:
- type: bind
source: /srv
target: /srv
read_only: true
- type: bind
source: /srv/sys/caddy
target: /data/caddy
configs:
- source: Caddyfile
target: /etc/caddy/Caddyfile
- source: index.html
target: /www/index.html
- source: index.mjs
target: /www/index.mjs
- source: deergrove.png
target: /www/deergrove.png
- source: index.css
target: /www/index.css
- source: browser.html
target: /browser.html
extra_hosts:
- host.docker.internal:host-gateway
authelia:
image: authelia/authelia
environment:
AUTHELIA_JWT_SECRET_FILE: /run/secrets/jwt.secret
AUTHELIA_SESSION_SECRET_FILE: /run/secrets/session.secret
AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE: /run/secrets/storage.secret
secrets:
- jwt.secret
- session.secret
- storage.secret
- users.yaml
configs:
- source: authelia.yaml
target: /config/configuration.yml
volumes:
- type: bind
source: /srv/sys/authelia
target: /srv/sys/authelia
forgejo:
image: codeberg.org/forgejo/forgejo:1.18-rootless
secrets:
- source: forgejo.ini
target: /etc/gitea/app.ini
uid: "1000"
gid: "1000"
mode: 0400
volumes:
- type: bind
source: /srv/sys/forgejo
target: /data
- type: bind
source: /etc/timezone
target: /etc/timezone
read_only: true
- type: bind
source: /etc/localtime
target: /etc/localtime
read_only: true
geneweb:
image: ravermeister/geneweb
volumes:
- type: bind
source: /srv/sys/geneweb/etc
target: /usr/local/share/geneweb/etc
- type: bind
source: /srv/sys/geneweb/share/data
target: /usr/local/share/geneweb/share/data
- type: bind
source: /srv/sys/geneweb/log
target: /usr/local/share/geneweb/log
webdav:
image: micromata/dave
volumes:
- type: bind
source: /srv
target: /data
configs:
- source: dave.yaml
target: /config/config.yaml
user: "911:911"
ddns:
image: qmcgaw/ddns-updater
dns:
- 1.1.1.1
volumes:
- type: bind
source: /srv/sys/ddns-updater
target: /updater/data
configs:
dave.yaml:
file: dave.yaml
name: dave.yaml-v3
Corefile:
file: Corefile
name: Corefile-v4
Caddyfile:
file: Caddyfile
name: Caddyfile-v98
index.html:
file: www/index.html
name: index.html-v36
index.mjs:
file: www/index.mjs
name: index.mjs-v1
index.css:
file: www/index.css
name: index.css-v1
browser.html:
file: www/browser.html
name: browser.html-v3
deergrove.png:
file: www/deergrove.png
name: deergrove.png-v1
authelia.yaml:
file: authelia.yaml
name: authelia.yaml-v6
secrets:
passwd:
file: secrets/passwd
name: passwd-v2
simpleauth.key:
file: secrets/simpleauth.key
name: simpleauth.key-v1
tunnel:
file: secrets/tunnel
name: tunnel-v1
known_hosts:
file: secrets/known_hosts
name: known_hosts-v1
forgejo.ini:
file: secrets/forgejo.ini
name: forgejo.ini-v1
jwt.secret:
file: secrets/jwt.secret
name: jwt.secret-v1
storage.secret:
file: secrets/storage.secret
name: storage.secret-v1
session.secret:
file: secrets/session.secret
name: session.secret-v1
users.yaml:
file: secrets/users.yaml
name: users.yaml-v2
networks:
hostnet:
external: true
name: host
d. My complete Caddy config:
{
email neale@woozle.org
}
(authelia) {
uri /api/verify?rd=https://auth.woozle.org/
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
}
(restricted-access) {
@noauth header !Authorization
route {
forward_auth @noauth authelia:9091 {
import authelia
}
forward_auth authelia:9091 {
import authelia
header_up Proxy-Authorization {header.authorization}
}
}
}
auth.woozle.org {
reverse_proxy authelia:9091
}
git.woozle.org {
reverse_proxy forgejo:3000
}
drive.woozle.org {
import restricted-access
@nondav {
method HEAD GET
}
# route overrides built-in ordering
route {
file_server @nondav {
root /srv/
browse /browser.html
}
reverse_proxy webdav:8000
}
}
media.woozle.org {
reverse_proxy jellyfin:8096
}
ancestry.woozle.org {
reverse_proxy geneweb:2317
}
##
## handle sends original path
## handle_path truncates path
##
(deergrove) {
handle_path /ddns/* {
import restricted-access
reverse_proxy ddns:8000
}
handle_path /octoprint/* {
import restricted-access
reverse_proxy {
to 192.168.86.20:80
header_up X-Script-Name "/octoprint"
}
}
handle /webcam/* {
# Octoprint doesn't properly prefix webcam URLs
import restricted-access
reverse_proxy {
to 192.168.86.20:80
}
}
handle_path /public/* {
file_server {
root /srv/storage/public
}
}
handle {
import restricted-access
file_server {
root /www
}
}
}
deergrove.woozle.org {
import deergrove
}
sweetums.lan {
tls internal
import deergrove
}
3. The problem I’m having:
Authelia needs the Authorization
header passed in as Proxy-Authorization
, but only if an Authorization
header was sent by the client.
I’m trying to make that happen without modifying Caddy’s source code.
What I have right now is getting close, but appears to be running requests with no Authorization
header set, through both forward_auth
sections. This means every client request results in Caddy sending two requests to Authelia, which makes Authelia complain that the Proxy-Authorization
header of the second request is incorrectly formatted. That’s true: {headers.authorization}
is an empty string.
4. Error messages and/or full log output:
This is the result of running two curl commands:
curl -b /tmp/jar -c /tmp/jar https://auth.woozle.org/api/firstfactor --data-raw '{"username":"neale","password":"'$password'"}'
curl -b /tmp/jar -c /tmp/jar -v https://deergrove.woozle.org/
homelab_caddy.1.s7dc6ketwosz@sweetums | {"level":"debug","ts":1675702891.2546768,"logger":"events","msg":"event","name":"tls_get_certificate","id":"2dfddc06-f5e8-40cd-bbd2-35d7a1868b07","origin":"tls","data":{"client_hello":{"CipherSuites":[4866,4867,4865,49196,49200,159,52393,52392,52394,49195,49199,158,49188,49192,107,49187,49191,103,49162,49172,57,49161,49171,51,157,156,61,60,53,47,255],"ServerName":"auth.woozle.org","SupportedCurves":[29,23,30,25,24,256,257,258,259,260],"SupportedPoints":"AAEC","SignatureSchemes":[1027,1283,1539,2055,2056,2057,2058,2059,2052,2053,2054,1025,1281,1537,771,769,770,1026,1282,1538],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[772,771],"Conn":{}}}}
homelab_caddy.1.s7dc6ketwosz@sweetums | {"level":"debug","ts":1675702891.2548246,"logger":"tls.handshake","msg":"choosing certificate","identifier":"auth.woozle.org","num_choices":1}
homelab_caddy.1.s7dc6ketwosz@sweetums | {"level":"debug","ts":1675702891.2548718,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"auth.woozle.org","subjects":["auth.woozle.org"],"managed":true,"issuer_key":"acme-v02.api.letsencrypt.org-directory","hash":"e3fa1ab50a261ccf2aae92d4df5e65b75723c0e271e8da3b00f1f90a69efd115"}
homelab_caddy.1.s7dc6ketwosz@sweetums | {"level":"debug","ts":1675702891.2548974,"logger":"tls.handshake","msg":"matched certificate in cache","remote_ip":"172.18.0.1","remote_port":"55414","subjects":["auth.woozle.org"],"managed":true,"expiration":1683429570,"hash":"e3fa1ab50a261ccf2aae92d4df5e65b75723c0e271e8da3b00f1f90a69efd115"}
homelab_caddy.1.s7dc6ketwosz@sweetums | {"level":"debug","ts":1675702891.2662523,"logger":"http.handlers.reverse_proxy","msg":"selected upstream","dial":"authelia:9091","total_upstreams":1}
homelab_caddy.1.s7dc6ketwosz@sweetums | {"level":"debug","ts":1675702892.2769196,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"authelia:9091","duration":1.010491726,"request":{"remote_ip":"172.18.0.1","remote_port":"55414","proto":"HTTP/2.0","method":"POST","host":"auth.woozle.org","uri":"/api/firstfactor","headers":{"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["auth.woozle.org"],"User-Agent":["curl/7.81.0"],"Accept":["*/*"],"Cookie":[],"Content-Length":["62"],"Content-Type":["application/x-www-form-urlencoded"]},"tls":{"resumed":false,"version":772,"cipher_suite":4867,"proto":"h2","server_name":"auth.woozle.org"}},"headers":{"Content-Type":["application/json; charset=utf-8"],"X-Content-Type-Options":["nosniff"],"X-Frame-Options":["SAMEORIGIN"],"Set-Cookie":[],"X-Xss-Protection":["1; mode=block"],"Pragma":["no-cache"],"Cache-Control":["no-store"],"Content-Security-Policy":["default-src 'none';"],"Date":["Mon, 06 Feb 2023 17:01:31 GMT"],"Content-Length":["15"],"Referrer-Policy":["strict-origin-when-cross-origin"],"Permissions-Policy":["interest-cohort=()"]},"status":200}
homelab_caddy.1.s7dc6ketwosz@sweetums | {"level":"debug","ts":1675702892.5105677,"logger":"events","msg":"event","name":"tls_get_certificate","id":"ffe78729-0dcf-4ea0-be48-b173b2f08e75","origin":"tls","data":{"client_hello":{"CipherSuites":[4866,4867,4865,49196,49200,159,52393,52392,52394,49195,49199,158,49188,49192,107,49187,49191,103,49162,49172,57,49161,49171,51,157,156,61,60,53,47,255],"ServerName":"deergrove.woozle.org","SupportedCurves":[29,23,30,25,24,256,257,258,259,260],"SupportedPoints":"AAEC","SignatureSchemes":[1027,1283,1539,2055,2056,2057,2058,2059,2052,2053,2054,1025,1281,1537,771,769,770,1026,1282,1538],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[772,771],"Conn":{}}}}
homelab_caddy.1.s7dc6ketwosz@sweetums | {"level":"debug","ts":1675702892.5106957,"logger":"tls.handshake","msg":"choosing certificate","identifier":"deergrove.woozle.org","num_choices":1}
homelab_caddy.1.s7dc6ketwosz@sweetums | {"level":"debug","ts":1675702892.5107353,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"deergrove.woozle.org","subjects":["deergrove.woozle.org"],"managed":true,"issuer_key":"acme-v02.api.letsencrypt.org-directory","hash":"37b6cc293a72b89fc417bfe5e5868e2e5f5ea93bee17c25341785d373694a028"}
homelab_caddy.1.s7dc6ketwosz@sweetums | {"level":"debug","ts":1675702892.5107617,"logger":"tls.handshake","msg":"matched certificate in cache","remote_ip":"172.18.0.1","remote_port":"55420","subjects":["deergrove.woozle.org"],"managed":true,"expiration":1680724900,"hash":"37b6cc293a72b89fc417bfe5e5868e2e5f5ea93bee17c25341785d373694a028"}
homelab_caddy.1.s7dc6ketwosz@sweetums | {"level":"debug","ts":1675702892.5253625,"logger":"http.handlers.reverse_proxy","msg":"selected upstream","dial":"authelia:9091","total_upstreams":1}
homelab_caddy.1.s7dc6ketwosz@sweetums | {"level":"debug","ts":1675702892.5300813,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"authelia:9091","duration":0.00454495,"request":{"remote_ip":"172.18.0.1","remote_port":"55420","proto":"HTTP/2.0","method":"GET","host":"deergrove.woozle.org","uri":"/api/verify?rd=https://auth.woozle.org/","headers":{"Accept":["*/*"],"X-Forwarded-Method":["GET"],"Cookie":[],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["deergrove.woozle.org"],"User-Agent":["curl/7.81.0"],"X-Forwarded-Uri":["/"]},"tls":{"resumed":false,"version":772,"cipher_suite":4867,"proto":"h2","server_name":"deergrove.woozle.org"}},"headers":{"Referrer-Policy":["strict-origin-when-cross-origin"],"Permissions-Policy":["interest-cohort=()"],"X-Frame-Options":["SAMEORIGIN"],"X-Xss-Protection":["1; mode=block"],"Remote-User":["neale"],"Remote-Groups":["admins,deergrove,fam"],"Remote-Name":["Neale Pickett"],"Date":["Mon, 06 Feb 2023 17:01:31 GMT"],"Content-Length":["0"],"X-Content-Type-Options":["nosniff"],"Remote-Email":["neale@woozle.org"],"Set-Cookie":[]},"status":200}
homelab_caddy.1.s7dc6ketwosz@sweetums | {"level":"debug","ts":1675702892.5301678,"logger":"http.handlers.reverse_proxy","msg":"handling response","handler":0}
homelab_caddy.1.s7dc6ketwosz@sweetums | {"level":"debug","ts":1675702892.5303807,"logger":"http.handlers.reverse_proxy","msg":"selected upstream","dial":"authelia:9091","total_upstreams":1}
homelab_caddy.1.s7dc6ketwosz@sweetums | {"level":"debug","ts":1675702892.5347753,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"authelia:9091","duration":0.0042059,"request":{"remote_ip":"172.18.0.1","remote_port":"55420","proto":"HTTP/2.0","method":"GET","host":"deergrove.woozle.org","uri":"/api/verify?rd=https://auth.woozle.org/","headers":{"Accept":["*/*"],"Remote-Groups":["admins,deergrove,fam"],"X-Forwarded-Method":["GET"],"User-Agent":["curl/7.81.0"],"X-Forwarded-Proto":["https"],"X-Forwarded-For":["172.18.0.1"],"Proxy-Authorization":[],"Cookie":[],"X-Forwarded-Host":["deergrove.woozle.org"],"Remote-User":["neale"],"Remote-Email":["neale@woozle.org"],"Remote-Name":["Neale Pickett"],"X-Forwarded-Uri":["/"]},"tls":{"resumed":false,"version":772,"cipher_suite":4867,"proto":"h2","server_name":"deergrove.woozle.org"}},"headers":{"Www-Authenticate":["Basic realm=\"Authentication required\""],"Date":["Mon, 06 Feb 2023 17:01:31 GMT"],"Content-Type":["text/plain; charset=utf-8"],"Content-Length":["16"]},"status":401}
5. What I already tried:
I have tried various methods involving matchers, but always get a fall-through to the second forward_auth
. I believe what I need is a header_up
that is conditional on a matcher, but I don’t know how to do this.
6. Links to relevant resources:
- Authelia discussing Proxy-Authorization
- The Proxy-Authorization Header which, honestly, hints that Authelia isn’t using this the right way
- Caddy forward_auth