Automatic Certificate Issuance with ACME
In a previous article here, I described how to setup an ACME server using Step-CA software. While writing that article I got tired fighting with some of the Step-CA nuances and asked myself if there was a better way.
Not finding a better way to issue certificates using the ACME protocol, I created ACME.cgi. This is a CGI script that will utilize an external CA tool to issue/revoke certificates using the ACME protocol (and yes... it's a shell script!).
Overview
The ACME.cgi script will utilize a standard web sever and CGI environment to allow ACME clients to register and issue certificates for domains. The ACME.cgi script does not actually do the work of creating, signing, revoking certificates. Those operations are farmed out to external tools.
This article, and ACME.cgi in general, assume that the external tools is Personal Certificate Authority (PCA). However, this is not required.
Setup
Since servers and requirements vary greatly, the options below give the steps to deploy ACME.cgi into a Docker, non-chroot and chroot web environment. Please note, that I cannot know your specific server setup and you may need to make additional adjustments.
These setups all assume that the ACME.cgi service will utilize the Personal Certificate Authority (PCA) script to manage certificate functions. (If you want more information on what PCA is checkout this article.) If you choose to utilize another tool, update/replace the ACME_helper.* files with whatever makes sense for your setup.
For All Options
The following will need to be done for all deployment options.
- Download the latest version of ACME.cgi from github.
- Download the latest version of PCA from github.
- Copy the pca script to a location in the PATH. i.e. /usr/local/bin/. If you will setup ACME.cgi in a CHROOT environment, then this path must be callable from that environment.
- Since these instruction assume that PCA will be called from the ACME_helper script, set the PCA_ROOT environment variable to the location where the ACME.cgi service is expected to store the certificate environment. i.e. /var/www/etc/pca.
- Install a FastCGI server. e.g. fcgiwrap, slowcgi. This program is probably already in the package manager of the server.
- Setup web server to send traffic to CGI file. Below is a quick config for nginx. This configlet assumes the following (adjust for your setup):
- The path ACME.cgi will be called from is https://ISSUER_DOMAIN/acme/ (where ISSUER_DOMAIN is the FQDN of the server hosting the application)
- The web root is /var/www/html
- Web server certificates are kept in /etc/certs
- FastCGI program is listening on localhost port 9000
server {
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/certs/server.fullchain.pem;
ssl_certificate_key /etc/certs/server.key;
# use if the server key is encrypted
# ssl_password_file /etc/certs/server.pass;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
index index.html;
root /var/www/html;
location /acme/ACME.cgi {
include /etc/nginx/fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME nfquery.cgi;
}
}nginx server configlet
Deployment Options
On Docker system
This is the simplest deployment method. There is a Makefile created to assist in creation of the docker container. Three volumes are used, /config, /data and /logs. Config is used to store the configuration files for ACME.cgi and the web server. Data is where PCA certificates are stored as well as the ACME working files. Logs as the name implies, is where the logs are stored.
- Change to the package/Docker path.
- Build the docker image by running make build.
make build- Copy the env.example file to .env. Configure the .env file as necessary. Note: Set SERVER_NAME, CONTACT_EMAIL and SERVER_SECRET for the site.
- Start the docker container. Use option A or B
- Start docker via make run.
- Start docker-container via docker-container up -d
The docker container will create a new CA, create and export a CA directory, create certificate and keys for the container and enable all cron scripts.
On non-chroot systems
This is only slightly more complicated that the docker image. In this option, all configuration steps will need to be performed manually.
- Copy the ACME.cgi ACME_helper.sh and ACME_cleaner.sh files to the CGI-BIN directory.
- Copy the ACME.conf example to the /etc directory.
- Update the ACME.conf file as necessary. Note that the ISSUER_DOMAIN and ISSUER_EMAIL do not have default values and are required to be filled out.
- Copy the ACME_helper.conf file to /etc directory.
- Set the CA_NAME to be the name to initialize PCA with (or the name that PCA was initialized with, if done in another step.)
- Optional: If initializing PCA here run the PCA init command. These commands will:
- Initialize the PCA environment using the CA_NAME that was described above.
- Create a certificate chain
- Create a CRL file (nothing will be in it yet)
- Export an html directory with those files in them.
- Extract that html export into a web root under a /ca directory.
PCA_ROOT=/etc/pca pca <ca_name> init
PCA_ROOT=/etc/pca pca <ca_name> create chain
PCA_ROOT=/etc/pca pca <ca_name> create crl
PCA_Root=/etc/pca pca <ca_name> export html -file /tmp/ca_html.tgz -overwrite
tar -C /var/www/html -zxf /tmp/ca_hml.tgzcommands to create the CA HTML files on the CA server
- Verify that everything is working by connecting to https://ISSUER_DOMAIN/acme/directory. This will return a directory listing of ACME functions and confirm the script is installed.
- Setup the ACME_cleaner.sh script to run daily.
- Create a script and set it to run daily that will create a new CRL, HTML export and deploy that export.
#!/bin/sh
export PCA_ROOT=/etc/pca
export CANAME=<ca_name>
pca ${CANAME} create crl
pca ${CANAME} export html -file /tmp/ca_html.tgz -overwrite
tar -C /var/www/html -zxf /tmp/ca_html.tgzscript to maintain the CRL on the CA server
On CHROOT systems
This is perhaps the most complicated setup. If the system is running on OpenBSD, check out the packages/OpenBSD directory. There is a port that can be run to create an OpenBSD port to deploy. In addition read packages/OpenBSD/pkg/README. That file has all command necessary to copy the helper programs into the CHROOT environment.
- Copy the ACME.cgi, ACME_helper.sh and ACME_cleaner.sh files to the CGI-BIN directory.
- Copy the required helper programs into the CHROOT directory (Assume the directory /var/www/etc)
- The following directories are used and will need to be created in the CHROOT environment: /tmp, /var/run
- The following programs are required to be accessible by the CHROOT environment, as well as any libraries that the programs call: cat, sleep, rm, cut, mktemp, host, curl, fold, od, openssl, printf, wc, jq
- The following device is required to be available in the CHROOT environment: /dev/null
- Copy the ACME.conf into a config directory located in the CHROOT path. i.e /var/www/etc
- Update the ACME.conf file as necessary. Note that the ISSUER_DOMAIN and ISSUER_EMAIL do not have default values and are required to be filled out.
- Copy the ACME.conf into a config directory located in the CHROOT path. i.e /var/www/etc
- Update the ACME.conf file as necessary. Note that the ISSUER_DOMAIN and ISSUER_EMAIL do not have default values and are required to be filled out.
- Copy the AMCE_helper.conf into a config directory located in the CHROOT path. i.e. /var/www/etc
- Set the CA_NAME to be the name to initialize PCA with (or the name that PCA was initialized with, if done in another step.)
- Optional: If initialized PCA here run the PCA init command. Make sure to initialize from the CHROOT path.
- Initialize the PCA environment using the CA_NAME that was described above.
- Create a certificate chain
- Create a CRL file (nothing will be in it yet)
- Export an html directory with those files in them.
- Extract that html export into a web root under a /ca directory. (see example below)
- Verify that everything is working by connecting to https://ISSUER_DOMAIN/acme/directory. This will return a directory listing of ACME functions and confirm the script is installed.
- Setup the ACME_cleaner.sh script to run daily.
- Create a script and set it to run daily that will create a new CRL, HTML export and deploy that export.
PCA_ROOT=/etc/pca chroot /var/www pca <ca_name> init
PCA_ROOT=/etc/pca chroot /var/www pca <ca_name> create chain
PCA_ROOT=/etc/pca chroot /var/www pca <ca_name> create crl
PCA_Root=/etc/pca chroot /var/www pca <ca_name> export html -file /tmp/ca_html.tgz -overwrite
tar -C /var/www/html -zxf /var/www/tmp/ca_hml.tgzexample initializing PCA in CHROOT path
Clients
Before you begin selecting your favorite ACME client to pull a certificate, you will need to download and install PCA root certificate.
- Browse to the https://ISSUER_DOMAIN/ca/ site. Accept any certificate errors for "unknown authority".
- Download the Certificate Chain. It should be in a PEM format.
- Install that chain into the trusted certificate store on the client. (The process will vary depending on client)
Now use your preferred ACME client to request and download a certificate.
Conclusions
This is the first step in gaining independence from public certificate authorities.