1. The problem I’m having:
I want to use caddy with mTLS and an authelia fallback for users who are not given certificates. I have a Caddyfile which works but due to things not running in order, i am unsure if it is actually secure.
This is the Caddyfile snippet i use:
(setAuthLevel) {
vars authEmail {args[0]}
vars authGroups {args[1]}
@detect expression {vars.authGroups} == "detect"
handle @detect {
map {vars.authEmail} {authGroup} {
email@example.com owner
default unknown
}
vars authGroups {authGroup}
}
map {vars.authGroups} {authLevel} {
~(^|.*,)owner($|,.*) 1
~(^|.*,)admin($|,.*) 10
~(^|.*,)user($|,.*) 100
default 1000
}
}
(authenticated) {
tls {
protocols tls1.3
client_auth {
mode verify_if_given
trust_pool file /data/mtls/root.crt
}
}
route {
@has_email expression {http.request.tls.client.san.emails.0} != null
route @has_email {
import setAuthLevel {http.request.tls.client.san.emails.0} detect
}
@invalid_cert expression {http.request.tls.client.subject} == null || {vars.authGroups} == "unknown"
reverse_proxy @invalid_cert authelia:9091 {
method GET
rewrite /api/authz/forward-auth
header_up X-Forwarded-Method {method}
header_up X-Forwarded-Uri {uri}
@good status 2xx
handle_response @good {
import setAuthLevel {rp.header.Remote-Email} {rp.header.Remote-Groups}
}
}
log_append user_email {vars.authEmail}
log_append user_level {authLevel}
@authorised expression int({authLevel}) <= int({args[0]})
route @authorised {
{block}
}
error 403
}
}
Initially, it was not working but when i changed all the handle directives to route, it started working. I think this is because of the order difference between the 2 directives. I also could not figure out a way to get the groups from ldap for the users with certificates but since only a few users have certificates, i opted to put it in the Caddyfile.
This is how i use the snippet:
domain.example.com {
log
import authenticated 10 {
reverse_proxy http://192.168.1.17:8080
}
}
And i generated the certificates with these scripts
createRoot.sh:
#! /bin/sh
openssl req -x509 -newkey rsa:4096 -keyout root.key -out root.crt -subj "/CN=example" -days 3650
createClient.sh:
#! /bin/sh
if [ "$#" -lt 2 ]; then
echo "Missing parameters. Run the script with './createClient.sh cert_name email_address'"
exit 1
fi
mkdir -p "./$1"
openssl req -newkey rsa:4096 -keyout "./$1/client.key" -subj "/CN=example" -addext "subjectAltName = email:$2" -out "./$1/client.csr"
openssl x509 -req -in "./$1/client.csr" -CA root.crt -CAkey root.key -CAcreateserial -copy_extensions copy -extensions san -out "./$1/client.crt" -days 730
openssl pkcs12 -export -in "./$1/client.crt" -inkey "./$1/client.key" -certfile root.crt -name "$1" -out "./$1/client.p12"
2. Error messages and/or full log output:
There is no error. I am just wanting to check if authelia can be bypassed due to the order of operations.
3. Caddy version:
v2.10.2
4. How I installed and ran Caddy:
a. System environment:
Running in Docker
b. Command:
docker compose up -d
c. Service/unit/compose file:
services:
caddy:
image: caddy:2
user: 1000:1000
ports:
- '80:80'
- '443:443'
env_file: ./caddy/vars.env
volumes:
- ./caddy/conf:/etc/caddy
- ./caddy/data:/data
- ./caddy/config:/config
restart: unless-stopped
d. My complete Caddy config:
Provided in section 1
5. Links to relevant resources:
I looked at this page but since everything is inside a route, i am hoping it runs in order