Route specific mTLS authentication

1. Caddy version (caddy version):

2.4.0-alpine

2. How I run Caddy:

Docker

d. My complete Caddyfile or JSON config:

{
  email ...
}

(mTLS) {
    tls {
        client_auth {
            mode require_and_verify
            trusted_ca_cert_file /certs/server.cer
        }
    }
}

https://our.organization.fi {
  handle / {
    file_server
  }

  handle  {
    reverse_proxy {
      to https://shibboleth
      transport http {
        tls_insecure_skip_verify
      }
    }
  }
}

https://our.organization.fi/service-1/* {
  import mTLS
  
  handle {
    reverse_proxy service-1-container:8080
  }
}

3. The problem I’m having:

Is it possible to have route specific mTLS authentication? For example route service-1/* would need a client certificate but route api/* would not.

4. Error messages and/or full log output:

5. What I already tried:

To define new site block with https://our.organization.fi/service-1* requiring mTLS for all routes inside that block. Instead requiring certificate for routes service-1/* Caddy requires now certificate for all routes defined in any site block.

6. Links to relevant resources:

Unfortunately, that’s not possible right now, because the TLS auth is performed when the TLS handshake happens, which is before Caddy has decrypted the bytes and can look at the HTTP request.

Any thoughts @matt ?

You’d have to at least request a client cert at the TLS handshake, then enforce the certificate later in your HTTP routes.

Since not all TLS connections require a client cert in your case, you’d want to set a looser mode of client auth such as request or verify_if_given instead of require_and_verify.

Then you’d probably want to use the {http.request.tls.client.*} placeholders here:

along with a matcher, maybe the expression matcher, to enforce the client cert at the HTTP routing layer.

If this works, is common enough, and cumbersome at the same time, maybe a first-class matcher for TLS connections is warranted.

1 Like

Thanks @matt for the clarification! It sounds indeed a bit cumbersome to configure. Probably we will also look into alternative ways to verify that the sender is whom it says it is, e.g. with private key signed JWT tokens.

Just a silly question, is it possible to include the client certificates when reverse proxying the request? By this, the certificate could be verified in the service behind Caddy.

Yes, you can pass it through as a request header with header_up and using one of those request placeholders.

You may need to wait for the next release or build from source if you take this approach though, since a new header-safe placeholder was just recently added to the codebase:

Thanks @francislavoie, this is probably then the way to go for us!

1 Like