Caddy: request: additional tls client certificate placeholders

Created on 17 Jul 2020  ·  19Comments  ·  Source: caddyserver/caddy

As far as I can see this is available to set as values for upstream servers, something you'd usually do when terminating required/optional mutual-TLS and needed to work with the certificate in the upstream application.

// `{http.request.tls.client.fingerprint}` | The SHA256 checksum of the client certificate
// `{http.request.tls.client.public_key}` | The public key of the client certificate.
// `{http.request.tls.client.public_key_sha256}` | The SHA256 checksum of the client's public key.
// `{http.request.tls.client.issuer}` | The issuer DN of the client certificate
// `{http.request.tls.client.serial}` | The serial number of the client certificate
// `{http.request.tls.client.subject}` | The subject DN of the client certificate
// `{http.request.tls.client.san.dns_names.*}` | SAN DNS names(index optional)
// `{http.request.tls.client.san.emails.*}` | SAN email addresses (index optional)
// `{http.request.tls.client.san.ips.*}` | SAN IP addresses (index optional)
// `{http.request.tls.client.san.uris.*}` | SAN URIs (index optional)

For OAuth 2.0 servers with mTLS support to be deployed behind Caddy I lack the following

  • PEM-encoded value of the certificate (not just the public key in it)
  • whether the client certificate was verified or not (in case of client_auth mode being request)

This is analogous apache's variables SSL_CLIENT_CERT, SSL_CLIENT_CERT_CHAIN and SSL_CLIENT_VERIFY (values NONE, SUCCESS, GENEROUS or FAILED:reason)

feature request

Most helpful comment

I'd like to work on this.

All 19 comments

@panva Thanks for the feature request. I'm sure we or someone will be able to work that in.

FYI:

whether the client certificate was verified or not (in case of client_auth mode being request)

Note the docs on the request mode:

Ask clients for a certificate, but allow even if there isn't one; do not verify it

So a client cert used with "request" mode will never be verified.

Which is fine for self signed certs only. What mode would attempt to verify based kn configured CA but allow unverified requests through as well?

Which is fine for self signed certs only.

I am not sure why that has anything to do with it; a cert's signer is orthogonal to client authentication modes.

What mode would attempt to verify based kn configured CA but allow unverified requests through as well?

To ask for a client cert and make verification optional, use:

verify_if_given | Ask clients for a certificate; allow even if there isn't one, but verify it if there is

Which is fine for self signed certs only.

I am not sure why that has anything to do with it; a cert's signer is orthogonal to client authentication modes.

I mean for when the upstream app only wants to work with self signed certs where it takes the used certificate and matches it against pre-registered certs on e.g. an oauth client. But that's just one mode of operation, there's also one where only PKI based certs are allowed (which we can solve with require), but also a hybrid one - where oauth clients exist both configured to authenticate with PKI certs as well as other clients configured to use pre-registered self signed certs.

What mode would attempt to verify based on configured CA but allow unverified requests through as well?

To ask for a client cert and make verification optional, use:

verify_if_given | Ask clients for a certificate; allow even if there isn't one, but verify it if there is

I'm not sure that fits - Ask clients for a cert ✅ and verify against configured CA certs ✅, allow even if there isn't one (irrelevant), allow even if verification failed (self-signed) ❓ and report the status in a SSL_CLIENT_VERIFY like header ❓. That would be nginx's optional_no_ca behaviour where self signed is allowed through but if ssl_trusted_certificate is configured it is still checked. The verification result is then available in var ssl_client_verify

I'd like to work on this.

@gdhameeja Great! The placeholders, at least, should be pretty straightforward.

I'm new to this repository and the the issue in general. Could someone please guide me on the issue.

@gdhameeja Sure, the tls placeholders for the HTTP handlers are set in this function: https://github.com/caddyserver/caddy/blob/6a14e2c2a8881d5e90f1ee363ec4662a3f87402b/modules/caddyhttp/replacer.go#L250

There are tests in the adjacent file: https://github.com/caddyserver/caddy/blob/6a14e2c2a8881d5e90f1ee363ec4662a3f87402b/modules/caddyhttp/replacer_test.go

Looks like what is being asked for is a placeholder for the PEM content of the client certificate.

