2### https://github.com/NixOS/nixpkgs/blob/1d36ad6d16dbf1d3937f899a087a4360332eb141/nixos/modules/security/acme.nix
4### to support new plugins in simp_le for ACMEv2 support.
5### Changed lines are marked with a comment: ### CHANGED
7{ config, lib, pkgs, ... }:
13 cfg = config.security.acme;
15 certOpts = { name, ... }: {
19 example = "/var/lib/acme/acme-challenges";
21 Where the webroot of the HTTP vhost is located.
22 <filename>.well-known/acme-challenge/</filename> directory
23 will be created below the webroot if it doesn't exist.
24 <literal>http://example.org/.well-known/acme-challenge/</literal> must also
25 be available (notice unencrypted HTTP).
32 description = "Domain to fetch certificate for (defaults to the entry name)";
36 type = types.nullOr types.str;
38 description = "Contact email address for the CA to be able to reach you.";
44 description = "User running the ACME client.";
50 description = "Group running the ACME client.";
53 allowKeysForGroup = mkOption {
57 Give read permissions to the specified group
58 (<option>security.acme.cert.<name>.group</option>) to read SSL private certificates.
65 example = "systemctl reload nginx.service";
67 Commands to run after new certificates go live. Typically
68 the web server and other servers using certificates need to
71 Executed in the same directory with the new certificate.
76 type = types.listOf (types.enum [
77 "cert.der" "cert.pem" "chain.pem" "external.sh"
78 "fullchain.pem" "full.pem" "key.der" "key.pem" "account_key.json" "account_reg.json" ### CHANGED
80 default = [ "fullchain.pem" "full.pem" "key.pem" "account_key.json" "account_reg.json" ]; ### CHANGED
82 Plugins to enable. With default settings simp_le will
83 store public certificate bundle in <filename>fullchain.pem</filename>,
84 private key in <filename>key.pem</filename> and those two previous
85 files combined in <filename>full.pem</filename> in its state directory.
89 activationDelay = mkOption {
90 type = types.nullOr types.str;
93 Systemd time span expression to delay copying new certificates to main
94 state directory. See <citerefentry><refentrytitle>systemd.time</refentrytitle>
95 <manvolnum>7</manvolnum></citerefentry>.
103 Commands to run after certificates are re-issued but before they are
104 activated. Typically the new certificate is published to DNS.
106 Executed in the same directory with the new certificate.
110 extraDomains = mkOption {
111 type = types.attrsOf (types.nullOr types.str);
113 example = literalExample ''
115 "example.org" = "/srv/http/nginx";
116 "mydomain.org" = null;
120 A list of extra domain names, which are included in the one certificate to be issued, with their
121 own server roots if needed.
135 directory = mkOption {
136 default = "/var/lib/acme";
139 Directory where certs and other state will be stored by default.
143 validMin = mkOption {
145 default = 30 * 24 * 3600;
146 description = "Minimum remaining validity before renewal in seconds.";
149 renewInterval = mkOption {
153 Systemd calendar expression when to check for renewal. See
154 <citerefentry><refentrytitle>systemd.time</refentrytitle>
155 <manvolnum>7</manvolnum></citerefentry>.
159 preliminarySelfsigned = mkOption {
163 Whether a preliminary self-signed certificate should be generated before
164 doing ACME requests. This can be useful when certificates are required in
165 a webserver, but ACME needs the webserver to make its requests.
167 With preliminary self-signed certificate the webserver can be started and
168 can later reload the correct ACME certificates.
172 production = mkOption {
176 If set to true, use Let's Encrypt's production environment
177 instead of the staging environment. The main benefit of the
178 staging environment is to get much higher rate limits.
181 <literal>https://letsencrypt.org/docs/staging-environment</literal>
188 type = with types; attrsOf (submodule certOpts);
190 Attribute set of certificates to get signed and renewed.
192 example = literalExample ''
195 webroot = "/var/www/challenges/";
196 email = "foo@example.com";
197 extraDomains = { "www.example.com" = null; "foo.example.com" = "/var/www/foo/"; };
199 "bar.example.com" = {
200 webroot = "/var/www/challenges/";
201 email = "bar@example.com";
209 ###### implementation
211 (mkIf (cfg.certs != { }) {
213 systemd.services = let
214 services = concatLists servicesLists;
215 servicesLists = mapAttrsToList certToServices cfg.certs;
216 certToServices = cert: data:
218 cpath = lpath + optionalString (data.activationDelay != null) ".staging";
219 lpath = "${cfg.directory}/${cert}";
220 rights = if data.allowKeysForGroup then "750" else "700";
221 cmdline = [ "-v" "-d" data.domain "--default_root" data.webroot "--valid_min" cfg.validMin ]
222 ++ optionals (data.email != null) [ "--email" data.email ]
223 ++ concatMap (p: [ "-f" p ]) data.plugins
224 ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains)
225 ++ optionals (!cfg.production) ["--server" "https://acme-staging.api.letsencrypt.org/directory"];
227 description = "Renew ACME Certificate for ${cert}";
228 after = [ "network.target" "network-online.target" ];
229 wants = [ "network-online.target" ];
232 SuccessExitStatus = [ "0" "1" ];
233 PermissionsStartOnly = true;
238 path = with pkgs; [ simp_le systemd ];
240 mkdir -p '${cfg.directory}'
241 chown 'root:root' '${cfg.directory}'
242 chmod 755 '${cfg.directory}'
243 if [ ! -d '${cpath}' ]; then
246 chmod ${rights} '${cpath}'
247 chown -R '${data.user}:${data.group}' '${cpath}'
248 mkdir -p '${data.webroot}/.well-known/acme-challenge'
249 chown -R '${data.user}:${data.group}' '${data.webroot}/.well-known/acme-challenge'
254 simp_le ${escapeShellArgs cmdline}
257 echo "$EXITCODE" > /tmp/lastExitCode
263 if [ -e /tmp/lastExitCode ] && [ "$(cat /tmp/lastExitCode)" = "0" ]; then
264 ${if data.activationDelay != null then ''
268 if [ -d '${lpath}' ]; then
269 systemd-run --no-block --on-active='${data.activationDelay}' --unit acme-setlive-${cert}.service
271 systemctl --wait start acme-setlive-${cert}.service
273 '' else data.postRun}
275 # noop ensuring that the "if" block is non-empty even if
276 # activationDelay == null and postRun == ""
281 before = [ "acme-certificates.target" ];
282 wantedBy = [ "acme-certificates.target" ];
285 description = "Set certificate for ${cert} live";
286 path = with pkgs; [ rsync ];
291 rsync -a --delete-after '${cpath}/' '${lpath}'
293 postStop = data.postRun;
295 selfsignedService = {
296 description = "Create preliminary self-signed certificate for ${cert}";
297 path = [ pkgs.openssl ];
299 if [ ! -d '${cpath}' ]
302 chmod ${rights} '${cpath}'
303 chown '${data.user}:${data.group}' '${cpath}'
308 workdir="$(mktemp -d)"
311 openssl genrsa -des3 -passout pass:xxxx -out $workdir/ca.pass.key 2048
312 openssl rsa -passin pass:xxxx -in $workdir/ca.pass.key -out $workdir/ca.key
313 openssl req -new -key $workdir/ca.key -out $workdir/ca.csr \
314 -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=Security Department/CN=example.com"
315 openssl x509 -req -days 1 -in $workdir/ca.csr -signkey $workdir/ca.key -out $workdir/ca.crt
318 openssl genrsa -des3 -passout pass:xxxx -out $workdir/server.pass.key 2048
319 openssl rsa -passin pass:xxxx -in $workdir/server.pass.key -out $workdir/server.key
320 openssl req -new -key $workdir/server.key -out $workdir/server.csr \
321 -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=IT Department/CN=example.com"
322 openssl x509 -req -days 1 -in $workdir/server.csr -CA $workdir/ca.crt \
323 -CAkey $workdir/ca.key -CAserial $workdir/ca.srl -CAcreateserial \
324 -out $workdir/server.crt
326 # Copy key to destination
327 cp $workdir/server.key ${cpath}/key.pem
329 # Create fullchain.pem (same format as "simp_le ... -f fullchain.pem" creates)
330 cat $workdir/{server.crt,ca.crt} > "${cpath}/fullchain.pem"
332 # Create full.pem for e.g. lighttpd
333 cat $workdir/{server.key,server.crt,ca.crt} > "${cpath}/full.pem"
335 # Give key acme permissions
336 chown '${data.user}:${data.group}' "${cpath}/"{key,fullchain,full}.pem
337 chmod ${rights} "${cpath}/"{key,fullchain,full}.pem
341 PermissionsStartOnly = true;
347 # Do not create self-signed key when key already exists
348 ConditionPathExists = "!${cpath}/key.pem";
351 "acme-selfsigned-certificates.target"
354 "acme-selfsigned-certificates.target"
358 [ { name = "acme-${cert}"; value = acmeService; } ]
359 ++ optional cfg.preliminarySelfsigned { name = "acme-selfsigned-${cert}"; value = selfsignedService; }
360 ++ optional (data.activationDelay != null) { name = "acme-setlive-${cert}"; value = delayService; }
362 servicesAttr = listToAttrs services;
364 after = [ "acme-selfsigned-certificates.target" ];
365 wants = [ "acme-selfsigned-certificates.target" "acme-certificates.target" ];
369 (if config.services.nginx.enable then { nginx = injectServiceDep; } else {}) //
370 (if config.services.lighttpd.enable then { lighttpd = injectServiceDep; } else {});
372 systemd.timers = flip mapAttrs' cfg.certs (cert: data: nameValuePair
375 description = "Renew ACME Certificate for ${cert}";
376 wantedBy = [ "timers.target" ];
378 OnCalendar = cfg.renewInterval;
379 Unit = "acme-${cert}.service";
382 RandomizedDelaySec = "1h";
387 systemd.targets."acme-selfsigned-certificates" = mkIf cfg.preliminarySelfsigned {};
388 systemd.targets."acme-certificates" = {};
394 maintainers = with lib.maintainers; [ abbradar fpletz globin ];
395 #doc = ./acme.xml; ### CHANGED
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list