Wildcard certificates now valid for their non-wildcard form

Wildcard certificates (*.your-domain.tld) generated on Fly are now valid for their non-wildcard form (your-domain.tld).

Before yesterday afternoon, none of our generated wildcard certificates included the non-wildcard hostname. Now they do and you don’t have to change anything. This does not apply to previously generated wildcard certificates, only newly created ones or renewals of previous created ones.

In other words: If you add a *.example.com certificate to your app, it will also be valid for example.com and you don’t have to add a second certificate for your bare domain.

I specifically did not use the term “apex” here to describe the non-wildcard hostname because, even though most aren’t used this way, you can have wildcards that aren’t descendants of an apex hostname (e.g. *.test.example.com, its non-wildcard form is therefore test.example.com).

Why we’re making this change

Over the years, not doing this created a lot of confusion. There is an expectation that a wildcard certificate is valid for it’s non-wildcard form. In practice, that’s not how it works unless you specifically configure it that way when requesting / generating the certificate.

An *.example.com certificate is only really valid for <anything>.example.com, it is not valid for example.com or extra.level.example.com.

So when we figured this would not be a disruptive change it would be a net positive for all our users of wildcard certificates, we just did it.

How does it work?

TLS certificates can be valid for multiple “server names”. In the past, we only supported 1 server name (the same value as the “common name” field) per certificate. That’s still how it works for non-wildcard hostname.

There’s a x509 extension called SAN (Subject Alternative Name) that lets you specify, among other things, alternative “DNS” names a certificate is valid for.

Let’s Encrypt ensures this is valid by testing that all requested names pass the challenge they ask of the user to verify. For wildcard certificates, the only acceptable challenge is the dns-01 challenge. To fulfill the challenge, as a Fly user, you need to point a _acme-challenge CNAME from your non-wildcard hostname’s DNS records to our <gibberish specific to your app>.flydns.net record. This is populated with the latest challenge content required for verification. That’s how Let’s Encrypt knows for sure you control the hostname.

Since the dns-01 challenge’s requirements are the same for the wildcard and non-wildcard hostnames, there is nothing more that our users need to do.

Testing the change from a user’s perspective

OpenSSL’s s_client subcommand allows us to look at the returned certificate. There’s a good chance you already have OpenSSL or LibreSSL installed on your local machine.

echo | openssl s_client -connect checking123.example.com:443 -servername checking123.example.com -showcerts 2>/dev/null | openssl x509 -text

Dissecting the command:

  • We pipe an echo of an empty string into the following command’s STDIN echo |
  • openssl s_client
    • -connect checking123.example.com:443 will resolve the hostname to an IP and tell OpenSSL’s client to connect to it on port 443
    • -servername checking123.example.com is the SNI (Server Name Indicator) that the client will send in its ClientHello
    • -showcerts shows the contents of the certificates returned
    • 2 > /dev/null redirects STDERR to /dev/null (which blackholes the information)
  • | openssl x509 -text parses the input and (piped from the OpenSSL client) as x509 and prints a textual representation.

This should result in something like:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            03:87:6e:46:72:34:d1:61:c7:f9:22:4b:39:9f:3d:f2:c9:b0
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=Let's Encrypt, CN=R3
        Validity
            Not Before: Feb 16 21:55:19 2023 GMT
            Not After : May 17 21:55:18 2023 GMT
        Subject: CN=*.example.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:b3:55:f5:b1:32:91:63:10:ca:43:62:7a:fb:c9:
                    b6:3a:58:e6:9d:d5:20:5f:9a:0a:c7:c9:13:f1:eb:
                    72:16:2a:3a:fd:c0:9c:fd:c3:9e:1a:0c:7d:0e:11:
                    c9:12:0b:b6:15:07:17:76:af:d6:07:37:ee:8d:da:
                    be:2a:91:d9:c7
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier:
                C2:5A:4F:AB:DC:C2:AA:99:E4:14:88:40:74:80:F1:B5:E5:DE:F7:7E
            X509v3 Authority Key Identifier:
                keyid:14:2E:B3:17:B7:58:56:CB:AE:50:09:40:E6:1F:AF:9D:8B:14:C2:C6

            Authority Information Access:
                OCSP - URI:http://r3.o.lencr.org
                CA Issuers - URI:http://r3.i.lencr.org/

            X509v3 Subject Alternative Name:
                DNS:*.example.com, DNS:example.com
            X509v3 Certificate Policies:
                Policy: 2.23.140.1.2.1
                Policy: 1.3.6.1.4.1.44947.1.1.1
                  CPS: http://cps.letsencrypt.org

            1.3.6.1.4.1.11129.2.4.2:
                ......v..>.$..M.u.9..X.l].B.z.5.....%.......\pt......G0E. b....[.;.@...+.._8.u..A~....w....!.....d..
%An.*..opk...-....P......v.....|.....=..>.j.g)]...$...4........\pt......G0E.!...UI...8......+.3j.K.]..l.,..f... =.f..s...Q..b.......S....e.U6.r.
    Signature Algorithm: sha256WithRSAEncryption
         2a:5f:13:77:cc:2a:56:37:c6:47:eb:84:f3:74:39:22:fd:b6:
         0c:3a:26:06:c2:aa:e3:0f:cd:63:32:40:9e:75:20:56:7e:89:
         57:fa:d6:ab:77:b8:a9:7f:69:b5:1f:b7:26:1b:fd:d5:87:c0:
         fd:86:51:48:f5:5f:d0:6c:37:59:19:4c:75:09:9e:10:6e:d5:
         7d:50:12:21:d2:da:e5:a0:5f:17:14:a1:5f:49:a6:6c:33:45:
         29:2d:f7:4a:20:e6:f4:59:6e:84:be:c1:5f:30:73:ff:0e:e7:
         45:4e:64:09:8b:a1:9f:8b:0c:b4:8f:61:b7:19:42:0a:fa:8f:
         0e:dc:09:ae:36:3f:49:86:c2:b3:d0:e4:0a:14:16:6c:4d:3b:
         77:cc:7b:af:23:e7:fd:31:b7:07:09:4b:c6:03:c0:50:fc:4b:
         7c:89:86:54:4b:2d:11:b8:48:5a:95:4d:7a:1d:d0:c9:59:90:
         1d:91:64:be:86:91:e2:6e:0e:27:d3:a2:ec:33:30:07:01:d7:
         a3:fd:7a:86:17:02:59:2a:19:2f:bf:34:4d:74:46:55:fc:a5:
         59:c7:96:ef:f7:14:6b:0b:ed:ba:b0:c3:76:fe:be:6b:e0:36:
         b3:a0:24:25:de:62:72:34:52:7a:62:f2:10:b7:3b:e5:0d:3c:
         cd:91:17:c4

That’s it for now!

3 Likes

Is it possible this change created an issue to create wildcard certificates? I tried to create one for “*.sportbrook.be”, and I get the correct check for ‘verified’, but the certificate keeps pending, “not been issued yet”. I tried several times by deleting it and trying again, but without success.

(tried both UI and command line)

It did create an issue. I thought I fixed it. Looking into this one now.

1 Like