lacme(8)

🌸 lacme(8) β€” the manual, but make it make sense πŸ’…

System Manager's Manual Β· lacme(8)


✨ NAME ✨

lacme β€” an ACME client written with process isolation and minimal privileges in mind 🫢


✨ SYNOPSIS ✨

lacme [--config=FILENAME] [--socket=PATH] [OPTION ...]  COMMAND
[ARGUMENT ...]

✨ DESCRIPTION ✨

okay so, bestie, lacme is a lil ACME client β€” and the whole vibe is process isolation + minimal privileges. It's not one big blob, it's four separate components, each with its own executable. iconic behavior honestly. πŸ‘‘

  1. A lacme-accountd(1) process that babysits the account key and issues the SHA-256 signatures needed for every ACME command. (It binds to a UNIX-domain socket so it can reply to signature requests from the ACME client.) And get this β€” you can use the UNIX-domain socket forwarding facility of OpenSSH 6.7 and later to run lacme-accountd(1) and lacme on different hosts. fr fr. Or alternatively, the "master" lacme process (that's component 2, hold on) can spawn the lacme-accountd(1) process itself β€” and in that case the two processes talk through a socket pair. πŸ¦‹
  2. A "master" lacme process that runs as root and is the only component with access to the private key material of the server keys. Its whole job is to fork the ACME client (and optionally the ACME webserver) after dropping root privileges. For certificate issuances (the newOrder command), it also generates Certificate Signing Requests, then verifies the validity of the issued certificate, and optionally reloads or restarts services when the notify setting is set. main character energy tbh.
  3. The actual ACME client β€” specified with the command setting of the [client] section of the configuration file. This one builds ACL commands and dialogues with the remote ACME server. Wait, no, it builds ACME commands, obviously. 😭 Since ACME commands need to be signed with the account key, the "master" lacme process passes the lacme-accountd(1) UNIX-domain socket over to the ACME client: data signatures get requested by writing the data to be signed straight to the socket. neat, right?
  4. For certificate issuances (the newOrder command), there's an optional webserver β€” specified with the command setting of the [webserver] section of the configuration file β€” which gets spawned by the "master" lacme. (Heads up: the only challenge type currently supported by lacme is http-01, which requires a webserver to answer challenges.) That webserver only processes GET and HEAD requests under the /.well-known/acme-challenge/ URI. And moreover, temporary iptables(8) rules can be automatically installed to open the HTTP port. so much going on πŸ’€

✨ COMMANDS ✨

  • lacme account [--tos-agreed] [--register] [CONTACT ...]

Register (if --registered is set) a lacme-accountd(1)-managed account key. You can pass a list of CONTACT information (such as maito: URIs) so the ACME server can reach out to the client about issues related to this registration (like notifications about server-initiated revocations). And --tos-agreed means you're indicating agreement with the ACME server's Terms of Service β€” and it might be required for registration, jsyk.

If the account key is already registered, this updates the contact info with the given list of CONTACT information instead.

Upon success, lacme prints the new or updated Account Object from the ACME server. slay. πŸŽ€

  • lacme newOrder [--config-certs=FILE] [--min-days=INT|--force] [SECTION ...]

Reads the certificate configuration FILE (peep the certificate configuration file section below for all the configuration options, it's a whole thing), and requests new Certificate Issuance for each of its sections (or just the given list of *SECTION*s). command alias: new-order.

The flag --force is an alias for --min-days=-1, which forces renewal regardless of the expiration date of existing certificates. no cap, it just goes. πŸ”₯

  • lacme revokeCert FILE [FILE ...]

Request that the given certificate(s) FILE(s) be revoked. For this command, lacme-accountd(1) can be pointed to either the account key or the certificate key. command alias: revoke-cert.


✨ GENERIC SETTINGS ✨

  • --config=filename β€” Use filename as the configuration file instead of %E/lacme/lacme.conf. The value is subject to %-specifier expansion. ✨

See the configuration file section below for the configuration options.

  • --socket=path β€” Use path as the lacme-accountd(1) UNIX-domain socket to connect to for signature requests from the ACME client. The value is subject to %-specifier expansion. 🫢
careful bestie β€” lacme aborts if path exists, or if its parent directory is writable by other users. πŸ’€

Default: %t/S.lacme.

This command-line option overrides the socket setting of the [client] section of the configuration file; it also causes the [accountd] section to be ignored.

  • -h, --help β€” Display a brief help and exit. 🌈
  • -q, --quiet β€” Be quiet. 🀫
  • --debug β€” Turn on debug mode. πŸ›

🌈 CONFIGURATION FILE 🌈

Okay bestie, so these are all the valid settings you can put in your config file. Buckle up πŸ’–

✨ DEFAULT SECTION ✨

  • config-certs β€” for certificate issuances (aka the newOrder command), you give me a space-separated list of certificate configuration files or directories to use (scroll down to the certificate configuration file section for what goes in those). Each item in that list gets %-specifier expansion independently, one by one. πŸŽ€

Paths that don't start with / (after the %-expansion happens) are relative to the parent directory of the configuration filename. The files and directories get processed in the order you wrote them, and later items win over earlier ones (precedence, slay). Files inside a directory are processed in lexicographic order, and only the ones with the .conf suffix even get considered.

Default: lacme-certs.conf lacme-certs.conf.d/


🌸 [client] SECTION 🌸

This section configures the ACME client (the lil guy in charge of ACME commands and yapping with the remote ACME server).

  • socket β€” see --socket=.
  • user β€” the username to drop privileges to (sets both effective and real uid). If the value is empty, privilege drop is skipped β€” and no, that's not recommended πŸ™ˆ. Default: _lacme-client.
  • group β€” the groupname to drop privileges to (sets both effective and real gid, and also sets the list of supplementary gids to that single group). Empty value = skipping privilege drop again, still not recommended. Default: nogroup.
  • command β€” the ACME client command. It gets split on whitespace, first item is the command to execute, second item is its first argument, etc. (heads up: lacme might append more arguments when it runs the command internally πŸ‘€). Default: /usr/libexec/lacme/client.
  • server β€” the root URI of the ACME server. Default: https://acme-v02.api.letsencrypt.org/directory.
  • timeout β€” timeout in seconds, after which the client stops polling the ACME server and just calls the request a ✨fail✨. Default: 30.
  • SSL_verify β€” whether to verify the server certificate chain. Default: Yes.
  • SSL_version β€” specify the version of the SSL protocol used to transmit data.
  • SSL_cipher_list β€” specify the cipher list for the connection, see ciphers(1ssl) for more info.

πŸ’… [webserver] SECTION πŸ’…

This section configures how ACME challenge responses get served during certificate issuance.

  • listen β€” comma- or space-separated list of addresses to listen on. Valid addresses look like IPV4:PORT, [IPV6]:PORT (the :PORT suffix is optional and defaults to the HTTP port 80), or an absolute path of a UNIX-domain socket (created with mode 0666). Default: /run/lacme-www.socket.
Note: that default only works when an external HTTP daemon is publicly reachable and passes all ACME challenge requests to the webserver component through the UNIX-domain socket /run/lacme-www.socket (for example using the provided /etc/lacme/apache2.conf or /etc/lacme/nginx.conf config snippets for each virtual host that needs authorization). If there's no HTTP daemon bound to port 80, you gotta set listen to [::] (or 0.0.0.0 [::] when the dual IPv4/IPv6 stack is disabled or unavailable), and possibly also set iptables to Yes. 😌
  • challenge-directory β€” directory under which an external HTTP daemon is configured to serve GET requests for challenge files under /.well-known/acme-challenge/ (for each virtual host requiring authorization) as static files. The dir must already exist, must be empty, and the lacme client user (by default _lacme-client) needs to be able to create files inside it.

This setting is required when listen is empty. Also, its value is subject to %-specifier expansion before privilege drop.

  • user β€” the username to drop privileges to (both effective and real uid). Empty value = skip privilege drop (not recommended, we been knew). Default: _lacme-www.
  • group β€” the groupname to drop privileges to (both effective and real gid, plus the list of supplementary gids becomes that one group). Empty value = skip privilege drop (again, not recommended). Default: nogroup.
  • command β€” the ACME webserver command. Split on whitespace, first item = command to execute, second = its first argument, etc. (once more: lacme might append extra arguments when executing internally.) A separate process is spawned for each address to listen on. (And in particular, no webserver process gets forked at all when the listen setting is empty.) Default: /usr/libexec/lacme/webserver.
  • iptables β€” whether to automatically install temporary iptables(8) rules to open the ADDRESS[:PORT] specified with listen. The rules get automatically removed once lacme exits. This setting is ignored when challenge-directory is set. Default: No.

πŸ¦‹ [accountd] SECTION πŸ¦‹

This section configures the lacme-accountd(1) child process. If the section (header included) is absent or commented out, or if the CLI option --socket is specified, then lacme connects to an existing lacme-accountd(1) process via the specified UNIX-domain socket instead. πŸ‘‘

  • user β€” the username to drop privileges to (both effective and real uid). Empty value = skip privilege drop (this is the default).
  • group β€” the groupname to drop privileges to (both effective and real gid, and also the list of supplementary gids becomes that single group). Empty value = skip privilege drop (default again).
  • command β€” the lacme-accountd(1) command. Split on whitespace: first item is the command to execute, second its first argument, etc. (spoiler: lacme appends more arguments when executing the command internally.) Each item in that list is independently subject to %-specifier expansion, after privilege drop. Default: /usr/bin/lacme-accountd.

Wanna go remote? Use for instance ssh -T lacme@account.example.net lacme-accountd to spawn a remote lacme-accountd(1) server. πŸ’«

  • config β€” path to the lacme-accountd(1) configuration file. Note that the value might be subject to %-expansion by lacme-accountd(1).
  • quiet β€” be quiet. Possible values: Yes/No.

πŸŽ€ CERTIFICATE CONFIGURATION FILE πŸŽ€

For certificate issuances (the newOrder command) you use a separate file to configure paths to the certificate and key, plus the subject, subjectAltName, etc. that get used to generate Certificate Signing Requests. Each section in that file = a separate certificate issuance. Valid settings are:

  • certificate β€” where to store the issued certificate (in PEM format). At least one of certificate or certificate-chain is required.
  • certificate-chain β€” where to store the issued certificate along with its chain of trust (in PEM format). At least one of certificate or certificate-chain is required.
  • certificate-key β€” path to the service's private key. This setting is required. πŸ”‘ You can use the genpkey(1ssl) command to generate a new service RSA key:
$ install -vm0600 /dev/null /path/to/service.rsa.key
$ openssl genpkey -algorithm RSA -out /path/to/service.rsa.key

Or, for an ECDSA key using the NIST P-256 curve:

$ install -vm0600 /dev/null /path/to/service.ecdsa.key
$ openssl genpkey -algorithm EC -out /path/to/service.ecdsa.key \
    -pkeyopt ec_paramgen_curve:P-256 \
    -pkeyopt ec_param_enc:named_curve

lacme supports any key algorithm that the underlying libssl (OpenSSL) version is able to manipulate, but the ACME server might reject CSRs associated with private keys of deprecated and/or "exotic" algorithms. 😬

For a dual cert setup (like RSA+ECDSA), duplicate the certificate section and use a distinct certificate-key resp. certificate (or certificate-chain) value for each key algorithm.

  • min-days β€” for an existing certificate, the minimum number of days before its expiration date that the section is considered for re-issuance. A negative value forces reissuance, while the number 0 limits reissuance to expired certificates. Default: the value of the CLI option --min-days, or 21 if there's no such option.
  • subject β€” Subject field of the Certificate Signing Request, in the form /type0=value0/type1=value1/type2=.... This setting is required.
  • subjectAltName β€” comma-separated list of Subject Alternative Names, in the form type0:value1,type1:value1,type2:.... The only type currently supported is DNS, to specify an alternative domain name.
  • CAfile β€” path to the bundle of trusted issuer certificates. This is used for validating each certificate after issuance or renewal. An empty value skips certificate validation. Default: /usr/share/lacme/ca-certificates.crt.
  • hash β€” message digest to sign the Certificate Signing Request with, overriding the req(1ssl) default.
  • keyUsage β€” comma-separated list of Key Usages, for instance digitalSignature, keyEncipherment, to include in the Certificate Signing Request. See x509v3_config(5ssl) for a list of possible values. Note that the ACME server might override whatever value you provide here. πŸ™ƒ
  • tlsfeature β€” comma-separated list of TLS extension identifiers, like status_request for OCSP Must-Staple. See x509v3_config(5ssl) for a list of possible values. Note that the ACME server might override the value provided here too.
  • owner, chown β€” an optional username[:groupname] to chown the issued certificate and certificate-chain to.
  • mode, chmod β€” an optional octal mode to chmod the issued certificate and certificate-chain to. By default the files are created with mode 0644 minus umask restrictions.
  • notify β€” command to pass to the system's command shell (/bin/sh -c) after successful installation of the certificate and/or certificate-chain.

🧩 %-SPECIFIERS ✨

okay bestie, listen β€” some of the CLI options and config settings go through %-expansion, like little placeholder slots that get filled in. πŸ‘‘ Which settings? Check each one's own docs, because only some of them are affected, iykyk.

  • %C β€” /var/cache if you're root, and $XDG_CACHE_HOME for everyone else (or $HOME/.cache when the XDG_CACHE_HOME environment variable is unset or empty πŸ™ˆ).
  • %E β€” /etc if you're root, and $XDG_CONFIG_HOME for everyone else (or $HOME/.config when the XDG_CONFIG_HOME environment variable is unset or empty πŸ’…).
  • %g β€” the current group name 🫢
  • %G β€” the current group ID πŸ”’
  • %h β€” home directory of the current user 🏠
  • %t β€” /run if you're root, and $XDG_RUNTIME_DIR for everyone else. Non-root users can only use %t when the XDG_RUNTIME_DIR environment variable is set to a non-empty value β€” so no empty vibes allowed, periodt.
  • %T β€” $TMPDIR, or /tmp if the TMPDIR environment variable is unset or empty ✨
  • %u β€” the current user name πŸŽ€
  • %U β€” the current user ID πŸ”’
  • %% β€” a literal % πŸ’€ (yes, escaping your escape, iconic)

🌸 EXAMPLES 🌸

like, here's the whole glow-up in three moves:

$ sudo lacme account --register --tos-agreed mailto:noreply@example.com
$ sudo lacme newOrder
$ sudo lacme revokeCert /path/to/service.crt

Automatic renewal? Bestie, you schedule that with crontab(5) or systemd.timer(5). ⏰

And hmm, awkward situation: you don't wanna be deploying a single account key onto a whole bunch of nodes, and you also don't wanna be juggling multiple account keys. 😭 The move? Install a single lacme-accountd(1) instance on a dedicated host, generate one single account key right there (and guard it with your life, fr fr πŸ’–), then drop this into the [accountd] section:

command = ssh -T lacme@account.example.net lacme-accountd

So if the user running lacme can connect to lacme@account.example.net using (passwordless) key authentication, this setting spawns a remote lacme-accountd(1) and uses it to sign ACME requests. obsessed. πŸ”₯

Wanna harden it even further? Do it with authorized_keys(5) restrictions, like so:

restrict,from="...",command="/usr/bin/lacme-accountd --quiet --stdio" ssh-rsa ...

that's the main character energy right there πŸ‘‘


πŸ’€ BUGS AND FEEDBACK πŸ’€

Got bugs? Got feature requests? They should be filed with the Debian project's bug tracker at https://www.debian.org/Bugs/. Do it, ngl, they want to hear from you 🫢

πŸ¦‹ SEE ALSO πŸ¦‹

🌈 AUTHORS 🌈

  • Guilhem Moulin

and that's a wrap on lacme(8) bestie πŸ’… the source/version line says this one's from the lacme package, and it's dated December 2015 β€” vintage, honestly. go forth and get those certs, you ate πŸŽ€βœ¨

πŸ“¦ where this page comes from