Reverse proxy + tls client_auth depending on path

1. The problem I’m having:

I want to reverse_proxy somewhere. Depending on the path I’d like to either require no client authentication or mtls / client_auth - e.g. for /admin/*.
I dont need authentication towards the backend.

2. Error messages and/or full log output:

Error: adapting config using caddyfile: parsing caddyfile tokens for 'handle': directive 'tls' is not an ordered HTTP handler, so it cannot be used here - try placing within a route block or using the order global option

When I try with route {}:

Error: adapting config using caddyfile: parsing caddyfile tokens for 'route': tls directive returned something other than an HTTP route or subroute: &caddytls.InternalIssuer{CA:"", Lifetime:0, SignWithRoot:false, ca:(*caddypki.CA)(nil), logger:(*zap.Logger)(nil)} (only handler directives can be used in routes), at /etc/caddy/Caddyfile:241

3. Caddy version:

v2.7.6

4. How I installed and ran Caddy:

xcaddy AFAIR

a. System environment:

Debian 11, linux 5.10.0-27-armmp

b. Command:

# systemd unit:
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile

c. Service/unit/compose file:

[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target

[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

d. My complete Caddy config:

domain.tld {
        import errors # handle_errors {}
        handle /admin/* {
                import mtls_conf
                reverse_proxy 192.168.xxx:80
        }
        handle {
                reverse_proxy 192.168.xxx:80
        }
        import log_conf # log {}
        import tls_conf # tls foo@bar.tld {  on_demand }
}
(mtls_conf) {
    tls internal {
        client_auth {
            mode require_and_verify
            trusted_ca_cert_file /etc/caddy/ca-cert-bundle.crt
        }
    }
}

5. Links to relevant resources:

It’s impossible to configure client_auth based on request path, because the TLS handshake happens before HTTP is event sent down the wire, so the server has no idea what the request path is until after TLS is agreed upon.

Use subdomains instead. Domains are passed via TLS-SNI during the handshake, so you can configure Caddy to have different TLS config per subdomain.

As the error tells you, the tls directive is not an HTTP handler directive, it cannot go within handle, it must go top-level within a site block.

2 Likes