Attention: this version of acme-client has been archived, as it now lives in OpenBSD base. If you're using the upstream version of this code, you're using old code! The live code, /usr/sbin/acme-client in OpenBSD, is well-maintained and current. See the CVS repository for current code.

If you can't use OpenBSD, the current code-base can fairly easily be ported, but this is not an effort I have a need for. Please see the portable version's GitHub for hints. Thank you!

I'm leaving this page up for historical reference.


a secure acme/Let's Encrypt client version (older…)

Be up-front about security: OpenSSL is known to have issues, you can't trust what comes down the pipe, and your private key's integrity is a hard requirement. Not a situation where you can be careless. acme-client is a client for Let's Encrypt users, but one designed for security. No Python. No Ruby. No Bash. A straightforward, open source implementation in C that isolates each step of the sequence.

How can you use it?

First, download the source. If you're on OpenBSD 5.9 and newer, use the standard acme-client.tgz. Otherwise (FreeBSD, NetBSD, Linux, Mac OS X, old OpenBSD), use acme-client-portable.tgz. You can also use the bleeding-edge from GitHub (acme-client | acme-client-portable). Unpack, SHA512 verify (acme-client.sha512 | acme-client-portable.sha512), then run make and make install. Lastly, read acme-client(1). It should have all you need. (This project used to be named letskencrypt, but was renamed to acme-client in version 0.1.11.)

The dependencies of acme-client-portable are LibreSSL, libbsd (Linux), and libseccomp (Linux, and optional but strongly suggested). The standard acme-client has no dependencies.

How does it work?

acme-client consists of isolated independent components. Each of these is responsible for part of the sequence of manipulating a Let's Encrypt certificate for one or more domains:

  1. read and parse an account and domain private key
  2. authenticate with the Let's Encrypt server
  3. authorise each domain listed for the certificate
  4. submit the X509 request
  5. receive and serialise the signed X509 certificate
  6. request, receive, and serialise the certificate chain from the issuer

Why so complicated?

Key integrity and trust.

You don't want the private key processes interacting with anybody else (acctproc.c, keyproc.c). You don't want network-touching processes interacting with the file-system (dnsproc.c and netproc.c). You don't want the process parsing (revokeproc.c) your certificate — which comes down the pipe and might be rigged to blow — to touch your file-system or the network. Same goes with the process converting the downloaded certificates to the format usable by your web server (certproc.c). Moreover, you don't want the process scribbling in your webroot to scribble elsewhere (chngproc.c). Same goes with the process scribbling in your public certificate directory (fileproc.c).

In the diagramme, processes with file-system write access are in red. and are isolated in a chroot(2) file-system jail. Those with network access are in blue and are pledge(2)ed only for networking and DNS resolution. Orange nodes are off-site. All other processes are locked down with full pledge(2)s. Each process uses the fork+exec model: Use of fork+exec in privilege separated programs. The strategy is to give each process a fresh & unique address space for ASLR, stack protector — as protection against address space discovery attacks. (From OpenBSD's innovations.)

In the portable version without pledge(2), all processes but the red are privilege-dropped and chroot(2)ed. The red file-system processes do not drop privileges.

Version history