Chicken-or-egg on port 443 vs 80 on challenge

I have a chicken-or-the-egg situation on my server. I am using acme_tiny.py and lighttpd to serve up the challenge token files. I want to run with port 80 disabled (it is actually used for a different http-to-stream proxy), and serve up token files on port 443 https. This works fine for any SAN subdomain that I have already obtained an earlier certificate for. But it doesn't work for any SAN subdomain the first time, creating a chicken-or-the egg situation or death by recursion. I must first have a certificate before I can get a certificate?

Is there a way in the protocol to specify I want only to perform https challenge responses and have it work for a subdomain I don't yet have a certificate for.

My domain is:
va1der.net plus SAN's (upload.va1der.net, www, mail, etc)

My web server is (include version):
lighttpd

The operating system my web server runs on is (include version):
Linux, Debian 12

If you redirect all HTTP to HTTPS, then the HTTPS must use a certificate to converse.
[but it can be any certificate - even a self-signed cert OR any valid cert from any other FQDN]

6 Likes

From the HTTP-01 challenge

"Our implementation of the HTTP-01 challenge follows redirects, up to 10 redirects deep. It only accepts redirects to “http:” or “https:”, and only to ports 80 or 443. It does not accept redirects to IP addresses. When redirected to an HTTPS URL, it does not validate certificates (since this challenge is intended to bootstrap valid certificates, it may encounter self-signed or expired certificates along the way)."

5 Likes

My issue is not with a redirect, and I don't care if it validates certificates. My issue is that I have another service on port 80.

For any certificate for a domain which is a renewal, the challenge will first try https on port 443 and succeeds because that is where my web server lives.

For any SAN on a (sub)-domain which is not a renewal, the challenge will first try http on port 80. If that fails, even with with a rejected connection (for example if I first shut down my port 80 service), the verification fails and it will not then try 443.

This is puzzling. Either: 1) try 443 first all the time - there is no reason why port 80 should ever be the first stop any more. Or 2) If port 80 fails on a rejected connection, move on to 443 and try that.

No, that is not correct. An http challenge always starts with an HTTP request to port 80. You may redirect that to https port 443. Always starts on port 80 for both new certs and renewals

4 Likes

Either Letsencrypt starts with 443 on these SANs, or it is starting with 80 but automatically proceeding to 443 without failing the challenge for domains which already have certificates.

I have one VPS with SANs that have not changed in three years, and it has not had port 80 enabled in that time. It has successfully renewed its certificates a dozen times with no port 80.

This IS the way Letsencrypt is working.

What I am saying is that I want the same behaviour for new SANs as existing ones. This is not an unreasonable request, since it is clearly already established behaviour.

What kind of challenge do they use? Because TLS-ALPN starts with HTTPS on port 443. An HTTP Challenge starts on port 80. These are ACME industry standard requirements.

What ACME Client are you using and what is the command format? Perhaps knowing more will help us resolve this riddle.

Also please provide these other domain name(s) you say work without having port 80 open. Perhaps you don't notice something that we will. You haven't given us much to work with. Your va1der.net has port 80 open, for example.

4 Likes

HTTP-01

acme_tiny.py

# acme_tiny.py --help
usage: acme_tiny.py [-h] --account-key ACCOUNT_KEY --csr CSR --acme-dir ACME_DIR [--quiet] [--disable-check] [--directory-url DIRECTORY_URL] [--ca CA] [--contact [CONTACT ...]]

This script automates the process of getting a signed TLS certificate from Let's Encrypt using
the ACME protocol. It will need to be run on your server and have access to your private
account key, so PLEASE READ THROUGH IT! It's only ~200 lines, so it won't take long.

Example Usage:
python acme_tiny.py --account-key ./account.key --csr ./domain.csr --acme-dir /usr/share/nginx/html/.well-known/acme-challenge/ > signed_chain.crt

Example Crontab Renewal (once per month):
0 0 1 * * python /path/to/acme_tiny.py --account-key /path/to/account.key --csr /path/to/domain.csr --acme-dir /usr/share/nginx/html/.well-known/acme-challenge/ > /path/to/signed_chain.crt 2>> /var/log/acme_tiny.log

options:
  -h, --help            show this help message and exit
  --account-key ACCOUNT_KEY
                        path to your Let's Encrypt account private key
  --csr CSR             path to your certificate signing request
  --acme-dir ACME_DIR   path to the .well-known/acme-challenge/ directory
  --quiet               suppress output except for errors
  --disable-check       disable checking if the challenge file is hosted correctly before telling the CA
  --directory-url DIRECTORY_URL
                        certificate authority directory url, default is Let's Encrypt
  --ca CA               DEPRECATED! USE --directory-url INSTEAD!
  --contact [CONTACT ...]
                        Contact details (e.g. mailto:aaa@bbb.com) for your account-key

I have a rather extensive bash script to execute acme_tiny.py without root privs and handle the resulting certificates.

I employ port 80 for ALLCALL protocol. Technically it's http but it doesn't serve up any files. You can try and point a browser to it - no matter what you request you'll get a 418 "I'm a teapot" response.

