403 Forbidden when using Client Certificate

(And Hell) #1

Hello together

I’m currently playing around with client cert based auth on caddy.
But i keep getting 403 Error when i try to connect over the network (localhost somehow works.)

pi@httpsclientcert:~/test $ curl --insecure -E caCert/alice.p12 --cert-type p12                
403 Forbidden 
pi@httpsclientcert:~/test $ curl --insecure -E caCert/alice.p12 --cert-type p12 https://localhost:8080                  
<h1 >Darfst du das?<h1>

For “Bobs” cert, that is not signd with the correct CA I get the bad certificate response on both (localhost and remote). So I amuse the Alice cert is correctly signed.

pi@httpsclientcert:~/test/caCert $ curl --insecure -E bob.p12 --cert-type p12      
curl: (35) error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate                      

pi@httpsclientcert:~/test/caCert $ curl --insecure -E bob.p12 --cert-type p12 https://localhost:8080      
curl: (35) error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate 

Has anyone an idea about what I’m doing worng?

My Caddyfile looks like this:

log ../caddyaccess.log                                                                                                 
tls self_signed {                                                                                                               
                     clients caCert/ca.pem                                                                                           

and I created my certs with these commands:

Create CA
openssl req -nodes -new -x509 -days 3650 -extensions v3_ca -keyout ca.key -out ca.pem  

Create Alice Cert
openssl req -newkey rsa:4096 -keyout alice_key.pem -out alice_csr.pem -nodes -days 365 -subj "/CN=Alice"               

Sign Alice Cert
openssl x509 -req -in alice_csr.pem -CA ca.pem -CAkey ca.key -out alice_cert.pem -set_serial 01 -days 365                

Create p12 fiel for Alice
openssl pkcs12 -export -clcerts -in alice_cert.pem -inkey alice_key.pem -out alice.p12

(Matthew Fay) #2

When you try, what appears in Caddy’s log?

Edit: Also, try adding the -I flag to get header information which could be useful, i.e.: curl -I --insecure -E caCert/alice.p12 --cert-type p12

(And Hell) #3

No logentry is written for that request.

Will try this when I’m back in office.

(Matthew Fay) #4

This heavily implies that Caddy isn’t actually receiving this request. Especially if you’re getting a 403 - some web server is responding, but if it were Caddy, there would be an access log entry for this result.

Let us know what you get back from curl -I, it might tell us more about what you’re contacting at that IP address.

(And Hell) #5

The https certificate I get back is the one create by caddy. Also works without issues if I remove the clients caCert/ca.pem line.

This runs on an frech installation of rasbian on a Pi3. So there should be no other webserver running.

(Matthew Fay) #6

Hmm, that’s interesting. Might be worth investigating as a potential bug. I can’t think of any reason why a client certificate should work on localhost, but fail when accessed by IP address, generate a 403, and then not log it.

Would you mind filling out an issue template at https://github.com/mholt/caddy/issues? (see below)

(Matt Holt) #7

That’s not entirely true; it’s a shortcoming of the current design of one safeguard I recently implemented. See https://github.com/mholt/caddy/pull/2099, particularly StrictHostMatching: https://github.com/mholt/caddy/pull/2099/files#diff-e74f72185700e304b2047c4af2ad1a48R428

When using TLS ClientAuth, the Host header of the HTTP request absolutely must match the SNI value of the handshake.

(Matt Holt) #8

Sorry I’m late on this thread. It’s a good thing to ask about, and important to know if you’re using client auth. It should be documented better but sometimes I like to wait and see how it plays out in the wild before assuming I know how people will encounter a certain feature.

And yes, it’s a feature. :slight_smile: Everything is working as expected.

When your site is protected with TLS client authentication, Caddy has to take some precautions to ensure it doesn’t expose a site that is protected by client auth to a client that is not authorized. This could happen in older versions of Caddy, but has since been fixed, and the fix is what you’re bumping up into here. Imagine two sites, A and B, both on the same port, where A is protected with client auth but B is not. An attacker simply crafts a TLS connection for B, sending B in the ServerNameIndication (SNI) extension. That handshake succeeds because B’s TLS configuration has no client auth. But then the HTTP request made through that connection sends A in the Host header of the request. That will get routed through A’s middleware chain. That’s bad, because client was not authorized for A; only B.

So, in the presence of client auth, the Host header must match the SNI value exactly.

If it doesn’t, Caddy responds with 403 Forbidden and closes the connection. Your log didn’t pick up that error because the request logger is chained into the middleware stack. The problem is, Caddy was unable to answer the question: “Which middleware stack should we execute?” because of the conflicting SNI and Host values. So it chose none of them as a security precaution, and thus no log was ever invoked. Sorry.

Ready for more fun? Clients don’t (and shouldn’t, according to spec) send IP addresses in SNI, because they are reserved specifically for server names, not addresses. That’s why localhost worked (it matches your site definition), but curl’ing with did not: the SNI value is empty, which is not the same as wildcard server name (*).

It’s subtle, but it’s there for a good reason. Hope this helps.

(And Hell) #9

Thank you for that detailed response.

After changing from the IP to the hostname everything works.

curl --insecure -E caCert/alice.p12 --cert-type p12 https://raspberrypi:8080