Motivation

I have got quite a few domains that I use for different things. Typically, most up to all of them should have valid TLS certificates. Now in an earlier post, I wrote down how generate wildcard certificates, using the DNS challenge. Still, it is sometimes useful to have the “normal” HTTP challenge. Now I wanted to achive that, without having acme.sh on more than one host - I wanted to manage all my certificates on one server.

The challenge was: I have got multiple exposed hosts that are running different services. Some HTTP challenges should be handled on one server, while others had to be handled on another one. I had to find a way around that.

A word on security

Distributing TLS certificates can be dangerous, as the private key should typically not be given away. In my case, this is not too hard - as on all these virtual machines, I am the only person who has shell access to it. Additionally, most of them are only available on my local network.

Setup nginx

The solution was surprisingly simple: When requesting a HTTP challenge for subdomain.tech-tales.blog then Let’s encrypt expects a specific file to be found in http://subdomain.tech-tales.blog/.well-known/acme-challenge/something. So let’s just redirect this path to my certificate server!

I updated my default nginx configuration as follows:

server {
  listen 80 default_server;
  listen [::]:80 default_server;

  # ...

  location /.well-known/acme-challenge/ {
    proxy_pass http://ip.of.cert.server/.well-known/acme-challenge/;
  }
}

Before, I just redirected every HTTP request to HTTPS. Newly, this is still the case - unless the request is something in /.well-known/acme-challenge/; this one will be forwarded to my certificate server.

The certificate server got a similar setup:

server {

  listen 80;
  server_name ip.of.cert.server;

  root /var/www/acme-http;

}

Setup acme.sh

Now the certificate server, or to be more precise, acme.sh, just has to be told where to put the challenge file. This also was not too hard:

acme.sh --issue --server letsencrypt_test -d subdomain.tech-tales.blog --webroot /var/www/acme-http/

That way, the following happens:

  • acme.sh issues a TLS certificate for subdomain.tech-tales.blog.
  • It is told to use /var/www/acme-http/ as webroot.
  • acme.sh receives a challenge file from Let’s encrypt and places it in /var/www/acme-http/.well-known/acme-challenge/something.
  • Let’s encrypt requests http://subdomain.tech-tales.blog/.well-known/acme-challenge/something. This is first responded by nginx on my cloud server and reverse proxied to my certificate server. The certificate server finds the file placed by acme.sh and delivers it.
  • Let’s Encrypt is happy and I get my certificate!
  • Note: Now the certificate still has to be deployed back to the cloud server! I will write about this in another post.

Conclusion

I like the concept of having all TLS certificates stored on one server, as I don’t loose the overview that way. And with this setup, I can combine both HTTP and DNS challenge.