@mholt What does it mean by placeholders? I tried searching the tls rfc for the term but found nothing. So far reading some stackoverflow answers and some documentation I understand that the client could send the client certificate for authentication. I also tried searching the caddy documentation for "placeholder" but didn't find anything. By "placeholder" do you mean the "-----BEGIN CERTIFICATE REQUEST-----" header? Also is there a chat kind of community where we can discuss doubts in real time? Thank you.

@gdhameeja A placeholder is a "Caddy-ism" (a term that Caddy uses): https://caddyserver.com/docs/conventions#placeholders

Basically, by putting something like {http.request.tls.client.certificate_pem} in their config, Caddy can expand it out to the PEM value of the certificate. (This is expensive, as it requires re-encoding the certificate on-the-fly, just FYI.)

Also is there a chat kind of community where we can discuss doubts in real time?

We do have a Slack for developers and sponsors -- if you're serious about working on this then we can definitely send you an invite! Just let me know which email to invite. We don't use Slack for support though since chats get lost to time.

Great. Yes I'm determined to contribute to caddy regularly. Mail: [email protected]
Thanks for the info.

whether the client certificate was verified or not (in case of client_auth mode being request)

If this is still pending, I can take this up.

@gdhameeja I believe it is, feel free to have it!

I've spent a couple hours looking into this today... can we go over what still needs to be done? I am a little confused and want to be sure I understand.

My understanding is that you want to request a client certificate, and verify if it is given (verify_if_given), except you want to allow the connection even if verification fails. Is that correct?

I am not sure I know of a mode ("client auth type") in the Go standard library that works like that.

If what I've said is all accurate, this might be a feature request better suited for the Go standard library.

can we go over what still needs to be done? I am a little confused and want to be sure I understand.

@mholt it's really just the missing placeholders

  • value of the certificate (SSL_CLIENT_CERT)
  • verification status (SSL_CLIENT_VERIFY)

@panva So is my understanding of the behavior you want correct then?

value of the certificate (SSL_CLIENT_CERT)

I believe this is already available as {http.request.tls.client.certificate_pem} (assuming you mean PEM format?)

verification status (SSL_CLIENT_VERIFY)

This is what I'm saying doesn't make sense with the current Go standard library. If the cert is verified and passes, the handshake will succeed. If the cert is verified and fails, it will abort the handshake. I don't think there's a mode (see my comment above) that says "accept a failed verification" -- because then there's no point in verifying.

I believe this is already available as {http.request.tls.client.certificate_pem} (assuming you mean PEM format?)

Cool, when was {http.request.tls.client.certificate_pem} added?

This is what I'm saying doesn't make sense with the current Go standard library. If the cert is verified and passes, the handshake will succeed. If the cert is verified and fails, it will abort the handshake. I don't think there's a mode (see my comment above) that says "accept a failed verification" -- because then there's no point in verifying.

Screenshot 2020-10-29 at 21 13 53

So you're saying there's no optional_no_ca in Go?

I'm not sure if i'm getting the point across here. Ideally I can set

  • require cert - validate against CA
  • optional cert - validate against CA - the variable is then useful to know no cert was passed in, what you suggest is one uses the presence of other client auth placeholders to get this info instead?
  • optional cert - allow self-signed
  • require cert - allow self-signed

Cool, when was {http.request.tls.client.certificate_pem} added?

Last month, by @gdhameeja :) https://github.com/caddyserver/caddy/commit/b01bb275b395643542ceca4fbc82bedea8e43937 - I just realized the commit is missing adding it to the docs in this table: https://github.com/caddyserver/caddy/blob/c9fdff9976da38c650a51cd3fb4f2667853ff627/modules/caddyhttp/app.go#L48

So you're saying there's no optional_no_ca in Go?

The docs for that say:

requests the client certificate but does not require it to be signed by a trusted CA certificate

This is essentially the same thing as request or require modes in Caddy, which correspond to ClientAuthTypes in the stdlib. https://caddyserver.com/docs/json/apps/http/servers/tls_connection_policies/client_authentication/mode/

Then i think we can close this. Thank you very much.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

whs picture whs  ·  3Comments

ericmdantas picture ericmdantas  ·  3Comments

xfzka picture xfzka  ·  3Comments

SteffenDE picture SteffenDE  ·  3Comments

PhilmacFLy picture PhilmacFLy  ·  3Comments