aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/acme2.nix340
-rw-r--r--modules/default.nix1
-rw-r--r--modules/private/certificates.nix24
-rw-r--r--modules/private/databases/mariadb.nix8
-rw-r--r--modules/private/databases/openldap/default.nix10
-rw-r--r--modules/private/databases/postgresql.nix8
-rw-r--r--modules/private/ejabberd/default.nix6
-rw-r--r--modules/private/ftp.nix4
-rw-r--r--modules/private/irc.nix4
-rw-r--r--modules/private/mail/default.nix2
-rw-r--r--modules/private/mail/dovecot.nix2
-rw-r--r--modules/private/mail/postfix.nix2
-rw-r--r--modules/private/mail/relay.nix2
-rw-r--r--modules/private/monitoring/status.nix2
-rw-r--r--modules/private/tasks/default.nix10
-rw-r--r--modules/private/websites/default.nix2
-rw-r--r--modules/private/websites/florian/integration.nix2
-rw-r--r--modules/private/websites/florian/production.nix2
-rw-r--r--modules/private/websites/nassime/production.nix2
-rw-r--r--modules/private/websites/naturaloutil/production.nix2
-rw-r--r--modules/private/websites/papa/surveillance.nix2
-rw-r--r--modules/private/websites/teliotortay/production.nix2
-rw-r--r--modules/websites/default.nix10
-rw-r--r--pkgs/certbot/default.nix65
-rw-r--r--pkgs/default.nix3
-rw-r--r--pkgs/simp_le/default.nix32
26 files changed, 495 insertions, 54 deletions
diff --git a/modules/acme2.nix b/modules/acme2.nix
new file mode 100644
index 0000000..408c098
--- /dev/null
+++ b/modules/acme2.nix
@@ -0,0 +1,340 @@
1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.security.acme2;
8
9 certOpts = { name, ... }: {
10 options = {
11 webroot = mkOption {
12 type = types.str;
13 example = "/var/lib/acme/acme-challenges";
14 description = ''
15 Where the webroot of the HTTP vhost is located.
16 <filename>.well-known/acme-challenge/</filename> directory
17 will be created below the webroot if it doesn't exist.
18 <literal>http://example.org/.well-known/acme-challenge/</literal> must also
19 be available (notice unencrypted HTTP).
20 '';
21 };
22
23 server = mkOption {
24 type = types.nullOr types.str;
25 default = null;
26 description = ''
27 ACME Directory Resource URI. Defaults to let's encrypt
28 production endpoint,
29 https://acme-v02.api.letsencrypt.org/directory, if unset.
30 '';
31 };
32
33 domain = mkOption {
34 type = types.str;
35 default = name;
36 description = "Domain to fetch certificate for (defaults to the entry name)";
37 };
38
39 email = mkOption {
40 type = types.nullOr types.str;
41 default = null;
42 description = "Contact email address for the CA to be able to reach you.";
43 };
44
45 user = mkOption {
46 type = types.str;
47 default = "root";
48 description = "User running the ACME client.";
49 };
50
51 group = mkOption {
52 type = types.str;
53 default = "root";
54 description = "Group running the ACME client.";
55 };
56
57 allowKeysForGroup = mkOption {
58 type = types.bool;
59 default = false;
60 description = ''
61 Give read permissions to the specified group
62 (<option>security.acme2.cert.&lt;name&gt;.group</option>) to read SSL private certificates.
63 '';
64 };
65
66 postRun = mkOption {
67 type = types.lines;
68 default = "";
69 example = "systemctl reload nginx.service";
70 description = ''
71 Commands to run after new certificates go live. Typically
72 the web server and other servers using certificates need to
73 be reloaded.
74
75 Executed in the same directory with the new certificate.
76 '';
77 };
78
79 plugins = mkOption {
80 type = types.listOf (types.enum [
81 "cert.der" "cert.pem" "chain.pem" "external.sh"
82 "fullchain.pem" "full.pem" "key.der" "key.pem" "account_key.json" "account_reg.json"
83 ]);
84 default = [ "fullchain.pem" "full.pem" "key.pem" "account_key.json" "account_reg.json" ];
85 description = ''
86 Plugins to enable. With default settings simp_le will
87 store public certificate bundle in <filename>fullchain.pem</filename>,
88 private key in <filename>key.pem</filename> and those two previous
89 files combined in <filename>full.pem</filename> in its state directory.
90 '';
91 };
92
93 directory = mkOption {
94 type = types.str;
95 readOnly = true;
96 default = "/var/lib/acme/${name}";
97 description = "Directory where certificate and other state is stored.";
98 };
99
100 extraDomains = mkOption {
101 type = types.attrsOf (types.nullOr types.str);
102 default = {};
103 example = literalExample ''
104 {
105 "example.org" = "/srv/http/nginx";
106 "mydomain.org" = null;
107 }
108 '';
109 description = ''
110 A list of extra domain names, which are included in the one certificate to be issued, with their
111 own server roots if needed.
112 '';
113 };
114 };
115 };
116
117in
118
119{
120
121 ###### interface
122 imports = [
123 (mkRemovedOptionModule [ "security" "acme2" "production" ] ''
124 Use security.acme2.server to define your staging ACME server URL instead.
125
126 To use the let's encrypt staging server, use security.acme2.server =
127 "https://acme-staging-v02.api.letsencrypt.org/directory".
128 ''
129 )
130 (mkRemovedOptionModule [ "security" "acme2" "directory"] "ACME Directory is now hardcoded to /var/lib/acme and its permisisons are managed by systemd. See https://github.com/NixOS/nixpkgs/issues/53852 for more info.")
131 (mkRemovedOptionModule [ "security" "acme" "preDelay"] "This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal")
132 (mkRemovedOptionModule [ "security" "acme" "activationDelay"] "This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal")
133 ];
134 options = {
135 security.acme2 = {
136
137 validMin = mkOption {
138 type = types.int;
139 default = 30 * 24 * 3600;
140 description = "Minimum remaining validity before renewal in seconds.";
141 };
142
143 renewInterval = mkOption {
144 type = types.str;
145 default = "weekly";
146 description = ''
147 Systemd calendar expression when to check for renewal. See
148 <citerefentry><refentrytitle>systemd.time</refentrytitle>
149 <manvolnum>7</manvolnum></citerefentry>.
150 '';
151 };
152
153 server = mkOption {
154 type = types.nullOr types.str;
155 default = null;
156 description = ''
157 ACME Directory Resource URI. Defaults to let's encrypt
158 production endpoint,
159 <literal>https://acme-v02.api.letsencrypt.org/directory</literal>, if unset.
160 '';
161 };
162
163 preliminarySelfsigned = mkOption {
164 type = types.bool;
165 default = true;
166 description = ''
167 Whether a preliminary self-signed certificate should be generated before
168 doing ACME requests. This can be useful when certificates are required in
169 a webserver, but ACME needs the webserver to make its requests.
170
171 With preliminary self-signed certificate the webserver can be started and
172 can later reload the correct ACME certificates.
173 '';
174 };
175
176 certs = mkOption {
177 default = { };
178 type = with types; attrsOf (submodule certOpts);
179 description = ''
180 Attribute set of certificates to get signed and renewed. Creates
181 <literal>acme-''${cert}.{service,timer}</literal> systemd units for
182 each certificate defined here. Other services can add dependencies
183 to those units if they rely on the certificates being present,
184 or trigger restarts of the service if certificates get renewed.
185 '';
186 example = literalExample ''
187 {
188 "example.com" = {
189 webroot = "/var/www/challenges/";
190 email = "foo@example.com";
191 extraDomains = { "www.example.com" = null; "foo.example.com" = "/var/www/foo/"; };
192 };
193 "bar.example.com" = {
194 webroot = "/var/www/challenges/";
195 email = "bar@example.com";
196 };
197 }
198 '';
199 };
200 };
201 };
202
203 ###### implementation
204 config = mkMerge [
205 (mkIf (cfg.certs != { }) {
206
207 systemd.services = let
208 services = concatLists servicesLists;
209 servicesLists = mapAttrsToList certToServices cfg.certs;
210 certToServices = cert: data:
211 let
212 lpath = "acme/${cert}";
213 rights = if data.allowKeysForGroup then "750" else "700";
214 cmdline = [ "-v" "-d" data.domain "--default_root" data.webroot "--valid_min" cfg.validMin ]
215 ++ optionals (data.email != null) [ "--email" data.email ]
216 ++ concatMap (p: [ "-f" p ]) data.plugins
217 ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains)
218 ++ optionals (cfg.server != null || data.server != null) ["--server" (if data.server == null then cfg.server else data.server)];
219 acmeService = {
220 description = "Renew ACME Certificate for ${cert}";
221 after = [ "network.target" "network-online.target" ];
222 wants = [ "network-online.target" ];
223 # simp_le uses requests, which uses certifi under the hood,
224 # which doesn't respect the system trust store.
225 # At least in the acme test, we provision a fake CA, impersonating the LE endpoint.
226 # REQUESTS_CA_BUNDLE is a way to teach python requests to use something else
227 environment.REQUESTS_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt";
228 serviceConfig = {
229 Type = "oneshot";
230 # With RemainAfterExit the service is considered active even
231 # after the main process having exited, which means when it
232 # gets changed, the activation phase restarts it, meaning
233 # the permissions of the StateDirectory get adjusted
234 # according to the specified group
235 RemainAfterExit = true;
236 SuccessExitStatus = [ "0" "1" ];
237 User = data.user;
238 Group = data.group;
239 PrivateTmp = true;
240 StateDirectory = lpath;
241 StateDirectoryMode = rights;
242 WorkingDirectory = "/var/lib/${lpath}";
243 ExecStart = "${pkgs.simp_le_0_17}/bin/simp_le ${escapeShellArgs cmdline}";
244 ExecStartPost =
245 let
246 script = pkgs.writeScript "acme-post-start" ''
247 #!${pkgs.runtimeShell} -e
248 ${data.postRun}
249 '';
250 in
251 "+${script}";
252 };
253
254 };
255 selfsignedService = {
256 description = "Create preliminary self-signed certificate for ${cert}";
257 path = [ pkgs.openssl ];
258 script =
259 ''
260 workdir="$(mktemp -d)"
261
262 # Create CA
263 openssl genrsa -des3 -passout pass:xxxx -out $workdir/ca.pass.key 2048
264 openssl rsa -passin pass:xxxx -in $workdir/ca.pass.key -out $workdir/ca.key
265 openssl req -new -key $workdir/ca.key -out $workdir/ca.csr \
266 -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=Security Department/CN=example.com"
267 openssl x509 -req -days 1 -in $workdir/ca.csr -signkey $workdir/ca.key -out $workdir/ca.crt
268
269 # Create key
270 openssl genrsa -des3 -passout pass:xxxx -out $workdir/server.pass.key 2048
271 openssl rsa -passin pass:xxxx -in $workdir/server.pass.key -out $workdir/server.key
272 openssl req -new -key $workdir/server.key -out $workdir/server.csr \
273 -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=IT Department/CN=example.com"
274 openssl x509 -req -days 1 -in $workdir/server.csr -CA $workdir/ca.crt \
275 -CAkey $workdir/ca.key -CAserial $workdir/ca.srl -CAcreateserial \
276 -out $workdir/server.crt
277
278 # Copy key to destination
279 cp $workdir/server.key /var/lib/${lpath}/key.pem
280
281 # Create fullchain.pem (same format as "simp_le ... -f fullchain.pem" creates)
282 cat $workdir/{server.crt,ca.crt} > "/var/lib/${lpath}/fullchain.pem"
283
284 # Create full.pem for e.g. lighttpd
285 cat $workdir/{server.key,server.crt,ca.crt} > "/var/lib/${lpath}/full.pem"
286
287 # Give key acme permissions
288 chown '${data.user}:${data.group}' "/var/lib/${lpath}/"{key,fullchain,full}.pem
289 chmod ${rights} "/var/lib/${lpath}/"{key,fullchain,full}.pem
290 '';
291 serviceConfig = {
292 Type = "oneshot";
293 PrivateTmp = true;
294 StateDirectory = lpath;
295 User = data.user;
296 Group = data.group;
297 };
298 unitConfig = {
299 # Do not create self-signed key when key already exists
300 ConditionPathExists = "!/var/lib/${lpath}/key.pem";
301 };
302 };
303 in (
304 [ { name = "acme-${cert}"; value = acmeService; } ]
305 ++ optional cfg.preliminarySelfsigned { name = "acme-selfsigned-${cert}"; value = selfsignedService; }
306 );
307 servicesAttr = listToAttrs services;
308 in
309 servicesAttr;
310
311 systemd.tmpfiles.rules =
312 flip mapAttrsToList cfg.certs
313 (cert: data: "d ${data.webroot}/.well-known/acme-challenge - ${data.user} ${data.group}");
314
315 systemd.timers = flip mapAttrs' cfg.certs (cert: data: nameValuePair
316 ("acme-${cert}")
317 ({
318 description = "Renew ACME Certificate for ${cert}";
319 wantedBy = [ "timers.target" ];
320 timerConfig = {
321 OnCalendar = cfg.renewInterval;
322 Unit = "acme-${cert}.service";
323 Persistent = "yes";
324 AccuracySec = "5m";
325 RandomizedDelaySec = "1h";
326 };
327 })
328 );
329
330 systemd.targets.acme-selfsigned-certificates = mkIf cfg.preliminarySelfsigned {};
331 systemd.targets.acme-certificates = {};
332 })
333
334 ];
335
336 meta = {
337 maintainers = with lib.maintainers; [ abbradar fpletz globin ];
338 #doc = ./acme.xml;
339 };
340}
diff --git a/modules/default.nix b/modules/default.nix
index 9ff6ea6..98dc77d 100644
--- a/modules/default.nix
+++ b/modules/default.nix
@@ -19,4 +19,5 @@
19 19
20 php-application = ./websites/php-application.nix; 20 php-application = ./websites/php-application.nix;
21 websites = ./websites; 21 websites = ./websites;
22 acme2 = ./acme2.nix;
22} // (if builtins.pathExists ./private then import ./private else {}) 23} // (if builtins.pathExists ./private then import ./private else {})
diff --git a/modules/private/certificates.nix b/modules/private/certificates.nix
index 2d24579..f057200 100644
--- a/modules/private/certificates.nix
+++ b/modules/private/certificates.nix
@@ -4,7 +4,7 @@
4 enable = lib.mkEnableOption "enable certificates"; 4 enable = lib.mkEnableOption "enable certificates";
5 certConfig = lib.mkOption { 5 certConfig = lib.mkOption {
6 default = { 6 default = {
7 webroot = "${config.security.acme.directory}/acme-challenge"; 7 webroot = "/var/lib/acme/acme-challenge";
8 email = "ismael@bouya.org"; 8 email = "ismael@bouya.org";
9 postRun = builtins.concatStringsSep "\n" [ 9 postRun = builtins.concatStringsSep "\n" [
10 (lib.optionalString config.services.httpd.Prod.enable "systemctl reload httpdProd.service") 10 (lib.optionalString config.services.httpd.Prod.enable "systemctl reload httpdProd.service")
@@ -12,7 +12,7 @@
12 (lib.optionalString config.services.httpd.Inte.enable "systemctl reload httpdInte.service") 12 (lib.optionalString config.services.httpd.Inte.enable "systemctl reload httpdInte.service")
13 (lib.optionalString config.services.nginx.enable "systemctl reload nginx.service") 13 (lib.optionalString config.services.nginx.enable "systemctl reload nginx.service")
14 ]; 14 ];
15 plugins = [ "cert.pem" "chain.pem" "fullchain.pem" "full.pem" "key.pem" "account_key.json" ]; 15 plugins = [ "cert.pem" "chain.pem" "fullchain.pem" "full.pem" "key.pem" "account_key.json" "account_reg.json"];
16 }; 16 };
17 description = "Default configuration for certificates"; 17 description = "Default configuration for certificates";
18 }; 18 };
@@ -20,7 +20,7 @@
20 20
21 config = lib.mkIf config.myServices.certificates.enable { 21 config = lib.mkIf config.myServices.certificates.enable {
22 services.duplyBackup.profiles.system.excludeFile = '' 22 services.duplyBackup.profiles.system.excludeFile = ''
23 + ${config.security.acme.directory} 23 + /var/lib/acme/acme-challenge
24 ''; 24 '';
25 services.nginx = { 25 services.nginx = {
26 recommendedTlsSettings = true; 26 recommendedTlsSettings = true;
@@ -30,9 +30,9 @@
30 myServices.databasesCerts = config.myServices.certificates.certConfig; 30 myServices.databasesCerts = config.myServices.certificates.certConfig;
31 myServices.ircCerts = config.myServices.certificates.certConfig; 31 myServices.ircCerts = config.myServices.certificates.certConfig;
32 32
33 security.acme.preliminarySelfsigned = true; 33 security.acme2.preliminarySelfsigned = true;
34 34
35 security.acme.certs = { 35 security.acme2.certs = {
36 "${name}" = config.myServices.certificates.certConfig // { 36 "${name}" = config.myServices.certificates.certConfig // {
37 domain = config.hostEnv.fqdn; 37 domain = config.hostEnv.fqdn;
38 }; 38 };
@@ -41,17 +41,17 @@
41 systemd.services = lib.attrsets.mapAttrs' (k: v: 41 systemd.services = lib.attrsets.mapAttrs' (k: v:
42 lib.attrsets.nameValuePair "acme-selfsigned-${k}" (lib.mkBefore { script = 42 lib.attrsets.nameValuePair "acme-selfsigned-${k}" (lib.mkBefore { script =
43 (lib.optionalString (builtins.elem "cert.pem" v.plugins) '' 43 (lib.optionalString (builtins.elem "cert.pem" v.plugins) ''
44 cp $workdir/server.crt ${config.security.acme.directory}/${k}/cert.pem 44 cp $workdir/server.crt ${config.security.acme2.certs."${k}".directory}/cert.pem
45 chown '${v.user}:${v.group}' ${config.security.acme.directory}/${k}/cert.pem 45 chown '${v.user}:${v.group}' ${config.security.acme2.certs."${k}".directory}/cert.pem
46 chmod ${if v.allowKeysForGroup then "750" else "700"} ${config.security.acme.directory}/${k}/cert.pem 46 chmod ${if v.allowKeysForGroup then "750" else "700"} ${config.security.acme2.certs."${k}".directory}/cert.pem
47 '') + 47 '') +
48 (lib.optionalString (builtins.elem "chain.pem" v.plugins) '' 48 (lib.optionalString (builtins.elem "chain.pem" v.plugins) ''
49 cp $workdir/ca.crt ${config.security.acme.directory}/${k}/chain.pem 49 cp $workdir/ca.crt ${config.security.acme2.certs."${k}".directory}/chain.pem
50 chown '${v.user}:${v.group}' ${config.security.acme.directory}/${k}/chain.pem 50 chown '${v.user}:${v.group}' ${config.security.acme2.certs."${k}".directory}/chain.pem
51 chmod ${if v.allowKeysForGroup then "750" else "700"} ${config.security.acme.directory}/${k}/chain.pem 51 chmod ${if v.allowKeysForGroup then "750" else "700"} ${config.security.acme2.certs."${k}".directory}/chain.pem
52 '') 52 '')
53 ; }) 53 ; })
54 ) config.security.acme.certs // { 54 ) config.security.acme2.certs // {
55 httpdProd = lib.mkIf config.services.httpd.Prod.enable 55 httpdProd = lib.mkIf config.services.httpd.Prod.enable
56 { after = [ "acme-selfsigned-certificates.target" ]; wants = [ "acme-selfsigned-certificates.target" ]; }; 56 { after = [ "acme-selfsigned-certificates.target" ]; wants = [ "acme-selfsigned-certificates.target" ]; };
57 httpdTools = lib.mkIf config.services.httpd.Tools.enable 57 httpdTools = lib.mkIf config.services.httpd.Tools.enable
diff --git a/modules/private/databases/mariadb.nix b/modules/private/databases/mariadb.nix
index 3359064..ed647ea 100644
--- a/modules/private/databases/mariadb.nix
+++ b/modules/private/databases/mariadb.nix
@@ -96,8 +96,8 @@ in {
96 dataDir = cfg.dataDir; 96 dataDir = cfg.dataDir;
97 extraOptions = '' 97 extraOptions = ''
98 ssl_ca = ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt 98 ssl_ca = ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
99 ssl_key = ${config.security.acme.directory}/mysql/key.pem 99 ssl_key = ${config.security.acme2.certs.mysql.directory}/key.pem
100 ssl_cert = ${config.security.acme.directory}/mysql/fullchain.pem 100 ssl_cert = ${config.security.acme2.certs.mysql.directory}/fullchain.pem
101 101
102 # for replication 102 # for replication
103 log-bin=mariadb-bin 103 log-bin=mariadb-bin
@@ -110,10 +110,10 @@ in {
110 }; 110 };
111 111
112 users.users.mysql.extraGroups = [ "keys" ]; 112 users.users.mysql.extraGroups = [ "keys" ];
113 security.acme.certs."mysql" = config.myServices.databasesCerts // { 113 security.acme2.certs."mysql" = config.myServices.databasesCerts // {
114 user = "mysql"; 114 user = "mysql";
115 group = "mysql"; 115 group = "mysql";
116 plugins = [ "fullchain.pem" "key.pem" "account_key.json" ]; 116 plugins = [ "fullchain.pem" "key.pem" "account_key.json" "account_reg.json" ];
117 domain = "db-1.immae.eu"; 117 domain = "db-1.immae.eu";
118 postRun = '' 118 postRun = ''
119 systemctl restart mysql.service 119 systemctl restart mysql.service
diff --git a/modules/private/databases/openldap/default.nix b/modules/private/databases/openldap/default.nix
index 22f6f7b..d7d61db 100644
--- a/modules/private/databases/openldap/default.nix
+++ b/modules/private/databases/openldap/default.nix
@@ -24,9 +24,9 @@ let
24 overlay syncprov 24 overlay syncprov
25 syncprov-checkpoint 100 10 25 syncprov-checkpoint 100 10
26 26
27 TLSCertificateFile ${config.security.acme.directory}/ldap/cert.pem 27 TLSCertificateFile ${config.security.acme2.certs.ldap.directory}/cert.pem
28 TLSCertificateKeyFile ${config.security.acme.directory}/ldap/key.pem 28 TLSCertificateKeyFile ${config.security.acme2.certs.ldap.directory}/key.pem
29 TLSCACertificateFile ${config.security.acme.directory}/ldap/fullchain.pem 29 TLSCACertificateFile ${config.security.acme2.certs.ldap.directory}/fullchain.pem
30 TLSCACertificatePath ${pkgs.cacert.unbundled}/etc/ssl/certs/ 30 TLSCACertificatePath ${pkgs.cacert.unbundled}/etc/ssl/certs/
31 #This makes openldap crash 31 #This makes openldap crash
32 #TLSCipherSuite DEFAULT 32 #TLSCipherSuite DEFAULT
@@ -117,10 +117,10 @@ in
117 users.users.openldap.extraGroups = [ "keys" ]; 117 users.users.openldap.extraGroups = [ "keys" ];
118 networking.firewall.allowedTCPPorts = [ 636 389 ]; 118 networking.firewall.allowedTCPPorts = [ 636 389 ];
119 119
120 security.acme.certs."ldap" = config.myServices.databasesCerts // { 120 security.acme2.certs."ldap" = config.myServices.databasesCerts // {
121 user = "openldap"; 121 user = "openldap";
122 group = "openldap"; 122 group = "openldap";
123 plugins = [ "fullchain.pem" "key.pem" "cert.pem" "account_key.json" ]; 123 plugins = [ "fullchain.pem" "key.pem" "cert.pem" "account_key.json" "account_reg.json" ];
124 domain = "ldap.immae.eu"; 124 domain = "ldap.immae.eu";
125 postRun = '' 125 postRun = ''
126 systemctl restart openldap.service 126 systemctl restart openldap.service
diff --git a/modules/private/databases/postgresql.nix b/modules/private/databases/postgresql.nix
index 3dcd311..27ea59c 100644
--- a/modules/private/databases/postgresql.nix
+++ b/modules/private/databases/postgresql.nix
@@ -107,10 +107,10 @@ in {
107 config = lib.mkIf cfg.enable { 107 config = lib.mkIf cfg.enable {
108 networking.firewall.allowedTCPPorts = [ 5432 ]; 108 networking.firewall.allowedTCPPorts = [ 5432 ];
109 109
110 security.acme.certs."postgresql" = config.myServices.databasesCerts // { 110 security.acme2.certs."postgresql" = config.myServices.databasesCerts // {
111 user = "postgres"; 111 user = "postgres";
112 group = "postgres"; 112 group = "postgres";
113 plugins = [ "fullchain.pem" "key.pem" "account_key.json" ]; 113 plugins = [ "fullchain.pem" "key.pem" "account_key.json" "account_reg.json" ];
114 domain = "db-1.immae.eu"; 114 domain = "db-1.immae.eu";
115 postRun = '' 115 postRun = ''
116 systemctl reload postgresql.service 116 systemctl reload postgresql.service
@@ -165,8 +165,8 @@ in {
165 # makes it order of magnitudes quicker 165 # makes it order of magnitudes quicker
166 synchronous_commit = off 166 synchronous_commit = off
167 ssl = on 167 ssl = on
168 ssl_cert_file = '${config.security.acme.directory}/postgresql/fullchain.pem' 168 ssl_cert_file = '${config.security.acme2.certs.postgresql.directory}/fullchain.pem'
169 ssl_key_file = '${config.security.acme.directory}/postgresql/key.pem' 169 ssl_key_file = '${config.security.acme2.certs.postgresql.directory}/key.pem'
170 ''; 170 '';
171 authentication = let 171 authentication = let
172 hosts = builtins.concatStringsSep "\n" ( 172 hosts = builtins.concatStringsSep "\n" (
diff --git a/modules/private/ejabberd/default.nix b/modules/private/ejabberd/default.nix
index 5e717f4..3537c24 100644
--- a/modules/private/ejabberd/default.nix
+++ b/modules/private/ejabberd/default.nix
@@ -14,7 +14,7 @@ in
14 }; 14 };
15 15
16 config = lib.mkIf cfg.enable { 16 config = lib.mkIf cfg.enable {
17 security.acme.certs = { 17 security.acme2.certs = {
18 "ejabberd" = config.myServices.certificates.certConfig // { 18 "ejabberd" = config.myServices.certificates.certConfig // {
19 user = "ejabberd"; 19 user = "ejabberd";
20 group = "ejabberd"; 20 group = "ejabberd";
@@ -58,7 +58,7 @@ in
58 text = '' 58 text = ''
59 host_config: 59 host_config:
60 "immae.fr": 60 "immae.fr":
61 domain_certfile: "${config.security.acme.directory}/ejabberd/full.pem" 61 domain_certfile: "${config.security.acme2.certs.ejabberd.directory}/full.pem"
62 auth_method: [ldap] 62 auth_method: [ldap]
63 ldap_servers: ["${config.myEnv.jabber.ldap.host}"] 63 ldap_servers: ["${config.myEnv.jabber.ldap.host}"]
64 ldap_encrypt: tls 64 ldap_encrypt: tls
@@ -81,7 +81,7 @@ in
81 ERLANG_NODE=ejabberd@localhost 81 ERLANG_NODE=ejabberd@localhost
82 ''; 82 '';
83 configFile = pkgs.runCommand "ejabberd.yml" { 83 configFile = pkgs.runCommand "ejabberd.yml" {
84 certificatePrivateKeyAndFullChain = "${config.security.acme.directory}/ejabberd/full.pem"; 84 certificatePrivateKeyAndFullChain = "${config.security.acme2.certs.ejabberd.directory}/full.pem";
85 certificateCA = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; 85 certificateCA = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
86 sql_config_file = config.secrets.fullPaths."ejabberd/psql.yml"; 86 sql_config_file = config.secrets.fullPaths."ejabberd/psql.yml";
87 host_config_file = config.secrets.fullPaths."ejabberd/host.yml"; 87 host_config_file = config.secrets.fullPaths."ejabberd/host.yml";
diff --git a/modules/private/ftp.nix b/modules/private/ftp.nix
index e3c1f70..585fe63 100644
--- a/modules/private/ftp.nix
+++ b/modules/private/ftp.nix
@@ -17,7 +17,7 @@ in
17 services.duplyBackup.profiles.ftp = { 17 services.duplyBackup.profiles.ftp = {
18 rootDir = "/var/lib/ftp"; 18 rootDir = "/var/lib/ftp";
19 }; 19 };
20 security.acme.certs."ftp" = config.myServices.certificates.certConfig // { 20 security.acme2.certs."ftp" = config.myServices.certificates.certConfig // {
21 domain = "eldiron.immae.eu"; 21 domain = "eldiron.immae.eu";
22 postRun = '' 22 postRun = ''
23 systemctl restart pure-ftpd.service 23 systemctl restart pure-ftpd.service
@@ -113,7 +113,7 @@ in
113 MaxDiskUsage 99 113 MaxDiskUsage 99
114 CustomerProof yes 114 CustomerProof yes
115 TLS 1 115 TLS 1
116 CertFile ${config.security.acme.directory}/ftp/full.pem 116 CertFile ${config.security.acme2.certs.ftp.directory}/full.pem
117 ''; 117 '';
118 in { 118 in {
119 description = "Pure-FTPd server"; 119 description = "Pure-FTPd server";
diff --git a/modules/private/irc.nix b/modules/private/irc.nix
index 4e6eaab..1054b96 100644
--- a/modules/private/irc.nix
+++ b/modules/private/irc.nix
@@ -20,7 +20,7 @@ in
20 services.duplyBackup.profiles.irc = { 20 services.duplyBackup.profiles.irc = {
21 rootDir = "/var/lib/bitlbee"; 21 rootDir = "/var/lib/bitlbee";
22 }; 22 };
23 security.acme.certs."irc" = config.myServices.ircCerts // { 23 security.acme2.certs."irc" = config.myServices.ircCerts // {
24 domain = "irc.immae.eu"; 24 domain = "irc.immae.eu";
25 postRun = '' 25 postRun = ''
26 systemctl restart stunnel.service 26 systemctl restart stunnel.service
@@ -49,7 +49,7 @@ in
49 bitlbee = { 49 bitlbee = {
50 accept = 6697; 50 accept = 6697;
51 connect = 6667; 51 connect = 6667;
52 cert = "${config.security.acme.directory}/irc/full.pem"; 52 cert = "${config.security.acme2.certs.irc.directory}/full.pem";
53 }; 53 };
54 }; 54 };
55 }; 55 };
diff --git a/modules/private/mail/default.nix b/modules/private/mail/default.nix
index b50e346..1c64e15 100644
--- a/modules/private/mail/default.nix
+++ b/modules/private/mail/default.nix
@@ -13,7 +13,7 @@
13 options.myServices.mailBackup.enable = lib.mkEnableOption "enable MX backup services"; 13 options.myServices.mailBackup.enable = lib.mkEnableOption "enable MX backup services";
14 14
15 config = lib.mkIf config.myServices.mail.enable { 15 config = lib.mkIf config.myServices.mail.enable {
16 security.acme.certs."mail" = config.myServices.certificates.certConfig // { 16 security.acme2.certs."mail" = config.myServices.certificates.certConfig // {
17 domain = config.hostEnv.fqdn; 17 domain = config.hostEnv.fqdn;
18 extraDomains = let 18 extraDomains = let
19 zonesWithMx = builtins.filter (zone: 19 zonesWithMx = builtins.filter (zone:
diff --git a/modules/private/mail/dovecot.nix b/modules/private/mail/dovecot.nix
index 4facef5..523c017 100644
--- a/modules/private/mail/dovecot.nix
+++ b/modules/private/mail/dovecot.nix
@@ -269,7 +269,7 @@ in
269 [ 269 [
270 "0 2 * * * root ${cron_script}/bin/cleanup-imap-folders" 270 "0 2 * * * root ${cron_script}/bin/cleanup-imap-folders"
271 ]; 271 ];
272 security.acme.certs."mail" = { 272 security.acme2.certs."mail" = {
273 postRun = '' 273 postRun = ''
274 systemctl restart dovecot2.service 274 systemctl restart dovecot2.service
275 ''; 275 '';
diff --git a/modules/private/mail/postfix.nix b/modules/private/mail/postfix.nix
index bd284cb..8fe06da 100644
--- a/modules/private/mail/postfix.nix
+++ b/modules/private/mail/postfix.nix
@@ -417,7 +417,7 @@
417 }; 417 };
418 }; 418 };
419 }; 419 };
420 security.acme.certs."mail" = { 420 security.acme2.certs."mail" = {
421 postRun = '' 421 postRun = ''
422 systemctl restart postfix.service 422 systemctl restart postfix.service
423 ''; 423 '';
diff --git a/modules/private/mail/relay.nix b/modules/private/mail/relay.nix
index 9111350..e0aa387 100644
--- a/modules/private/mail/relay.nix
+++ b/modules/private/mail/relay.nix
@@ -1,7 +1,7 @@
1{ lib, pkgs, config, nodes, name, ... }: 1{ lib, pkgs, config, nodes, name, ... }:
2{ 2{
3 config = lib.mkIf config.myServices.mailBackup.enable { 3 config = lib.mkIf config.myServices.mailBackup.enable {
4 security.acme.certs."mail" = config.myServices.certificates.certConfig // { 4 security.acme2.certs."mail" = config.myServices.certificates.certConfig // {
5 postRun = '' 5 postRun = ''
6 systemctl restart postfix.service 6 systemctl restart postfix.service
7 ''; 7 '';
diff --git a/modules/private/monitoring/status.nix b/modules/private/monitoring/status.nix
index d25d934..2860e96 100644
--- a/modules/private/monitoring/status.nix
+++ b/modules/private/monitoring/status.nix
@@ -34,7 +34,7 @@
34 locations."/".proxyPass = "http://unix:/run/naemon-status/socket.sock:/"; 34 locations."/".proxyPass = "http://unix:/run/naemon-status/socket.sock:/";
35 }; 35 };
36 }; 36 };
37 security.acme.certs."${name}".extraDomains."status.immae.eu" = null; 37 security.acme2.certs."${name}".extraDomains."status.immae.eu" = null;
38 38
39 myServices.certificates.enable = true; 39 myServices.certificates.enable = true;
40 networking.firewall.allowedTCPPorts = [ 80 443 ]; 40 networking.firewall.allowedTCPPorts = [ 80 443 ];
diff --git a/modules/private/tasks/default.nix b/modules/private/tasks/default.nix
index c4f065b..c0cc87b 100644
--- a/modules/private/tasks/default.nix
+++ b/modules/private/tasks/default.nix
@@ -192,9 +192,9 @@ in {
192 192
193 myServices.websites.webappDirs._task = ./www; 193 myServices.websites.webappDirs._task = ./www;
194 194
195 security.acme.certs."task" = config.myServices.certificates.certConfig // { 195 security.acme2.certs."task" = config.myServices.certificates.certConfig // {
196 inherit user group; 196 inherit user group;
197 plugins = [ "fullchain.pem" "key.pem" "cert.pem" "account_key.json" ]; 197 plugins = [ "fullchain.pem" "key.pem" "cert.pem" "account_key.json" "account_reg.json" ];
198 domain = fqdn; 198 domain = fqdn;
199 postRun = '' 199 postRun = ''
200 systemctl restart taskserver.service 200 systemctl restart taskserver.service
@@ -244,9 +244,9 @@ in {
244 inherit fqdn; 244 inherit fqdn;
245 listenHost = "::"; 245 listenHost = "::";
246 pki.manual.ca.cert = "${server_vardir}/keys/ca.cert"; 246 pki.manual.ca.cert = "${server_vardir}/keys/ca.cert";
247 pki.manual.server.cert = "${config.security.acme.directory}/task/fullchain.pem"; 247 pki.manual.server.cert = "${config.security.acme2.certs.task.directory}/fullchain.pem";
248 pki.manual.server.crl = "${config.security.acme.directory}/task/invalid.crl"; 248 pki.manual.server.crl = "${config.security.acme2.certs.task.directory}/invalid.crl";
249 pki.manual.server.key = "${config.security.acme.directory}/task/key.pem"; 249 pki.manual.server.key = "${config.security.acme2.certs.task.directory}/key.pem";
250 requestLimit = 104857600; 250 requestLimit = 104857600;
251 }; 251 };
252 252
diff --git a/modules/private/websites/default.nix b/modules/private/websites/default.nix
index 7f3e463..90f24a4 100644
--- a/modules/private/websites/default.nix
+++ b/modules/private/websites/default.nix
@@ -125,7 +125,7 @@ in
125 125
126 system.activationScripts = { 126 system.activationScripts = {
127 httpd = '' 127 httpd = ''
128 install -d -m 0755 ${config.security.acme.directory}/acme-challenge 128 install -d -m 0755 /var/lib/acme/acme-challenge
129 install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions 129 install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions
130 ''; 130 '';
131 }; 131 };
diff --git a/modules/private/websites/florian/integration.nix b/modules/private/websites/florian/integration.nix
index 00de761..ef7d13a 100644
--- a/modules/private/websites/florian/integration.nix
+++ b/modules/private/websites/florian/integration.nix
@@ -8,7 +8,7 @@ in {
8 options.myServices.websites.florian.integration.enable = lib.mkEnableOption "enable Florian's website integration"; 8 options.myServices.websites.florian.integration.enable = lib.mkEnableOption "enable Florian's website integration";
9 9
10 config = lib.mkIf cfg.enable { 10 config = lib.mkIf cfg.enable {
11 security.acme.certs."ftp".extraDomains."florian.immae.eu" = null; 11 security.acme2.certs."ftp".extraDomains."florian.immae.eu" = null;
12 12
13 services.websites.env.integration.modules = adminer.apache.modules; 13 services.websites.env.integration.modules = adminer.apache.modules;
14 services.websites.env.integration.vhostConfs.florian = { 14 services.websites.env.integration.vhostConfs.florian = {
diff --git a/modules/private/websites/florian/production.nix b/modules/private/websites/florian/production.nix
index 8d3dfb0..1abc715 100644
--- a/modules/private/websites/florian/production.nix
+++ b/modules/private/websites/florian/production.nix
@@ -8,7 +8,7 @@ in {
8 options.myServices.websites.florian.production.enable = lib.mkEnableOption "enable Florian's website production"; 8 options.myServices.websites.florian.production.enable = lib.mkEnableOption "enable Florian's website production";
9 9
10 config = lib.mkIf cfg.enable { 10 config = lib.mkIf cfg.enable {
11 security.acme.certs."ftp".extraDomains."tellesflorian.com" = null; 11 security.acme2.certs."ftp".extraDomains."tellesflorian.com" = null;
12 12
13 services.websites.env.production.modules = adminer.apache.modules; 13 services.websites.env.production.modules = adminer.apache.modules;
14 services.websites.env.production.vhostConfs.florian = { 14 services.websites.env.production.vhostConfs.florian = {
diff --git a/modules/private/websites/nassime/production.nix b/modules/private/websites/nassime/production.nix
index f9468f9..293519f 100644
--- a/modules/private/websites/nassime/production.nix
+++ b/modules/private/websites/nassime/production.nix
@@ -9,7 +9,7 @@ in {
9 config = lib.mkIf cfg.enable { 9 config = lib.mkIf cfg.enable {
10 services.webstats.sites = [ { name = "nassime.bouya.org"; } ]; 10 services.webstats.sites = [ { name = "nassime.bouya.org"; } ];
11 11
12 security.acme.certs."ftp".extraDomains."nassime.bouya.org" = null; 12 security.acme2.certs."ftp".extraDomains."nassime.bouya.org" = null;
13 13
14 services.websites.env.production.vhostConfs.nassime = { 14 services.websites.env.production.vhostConfs.nassime = {
15 certName = "nassime"; 15 certName = "nassime";
diff --git a/modules/private/websites/naturaloutil/production.nix b/modules/private/websites/naturaloutil/production.nix
index 628e129..a276c47 100644
--- a/modules/private/websites/naturaloutil/production.nix
+++ b/modules/private/websites/naturaloutil/production.nix
@@ -10,7 +10,7 @@ in {
10 config = lib.mkIf cfg.enable { 10 config = lib.mkIf cfg.enable {
11 services.webstats.sites = [ { name = "naturaloutil.immae.eu"; } ]; 11 services.webstats.sites = [ { name = "naturaloutil.immae.eu"; } ];
12 12
13 security.acme.certs."ftp".extraDomains."naturaloutil.immae.eu" = null; 13 security.acme2.certs."ftp".extraDomains."naturaloutil.immae.eu" = null;
14 14
15 secrets.keys = [{ 15 secrets.keys = [{
16 dest = "webapps/prod-naturaloutil"; 16 dest = "webapps/prod-naturaloutil";
diff --git a/modules/private/websites/papa/surveillance.nix b/modules/private/websites/papa/surveillance.nix
index 1bb6ac8..f6e1772 100644
--- a/modules/private/websites/papa/surveillance.nix
+++ b/modules/private/websites/papa/surveillance.nix
@@ -6,7 +6,7 @@ in {
6 options.myServices.websites.papa.surveillance.enable = lib.mkEnableOption "enable Papa surveillance's website"; 6 options.myServices.websites.papa.surveillance.enable = lib.mkEnableOption "enable Papa surveillance's website";
7 7
8 config = lib.mkIf cfg.enable { 8 config = lib.mkIf cfg.enable {
9 security.acme.certs."ftp".extraDomains."surveillance.maison.bbc.bouya.org" = null; 9 security.acme2.certs."ftp".extraDomains."surveillance.maison.bbc.bouya.org" = null;
10 10
11 services.cron = { 11 services.cron = {
12 systemCronJobs = let 12 systemCronJobs = let
diff --git a/modules/private/websites/teliotortay/production.nix b/modules/private/websites/teliotortay/production.nix
index 59090f5..2c62d10 100644
--- a/modules/private/websites/teliotortay/production.nix
+++ b/modules/private/websites/teliotortay/production.nix
@@ -10,7 +10,7 @@ in {
10 config = lib.mkIf cfg.enable { 10 config = lib.mkIf cfg.enable {
11 services.webstats.sites = [ { name = "telio-tortay.immae.eu"; } ]; 11 services.webstats.sites = [ { name = "telio-tortay.immae.eu"; } ];
12 12
13 security.acme.certs."ftp".extraDomains."telio-tortay.immae.eu" = null; 13 security.acme2.certs."ftp".extraDomains."telio-tortay.immae.eu" = null;
14 14
15 system.activationScripts.telio-tortay = { 15 system.activationScripts.telio-tortay = {
16 deps = [ "httpd" ]; 16 deps = [ "httpd" ];
diff --git a/modules/websites/default.nix b/modules/websites/default.nix
index 6ba0d68..e69080e 100644
--- a/modules/websites/default.nix
+++ b/modules/websites/default.nix
@@ -149,7 +149,7 @@ in
149 serverAliases = [ "*" ]; 149 serverAliases = [ "*" ];
150 enableSSL = false; 150 enableSSL = false;
151 logFormat = "combinedVhost"; 151 logFormat = "combinedVhost";
152 documentRoot = "${config.security.acme.directory}/acme-challenge"; 152 documentRoot = "/var/lib/acme/acme-challenge";
153 extraConfig = '' 153 extraConfig = ''
154 RewriteEngine on 154 RewriteEngine on
155 RewriteCond "%{REQUEST_URI}" "!^/\.well-known" 155 RewriteCond "%{REQUEST_URI}" "!^/\.well-known"
@@ -178,9 +178,9 @@ in
178 }; 178 };
179 toVhost = ips: vhostConf: { 179 toVhost = ips: vhostConf: {
180 enableSSL = true; 180 enableSSL = true;
181 sslServerCert = "${config.security.acme.directory}/${vhostConf.certName}/cert.pem"; 181 sslServerCert = "${config.security.acme2.certs."${vhostConf.certName}".directory}/cert.pem";
182 sslServerKey = "${config.security.acme.directory}/${vhostConf.certName}/key.pem"; 182 sslServerKey = "${config.security.acme2.certs."${vhostConf.certName}".directory}/key.pem";
183 sslServerChain = "${config.security.acme.directory}/${vhostConf.certName}/chain.pem"; 183 sslServerChain = "${config.security.acme2.certs."${vhostConf.certName}".directory}/chain.pem";
184 logFormat = "combinedVhost"; 184 logFormat = "combinedVhost";
185 listen = map (ip: { inherit ip; port = 443; }) ips; 185 listen = map (ip: { inherit ip; port = 443; }) ips;
186 hostName = builtins.head vhostConf.hosts; 186 hostName = builtins.head vhostConf.hosts;
@@ -223,7 +223,7 @@ in
223 } 223 }
224 ) cfg.env; 224 ) cfg.env;
225 225
226 config.security.acme.certs = let 226 config.security.acme2.certs = let
227 typesToManage = attrsets.filterAttrs (k: v: v.enable) cfg.env; 227 typesToManage = attrsets.filterAttrs (k: v: v.enable) cfg.env;
228 flatVhosts = lists.flatten (attrsets.mapAttrsToList (k: v: 228 flatVhosts = lists.flatten (attrsets.mapAttrsToList (k: v:
229 attrValues v.vhostConfs 229 attrValues v.vhostConfs
diff --git a/pkgs/certbot/default.nix b/pkgs/certbot/default.nix
new file mode 100644
index 0000000..8fdbfd1
--- /dev/null
+++ b/pkgs/certbot/default.nix
@@ -0,0 +1,65 @@
1{ stdenv, python37Packages, fetchFromGitHub, fetchurl, dialog, autoPatchelfHook }:
2
3
4python37Packages.buildPythonApplication rec {
5 pname = "certbot";
6 version = "1.0.0";
7
8 src = fetchFromGitHub {
9 owner = pname;
10 repo = pname;
11 rev = "v${version}";
12 sha256 = "180x7gcpfbrzw8k654s7b5nxdy2yg61lq513dykyn3wz4gssw465";
13 };
14
15 patches = [
16 ./0001-Don-t-use-distutils.StrictVersion-that-cannot-handle.patch
17 ];
18
19 propagatedBuildInputs = with python37Packages; [
20 ConfigArgParse
21 acme
22 configobj
23 cryptography
24 distro
25 josepy
26 parsedatetime
27 psutil
28 pyRFC3339
29 pyopenssl
30 pytz
31 six
32 zope_component
33 zope_interface
34 ];
35
36 buildInputs = [ dialog ] ++ (with python37Packages; [ mock gnureadline ]);
37
38 checkInputs = with python37Packages; [
39 pytest_xdist
40 pytest
41 dateutil
42 ];
43
44 postPatch = ''
45 cd certbot
46 substituteInPlace certbot/_internal/notify.py --replace "/usr/sbin/sendmail" "/run/wrappers/bin/sendmail"
47 '';
48
49 postInstall = ''
50 for i in $out/bin/*; do
51 wrapProgram "$i" --prefix PYTHONPATH : "$PYTHONPATH" \
52 --prefix PATH : "${dialog}/bin:$PATH"
53 done
54 '';
55
56 doCheck = true;
57
58 meta = with stdenv.lib; {
59 homepage = src.meta.homepage;
60 description = "ACME client that can obtain certs and extensibly update server configurations";
61 platforms = platforms.unix;
62 maintainers = [ maintainers.domenkozar ];
63 license = licenses.asl20;
64 };
65}
diff --git a/pkgs/default.nix b/pkgs/default.nix
index 82be20e..54868ba 100644
--- a/pkgs/default.nix
+++ b/pkgs/default.nix
@@ -48,6 +48,9 @@ rec {
48 naemon = callPackage ./naemon { inherit mylibs monitoring-plugins; }; 48 naemon = callPackage ./naemon { inherit mylibs monitoring-plugins; };
49 naemon-livestatus = callPackage ./naemon-livestatus { inherit mylibs naemon; }; 49 naemon-livestatus = callPackage ./naemon-livestatus { inherit mylibs naemon; };
50 50
51 simp_le_0_17 = callPackage ./simp_le {};
52 certbot = callPackage ./certbot {};
53
51 private = if builtins.pathExists (./. + "/private") 54 private = if builtins.pathExists (./. + "/private")
52 then import ./private { inherit pkgs; } 55 then import ./private { inherit pkgs; }
53 else { webapps = {}; }; 56 else { webapps = {}; };
diff --git a/pkgs/simp_le/default.nix b/pkgs/simp_le/default.nix
new file mode 100644
index 0000000..eaefba3
--- /dev/null
+++ b/pkgs/simp_le/default.nix
@@ -0,0 +1,32 @@
1{ stdenv, python3Packages, bash }:
2
3python3Packages.buildPythonApplication rec {
4 pname = "simp_le-client";
5 version = "0.17.0";
6
7 src = python3Packages.fetchPypi {
8 inherit pname version;
9 sha256 = "0m1jynar4calaffp2zdxr5yy9vnhw2qf2hsfxwzfwf8fqb5h7bjb";
10 };
11
12 postPatch = ''
13 # drop upper bound of idna requirement
14 sed -ri "s/'(idna)<[^']+'/'\1'/" setup.py
15 substituteInPlace simp_le.py \
16 --replace "/bin/sh" "${bash}/bin/sh"
17 '';
18
19 checkPhase = ''
20 $out/bin/simp_le --test
21 '';
22
23 propagatedBuildInputs = with python3Packages; [ acme setuptools_scm josepy idna ];
24
25 meta = with stdenv.lib; {
26 homepage = https://github.com/zenhack/simp_le;
27 description = "Simple Let's Encrypt client";
28 license = licenses.gpl3;
29 maintainers = with maintainers; [ gebner makefu ];
30 platforms = platforms.linux;
31 };
32}