acme-distributed is a (still somewhat incomplete) command line ACME client for distributed certificate ordering.
acme-distributed is work in progress. I use it as my primary ACME client for ordering and renewing all of my Let's Encrypt certificates so far. You are welcome to start using it as well. Please use the issue tracker to file bug reports.
The current version is v0.4.0
First things first:
- This documentation is quite out of date - sorry for that, I'm working on new documentation.
Only HTTP authorization requests are currently supportedBoth, HTTP and DNS challenges are supported. DNS challenges are only supported with the unbound DNS server currently, tho.- You need an existing ACME account and private key, this client currently cannot create accounts (feature is on the roadmap, tho)
The master branch in this repository most likely is unstable from time to time. Please download the latest release.
USAGE: ./acme-distributed.rb [options] <configuration> -V, --version Display version number and exit -e, --endpoint <name> The endpoint to use for the request -c <cert1[, cert2[, ...]]>, Certificates to request --certificates -L, --log-level <level> Log level to use [DEBUG, INFO, WARN, ERROR]. Default is INFO. -r, --remaining-lifetime <days> Only renew certificates which have a remaining validity less than <days> days -n, --dry-run Dry-run mode, does not perform any actual change.
If you run acme-distributed behind a HTTP proxy server, be sure to set https_proxy
environment accordingly.
acme-distributed is a simple ACME client for special use cases. It does not implement all functionality that other ACME clients offer. If you are just looking for an ordinary ACME client, please look elsewhere.
The corner case implemented by this client is the separation of certificate ordering from fullfilling http-01 authorization requests. This can be useful in the following scenarios:
- You can not have (or do not want) the ACME client on your web server(s) for whatever reasons
- Your webservers cannot initiate connections to the outside world
- You do not want your private account key on your web servers
- You want to centralize your Let's Encrypt certficate management and not have multiple hosts (i.e. your webservers) being responsible for that
Please note that acme-distributed will not (nor will ever) deploy any certificates to your servers. This task is left to whatever configuration management or provisioning tools you might have in place.
- Clone the repository to a convinient location on your system
- Run
bundler install
to install dependencies
Basically, what acme-distributed does is similar to other ACME clients when requesting certificates, except that it places the http-01 authorization challenges not on the local machine, but on each of the configured remote servers:
- Place a new order for certificate generation with the ACME provider
- For each configured challenge server, place the authorization challenge(s) generated by the ACME provider via SSH at the configured locations on the remote servers
- Trigger authorization check(s) from ACME provider
- Remove the authorization challenges created in (2) from each configured server
- Evaluate the results of the authorization(s)
- On success, create a CSR file for the requested certificates and send it to the ACME provider
- Retrieve the signed certificates from the ACME provider and store them on the local filesystem
acme-distributed uses the Ruby ACME implementation acme-client by Charles Barbier and requires
- Ruby 2.1 or higher
- The following ruby gems
- acme-client
- net-ssh
Depending on the connector type(s) you use, there might be more dependencies and/or requirements (e.g. password-less SSH access to your remote web servers, etc).
Furthermore, you will need a Let's Encrypt account already setup, i.e. you need a valid, registered account key. acme-distributed does not offer registration functionality right now. This functionality is planned for the v1.0 release of this client.
acme-distributed uses configuration files in simple YAML format. The following configurables are available:
Define the available ACME endpoints for this configuration.
- url is the ACME API endpoint URL to use
- private_key refers to the account's private RSA key in PEM format. It must exist (i.e. you need to have setup an account before)
The url and private_key options are mandatory.
You can name the endpoints as you wish, the names production and staging below are just examples.
endpoints: production: url: https://acme-v02.api.letsencrypt.org/directory private_key: /etc/acme-deploy/accounts/production/private-key.pem email_addr: certs@example.comstaging: url: https://acme-staging-v02.api.letsencrypt.org/directoryprivate_key: /etc/acme-deploy/accounts/staging/private-key.pememail_addr: certs@example.com
You can define any number of certificates acme-distributed should handle. Each certificate needs a unique name name, which is given as the entry key.
- subject specifies the CN in the certificate's subject
- key specifies the (local) path to the private key used for generating the CSR and for the final certificate
- path specifies the (local) path the final certificate will be stored at in PEM format
- san specifies a list of additional DNS names the certificate shall be valid for
- renew_days renew certificate only if its lifetime is less than specified number of days (defaults to 30)
The options subject, key and path are mandatory.
You can use the special variable {{endpoint}}
for the name of the endpoint in key and path values. If found, it will be replaced with the endpoint configuration name (e.g. staging or production if defined as in the example above).
certificates: ssl.example.com: subject: ssl.example.comsan: - ssl2.example.com - ssl3.example.comkey: /etc/acme-deploy/{{endpoint}}/keys/ssl.example.com.keypath: /etc/acme-deploy/{{endpoint}}/certs/ssl.example.com.pemconnector_group: frontend_web_serverssecure.example.com: subject: secure.example.comkey: /etc/acme-deploy/{{endpoint}}/keys/secure.example.com.keypath: /etc/acme-deploy/{{endpoint}}/certs/secure.example.com.pemconnector_group: backend_web_servers
Connectors are used to connect to the remote systems that handle the authorization requests (e.g. your web servers). Connectors are grouped into connector groups which share a similar connector type. Each connector group must have at least one connector defined, but can (theoretically) contain infinite connectors. As a rule of thumb, if your web servers are behind a load balancer, each server should be configured as a connector in the same group.
Connectors are required to be unique within each group, but not across groups. You can use a connector with the same properties as an existing one in different groups.
Certificates can be configured to be handled by a specific connector group or by a configurable default connector group.
Connector groups know two properties:
type
: The type of the connector. See Connector types for more details.connectors
: An array that contains connector configuration as a hash, one element for each connector.
Currently, the following connector types are supported.
Uses SSH to put an authorization challenge on the file system on a remote server, to be authorized via HTTP.
The following configuration options are mandatory and must be set:
hostname
specifies the DNS hostname (or IP address) of the server to connect to via SSHusername
specifies the remote username to use for authenticationssh_port
specifies the TCP port the SSH daemon on the server listens toacme_path
specifies the path on the remote server where authorization challenges are put
Please note that only the base name of the path sent by the ACME challenge will be used when creating the challenge files on the remote servers -- the /.well-known/acme
part will be cut off. So you either have an alias configured on your web servers pointing to acme_path or you include /.well-known/acme-challenge
in acme_path setting. In the example below, the web server is configured with an alias /.well-known/acme-challenge -> /var/www/acme
for simplicity.
Uses SSH to connect to a DNS server running unbound
(or a server with control access to your DNS server) and executes unbound-control
to add an authorization record to your DNS zone (i.e. unbound-control local_data _acme_challenge.fqdn.example.com 5 IN TXT <challenge>
).
The following configuration options are mandatory and must be set:
hostname
specifies the DNS hostname (or IP address) of the server to connect to via SSHusername
specifies the remote username to use for authenticationssh_port
specifies the TCP port the SSH daemon on the server listens tounbound_ctrl
specifies the path on the remote server to theunbound-control
binary
connector_groups: frontend_web_servers: type: ssh_http_fileconnectors: - name: frontend_web_1hostname: www1.example.comusername: acmessh_port: 22acme_path: /var/www/acme - name: frontend_web_2:hostname: www2.example.comusername: acmessh_port: 22acme_path: /var/www/acmebackend_web_servers: type: ssh_dns_unboundconnectors: - name: backend_web_1hostname: www3.example.comusername: acmessh_port: 22unbound_ctrl: /usr/sbin/unbound-control
acme-distributed is put in the Public Domain under the Unlicense terms.
Pull requests and patches are accepted as long as they are put in the public domain as well. For this purpose, please accompany your patches with the following statement:
I dedicate any and all copyright interest in this software to the public domain. I make this dedication for the benefit of the public at large and to the detriment of my heirs and successors. I intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.