--- /dev/null
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.security.acme2;
+
+ certOpts = { name, ... }: {
+ options = {
+ webroot = mkOption {
+ type = types.str;
+ example = "/var/lib/acme/acme-challenges";
+ description = ''
+ Where the webroot of the HTTP vhost is located.
+ <filename>.well-known/acme-challenge/</filename> directory
+ will be created below the webroot if it doesn't exist.
+ <literal>http://example.org/.well-known/acme-challenge/</literal> must also
+ be available (notice unencrypted HTTP).
+ '';
+ };
+
+ server = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ ACME Directory Resource URI. Defaults to let's encrypt
+ production endpoint,
+ https://acme-v02.api.letsencrypt.org/directory, if unset.
+ '';
+ };
+
+ domain = mkOption {
+ type = types.str;
+ default = name;
+ description = "Domain to fetch certificate for (defaults to the entry name)";
+ };
+
+ email = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = "Contact email address for the CA to be able to reach you.";
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = "root";
+ description = "User running the ACME client.";
+ };
+
+ group = mkOption {
+ type = types.str;
+ default = "root";
+ description = "Group running the ACME client.";
+ };
+
+ allowKeysForGroup = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Give read permissions to the specified group
+ (<option>security.acme2.cert.<name>.group</option>) to read SSL private certificates.
+ '';
+ };
+
+ postRun = mkOption {
+ type = types.lines;
+ default = "";
+ example = "systemctl reload nginx.service";
+ description = ''
+ Commands to run after new certificates go live. Typically
+ the web server and other servers using certificates need to
+ be reloaded.
+
+ Executed in the same directory with the new certificate.
+ '';
+ };
+
+ plugins = mkOption {
+ type = types.listOf (types.enum [
+ "cert.der" "cert.pem" "chain.pem" "external.sh"
+ "fullchain.pem" "full.pem" "key.der" "key.pem" "account_key.json" "account_reg.json"
+ ]);
+ default = [ "fullchain.pem" "full.pem" "key.pem" "account_key.json" "account_reg.json" ];
+ description = ''
+ Plugins to enable. With default settings simp_le will
+ 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.
+ '';
+ };
+
+ directory = mkOption {
+ type = types.str;
+ readOnly = true;
+ default = "/var/lib/acme/${name}";
+ description = "Directory where certificate and other state is stored.";
+ };
+
+ extraDomains = mkOption {
+ type = types.attrsOf (types.nullOr types.str);
+ default = {};
+ example = literalExample ''
+ {
+ "example.org" = "/srv/http/nginx";
+ "mydomain.org" = null;
+ }
+ '';
+ description = ''
+ A list of extra domain names, which are included in the one certificate to be issued, with their
+ own server roots if needed.
+ '';
+ };
+ };
+ };
+
+in
+
+{
+
+ ###### interface
+ imports = [
+ (mkRemovedOptionModule [ "security" "acme2" "production" ] ''
+ Use security.acme2.server to define your staging ACME server URL instead.
+
+ To use the let's encrypt staging server, use security.acme2.server =
+ "https://acme-staging-v02.api.letsencrypt.org/directory".
+ ''
+ )
+ (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.")
+ (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")
+ (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")
+ ];
+ options = {
+ security.acme2 = {
+
+ 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>
+ <manvolnum>7</manvolnum></citerefentry>.
+ '';
+ };
+
+ server = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ ACME Directory Resource URI. Defaults to let's encrypt
+ production endpoint,
+ <literal>https://acme-v02.api.letsencrypt.org/directory</literal>, if unset.
+ '';
+ };
+
+ 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.
+ '';
+ };
+
+ certs = mkOption {
+ default = { };
+ type = with types; attrsOf (submodule certOpts);
+ description = ''
+ Attribute set of certificates to get signed and renewed. Creates
+ <literal>acme-''${cert}.{service,timer}</literal> systemd units for
+ each certificate defined here. Other services can add dependencies
+ to those units if they rely on the certificates being present,
+ or trigger restarts of the service if certificates get renewed.
+ '';
+ 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";
+ };
+ }
+ '';
+ };
+ };
+ };
+
+ ###### implementation
+ config = mkMerge [
+ (mkIf (cfg.certs != { }) {
+
+ systemd.services = let
+ services = concatLists servicesLists;
+ servicesLists = mapAttrsToList certToServices cfg.certs;
+ certToServices = cert: data:
+ let
+ lpath = "acme/${cert}";
+ rights = if data.allowKeysForGroup then "750" else "700";
+ cmdline = [ "-v" "-d" data.domain "--default_root" data.webroot "--valid_min" cfg.validMin ]
+ ++ optionals (data.email != null) [ "--email" data.email ]
+ ++ concatMap (p: [ "-f" p ]) data.plugins
+ ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains)
+ ++ optionals (cfg.server != null || data.server != null) ["--server" (if data.server == null then cfg.server else data.server)];
+ acmeService = {
+ description = "Renew ACME Certificate for ${cert}";
+ after = [ "network.target" "network-online.target" ];
+ wants = [ "network-online.target" ];
+ # simp_le uses requests, which uses certifi under the hood,
+ # which doesn't respect the system trust store.
+ # At least in the acme test, we provision a fake CA, impersonating the LE endpoint.
+ # REQUESTS_CA_BUNDLE is a way to teach python requests to use something else
+ environment.REQUESTS_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt";
+ serviceConfig = {
+ Type = "oneshot";
+ # With RemainAfterExit the service is considered active even
+ # after the main process having exited, which means when it
+ # gets changed, the activation phase restarts it, meaning
+ # the permissions of the StateDirectory get adjusted
+ # according to the specified group
+ RemainAfterExit = true;
+ SuccessExitStatus = [ "0" "1" ];
+ User = data.user;
+ Group = data.group;
+ PrivateTmp = true;
+ StateDirectory = lpath;
+ StateDirectoryMode = rights;
+ WorkingDirectory = "/var/lib/${lpath}";
+ ExecStart = "${pkgs.simp_le_0_17}/bin/simp_le ${escapeShellArgs cmdline}";
+ ExecStartPost =
+ let
+ script = pkgs.writeScript "acme-post-start" ''
+ #!${pkgs.runtimeShell} -e
+ ${data.postRun}
+ '';
+ in
+ "+${script}";
+ };
+
+ };
+ selfsignedService = {
+ description = "Create preliminary self-signed certificate for ${cert}";
+ path = [ pkgs.openssl ];
+ script =
+ ''
+ workdir="$(mktemp -d)"
+
+ # Create CA
+ openssl genrsa -des3 -passout pass:xxxx -out $workdir/ca.pass.key 2048
+ openssl rsa -passin pass:xxxx -in $workdir/ca.pass.key -out $workdir/ca.key
+ openssl req -new -key $workdir/ca.key -out $workdir/ca.csr \
+ -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=Security Department/CN=example.com"
+ openssl x509 -req -days 1 -in $workdir/ca.csr -signkey $workdir/ca.key -out $workdir/ca.crt
+
+ # Create key
+ openssl genrsa -des3 -passout pass:xxxx -out $workdir/server.pass.key 2048
+ openssl rsa -passin pass:xxxx -in $workdir/server.pass.key -out $workdir/server.key
+ 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"
+ openssl x509 -req -days 1 -in $workdir/server.csr -CA $workdir/ca.crt \
+ -CAkey $workdir/ca.key -CAserial $workdir/ca.srl -CAcreateserial \
+ -out $workdir/server.crt
+
+ # Copy key to destination
+ cp $workdir/server.key /var/lib/${lpath}/key.pem
+
+ # Create fullchain.pem (same format as "simp_le ... -f fullchain.pem" creates)
+ cat $workdir/{server.crt,ca.crt} > "/var/lib/${lpath}/fullchain.pem"
+
+ # Create full.pem for e.g. lighttpd
+ cat $workdir/{server.key,server.crt,ca.crt} > "/var/lib/${lpath}/full.pem"
+
+ # Give key acme permissions
+ chown '${data.user}:${data.group}' "/var/lib/${lpath}/"{key,fullchain,full}.pem
+ chmod ${rights} "/var/lib/${lpath}/"{key,fullchain,full}.pem
+ '';
+ serviceConfig = {
+ Type = "oneshot";
+ PrivateTmp = true;
+ StateDirectory = lpath;
+ User = data.user;
+ Group = data.group;
+ };
+ unitConfig = {
+ # Do not create self-signed key when key already exists
+ ConditionPathExists = "!/var/lib/${lpath}/key.pem";
+ };
+ };
+ in (
+ [ { name = "acme-${cert}"; value = acmeService; } ]
+ ++ optional cfg.preliminarySelfsigned { name = "acme-selfsigned-${cert}"; value = selfsignedService; }
+ );
+ servicesAttr = listToAttrs services;
+ in
+ servicesAttr;
+
+ systemd.tmpfiles.rules =
+ flip mapAttrsToList cfg.certs
+ (cert: data: "d ${data.webroot}/.well-known/acme-challenge - ${data.user} ${data.group}");
+
+ systemd.timers = flip mapAttrs' cfg.certs (cert: data: nameValuePair
+ ("acme-${cert}")
+ ({
+ description = "Renew ACME Certificate for ${cert}";
+ wantedBy = [ "timers.target" ];
+ timerConfig = {
+ OnCalendar = cfg.renewInterval;
+ Unit = "acme-${cert}.service";
+ Persistent = "yes";
+ AccuracySec = "5m";
+ RandomizedDelaySec = "1h";
+ };
+ })
+ );
+
+ systemd.targets.acme-selfsigned-certificates = mkIf cfg.preliminarySelfsigned {};
+ systemd.targets.acme-certificates = {};
+ })
+
+ ];
+
+ meta = {
+ maintainers = with lib.maintainers; [ abbradar fpletz globin ];
+ #doc = ./acme.xml;
+ };
+}
php-application = ./websites/php-application.nix;
websites = ./websites;
+ acme2 = ./acme2.nix;
} // (if builtins.pathExists ./private then import ./private else {})
enable = lib.mkEnableOption "enable certificates";
certConfig = lib.mkOption {
default = {
- webroot = "${config.security.acme.directory}/acme-challenge";
+ webroot = "/var/lib/acme/acme-challenge";
email = "ismael@bouya.org";
postRun = builtins.concatStringsSep "\n" [
(lib.optionalString config.services.httpd.Prod.enable "systemctl reload httpdProd.service")
(lib.optionalString config.services.httpd.Inte.enable "systemctl reload httpdInte.service")
(lib.optionalString config.services.nginx.enable "systemctl reload nginx.service")
];
- plugins = [ "cert.pem" "chain.pem" "fullchain.pem" "full.pem" "key.pem" "account_key.json" ];
+ plugins = [ "cert.pem" "chain.pem" "fullchain.pem" "full.pem" "key.pem" "account_key.json" "account_reg.json"];
};
description = "Default configuration for certificates";
};
config = lib.mkIf config.myServices.certificates.enable {
services.duplyBackup.profiles.system.excludeFile = ''
- + ${config.security.acme.directory}
+ + /var/lib/acme/acme-challenge
'';
services.nginx = {
recommendedTlsSettings = true;
myServices.databasesCerts = config.myServices.certificates.certConfig;
myServices.ircCerts = config.myServices.certificates.certConfig;
- security.acme.preliminarySelfsigned = true;
+ security.acme2.preliminarySelfsigned = true;
- security.acme.certs = {
+ security.acme2.certs = {
"${name}" = config.myServices.certificates.certConfig // {
domain = config.hostEnv.fqdn;
};
systemd.services = lib.attrsets.mapAttrs' (k: v:
lib.attrsets.nameValuePair "acme-selfsigned-${k}" (lib.mkBefore { script =
(lib.optionalString (builtins.elem "cert.pem" v.plugins) ''
- cp $workdir/server.crt ${config.security.acme.directory}/${k}/cert.pem
- chown '${v.user}:${v.group}' ${config.security.acme.directory}/${k}/cert.pem
- chmod ${if v.allowKeysForGroup then "750" else "700"} ${config.security.acme.directory}/${k}/cert.pem
+ cp $workdir/server.crt ${config.security.acme2.certs."${k}".directory}/cert.pem
+ chown '${v.user}:${v.group}' ${config.security.acme2.certs."${k}".directory}/cert.pem
+ chmod ${if v.allowKeysForGroup then "750" else "700"} ${config.security.acme2.certs."${k}".directory}/cert.pem
'') +
(lib.optionalString (builtins.elem "chain.pem" v.plugins) ''
- cp $workdir/ca.crt ${config.security.acme.directory}/${k}/chain.pem
- chown '${v.user}:${v.group}' ${config.security.acme.directory}/${k}/chain.pem
- chmod ${if v.allowKeysForGroup then "750" else "700"} ${config.security.acme.directory}/${k}/chain.pem
+ cp $workdir/ca.crt ${config.security.acme2.certs."${k}".directory}/chain.pem
+ chown '${v.user}:${v.group}' ${config.security.acme2.certs."${k}".directory}/chain.pem
+ chmod ${if v.allowKeysForGroup then "750" else "700"} ${config.security.acme2.certs."${k}".directory}/chain.pem
'')
; })
- ) config.security.acme.certs // {
+ ) config.security.acme2.certs // {
httpdProd = lib.mkIf config.services.httpd.Prod.enable
{ after = [ "acme-selfsigned-certificates.target" ]; wants = [ "acme-selfsigned-certificates.target" ]; };
httpdTools = lib.mkIf config.services.httpd.Tools.enable
dataDir = cfg.dataDir;
extraOptions = ''
ssl_ca = ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
- ssl_key = ${config.security.acme.directory}/mysql/key.pem
- ssl_cert = ${config.security.acme.directory}/mysql/fullchain.pem
+ ssl_key = ${config.security.acme2.certs.mysql.directory}/key.pem
+ ssl_cert = ${config.security.acme2.certs.mysql.directory}/fullchain.pem
# for replication
log-bin=mariadb-bin
};
users.users.mysql.extraGroups = [ "keys" ];
- security.acme.certs."mysql" = config.myServices.databasesCerts // {
+ security.acme2.certs."mysql" = config.myServices.databasesCerts // {
user = "mysql";
group = "mysql";
- plugins = [ "fullchain.pem" "key.pem" "account_key.json" ];
+ plugins = [ "fullchain.pem" "key.pem" "account_key.json" "account_reg.json" ];
domain = "db-1.immae.eu";
postRun = ''
systemctl restart mysql.service
overlay syncprov
syncprov-checkpoint 100 10
- TLSCertificateFile ${config.security.acme.directory}/ldap/cert.pem
- TLSCertificateKeyFile ${config.security.acme.directory}/ldap/key.pem
- TLSCACertificateFile ${config.security.acme.directory}/ldap/fullchain.pem
+ TLSCertificateFile ${config.security.acme2.certs.ldap.directory}/cert.pem
+ TLSCertificateKeyFile ${config.security.acme2.certs.ldap.directory}/key.pem
+ TLSCACertificateFile ${config.security.acme2.certs.ldap.directory}/fullchain.pem
TLSCACertificatePath ${pkgs.cacert.unbundled}/etc/ssl/certs/
#This makes openldap crash
#TLSCipherSuite DEFAULT
users.users.openldap.extraGroups = [ "keys" ];
networking.firewall.allowedTCPPorts = [ 636 389 ];
- security.acme.certs."ldap" = config.myServices.databasesCerts // {
+ security.acme2.certs."ldap" = config.myServices.databasesCerts // {
user = "openldap";
group = "openldap";
- plugins = [ "fullchain.pem" "key.pem" "cert.pem" "account_key.json" ];
+ plugins = [ "fullchain.pem" "key.pem" "cert.pem" "account_key.json" "account_reg.json" ];
domain = "ldap.immae.eu";
postRun = ''
systemctl restart openldap.service
config = lib.mkIf cfg.enable {
networking.firewall.allowedTCPPorts = [ 5432 ];
- security.acme.certs."postgresql" = config.myServices.databasesCerts // {
+ security.acme2.certs."postgresql" = config.myServices.databasesCerts // {
user = "postgres";
group = "postgres";
- plugins = [ "fullchain.pem" "key.pem" "account_key.json" ];
+ plugins = [ "fullchain.pem" "key.pem" "account_key.json" "account_reg.json" ];
domain = "db-1.immae.eu";
postRun = ''
systemctl reload postgresql.service
# makes it order of magnitudes quicker
synchronous_commit = off
ssl = on
- ssl_cert_file = '${config.security.acme.directory}/postgresql/fullchain.pem'
- ssl_key_file = '${config.security.acme.directory}/postgresql/key.pem'
+ ssl_cert_file = '${config.security.acme2.certs.postgresql.directory}/fullchain.pem'
+ ssl_key_file = '${config.security.acme2.certs.postgresql.directory}/key.pem'
'';
authentication = let
hosts = builtins.concatStringsSep "\n" (
};
config = lib.mkIf cfg.enable {
- security.acme.certs = {
+ security.acme2.certs = {
"ejabberd" = config.myServices.certificates.certConfig // {
user = "ejabberd";
group = "ejabberd";
text = ''
host_config:
"immae.fr":
- domain_certfile: "${config.security.acme.directory}/ejabberd/full.pem"
+ domain_certfile: "${config.security.acme2.certs.ejabberd.directory}/full.pem"
auth_method: [ldap]
ldap_servers: ["${config.myEnv.jabber.ldap.host}"]
ldap_encrypt: tls
ERLANG_NODE=ejabberd@localhost
'';
configFile = pkgs.runCommand "ejabberd.yml" {
- certificatePrivateKeyAndFullChain = "${config.security.acme.directory}/ejabberd/full.pem";
+ certificatePrivateKeyAndFullChain = "${config.security.acme2.certs.ejabberd.directory}/full.pem";
certificateCA = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
sql_config_file = config.secrets.fullPaths."ejabberd/psql.yml";
host_config_file = config.secrets.fullPaths."ejabberd/host.yml";
services.duplyBackup.profiles.ftp = {
rootDir = "/var/lib/ftp";
};
- security.acme.certs."ftp" = config.myServices.certificates.certConfig // {
+ security.acme2.certs."ftp" = config.myServices.certificates.certConfig // {
domain = "eldiron.immae.eu";
postRun = ''
systemctl restart pure-ftpd.service
MaxDiskUsage 99
CustomerProof yes
TLS 1
- CertFile ${config.security.acme.directory}/ftp/full.pem
+ CertFile ${config.security.acme2.certs.ftp.directory}/full.pem
'';
in {
description = "Pure-FTPd server";
services.duplyBackup.profiles.irc = {
rootDir = "/var/lib/bitlbee";
};
- security.acme.certs."irc" = config.myServices.ircCerts // {
+ security.acme2.certs."irc" = config.myServices.ircCerts // {
domain = "irc.immae.eu";
postRun = ''
systemctl restart stunnel.service
bitlbee = {
accept = 6697;
connect = 6667;
- cert = "${config.security.acme.directory}/irc/full.pem";
+ cert = "${config.security.acme2.certs.irc.directory}/full.pem";
};
};
};
options.myServices.mailBackup.enable = lib.mkEnableOption "enable MX backup services";
config = lib.mkIf config.myServices.mail.enable {
- security.acme.certs."mail" = config.myServices.certificates.certConfig // {
+ security.acme2.certs."mail" = config.myServices.certificates.certConfig // {
domain = config.hostEnv.fqdn;
extraDomains = let
zonesWithMx = builtins.filter (zone:
[
"0 2 * * * root ${cron_script}/bin/cleanup-imap-folders"
];
- security.acme.certs."mail" = {
+ security.acme2.certs."mail" = {
postRun = ''
systemctl restart dovecot2.service
'';
};
};
};
- security.acme.certs."mail" = {
+ security.acme2.certs."mail" = {
postRun = ''
systemctl restart postfix.service
'';
{ lib, pkgs, config, nodes, name, ... }:
{
config = lib.mkIf config.myServices.mailBackup.enable {
- security.acme.certs."mail" = config.myServices.certificates.certConfig // {
+ security.acme2.certs."mail" = config.myServices.certificates.certConfig // {
postRun = ''
systemctl restart postfix.service
'';
locations."/".proxyPass = "http://unix:/run/naemon-status/socket.sock:/";
};
};
- security.acme.certs."${name}".extraDomains."status.immae.eu" = null;
+ security.acme2.certs."${name}".extraDomains."status.immae.eu" = null;
myServices.certificates.enable = true;
networking.firewall.allowedTCPPorts = [ 80 443 ];
myServices.websites.webappDirs._task = ./www;
- security.acme.certs."task" = config.myServices.certificates.certConfig // {
+ security.acme2.certs."task" = config.myServices.certificates.certConfig // {
inherit user group;
- plugins = [ "fullchain.pem" "key.pem" "cert.pem" "account_key.json" ];
+ plugins = [ "fullchain.pem" "key.pem" "cert.pem" "account_key.json" "account_reg.json" ];
domain = fqdn;
postRun = ''
systemctl restart taskserver.service
inherit fqdn;
listenHost = "::";
pki.manual.ca.cert = "${server_vardir}/keys/ca.cert";
- pki.manual.server.cert = "${config.security.acme.directory}/task/fullchain.pem";
- pki.manual.server.crl = "${config.security.acme.directory}/task/invalid.crl";
- pki.manual.server.key = "${config.security.acme.directory}/task/key.pem";
+ pki.manual.server.cert = "${config.security.acme2.certs.task.directory}/fullchain.pem";
+ pki.manual.server.crl = "${config.security.acme2.certs.task.directory}/invalid.crl";
+ pki.manual.server.key = "${config.security.acme2.certs.task.directory}/key.pem";
requestLimit = 104857600;
};
system.activationScripts = {
httpd = ''
- install -d -m 0755 ${config.security.acme.directory}/acme-challenge
+ install -d -m 0755 /var/lib/acme/acme-challenge
install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions
'';
};
options.myServices.websites.florian.integration.enable = lib.mkEnableOption "enable Florian's website integration";
config = lib.mkIf cfg.enable {
- security.acme.certs."ftp".extraDomains."florian.immae.eu" = null;
+ security.acme2.certs."ftp".extraDomains."florian.immae.eu" = null;
services.websites.env.integration.modules = adminer.apache.modules;
services.websites.env.integration.vhostConfs.florian = {
options.myServices.websites.florian.production.enable = lib.mkEnableOption "enable Florian's website production";
config = lib.mkIf cfg.enable {
- security.acme.certs."ftp".extraDomains."tellesflorian.com" = null;
+ security.acme2.certs."ftp".extraDomains."tellesflorian.com" = null;
services.websites.env.production.modules = adminer.apache.modules;
services.websites.env.production.vhostConfs.florian = {
config = lib.mkIf cfg.enable {
services.webstats.sites = [ { name = "nassime.bouya.org"; } ];
- security.acme.certs."ftp".extraDomains."nassime.bouya.org" = null;
+ security.acme2.certs."ftp".extraDomains."nassime.bouya.org" = null;
services.websites.env.production.vhostConfs.nassime = {
certName = "nassime";
config = lib.mkIf cfg.enable {
services.webstats.sites = [ { name = "naturaloutil.immae.eu"; } ];
- security.acme.certs."ftp".extraDomains."naturaloutil.immae.eu" = null;
+ security.acme2.certs."ftp".extraDomains."naturaloutil.immae.eu" = null;
secrets.keys = [{
dest = "webapps/prod-naturaloutil";
options.myServices.websites.papa.surveillance.enable = lib.mkEnableOption "enable Papa surveillance's website";
config = lib.mkIf cfg.enable {
- security.acme.certs."ftp".extraDomains."surveillance.maison.bbc.bouya.org" = null;
+ security.acme2.certs."ftp".extraDomains."surveillance.maison.bbc.bouya.org" = null;
services.cron = {
systemCronJobs = let
config = lib.mkIf cfg.enable {
services.webstats.sites = [ { name = "telio-tortay.immae.eu"; } ];
- security.acme.certs."ftp".extraDomains."telio-tortay.immae.eu" = null;
+ security.acme2.certs."ftp".extraDomains."telio-tortay.immae.eu" = null;
system.activationScripts.telio-tortay = {
deps = [ "httpd" ];
serverAliases = [ "*" ];
enableSSL = false;
logFormat = "combinedVhost";
- documentRoot = "${config.security.acme.directory}/acme-challenge";
+ documentRoot = "/var/lib/acme/acme-challenge";
extraConfig = ''
RewriteEngine on
RewriteCond "%{REQUEST_URI}" "!^/\.well-known"
};
toVhost = ips: vhostConf: {
enableSSL = true;
- sslServerCert = "${config.security.acme.directory}/${vhostConf.certName}/cert.pem";
- sslServerKey = "${config.security.acme.directory}/${vhostConf.certName}/key.pem";
- sslServerChain = "${config.security.acme.directory}/${vhostConf.certName}/chain.pem";
+ sslServerCert = "${config.security.acme2.certs."${vhostConf.certName}".directory}/cert.pem";
+ sslServerKey = "${config.security.acme2.certs."${vhostConf.certName}".directory}/key.pem";
+ sslServerChain = "${config.security.acme2.certs."${vhostConf.certName}".directory}/chain.pem";
logFormat = "combinedVhost";
listen = map (ip: { inherit ip; port = 443; }) ips;
hostName = builtins.head vhostConf.hosts;
}
) cfg.env;
- config.security.acme.certs = let
+ config.security.acme2.certs = let
typesToManage = attrsets.filterAttrs (k: v: v.enable) cfg.env;
flatVhosts = lists.flatten (attrsets.mapAttrsToList (k: v:
attrValues v.vhostConfs
--- /dev/null
+{ stdenv, python37Packages, fetchFromGitHub, fetchurl, dialog, autoPatchelfHook }:
+
+
+python37Packages.buildPythonApplication rec {
+ pname = "certbot";
+ version = "1.0.0";
+
+ src = fetchFromGitHub {
+ owner = pname;
+ repo = pname;
+ rev = "v${version}";
+ sha256 = "180x7gcpfbrzw8k654s7b5nxdy2yg61lq513dykyn3wz4gssw465";
+ };
+
+ patches = [
+ ./0001-Don-t-use-distutils.StrictVersion-that-cannot-handle.patch
+ ];
+
+ propagatedBuildInputs = with python37Packages; [
+ ConfigArgParse
+ acme
+ configobj
+ cryptography
+ distro
+ josepy
+ parsedatetime
+ psutil
+ pyRFC3339
+ pyopenssl
+ pytz
+ six
+ zope_component
+ zope_interface
+ ];
+
+ buildInputs = [ dialog ] ++ (with python37Packages; [ mock gnureadline ]);
+
+ checkInputs = with python37Packages; [
+ pytest_xdist
+ pytest
+ dateutil
+ ];
+
+ postPatch = ''
+ cd certbot
+ substituteInPlace certbot/_internal/notify.py --replace "/usr/sbin/sendmail" "/run/wrappers/bin/sendmail"
+ '';
+
+ postInstall = ''
+ for i in $out/bin/*; do
+ wrapProgram "$i" --prefix PYTHONPATH : "$PYTHONPATH" \
+ --prefix PATH : "${dialog}/bin:$PATH"
+ done
+ '';
+
+ doCheck = true;
+
+ meta = with stdenv.lib; {
+ homepage = src.meta.homepage;
+ description = "ACME client that can obtain certs and extensibly update server configurations";
+ platforms = platforms.unix;
+ maintainers = [ maintainers.domenkozar ];
+ license = licenses.asl20;
+ };
+}
naemon = callPackage ./naemon { inherit mylibs monitoring-plugins; };
naemon-livestatus = callPackage ./naemon-livestatus { inherit mylibs naemon; };
+ simp_le_0_17 = callPackage ./simp_le {};
+ certbot = callPackage ./certbot {};
+
private = if builtins.pathExists (./. + "/private")
then import ./private { inherit pkgs; }
else { webapps = {}; };
--- /dev/null
+{ stdenv, python3Packages, bash }:
+
+python3Packages.buildPythonApplication rec {
+ pname = "simp_le-client";
+ version = "0.17.0";
+
+ src = python3Packages.fetchPypi {
+ inherit pname version;
+ sha256 = "0m1jynar4calaffp2zdxr5yy9vnhw2qf2hsfxwzfwf8fqb5h7bjb";
+ };
+
+ postPatch = ''
+ # drop upper bound of idna requirement
+ sed -ri "s/'(idna)<[^']+'/'\1'/" setup.py
+ substituteInPlace simp_le.py \
+ --replace "/bin/sh" "${bash}/bin/sh"
+ '';
+
+ checkPhase = ''
+ $out/bin/simp_le --test
+ '';
+
+ propagatedBuildInputs = with python3Packages; [ acme setuptools_scm josepy idna ];
+
+ meta = with stdenv.lib; {
+ homepage = https://github.com/zenhack/simp_le;
+ description = "Simple Let's Encrypt client";
+ license = licenses.gpl3;
+ maintainers = with maintainers; [ gebner makefu ];
+ platforms = platforms.linux;
+ };
+}