| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  | { config, lib, pkgs, ... }: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | with lib; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | let | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-11 17:42:17 +01:00
										 |  |  |   cfg = config.security.acme; | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-06 22:08:57 +01:00
										 |  |  |   certOpts = { name, ... }: { | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  |     options = { | 
					
						
							|  |  |  |       webroot = mkOption { | 
					
						
							| 
									
										
										
										
											2015-12-08 21:09:19 +03:00
										 |  |  |         type = types.str; | 
					
						
							| 
									
										
										
										
											2018-02-06 22:08:57 +01:00
										 |  |  |         example = "/var/lib/acme/acme-challenges"; | 
					
						
							| 
									
										
										
										
											2015-12-08 21:09:19 +03:00
										 |  |  |         description = ''
 | 
					
						
							|  |  |  |           Where the webroot of the HTTP vhost is located. | 
					
						
							|  |  |  |           <filename>.well-known/acme-challenge/</filename> directory | 
					
						
							| 
									
										
										
										
											2017-06-08 08:46:40 +02:00
										 |  |  |           will be created below the webroot if it doesn't exist. | 
					
						
							| 
									
										
										
										
											2015-12-08 21:09:19 +03:00
										 |  |  |           <literal>http://example.org/.well-known/acme-challenge/</literal> must also | 
					
						
							|  |  |  |           be available (notice unencrypted HTTP). | 
					
						
							|  |  |  |         '';
 | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-11 12:28:05 -04:00
										 |  |  |       domain = mkOption { | 
					
						
							| 
									
										
										
										
											2018-02-06 22:08:57 +01:00
										 |  |  |         type = types.str; | 
					
						
							|  |  |  |         default = name; | 
					
						
							| 
									
										
										
										
											2017-04-11 12:28:05 -04:00
										 |  |  |         description = "Domain to fetch certificate for (defaults to the entry name)"; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  |       email = mkOption { | 
					
						
							|  |  |  |         type = types.nullOr types.str; | 
					
						
							|  |  |  |         default = null; | 
					
						
							|  |  |  |         description = "Contact email address for the CA to be able to reach you."; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-08 21:09:19 +03:00
										 |  |  |       user = mkOption { | 
					
						
							|  |  |  |         type = types.str; | 
					
						
							|  |  |  |         default = "root"; | 
					
						
							| 
									
										
										
										
											2015-12-11 17:42:17 +01:00
										 |  |  |         description = "User running the ACME client."; | 
					
						
							| 
									
										
										
										
											2015-12-08 21:09:19 +03:00
										 |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       group = mkOption { | 
					
						
							|  |  |  |         type = types.str; | 
					
						
							|  |  |  |         default = "root"; | 
					
						
							| 
									
										
										
										
											2015-12-11 17:42:17 +01:00
										 |  |  |         description = "Group running the ACME client."; | 
					
						
							| 
									
										
										
										
											2015-12-08 21:09:19 +03:00
										 |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-06 04:59:14 +03:00
										 |  |  |       allowKeysForGroup = mkOption { | 
					
						
							|  |  |  |         type = types.bool; | 
					
						
							|  |  |  |         default = false; | 
					
						
							| 
									
										
										
										
											2017-06-08 08:46:40 +02:00
										 |  |  |         description = ''
 | 
					
						
							|  |  |  |           Give read permissions to the specified group | 
					
						
							| 
									
										
										
										
											2018-02-07 00:27:28 +01:00
										 |  |  |           (<option>security.acme.cert.<name>.group</option>) to read SSL private certificates. | 
					
						
							| 
									
										
										
										
											2017-06-08 08:46:40 +02:00
										 |  |  |         '';
 | 
					
						
							| 
									
										
										
										
											2016-01-06 04:59:14 +03:00
										 |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-08 21:09:19 +03:00
										 |  |  |       postRun = mkOption { | 
					
						
							|  |  |  |         type = types.lines; | 
					
						
							|  |  |  |         default = ""; | 
					
						
							|  |  |  |         example = "systemctl reload nginx.service"; | 
					
						
							|  |  |  |         description = ''
 | 
					
						
							|  |  |  |           Commands to run after certificates are re-issued. Typically | 
					
						
							|  |  |  |           the web server and other servers using certificates need to | 
					
						
							|  |  |  |           be reloaded. | 
					
						
							|  |  |  |         '';
 | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  |       plugins = mkOption { | 
					
						
							|  |  |  |         type = types.listOf (types.enum [ | 
					
						
							| 
									
										
										
										
											2016-02-26 01:31:58 +01:00
										 |  |  |           "cert.der" "cert.pem" "chain.pem" "external.sh" | 
					
						
							| 
									
										
										
										
											2016-02-09 23:37:02 +03:00
										 |  |  |           "fullchain.pem" "full.pem" "key.der" "key.pem" "account_key.json" | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  |         ]); | 
					
						
							| 
									
										
										
										
											2017-06-07 21:38:01 +02:00
										 |  |  |         default = [ "fullchain.pem" "full.pem" "key.pem" "account_key.json" ]; | 
					
						
							| 
									
										
										
										
											2015-12-08 21:09:19 +03:00
										 |  |  |         description = ''
 | 
					
						
							|  |  |  |           Plugins to enable. With default settings simp_le will | 
					
						
							| 
									
										
										
										
											2017-06-07 21:38:01 +02:00
										 |  |  |           store public certificate bundle in <filename>fullchain.pem</filename>, | 
					
						
							|  |  |  |           private key in <filename>key.pem</filename> and those two previous | 
					
						
							|  |  |  |           files combined in <filename>full.pem</filename> in its state directory. | 
					
						
							| 
									
										
										
										
											2015-12-08 21:09:19 +03:00
										 |  |  |         '';
 | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       extraDomains = mkOption { | 
					
						
							| 
									
										
										
										
											2015-12-08 21:09:19 +03:00
										 |  |  |         type = types.attrsOf (types.nullOr types.str); | 
					
						
							|  |  |  |         default = {}; | 
					
						
							| 
									
										
										
										
											2017-06-08 08:46:40 +02:00
										 |  |  |         example = literalExample ''
 | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             "example.org" = "/srv/http/nginx"; | 
					
						
							|  |  |  |             "mydomain.org" = null; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         '';
 | 
					
						
							| 
									
										
										
										
											2015-12-08 21:09:19 +03:00
										 |  |  |         description = ''
 | 
					
						
							| 
									
										
										
										
											2018-02-06 22:08:57 +01:00
										 |  |  |           A list of extra domain names, which are included in the one certificate to be issued, with their | 
					
						
							| 
									
										
										
										
											2015-12-08 21:09:19 +03:00
										 |  |  |           own server roots if needed. | 
					
						
							|  |  |  |         '';
 | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  |       }; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | in | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ###### interface | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   options = { | 
					
						
							| 
									
										
										
										
											2015-12-11 17:42:17 +01:00
										 |  |  |     security.acme = { | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  |       directory = mkOption { | 
					
						
							| 
									
										
										
										
											2015-12-11 17:42:17 +01:00
										 |  |  |         default = "/var/lib/acme"; | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  |         type = types.str; | 
					
						
							|  |  |  |         description = ''
 | 
					
						
							| 
									
										
										
										
											2015-12-08 21:09:19 +03:00
										 |  |  |           Directory where certs and other state will be stored by default. | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  |         '';
 | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-12 14:21:44 +01:00
										 |  |  |       validMin = mkOption { | 
					
						
							|  |  |  |         type = types.int; | 
					
						
							|  |  |  |         default = 30 * 24 * 3600; | 
					
						
							|  |  |  |         description = "Minimum remaining validity before renewal in seconds."; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       renewInterval = mkOption { | 
					
						
							|  |  |  |         type = types.str; | 
					
						
							|  |  |  |         default = "weekly"; | 
					
						
							|  |  |  |         description = ''
 | 
					
						
							|  |  |  |           Systemd calendar expression when to check for renewal. See | 
					
						
							|  |  |  |           <citerefentry><refentrytitle>systemd.time</refentrytitle> | 
					
						
							| 
									
										
										
										
											2017-03-21 08:27:56 +01:00
										 |  |  |           <manvolnum>7</manvolnum></citerefentry>. | 
					
						
							| 
									
										
										
										
											2015-12-12 14:21:44 +01:00
										 |  |  |         '';
 | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 12:39:46 +02:00
										 |  |  |       preliminarySelfsigned = mkOption { | 
					
						
							|  |  |  |         type = types.bool; | 
					
						
							|  |  |  |         default = true; | 
					
						
							|  |  |  |         description = ''
 | 
					
						
							|  |  |  |           Whether a preliminary self-signed certificate should be generated before | 
					
						
							|  |  |  |           doing ACME requests. This can be useful when certificates are required in | 
					
						
							|  |  |  |           a webserver, but ACME needs the webserver to make its requests. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           With preliminary self-signed certificate the webserver can be started and | 
					
						
							|  |  |  |           can later reload the correct ACME certificates. | 
					
						
							|  |  |  |         '';
 | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-11 14:19:15 +00:00
										 |  |  |       tosHash = mkOption { | 
					
						
							|  |  |  |         type = types.string; | 
					
						
							|  |  |  |         default = "cc88d8d9517f490191401e7b54e9ffd12a2b9082ec7a1d4cec6101f9f1647e7b"; | 
					
						
							|  |  |  |         description = ''
 | 
					
						
							|  |  |  |           SHA256 of the Terms of Services document. This changes once in a while. | 
					
						
							|  |  |  |         '';
 | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-02 11:10:47 +01:00
										 |  |  |       production = mkOption { | 
					
						
							|  |  |  |         type = types.bool; | 
					
						
							|  |  |  |         default = true; | 
					
						
							|  |  |  |         description = ''
 | 
					
						
							|  |  |  |           If set to true, use Let's Encrypt's production environment | 
					
						
							|  |  |  |           instead of the staging environment. The main benefit of the | 
					
						
							|  |  |  |           staging environment is to get much higher rate limits. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           See | 
					
						
							|  |  |  |           <literal>https://letsencrypt.org/docs/staging-environment</literal> | 
					
						
							|  |  |  |           for more detail. | 
					
						
							|  |  |  |         '';
 | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  |       certs = mkOption { | 
					
						
							|  |  |  |         default = { }; | 
					
						
							| 
									
										
										
										
											2016-11-16 16:28:27 +09:00
										 |  |  |         type = with types; attrsOf (submodule certOpts); | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  |         description = ''
 | 
					
						
							|  |  |  |           Attribute set of certificates to get signed and renewed. | 
					
						
							|  |  |  |         '';
 | 
					
						
							| 
									
										
										
										
											2017-06-08 08:46:40 +02:00
										 |  |  |         example = literalExample ''
 | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             "example.com" = { | 
					
						
							|  |  |  |               webroot = "/var/www/challenges/"; | 
					
						
							|  |  |  |               email = "foo@example.com"; | 
					
						
							|  |  |  |               extraDomains = { "www.example.com" = null; "foo.example.com" = "/var/www/foo/"; }; | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |             "bar.example.com" = { | 
					
						
							|  |  |  |               webroot = "/var/www/challenges/"; | 
					
						
							|  |  |  |               email = "bar@example.com"; | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         '';
 | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  |       }; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ###### implementation | 
					
						
							| 
									
										
										
										
											2015-12-12 16:06:24 +01:00
										 |  |  |   config = mkMerge [ | 
					
						
							|  |  |  |     (mkIf (cfg.certs != { }) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 12:39:46 +02:00
										 |  |  |       systemd.services = let | 
					
						
							|  |  |  |           services = concatLists servicesLists; | 
					
						
							|  |  |  |           servicesLists = mapAttrsToList certToServices cfg.certs; | 
					
						
							|  |  |  |           certToServices = cert: data: | 
					
						
							|  |  |  |               let | 
					
						
							|  |  |  |                 cpath = "${cfg.directory}/${cert}"; | 
					
						
							|  |  |  |                 rights = if data.allowKeysForGroup then "750" else "700"; | 
					
						
							| 
									
										
										
										
											2018-02-06 22:08:57 +01:00
										 |  |  |                 cmdline = [ "-v" "-d" data.domain "--default_root" data.webroot "--valid_min" cfg.validMin "--tos_sha256" cfg.tosHash ] | 
					
						
							| 
									
										
										
										
											2016-06-01 12:39:46 +02:00
										 |  |  |                           ++ optionals (data.email != null) [ "--email" data.email ] | 
					
						
							|  |  |  |                           ++ concatMap (p: [ "-f" p ]) data.plugins | 
					
						
							| 
									
										
										
										
											2017-11-02 11:10:47 +01:00
										 |  |  |                           ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains) | 
					
						
							|  |  |  |                           ++ (if cfg.production then [] | 
					
						
							|  |  |  |                               else ["--server" "https://acme-staging.api.letsencrypt.org/directory"]); | 
					
						
							| 
									
										
										
										
											2016-06-01 12:39:46 +02:00
										 |  |  |                 acmeService = { | 
					
						
							|  |  |  |                   description = "Renew ACME Certificate for ${cert}"; | 
					
						
							| 
									
										
										
										
											2016-09-06 17:39:46 +02:00
										 |  |  |                   after = [ "network.target" "network-online.target" ]; | 
					
						
							|  |  |  |                   wants = [ "network-online.target" ]; | 
					
						
							| 
									
										
										
										
											2016-06-01 12:39:46 +02:00
										 |  |  |                   serviceConfig = { | 
					
						
							|  |  |  |                     Type = "oneshot"; | 
					
						
							|  |  |  |                     SuccessExitStatus = [ "0" "1" ]; | 
					
						
							|  |  |  |                     PermissionsStartOnly = true; | 
					
						
							|  |  |  |                     User = data.user; | 
					
						
							|  |  |  |                     Group = data.group; | 
					
						
							|  |  |  |                     PrivateTmp = true; | 
					
						
							|  |  |  |                   }; | 
					
						
							|  |  |  |                   path = [ pkgs.simp_le ]; | 
					
						
							|  |  |  |                   preStart = ''
 | 
					
						
							|  |  |  |                     mkdir -p '${cfg.directory}' | 
					
						
							| 
									
										
										
										
											2017-05-08 18:14:37 +02:00
										 |  |  |                     chown 'root:root' '${cfg.directory}' | 
					
						
							|  |  |  |                     chmod 755 '${cfg.directory}' | 
					
						
							| 
									
										
										
										
											2016-06-01 12:39:46 +02:00
										 |  |  |                     if [ ! -d '${cpath}' ]; then | 
					
						
							|  |  |  |                       mkdir '${cpath}' | 
					
						
							|  |  |  |                     fi | 
					
						
							|  |  |  |                     chmod ${rights} '${cpath}' | 
					
						
							|  |  |  |                     chown -R '${data.user}:${data.group}' '${cpath}' | 
					
						
							| 
									
										
										
										
											2017-05-08 18:14:37 +02:00
										 |  |  |                     mkdir -p '${data.webroot}/.well-known/acme-challenge' | 
					
						
							|  |  |  |                     chown -R '${data.user}:${data.group}' '${data.webroot}/.well-known/acme-challenge' | 
					
						
							| 
									
										
										
										
											2016-06-01 12:39:46 +02:00
										 |  |  |                   '';
 | 
					
						
							|  |  |  |                   script = ''
 | 
					
						
							|  |  |  |                     cd '${cpath}' | 
					
						
							|  |  |  |                     set +e | 
					
						
							| 
									
										
										
										
											2016-06-12 18:11:37 +01:00
										 |  |  |                     simp_le ${escapeShellArgs cmdline} | 
					
						
							| 
									
										
										
										
											2016-06-01 12:39:46 +02:00
										 |  |  |                     EXITCODE=$? | 
					
						
							|  |  |  |                     set -e | 
					
						
							|  |  |  |                     echo "$EXITCODE" > /tmp/lastExitCode | 
					
						
							|  |  |  |                     exit "$EXITCODE" | 
					
						
							|  |  |  |                   '';
 | 
					
						
							|  |  |  |                   postStop = ''
 | 
					
						
							|  |  |  |                     if [ -e /tmp/lastExitCode ] && [ "$(cat /tmp/lastExitCode)" = "0" ]; then | 
					
						
							|  |  |  |                       echo "Executing postRun hook..." | 
					
						
							|  |  |  |                       ${data.postRun} | 
					
						
							|  |  |  |                     fi | 
					
						
							|  |  |  |                   '';
 | 
					
						
							| 
									
										
										
										
											2015-12-12 16:06:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 12:39:46 +02:00
										 |  |  |                   before = [ "acme-certificates.target" ]; | 
					
						
							|  |  |  |                   wantedBy = [ "acme-certificates.target" ]; | 
					
						
							|  |  |  |                 }; | 
					
						
							|  |  |  |                 selfsignedService = { | 
					
						
							|  |  |  |                   description = "Create preliminary self-signed certificate for ${cert}"; | 
					
						
							|  |  |  |                   preStart = ''
 | 
					
						
							|  |  |  |                       if [ ! -d '${cpath}' ] | 
					
						
							|  |  |  |                       then | 
					
						
							|  |  |  |                         mkdir -p '${cpath}' | 
					
						
							|  |  |  |                         chmod ${rights} '${cpath}' | 
					
						
							|  |  |  |                         chown '${data.user}:${data.group}' '${cpath}' | 
					
						
							|  |  |  |                       fi | 
					
						
							|  |  |  |                   '';
 | 
					
						
							|  |  |  |                   script =  | 
					
						
							|  |  |  |                     ''
 | 
					
						
							|  |  |  |                       # Create self-signed key | 
					
						
							|  |  |  |                       workdir="/run/acme-selfsigned-${cert}" | 
					
						
							|  |  |  |                       ${pkgs.openssl.bin}/bin/openssl genrsa -des3 -passout pass:x -out $workdir/server.pass.key 2048 | 
					
						
							|  |  |  |                       ${pkgs.openssl.bin}/bin/openssl rsa -passin pass:x -in $workdir/server.pass.key -out $workdir/server.key | 
					
						
							|  |  |  |                       ${pkgs.openssl.bin}/bin/openssl req -new -key $workdir/server.key -out $workdir/server.csr \ | 
					
						
							|  |  |  |                         -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=IT Department/CN=example.com" | 
					
						
							|  |  |  |                       ${pkgs.openssl.bin}/bin/openssl x509 -req -days 1 -in $workdir/server.csr -signkey $workdir/server.key -out $workdir/server.crt | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                       # Move key to destination | 
					
						
							|  |  |  |                       mv $workdir/server.key ${cpath}/key.pem | 
					
						
							|  |  |  |                       mv $workdir/server.crt ${cpath}/fullchain.pem | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-07 21:38:01 +02:00
										 |  |  |                       # Create full.pem for e.g. lighttpd (same format as "simp_le ... -f full.pem" creates) | 
					
						
							|  |  |  |                       cat "${cpath}/key.pem" "${cpath}/fullchain.pem" > "${cpath}/full.pem" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 12:39:46 +02:00
										 |  |  |                       # Clean up working directory | 
					
						
							|  |  |  |                       rm $workdir/server.csr | 
					
						
							|  |  |  |                       rm $workdir/server.pass.key | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                       # Give key acme permissions | 
					
						
							|  |  |  |                       chmod ${rights} '${cpath}/key.pem' | 
					
						
							|  |  |  |                       chown '${data.user}:${data.group}' '${cpath}/key.pem' | 
					
						
							|  |  |  |                       chmod ${rights} '${cpath}/fullchain.pem' | 
					
						
							|  |  |  |                       chown '${data.user}:${data.group}' '${cpath}/fullchain.pem' | 
					
						
							| 
									
										
										
										
											2017-06-07 21:38:01 +02:00
										 |  |  |                       chmod ${rights} '${cpath}/full.pem' | 
					
						
							|  |  |  |                       chown '${data.user}:${data.group}' '${cpath}/full.pem' | 
					
						
							| 
									
										
										
										
											2016-06-01 12:39:46 +02:00
										 |  |  |                     '';
 | 
					
						
							|  |  |  |                   serviceConfig = { | 
					
						
							|  |  |  |                     Type = "oneshot"; | 
					
						
							|  |  |  |                     RuntimeDirectory = "acme-selfsigned-${cert}"; | 
					
						
							|  |  |  |                     PermissionsStartOnly = true; | 
					
						
							|  |  |  |                     User = data.user; | 
					
						
							|  |  |  |                     Group = data.group; | 
					
						
							|  |  |  |                   }; | 
					
						
							|  |  |  |                   unitConfig = { | 
					
						
							|  |  |  |                     # Do not create self-signed key when key already exists | 
					
						
							|  |  |  |                     ConditionPathExists = "!${cpath}/key.pem"; | 
					
						
							|  |  |  |                   }; | 
					
						
							|  |  |  |                   before = [ | 
					
						
							|  |  |  |                     "acme-selfsigned-certificates.target" | 
					
						
							|  |  |  |                   ]; | 
					
						
							|  |  |  |                   wantedBy = [ | 
					
						
							|  |  |  |                     "acme-selfsigned-certificates.target" | 
					
						
							|  |  |  |                   ]; | 
					
						
							|  |  |  |                 }; | 
					
						
							|  |  |  |               in ( | 
					
						
							|  |  |  |                 [ { name = "acme-${cert}"; value = acmeService; } ] | 
					
						
							|  |  |  |                 ++ | 
					
						
							|  |  |  |                 (if cfg.preliminarySelfsigned | 
					
						
							|  |  |  |                   then [ { name = "acme-selfsigned-${cert}"; value = selfsignedService; } ] | 
					
						
							|  |  |  |                   else [] | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |               ); | 
					
						
							|  |  |  |           servicesAttr = listToAttrs services; | 
					
						
							| 
									
										
										
										
											2017-06-07 21:38:01 +02:00
										 |  |  |           injectServiceDep = { | 
					
						
							|  |  |  |             after = [ "acme-selfsigned-certificates.target" ]; | 
					
						
							|  |  |  |             wants = [ "acme-selfsigned-certificates.target" "acme-certificates.target" ]; | 
					
						
							| 
									
										
										
										
											2015-12-12 16:06:24 +01:00
										 |  |  |           }; | 
					
						
							| 
									
										
										
										
											2016-06-01 12:39:46 +02:00
										 |  |  |         in | 
					
						
							|  |  |  |           servicesAttr // | 
					
						
							| 
									
										
										
										
											2017-06-07 21:38:01 +02:00
										 |  |  |           (if config.services.nginx.enable then { nginx = injectServiceDep; } else {}) // | 
					
						
							|  |  |  |           (if config.services.lighttpd.enable then { lighttpd = injectServiceDep; } else {}); | 
					
						
							| 
									
										
										
										
											2015-12-12 16:06:24 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |       systemd.timers = flip mapAttrs' cfg.certs (cert: data: nameValuePair | 
					
						
							|  |  |  |         ("acme-${cert}") | 
					
						
							|  |  |  |         ({ | 
					
						
							| 
									
										
										
										
											2016-04-18 11:52:31 +02:00
										 |  |  |           description = "Renew ACME Certificate for ${cert}"; | 
					
						
							| 
									
										
										
										
											2015-12-12 16:06:24 +01:00
										 |  |  |           wantedBy = [ "timers.target" ]; | 
					
						
							|  |  |  |           timerConfig = { | 
					
						
							|  |  |  |             OnCalendar = cfg.renewInterval; | 
					
						
							| 
									
										
										
										
											2015-12-13 16:53:59 +03:00
										 |  |  |             Unit = "acme-${cert}.service"; | 
					
						
							| 
									
										
										
										
											2016-08-10 19:37:11 +02:00
										 |  |  |             Persistent = "yes"; | 
					
						
							| 
									
										
										
										
											2017-01-25 19:11:42 +01:00
										 |  |  |             AccuracySec = "5m"; | 
					
						
							|  |  |  |             RandomizedDelaySec = "1h"; | 
					
						
							| 
									
										
										
										
											2015-12-12 16:06:24 +01:00
										 |  |  |           }; | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2016-06-01 12:39:46 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       systemd.targets."acme-selfsigned-certificates" = mkIf cfg.preliminarySelfsigned {}; | 
					
						
							|  |  |  |       systemd.targets."acme-certificates" = {}; | 
					
						
							| 
									
										
										
										
											2015-12-12 16:06:24 +01:00
										 |  |  |     }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ]; | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-09 14:53:27 +09:00
										 |  |  |   meta = { | 
					
						
							|  |  |  |     maintainers = with lib.maintainers; [ abbradar fpletz globin ]; | 
					
						
							|  |  |  |     doc = ./acme.xml; | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2015-12-06 16:55:09 +01:00
										 |  |  | } |