A lot of system administrators are faced with the challenge of obtaining TLS certificates for internal machines
that are not exposed to the public. In these scenarios, a domain is mapped to a private IP address by a link-local
DNS server. For example, when a user on the internal network accesses https://example.com
, their browser connects to
172.16.0.1:443
. HTTP-based challenges don’t work with the internal machines. Let’s encrypt cannot connect to the internal servers.
However, there are a few solutions to obtain TLS certificates from a CA.
- Use an internal CA to sign certificates or self-signed certificates. The disadvantage is that each user on every device needs to trust the custom certificate.
- Use Let’s Encrypt (or any other CA) with a challenge that doesn’t require HTTP access to that machine. For example, the internal server could interact with a DNS service to pass a DNS-based challenge and obtain certificates.
- It is also possible to use an ad-hoc solution with a public-facing server that handles the public requests
to
https://example.org
. A system administrator performs the HTTP-based challenge on the public counterpart and copies the certificates to the internal machine. Although this is a very simple solution, it requires manual actions every two to three months when the certificates approach their expiry date.
There is another lesser-know solution, that leverages Let’s Encrypt accounts.
Concept
The key insight is that once an account proves eligibility for a certificate, e.g., via the HTTP challenge, Let’s Encrypt will mint multiple certificates for the same domain. The following sketch illustrates the idea.
The public surrogate server, reachable via https://example.com
, requests a certificate for example.com
and proves eligibility via the HTTP challenge (1). Certificate renewal can be automated such that
there is always a valid certificate on the server. This could be used to host a public-facing website
in place of the internal application. The surrogate server uses a user account, here xyz
,
to obtain the certificate.
Let’s Encrypt keeps track of which account passed which challenge. The internal server, hosting
an internal application, uses the same credentials and therefore also the same account
in the communication with Let’s Encrypt. The internal server can now request again a certificate
for example.com
. The eligibility has already been established and Let’s Encrypt mints the
certificate right away.
What sounds like a bug at first, is covered by ACME specification: RFC 8555.
The “authorizations” array of the order SHOULD reflect all authorizations that the CA takes into account in deciding to issue, even if some authorizations were fulfilled in earlier orders or in pre-authorization transactions. For example, if a CA allows multiple orders to be fulfilled based on a single authorization transaction, then it SHOULD reflect that authorization in all of the orders.
The specification does not mandate this behavior, but Let’s Encrypt supports it.
Implementation steps
On the public-facing surrogate server
- Set up certbot in combination with a web server.
- Request a certificate for the domain in question.
- Automate certificate renewal, e.g., using
certbot renew
as a cron job.
On the internal server
- Set up certbot.
- Copy
/etc/letsencrypt/accounts
from the surrogate server to the internal. - Request a certificate for the domain in question.
- Automate certificate renewal, e.g., using
certbot renew
as a cron job.
Done.
This might also interest you