Renewal log:
------------
Parsing account key...
Parsing CSR...
Found domains: wiki.va1der.net, mail.va1der.net, qro.va1der.net, proxy.va1der.net, quantum.va1der.net, artoo.va1der.net, qro1.va1der.net, conference.va1der.net, va1der.net, deetoo.va1der.net, upload.va1der.net, www.va1der.net
Getting directory...
Directory found!
Registering account...
Already registered!
Updated contact details:
mailto:keymaster@va1der.ca
Creating new order...
Order created!
Verifying artoo.va1der.net...
artoo.va1der.net verified!
Verifying conference.va1der.net...
conference.va1der.net verified!
Verifying deetoo.va1der.net...
deetoo.va1der.net verified!
Verifying mail.va1der.net...
mail.va1der.net verified!
Verifying proxy.va1der.net...
proxy.va1der.net verified!
Verifying qro.va1der.net...
qro.va1der.net verified!
Verifying qro1.va1der.net...
qro1.va1der.net verified!
Verifying quantum.va1der.net...
quantum.va1der.net verified!
Verifying upload.va1der.net...
upload.va1der.net verified!
Verifying va1der.net...
va1der.net verified!
Verifying wiki.va1der.net...
wiki.va1der.net verified!
Verifying www.va1der.net...
www.va1der.net verified!
Signing certificate...
Certificate signed!

The above is my test server. Since I did not add a SAN I was able to renew with no errors even though port 80 has ALLCALL server on it.

I just checked artoo.va1der.net. It has a short and only recent cert history. Once the first is issued Let's Encrypt caches a successful challenge for 30 days. So, renewals will occur without any challenge at all for 30 days. It won't even look at port 443 :slight_smile:

Details of this cache: FAQ - Let's Encrypt

I was hoping for an HTTP challenge for a domain you said works for many years.

4 Likes

The domains I gave are the ones I'm writing about.. the ones I want to get work properly with just 443 because I am adding SAN almost daily.

Main (established) server is va1der.ca, mail.va1der.ca, www.va1der.ca, productrevue.ca (plus mail, www, etc), mccullochcentre.ca, mccullochcenter.ca. That has run for years.

This is getting bogged down in irrelevancies though. Main point is getting letsencrypt to stop failing out on a port 80 rejection and only fail out on port 80 AND port 443 rejection on all domains and in all circumstances. What will this take?

Proper configuration of your webserver.

Edit

Using the online tool Let's Debug yields these results for va1der.ca
https://letsdebug.net/va1der.ca/2296557

ANotWorking
Error
va1der.ca has an A (IPv4) record (107.6.27.142) but a request to this address over port 80 did not succeed. Your web server must have at least one working IPv4 or IPv6 address.
Get "http://va1der.ca/.well-known/acme-challenge/letsdebug-test": dial tcp 107.6.27.142:80: connect: connection refused

Trace:
@0ms: Making a request to http://va1der.ca/.well-known/acme-challenge/letsdebug-test (using initial IP 107.6.27.142)
@0ms: Dialing 107.6.27.142
@113ms: Experienced error: dial tcp 107.6.27.142:80: connect: connection refused
IssueFromLetsEncrypt
Error
A test authorization for va1der.ca to the Let's Encrypt staging service has revealed issues that may prevent any certificate for this domain being issued.
107.6.27.142: Fetching http://va1der.ca/.well-known/acme-challenge/Gh1k0JQw6f763xOwu7xL0G-HQ3Dt8w-x9f4gS20GYq0: Connection refused

3 Likes

FFS I just added a SAN there and was trying it.

Can we please focus on the actual issue? Why is this hard? Port 80 isn't a thing. I use it for something more important. LE will already accept port 443, why can we not get it to do so at all times? Please answer THAT rather than other snide side issues.

And it looks like Port 80 is Closed; The HTTP-01 challenge states "The HTTP-01 challenge can only be done on port 80."
Best Practice - Keep Port 80 Open

4 Likes

Why NOT? Please explain.

2 Likes

Hi @Kurt,

You can please switch to the TLS-ALPN-01 challenge type.

Because of the challenge type you said you are using.

4 Likes

Because RFC8555, Section 8.3 says:

This request MUST be sent to TCP port 80 on the HTTP server.

We can be redirected to port 443 from there, but the initial request MUST be made to port 80. We don't have a choice in the matter. I'm sorry it makes your system difficult. As the others have said, I recommend either switching to TLS-ALPN-01 (which happens entirely over port 443) or to DNS-01 (which doesn't require an open port at all).

7 Likes

And provides the ability to issue a wildcard certificate, which could be useful if one uses many (single level) subdomains.

4 Likes

For years port 80's sole purpose has been to forward browsers that started with http over to https. Now browsers don't even do that any more. There is no purpose for http on port 80 outside listening on localhost for ssh-forwarded connections to local web applications like phpmyadmin that you don't want to expose to the wider internet.

So port 80 has no purpose.

From the page you referenced:

So the 443 test uses software no one outside Google and other enterprise users have. So.... for the vast majority of the internet it's useless. Which you very well know.

In the wider scheme, what is troubling is:
The internet moves to https on 443, LetsEncrypt response = port 80 requirement
The internet moves to QUIC on UDP, LetsEncrypt response = port 443 TCP TLS requirement

Letsencrypt could move TODAY to lead with https on 443 for http-01. That makes sense.

What is hard about that?

@Kurt, Kindly wait to see if there are more knowledgeable Let's Encrypt community volunteers willing to assist.

2 Likes

The reference also says " this (and Caddy already does)"

3 Likes