Incorrect SNI Configuration Means TLS-SNI Challenge Doesn't Pass

I cannot renew the certficate for one of my apache2 virtual hosts, because letsencrypt (obviously) does not use the certificate retrieved when using tls-sni-01. Here’s what I get:

 - The following errors were reported by the server:

   Domain: ks.eit.h-da.de
   Type:   unauthorized
   Detail: Incorrect validation certificate for tls-sni-01 challenge.
   Requested d976adad886868faf3e975a2c206d029.88bbaeb709da69d0294919b6
   ffd5c2d6.acme.invalid from 141.100.108.233:443. Received 2
   certificate(s), first certificate had names "cypht.eit.h-da.de"

Now, when I look at ssllabs’ analyses, I find that the certificate for cypht.eit.h-da.de is indeed returned, but only when not using SNI: “only very old clients get this certificate”. Why does letsencrypt behave like a very old client, i.e. use the certificate returned when not using SNI?

(Note: ssllabs shows the certificate for “cypht.eit.h-da.de” also when testing my other virtual hosts, but I never had renewal problems with those.)

It doesn't. The challenge name is tls-sni-01 (notice the sni ;)) for a reason.

Looks like an Apache configuration not 100 % compatible with certbot.

hi @mnlipp

review the functioning of the TLS-SNI Challenge

I can see you are not serving a different certificate on the acme.invalid SNI. This indicated an incorrect setup

With SNI:

openssl s_client -connect ks.eit.h-da.de:443 -servername d976adad886868faf3e975a2c206d029.88bbaeb709da69d0294919b6ffd5c2d6.acme.invalid

Without SNI

openssl s_client -connect ks.eit.h-da.de:443

Andrei

Well, any idea what could cause this in apache? As mentioned, there are several virtual hosts and only this one causes problems.

But I’m still not convinced that this is caused by apache. It works with the other virtual hosts and they obviously also report the redundant certificate.

How this should be

Certs -

One Self Signed (challenge-example.acme.invalid and one valid cert tls-sni.firecube.xyz)

I have a domain called tls-sni.firecube.xyz

Configuration in IIS

Two Bindings - one for the domain and for an example acme.invalid self signed certificate

Testing with Browser:

Testing with OpenSSL

Accessing with SNI for tls-sni.firecube.xyz

openssl s_client -connect tls-sni.firecube.xyz:443 -servername tls-sni.firecube.xyz

Accessing with SNI for challenge.example.acme.invalid

openssl s_client -connect tls-sni.firecube.xyz:443 -servername challenge.example.acme.invalid

As you can see the server serves up the correct certificate based on the bindings.

I am going to do a tutorial on apache and nginx as well but the idea is the same.

Andrei

Hi @ahaw021,

thanks a lot for the hints. But I’m still confused. Because
openssl s_client -connect ks.eit.h-da.de:443 -servername ks.eit.h-da.de does return the proper certificate:

CONNECTED(00000003)
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = ks.eit.h-da.de
verify return:1
---
Certificate chain
 0 s:/CN=ks.eit.h-da.de
   i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
   i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---

So doesn’t this simply mean that openssl by default uses the hostname only for ip lookup, but not as servername?

hi @mnlipp

TLS-SNI is one of the most misunderstood challenges

Lets go through what the draft that boulder adhere to https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-7.3

Given a Challenge/Response pair, the ACME server verifies the
client’s control of the domain by verifying that the TLS server was
configured appropriately.

Given a Challenge/Response pair, the ACME server verifies the
client’s control of the domain by verifying that the TLS server was
configured appropriately.

  1. Choose a subset of the N iterations to check, according to local
    policy.

  2. For each iteration, compute the Zi-value from the key
    authorization in the same way as the client.

  3. Open a TLS connection to the domain name being validated on the
    requested port, presenting the value
    "<Zi[0:32]>.<Zi[32:64]>.acme.invalid" in the SNI field (where the
    comparison is case-insensitive).

  4. Verify that the certificate contains a subjectAltName extension
    with the dNSName of “<Z[0:32]>.<Z[32:64]>.acme.invalid”, and that
    no other dNSName entries of the form “*.acme.invalid” are present
    in the subjectAltName extension.

So here are the gotachas and why I did my demo the way I did

The certificate that is currently being used for the domain SHOULD NOT be the one that is evaluated.

You should have a more specific binding/VHOST/serverblock which serves a certificate based on the challenge

My understanding is that if implemented properly a client should add an additional binding (like my example-challenge.acme.invalid) to the server but NOT INTERFERE with the primary domain bindings (my tls-sni.firecube.xyz )

One of the challenges I am facing is finding a client which will just give me the challenge token :frowning: most of them are too clever and try to do the TLS-SNI challenge themselves :frowning: :frowning:

Once I crack that puzzle (currently working through it with acme.py) I will be able to give you more solid directions

Andrei

Hi @ahaw021,

I certainly haven’t got the deep understanding of the protocol that you have. My assumption was simply that the client would pass the servername in its connection request.

Anyway, the problem is solved. To get a better idea about what really happens, I used the --manual option. Surprisingly, this went through without any problem. After this I tried --apache again (“forcing”, i.e. making use of the 5 in 7 days “extra” certificates) and this time it went through as well.

Let’s see what happens in 3 months…

And thanks again! I’ll have a closer look at the protocol soon…

1 Like

Howcome "obviously"? Every virtualhost is supposed to serve only one certificate, without any "redundant" certs.

As mentioned I’m (obviously) not an expert on this. But I think that “only very old clients get this certificate” simply means that this certificate is returned when making a request without SNI (actually, that’s what ssllabs puts in the title of the second certificate). Requesting a connection without supplying a server name allows the server to choose one (better: use the default) which seems to be “cypht” in my case.

As the openssl connection tests show, my server returns the proper certificate when the connection is made with the servername option and the cypth-certificate when making the connection without a server name. This seems perfectly logical to me.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.