Jul 18, 2020 6 min read

Self-Host ACME Server

Blog post covering how to setup a private, internal ACME server.

Self-Host ACME Server

ACME (Automated Certificate Management Environment), is an automated means of requesting and renewing certificates. This is the brain child of Let's Encrypt, and it really has changed the way in which we obtain and deal with certificates. When this is used, the days of expired certificates should become increasingly rare. Difficulties of understanding certificates, how to request them and how to handle them securely are also no longer a concern. The protocol and tooling handles this all for you (such as the amazing certbot). The most well known ACME service in use today is Let's Encrypt (and in fact the world's largest CA as well). I'm a huge fan of all three (ACME, Let's Encrypt and certbot), and certainly try use them wherever I can. However one area I avoid using it is for internal services on my own private network. The reason for this is down to CT logs, and something which I covered several years ago in a blog post. So, I've by in large, been running my own manual PKI process using OpenSSL. It works and does the job, but it would be amazing if I could automate this using ACME! And there is a solution in the name of step!


I've created my installation on Ubuntu (20.04). The first steps (yeah I know, funny pun there) are as follows:


$ sudo dpkg -i step-certificates_X.Y.Z_amd64.deb


$ sudo dpkg -i step-cli_X.Y.Z_amd64.deb


Most of the steps were taken from the documentation: https://github.com/smallstep/certificates/blob/master/docs/GETTING_STARTED.md. I've added appropriate modifications where needed.

The next bit is to setup the user. Create a user which will be used to run the service:

$ useradd step

Next create the service file /etc/systemd/system/step-ca.service which contains the following:

After=syslog.target network.target

ExecStart=/bin/sh -c '/bin/step-ca /home/step/.step/config/ca.json --password-file=/home/step/.step/pwd >> /var/log/step-ca/output.log 2>&1'


Make sure that the home directory for the step user is created:

$ sudo chown -R step:step /home/step

Also make sure that the log directory is created and owned by the step user:

$ mkdir -p /var/log/step-ca
$ chown -R step:step /var/log/step-ca

Create CA

The next step is to sudo to the step user (you can try create the config and copy it to the appropriate directory instead, this is just easier to do it this way):

$ step ca init

You will be prompted for details for the CA which you are now creating:

  • What would you like to name your new PKI? (e.g. Smallstep) - Enter an appropriate name for the CA.
  • What DNS names or IP addresses would you like to add to your new CA? (e.g. ca.smallstep.com[,,etc.]) - Enter the IP or host of the box which you running this on (creating the CA on).
  • What address will your new CA listen at? (e.g. :443) - Enter an appropriate port (I would recommend 8443, to avoid permission issues with the script above). The format is <host>:<port>, you can leave the host part out (so just have :<port>)
  • What would you like to name the first provisioner for your new CA? (e.g. [email protected]) - Give an appropriate email address.
  • What do you want your password to be? [leave empty and we'll generate one] - Either enter an appropriate password, or leave it blank to have one generated for you. I recommend letting it generate one for you since I had issues trying specify one.

One you've done the steps above you will need to set the password. Create the file /home/step/.step/pwd and put the password into this file. Ensure that you set the appropriate file permissions on the file:

$ chmod 400 /home/step/.step/pwd

The last bit of configuration is to add the ACME magic! Run the following to add the ACME provisioner to the setup:

$ step ca provisioner add acme --type ACME

If you want certificates to be valid for longer than 24 hours, modify the file /home/step/.step/config/ca.json and add the following configuration to the acme section (will extend the lifetime of certificates to 90 days):

"claims": {
    "maxTLSCertDuration": "2160h",
    "defaultTLSCertDuration": "2160h"

So the acme section will now look like:

"type": "ACME",
"name": "acme",
"claims": {
    "maxTLSCertDuration": "2160h",
    "defaultTLSCertDuration": "2160h"

Finally you will need to start the server!

$ sudo systemctl start step-ca

You will most probably also want to autostart the service whenever the system restarts:

$ sudo systemctl enable step-ca

Testing it out

That's the server all set! Now time to test it out.

Firstly grab the root CA certificate from the file /home/step/.step/certs/root_ca.crt.

On your testing server (I'm using Ubuntu again), add it to your system trust store. The instruction below will work for Ubuntu. This guide will help you for adding this CA cert to other OS's.

Firstly, add the certificate to the file /usr/local/share/ca-certificates/<name>_root_ca.crt.

Then run the following to import CA certificate into the system trust store:

$ sudo update-ca-certificates

You should see something like the following in the output of the command:

Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...

Adding debian:redacted_root_ca.pem

Next, install that amazing tool, certbot:

$ sudo apt install certbot

Next up, get a certificate! Run the following to test it out:

$ sudo certbot certonly --standalone -d <host> --server https://<step-host>:<port>/acme/acme/directory

Where <host> is the hostname which to get the certificate for. <step-host> is the hostname of your step (ACME) server, and <port> is the port number which you configured during setup.

You will first be prompted for an email address to set on the certificate, enter an appropriate email.

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Enter email address (used for urgent renewal and security notices) (Enter 'c' to

You will then be prompted to agree to the Terms of Service (just agree, this is your own internal service afterall!):

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at None. You must agree in order to register
with the ACME server at https://<step-host>:<port>/acme/acme/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

You will then be prompted if you willing to share your information with EFF. This is entirely up to you if you want to share your information:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

And if all goes well you should have a freshly created certificate!

Obtaining a new certificate
Performing the following challenges:
http-01 challenge for <redacted>
Waiting for verification...
Cleaning up challenges

 - Congratulations! Your certificate and chain have been saved at:
   Your key file has been saved at:
   Your cert will expire on 2020-07-19. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Finally validate your brand new cert:

$ sudo x509 -in /etc/letsencrypt/live/<cert-domain-name>/fullchain.pem -text -noout

If you want to look at further configuration, I would recommend referring to smallstep's online documentation: https://github.com/smallstep/certificates/blob/master/docs/GETTING_STARTED.md.

You now are ready to have certs automatically handled without having to leak details of your internal network and infrastructure!

Sean Wright
Sean Wright
Experienced application security engineer with an origin as a software developer. Primarily focused on web-based application security with a special interest in TLS and supply chain related subjects.
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Sean Wright.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.