acmeproxy is a proxy for ACME
compliant certificate authorities.
acmeproxy is meant for situations similar to the one shown in the following
overview diagram:
A private network is separated from the Internet by a firewall. All traffic to and from the Internet must go through that firewall. While local machines are able to access the Internet they are not accessible from the Internet.
Those local machines provide services to users within the private network. Each local machine has a private network IP address. A local DNS server maps those IP addresses to valid DNS host names. Connection to those services has to be TLS encrypted. This requires valid SSL/TLS certificates.
One way to provide such certificates is using self-signed certificates. Another would be to create a private certificate authority, which then issues those certificates. An excellent article on how to do this can be found on Jamie Nguyen's website.
Unfortunately both approaches have a serious drawback. Most browsers and other TLS clients rightfully treat self-signed certificates as a security risk. Even when using ones own certificate authority, it is necessary to import the root certificate used to sign the services' certificates into all browsers or other clients used to access those services. This quickly becomes tedious and annoying.
The ACME Protocol, and especially Let's Encrypt, provide an alternative to creating ones own certificate authority. Additionally Let's Encrypts root certificate is included in most browsers and clients. Therefore the annoying import of root certificates is not necessary anymore.
However, there is no free lunch. It comes as no surprise that also Let's Encrypt has a drawback. Before Lets Encrypt is able to issue a certificate for the requested domain name it relies on a proof of ownership of the domain name.
The most straightforward way of proofing ownership is the
HTTP-01 challenge. This
challenge relies on a token being served over HTTP from the domain
requesting the certificate. If for example host.example.com wants
a certificate from Let's Encrypt, it has to provide a token which it
obtained from Let's Encrypt under
http://host.example.com/.well-known/acme-challenge/<TOKEN>. Naturally
this token is random and changes for each certificate renewal.
Since the hosts in the private network are not accessible from the Internet Let's Encrypt will not be able to access the above URL and this will not be able to validate our ownership. Thus, it will not issue a certificate for those hosts.
There exists another challenge type for such circumstances, which is
called DNS-01. It
relies on the token being available as a TXT record on the DNS server
managing the DNS entries for the requesting domain. Unfortunately this
requires some kind of API to create such an TXT record. Not all DNS
providers provide such an API.
This is where acmeproxy comes into play. It allows hosts otherwise
unaccessible from the Internet to offload the HTTP-01 challenge. It
relies on the fact that all local machines have a valid DNS name. While
the local DNS server maps those names to a private network IP address,
a public DNS server may map those DNS names to the IP address of
a server accessible to Let's Encrypt or a similar certificate authority.
Naturally this server then runs acmeproxy.
acmeproxy supports three operation modes which allow its clients to
retrieve valid SSL/TLS certificates from Let's Encrypt.
-
acme-client:acmeproxyacts like any other ACME protocol client. It performs an HTTP-01 challenge, retrieves the certificates, and stores them locally. Additionally it makes sure that certificates get renewed before they expire. By executing acertificate-obtained-hookacmeproxyis able to inform clients on the same system about a new certificate. -
certificate-agent:acmeproxyperforms the HTTP-01 challenge to retrieve or renew certificates for its client. It encrypts the certificates using the client's public key and stores them locally. The client may retrieve the certificates whenever it is able to do so. -
acme-gateway:acmeproxyacts as a gateway for its client. The client instructsacmeproxyto perform an HTTP-01 challenge flow to either retrieve or renew a certificate.acmeproxythen immediately forwards the certificate to the client without storing it, or keeping any meta data about the certificate.
Furthermore acmeproxy can act as its own client. In this mode an
acmeproxy instance within the local network connects to a remote
acmeproxy to retrieve the certificates obtained using the
certificate-agent or acme-gateway operation modes.
Regardless of the operation mode acmeproxy makes sure that only
authenticated and authorized clients are able to use acmeproxy to
retrieve certificates form Let's Encrypt or similar certificate
authorities. All communication between acmeproxy and its clients is
encrypted.
acmeproxy can act as a server as well as a client. The following
section details how to use acmeproxy in both ways.
In order to start acmeproxy as a server execute
acmeproxy serve
By default acmeproxy should work as intended out of the box. If you
want to override certain settings environment variables as well as
command line arguments can be used. The command
acmeproxy help serve
gives a detailed explanation of the various command line arguments.
If you use acmeproxy to connect to a certificate authority
using untrusted certificates you will receive certificate errors. In
order to connect to such a certificate authority you have to set the
LEGO_CA_CERTIFICATES variable to the certificate used. This instructs
lego to trust this
certificate.
For example, if you want acmeproxy to connect to a local installation
of pebble, you have to execute:
export LEGO_CA_CERTIFICATES=$PWD/.pebble/test/certs/pebble.minica.pemIn this example the .pebble directory in the current working directory
contains a local checkout of Pebble.
TODO explain client operation once it is implemented.
TODO: describe steps necessary to get a development environment going
Some of acmeproxy's tests require a running instance of
pebble and will be skipped if
such an instance is not configured.
If make test is called pebble is automatically downloaded and
compiled. All tests relying on pebble are executed.
In order to start an instance of acmeproxy in a test environment use
the test-env.sh script in the scripts directory:
./scripts/test-env.sh startTo stop the started test environment call:
./scripts/test-env-sh stop
The test-env.sh script creates a pod using podman and adds all
containers necessary to execute acmeproxy locally.
Copyright © 2019 Ferdinand Hofherr
Distributed under the MIT License.