1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
|
% lacme(8)
% [Guilhem Moulin](mailto:guilhem@fripost.org)
% December 2015
Name
====
lacme - [ACME] client written with process isolation and minimal
privileges in mind
Synopsis
========
`lacme` [`--config=FILENAME`] [`--socket=PATH`] [*OPTION* …] *COMMAND* [*ARGUMENT* …]
Description
===========
`lacme` is a small [ACME] client written with process isolation and
minimal privileges in mind. It is divided into four components, each
with its own executable:
1. A [`lacme-accountd`(1)] process to manage the account key and issue
SHA-256 signatures needed for each [ACME] command. (This process
binds to a UNIX-domain socket to reply to signature requests from
the [ACME] client.)
One can use the UNIX-domain socket forwarding facility of OpenSSH
6.7 and later to run [`lacme-accountd`(1)] and `lacme` on different
hosts. Alternatively, the [`lacme-accountd`(1)] process can be
spawned by the “master” `lacme` process below; in that case, the
two processes communicate through a socket pair.
2. A “master” `lacme` process, which runs as root and is the only
component with access to the private key material of the server
keys. It is used to fork the [ACME] client (and optionally the
[ACME] webserver) after dropping root privileges.
For certificate issuances (`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.
3. An actual [ACME] client (specified with the *command* setting of the
[`[client]` section](#client-section) of the configuration file),
which builds [ACME] commands and dialogues with the remote [ACME]
server.
Since [ACME] commands need to be signed with the account key, the
“master” `lacme` process passes the [`lacme-accountd`(1)]
UNIX-domain socket to the [ACME] client: data signatures are
requested by writing the data to be signed to the socket.
4. For certificate issuances (`newOrder` command), an optional
webserver (specified with the *command* setting of the [`[webserver]`
section](#webserver-section) of the configuration file), which is
spawned by the “master” `lacme`. (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.
Moreover temporary [`iptables`(8)] rules can be automatically
installed to open the HTTP port.
Commands
========
`lacme account` [`--tos-agreed`] [`--register`] [*CONTACT* …]
: Register (if `--registered` is set) a [`lacme-accountd`(1)]-managed
account key. A list of *CONTACT* information (such as `maito:`
URIs) can be specified in order for the [ACME] server to contact the
client for issues related to this registration (such as
notifications about server-initiated revocations). `--tos-agreed`
indicates agreement with the [ACME] server's Terms of Service (and
might be required for registration).
If the account key is already registered, update the contact info
with the given list of *CONTACT* information.
Upon success, `lacme` prints the new or updated Account Object from
the [ACME] server.
`lacme newOrder` [`--config-certs=`*FILE*] [`--min-days=`*INT*|`--force`] [*SECTION* …]
: Read the certificate configuration *FILE* (see the **[certificate
configuration file](#certificate-configuration-file)** section below
for the configuration options), and request new Certificate Issuance
for each of its sections (or 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.
`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 server's private key.
Command alias: `revoke-cert`.
Generic settings
================
`--config=`*filename*
: Use *filename* as configuration file. See the **[configuration
file](#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. `lacme`
aborts if `path` is readable or writable by other users, or if its
parent directory is writable by other users.
This command-line option overrides the *socket* setting of the
[`[client]` section](#client-section) of the configuration file; it
also causes the [`[accountd]` section](#accountd-section) to be
ignored.
`-h`, `--help`
: Display a brief help and exit.
`-q`, `--quiet`
: Be quiet.
`--debug`
: Turn on debug mode.
Configuration file
==================
If `--config=` is not given, `lacme` uses the first existing
configuration file among *$XDG_CONFIG_HOME/lacme/lacme.conf* (or
*~/.config/lacme/lacme.conf* if the `XDG_CONFIG_HOME` environment
variable is not set), and *@@sysconfdir@@/lacme/lacme.conf*.
Valid settings are:
Default section
---------------
*config-certs*
: For certificate issuances (`newOrder` command), specify the
space-separated list of certificate configuration files or
directories to use (see the **[certificate configuration
file](#certificate-configuration-file)** section below for the
configuration options).
Paths not starting with `/` are relative to the directory name of
the **[configuration filename](#configuration-file)**. The list of
files and directories is processed in order, with the later items
taking precedence. Files in a directory are processed in
lexicographic order, only considering the ones with suffix `.conf`.
Default: `lacme-certs.conf lacme-certs.conf.d/`.
`[client]` section
------------------
This section is used for configuring the [ACME] client (which takes care
of [ACME] commands and dialogues with the remote [ACME] server).
*socket*
: See `--socket=`.
Default: *$XDG_RUNTIME_DIR/S.lacme* if the `XDG_RUNTIME_DIR`
environment variable is set.
*user*
: The username to drop privileges to (setting both effective and real
uid). Skip privilege drop if the value is empty (not recommended).
Default: `@@lacme_client_user@@`.
*group*
: The groupname to drop privileges to (setting both effective and real
gid, and also setting the list of supplementary gids to that single
group). Skip privilege drop if the value is empty (not
recommended).
Default: `@@lacme_client_group@@`.
*command*
: The [ACME] client command. It is split on whitespace, with the
first item being the command to execute, the second its first
argument etc. (Note that `lacme` might append more arguments when
executing the command internally.)
Default: `@@libexecdir@@/lacme/client`.
*server*
: 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 considers the request failed.
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 information.
`[webserver]` section
---------------------
This section is used to configure how [ACME] challenge responses are
served during certificate issuance.
*listen*
: Comma- or space-separated list of addresses to listen on. Valid
addresses are of the form `IPV4:PORT`, `[IPV6]:PORT` (where 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: `@@runstatedir@@/lacme-www.socket`.
**Note**: The default value is only suitable when an external HTTP
daemon is publicly reachable and passes all ACME challenge requests
to the webserver component through the UNIX-domain socket
`@@runstatedir@@/lacme-www.socket` (for instance using the provided
`@@sysconfdir@@/lacme/apache2.conf` or `@@sysconfdir@@/lacme/nginx.conf` configuration
snippets for each virtual host requiring authorization). If there
is no HTTP daemon bound to port 80 one needs to set *listen* to
`[::]` (or `0.0.0.0 [::]` when 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 directory _must_ exist beforehand, _must_ be empty, and the
lacme client user (by default `@@lacme_client_user@@`) needs to be
able to create files under it.
This setting is required when *listen* is empty.
*user*
: The username to drop privileges to (setting both effective and real
uid). Skip privilege drop if the value is empty (not recommended).
Default: `@@lacme_www_user@@`.
*group*
: The groupname to drop privileges to (setting both effective and real
gid, and also setting the list of supplementary gids to that single
group). Skip privilege drop if the value is empty (not
recommended).
Default: `@@lacme_www_group@@`.
*command*
: The [ACME] webserver command. It is split on whitespace, with the
first item being the command to execute, the second its first
argument etc. (Note that `lacme` might append more arguments when
executing the command internally.)
A separate process is spawned for each address to *listen* on. (In
particular no webserver process is forked when the *listen* setting
is empty.)
Default: `@@libexecdir@@/lacme/webserver`.
*iptables*
: Whether to automatically install temporary [`iptables`(8)] rules to
open the `ADDRESS[:PORT]` specified with *listen*. The rules are
automatically removed once `lacme` exits.
This setting is ignored when *challenge-directory* is set.
Default: `No`.
`[accountd]` section
---------------------
This section is used for configuring the [`lacme-accountd`(1)] child
process. If the section (including its header) 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.
*user*
: The username to drop privileges to (setting both effective and real
uid). Skip privilege drop if the value is empty (the default).
*group*
: The groupname to drop privileges to (setting both effective and real
gid, and also setting the list of supplementary gids to that single
group). Skip privilege drop if the value is empty (the default).
*command*
: The [`lacme-accountd`(1)] command. It is split on whitespace, with
the first item being the command to execute, the second its first
argument etc. (Note that `lacme` appends more arguments when
executing the command internally.)
Use for instance `ssh -T lacme@account.example.net lacme-accountd`
in order to spawn a remote [`lacme-accountd`(1)] server. Default:
`@@bindir@@/lacme-accountd`.
*config*
: Path to the [`lacme-accountd`(1)] configuration file.
*quiet*
: Be quiet. Possible values: `Yes`/`No`.
Certificate configuration file
==============================
For certificate issuances (`newOrder` command), a separate file is used
to configure paths to the certificate and key, as well as the subject,
subjectAltName, etc. to generate Certificate Signing Requests.
Each section denotes 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. The
[`genpkey`(1ssl)] command can be used 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
Alternatively, 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 than 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 (for instance 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 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
is 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. Specifying
an empty value skips certificate validation.
Default: `@@datadir@@/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 the value provided here.
*tlsfeature*
: Comma-separated list of [TLS extension][TLS Feature extension]
identifiers, such as `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.
*chown*
: An optional `username[:groupname]` to chown the issued *certificate*
and *certificate-chain* to.
*chmod*
: An optional octal mode to chmod the issued *certificate* and
*certificate-chain* to.
*notify*
: Command to pass the the system's command shell (`/bin/sh -c`)
after successful installation of the *certificate* and/or
*certificate-chain*.
Examples
========
$ sudo lacme account --register --tos-agreed mailto:noreply@example.com
$ sudo lacme newOrder
$ sudo lacme revokeCert /path/to/service.crt
Automatic renewal can be scheduled via [`crontab`(5)] or
[`systemd.timer`(5)]. In order to avoid deploying a single account key
onto multiple nodes and/or dealing with multiple account keys, one can
install a single [`lacme-accountd`(1)] instance on a dedicated host,
generate a single account key there (and keep it well), and set the
following in the [`[accountd]` section](#accountd-section):
command = ssh -T lacme@account.example.net lacme-accountd
If the user running `lacme` can connect to `lacme@account.example.net`
using (passwordless) key authentication, this setting will spawn a
remote [`lacme-accountd`(1)] and use it to sign [ACME] requests.
Further hardening can be achieved my means of [`authorized_keys`(5)]
restrictions.
See also
========
[`lacme-accountd`(1)]
[ACME]: https://tools.ietf.org/html/rfc8555
[TLS Feature extension]: https://tools.ietf.org/html/rfc7633
[`lacme-accountd`(1)]: lacme-accountd.1.html
[`iptables`(8)]: https://linux.die.net/man/8/iptables
[`ciphers`(1ssl)]: https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html
[`x509v3_config`(5ssl)]: https://www.openssl.org/docs/manmaster/man5/x509v3_config.html
[`genpkey`(1ssl)]: https://www.openssl.org/docs/manmaster/man1/openssl-genpkey.html
[`req`(1ssl)]: https://www.openssl.org/docs/manmaster/man1/openssl-req.html
[`crontab`(5)]: https://linux.die.net/man/5/crontab
[`systemd.timer`(5)]: https://www.freedesktop.org/software/systemd/man/systemd.timer.html
[`authorized_keys`(5)]: https://man.openbsd.org/sshd.8#AUTHORIZED_KEYS_FILE_FORMAT
|