]> git.immae.eu Git - perso/Immae/Config/Nix/NUR.git/blobdiff - modules/acme2.nix
Upgrade nixos
[perso/Immae/Config/Nix/NUR.git] / modules / acme2.nix
diff --git a/modules/acme2.nix b/modules/acme2.nix
deleted file mode 100644 (file)
index b22e4cc..0000000
+++ /dev/null
@@ -1,353 +0,0 @@
-{ 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.&lt;name&gt;.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
-                    # Edit: Timers will never run because of this
-                    # RemainAfterExit = true;
-                    SuccessExitStatus = [ "0" "1" ];
-                    User = data.user;
-                    Group = data.group;
-                    PrivateTmp = true;
-                    StateDirectory = lpath;
-                    StateDirectoryMode = rights;
-                    ExecStartPre =
-                      let
-                        script = pkgs.writeScript "acme-pre-start" ''
-                          #!${pkgs.runtimeShell} -e
-                          mkdir -p '${data.webroot}/.well-known/acme-challenge'
-                          chmod a+w '${data.webroot}/.well-known/acme-challenge'
-                          #doesn't work for multiple concurrent runs
-                          #chown -R '${data.user}:${data.group}' '${data.webroot}/.well-known/acme-challenge'
-                        '';
-                      in
-                        "+${script}";
-                    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;
-
-      # FIXME: this doesn't work for multiple users
-      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;
-  };
-}