Configure popular ACME clients to use a private CA with the ACME protocol
The ACME protocol radically simplifies TLS and HTTPS's deployment by letting
you obtain certificates automatically, without human interaction. step-ca
works with any ACMEv2 (RFC8555) compliant client that supports the http-01,
dns-01, or tls-alpn-01 challenge. This page contains tutorial-style
example client configuration. Even if you don't use one of the listed
clients, the concepts are generally portable to arbitrary clients.
For step-ca, this tutorial assumes you have initialized and started up an instance using the steps in Getting Started. Additionally, you'll need to configure your CA with an ACME provisioner. Run step ca provisioner add acme --type ACME, and restart your CA.
Most ACME clients connect to Let’s Encrypt’s CA by default.
To connect to smallstep, you need to point the client to the right ACME Directory URL.
sudo is required in certbot's standalone
mode so it can listen on
port 80 to complete the http-01 challenge. If you already have a webserver
running you can use webroot
mode instead. With the
appropriate plugincertbot also supports the dns-01 challenge for most popular DNS providers.
Deeper integrations with nginx
and apache can even configure
your server to use HTTPS automatically (we'll set this up ourselves later). All
of this works with step-ca.
You can renew all of the certificates you've installed using cerbot by running:
More subtly, certbot's default renewal job is tuned for Let's Encrypt's 90
day certificate lifetimes: it's run every 12 hours, with actual renewals
occurring for certificates within 30 days of expiry. By default, step-ca
issues certificates with much shorter 24 hour lifetimes. The cron entry
above accounts for this by running certbot renew every 15 minutes. You'll
also want to configure your domain to only renew certificates when they're
within a few hours of expiry by adding a line like:
renew_before_expiry =8 hours
to the top of your renewal configuration (e.g., in /etc/letsencrypt/renewal/foo.internal.conf).
acme.sh is another popular command-line ACME client. It's written completely in shell (bash, dash, and sh compatible) with very few dependencies.
To get a certificate from step-ca using acme.sh you need to:
Point acme.sh at your ACME directory URL using the --server flag
Tell acme.sh to trust your root certificate using the --ca-bundle flag
Renewals are slightly easier since acme.sh remembers to use the right root certificate. It can also remember how long you'd like to wait before renewing a certificate. Unfortunately, the duration is specified in days (via the --days flag) which is too coarse for step-ca's default 24 hour certificate lifetimes. So the easiest way to schedule renewals with acme.sh is to force them at a reasonable frequency, like every 8 hours, via cron:
win-acme (wacs.exe) is a popular ACME client for Windows.
To use win-acme with step-ca, you'll need to do the following:
Add your root CA certificate (root_ca.crt) to the Windows trust store.
Change the ACMEv2 endpoint used by win-acme (in the settings.json file that comes with the program) to point to your CA's ACME provisioner (eg. https://ca.internal/acme/acme/). Or pass the --baseuri flag with your ACME provisioner's endpoint.
We recommend using the tls-alpn-01 challenge type to prove ownership.
Caddy is an HTTP/2 web server with automatic HTTPS powered by an integrated ACME client.
In addition to serving static websites, Caddy is commonly used as a TLS-terminating API gateway proxy.
Caddy comes with its own ACME server and by default it will generate an internal CA and issue certificates to itself.
But, you can configure Caddy to use a local step-ca instance to obtain certificates.
Here's a Caddyfile global config block.
Add this to the top of your Caddyfile to get certificates from ca.internal for all configured domains:
With this code, you are telling Nginx to listen on port 443 using TLS, with a certificate and private key stored on disk.
Other resources provide a more thorough explanation of NGINX's various TLS configuration options.
We can start an HTTP server using python and check our work with curl:
The server is configured to verify client certificates if they are sent.
That means the server is configured to support mutual TLS.
The handler checks whether a client certificate was provided, and responds with a personalized greeting if one was.
With a few tweaks to this code you can implement robust access control.
There are other good options for programmatic ACME in Go.
The certmagic package builds on lego and offers higher level, easier to use abstractions.
The x/crypto/acme package is lower level and offers more control, but it currently implements a pre-standardization draft version of ACME that doesn’t work with step-ca.
Traefik is a modern reverse-proxy with integrated support for ACME. It's designed primarily to handle ingress for a compute cluster, dynamically routing traffic to microservices and web applications.
It's easy to get a certificate from step-ca in Traefik v2, using the tls-alpn-01 ACME challenge type.
Most importantly, Traefik will need to trust your root CA certificate. Either use the LEGO_CA_CERTIFICATES environment variable to provide the full path to your root_ca.crt when running traefik, or install your root certificate in your system's trust store by running step certificate install root_ca.crt.
In your Traefik static configuration, you'll need to add a certificatesResolvers block: