diff --git a/ChangeLog.md b/ChangeLog.md
index 25078bd03..83b88366a 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -2,6 +2,11 @@
This project's release branch is `master`. This log is written from the perspective of the release branch: when changes hit `master`, they are considered released.
+## Unreleased
+
+* nixpkgs-overlays
+ * Remove override of acme module that pinned it to the version in nixpkgs-20.03. This is used for automatic https certificate provisioning.
+
## v1.0.0.0 - 2022-01-04
* Update reflex-platform to v0.9.2.0
diff --git a/acme.nix b/acme.nix
deleted file mode 100644
index b71d49a77..000000000
--- a/acme.nix
+++ /dev/null
@@ -1,430 +0,0 @@
-# Copied from https://github.com/NixOS/nixpkgs-channels/blob/70717a337f7ae4e486ba71a500367cad697e5f09/nixos/modules/security/acme.nix # MODIFIED
-# Lines marked '# MODIFIED' have been added or modified from the original. # MODIFIED
-{ config, lib, pkgs, ... }:
-with lib;
-let
-
- cfg = config.security.acme;
-
- certOpts = { name, ... }: {
- options = {
- webroot = mkOption {
- type = types.nullOr types.str;
- default = null;
- example = "/var/lib/acme/acme-challenges";
- description = ''
- Where the webroot of the HTTP vhost is located.
- .well-known/acme-challenge/ directory
- will be created below the webroot if it doesn't exist.
- http://example.org/.well-known/acme-challenge/ 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 = cfg.email;
- 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
- () 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.
- '';
- };
-
- 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" = null;
- "mydomain.org" = null;
- }
- '';
- description = ''
- A list of extra domain names, which are included in the one certificate to be issued.
- Setting a distinct server root is deprecated and not functional in 20.03+
- '';
- };
-
- keyType = mkOption {
- type = types.str;
- default = "ec256";
- description = ''
- Key type to use for private keys.
- For an up to date list of supported values check the --key-type option
- at https://go-acme.github.io/lego/usage/cli/#usage.
- '';
- };
-
- dnsProvider = mkOption {
- type = types.nullOr types.str;
- default = null;
- example = "route53";
- description = ''
- DNS Challenge provider. For a list of supported providers, see the "code"
- field of the DNS providers listed at https://go-acme.github.io/lego/dns/.
- '';
- };
-
- credentialsFile = mkOption {
- type = types.path;
- description = ''
- Path to an EnvironmentFile for the cert's service containing any required and
- optional environment variables for your selected dnsProvider.
- To find out what values you need to set, consult the documentation at
- https://go-acme.github.io/lego/dns/ for the corresponding dnsProvider.
- '';
- example = "/var/src/secrets/example.org-route53-api-token";
- };
-
- dnsPropagationCheck = mkOption {
- type = types.bool;
- default = true;
- description = ''
- Toggles lego DNS propagation check, which is used alongside DNS-01
- challenge to ensure the DNS entries required are available.
- '';
- };
- };
- };
-
-in
-
-{
-
- ###### interface
- imports = [
- (mkRemovedOptionModule [ "security" "acme" "production" ] ''
- Use security.acme.server to define your staging ACME server URL instead.
- To use the let's encrypt staging server, use security.acme.server =
- "https://acme-staging-v02.api.letsencrypt.org/directory".
- ''
- )
- # (mkRemovedOptionModule [ "security" "acme" "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.") # MODIFIED
- # (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") # MODIFIED
- # (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") # MODIFIED
- (mkChangedOptionModule [ "security" "acme" "validMin"] [ "security" "acme" "validMinDays"] (config: config.security.acme.validMin / (24 * 3600)))
- ];
- options = {
- security.acme = {
-
- validMinDays = mkOption {
- type = types.int;
- default = 30;
- description = "Minimum remaining validity before renewal in days.";
- };
-
- email = mkOption {
- type = types.nullOr types.str;
- default = null;
- description = "Contact email address for the CA to be able to reach you.";
- };
-
- renewInterval = mkOption {
- type = types.str;
- default = "daily";
- description = ''
- Systemd calendar expression when to check for renewal. See
- systemd.time
- 7.
- '';
- };
-
- 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.
- '';
- };
-
- 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.
- '';
- };
-
- acceptTerms = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Accept the CA's terms of service. The default provier is Let's Encrypt,
- you can find their ToS at https://letsencrypt.org/repository/
- '';
- };
-
- certs = mkOption {
- default = { };
- type = with types; attrsOf (submodule certOpts);
- description = ''
- Attribute set of certificates to get signed and renewed. Creates
- acme-''${cert}.{service,timer} 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" = null; };
- };
- "bar.example.com" = {
- webroot = "/var/www/challenges/";
- email = "bar@example.com";
- };
- }
- '';
- };
- };
- };
-
- ###### implementation
- config = mkMerge [
- (mkIf (cfg.certs != { }) {
-
- assertions = let
- certs = (mapAttrsToList (k: v: v) cfg.certs);
- in [
- {
- assertion = all (certOpts: certOpts.dnsProvider == null || certOpts.webroot == null) certs;
- message = ''
- Options `security.acme.certs..dnsProvider` and
- `security.acme.certs..webroot` are mutually exclusive.
- '';
- }
- {
- assertion = cfg.email != null || all (certOpts: certOpts.email != null) certs;
- message = ''
- You must define `security.acme.certs..email` or
- `security.acme.email` to register with the CA.
- '';
- }
- {
- assertion = cfg.acceptTerms;
- message = ''
- You must accept the CA's terms of service before using
- the ACME module by setting `security.acme.acceptTerms`
- to `true`. For Let's Encrypt's ToS see https://letsencrypt.org/repository/
- '';
- }
- ];
-
- systemd.services = let
- services = concatLists servicesLists;
- servicesLists = mapAttrsToList certToServices cfg.certs;
- certToServices = cert: data:
- let
- # StateDirectory must be relative, and will be created under /var/lib by systemd
- lpath = "acme/${cert}";
- apath = "/var/lib/${lpath}";
- spath = "/var/lib/acme/.lego/${cert}";
- fileMode = if data.allowKeysForGroup then "640" else "600";
- globalOpts = [ "-d" data.domain "--email" data.email "--path" "." "--key-type" data.keyType ]
- ++ optionals (cfg.acceptTerms) [ "--accept-tos" ]
- ++ optionals (data.dnsProvider != null && !data.dnsPropagationCheck) [ "--dns.disable-cp" ]
- ++ concatLists (mapAttrsToList (name: root: [ "-d" name ]) data.extraDomains)
- ++ (if data.dnsProvider != null then [ "--dns" data.dnsProvider ] else [ "--http" "--http.webroot" data.webroot ])
- ++ optionals (cfg.server != null || data.server != null) ["--server" (if data.server == null then cfg.server else data.server)];
- runOpts = escapeShellArgs (globalOpts ++ [ "run" ]);
- renewOpts = escapeShellArgs (globalOpts ++ [ "renew" "--days" (toString cfg.validMinDays) ]);
- acmeService = {
- description = "Renew ACME Certificate for ${cert}";
- after = [ "network.target" "network-online.target" ];
- wants = [ "network-online.target" ];
- wantedBy = mkIf (!config.boot.isContainer) [ "multi-user.target" ];
- serviceConfig = {
- Type = "oneshot";
- User = data.user;
- Group = data.group;
- PrivateTmp = true;
- StateDirectory = "acme/.lego/${cert} acme/.lego/accounts ${lpath}";
- StateDirectoryMode = if data.allowKeysForGroup then "750" else "700";
- WorkingDirectory = spath;
- # Only try loading the credentialsFile if the dns challenge is enabled
- EnvironmentFile = if data.dnsProvider != null then data.credentialsFile else null;
- ExecStart = pkgs.writeScript "acme-start" ''
- #!${pkgs.runtimeShell} -e
- test -L ${spath}/accounts -o -d ${spath}/accounts || ln -s ../accounts ${spath}/accounts
- ${pkgs.lego}/bin/lego ${renewOpts} || ${pkgs.lego}/bin/lego ${runOpts}
- '';
- ExecStartPost =
- let
- keyName = builtins.replaceStrings ["*"] ["_"] data.domain;
- script = pkgs.writeScript "acme-post-start" ''
- #!${pkgs.runtimeShell} -e
- cd ${apath}
- # Test that existing cert is older than new cert
- KEY=${spath}/certificates/${keyName}.key
- KEY_CHANGED=no
- if [ -e $KEY -a $KEY -nt key.pem ]; then
- KEY_CHANGED=yes
- cp -p ${spath}/certificates/${keyName}.key key.pem
- cp -p ${spath}/certificates/${keyName}.crt fullchain.pem
- cp -p ${spath}/certificates/${keyName}.issuer.crt chain.pem
- ln -sf fullchain.pem cert.pem
- cat key.pem fullchain.pem > full.pem
- fi
- chmod ${fileMode} *.pem
- chown '${data.user}:${data.group}' *.pem
- if [ "$KEY_CHANGED" = "yes" ]; then
- : # noop in case postRun is empty
- ${data.postRun}
- fi
- '';
- 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 ${apath}/key.pem
- # Create fullchain.pem (same format as "simp_le ... -f fullchain.pem" creates)
- cat $workdir/{server.crt,ca.crt} > "${apath}/fullchain.pem"
- # Create full.pem for e.g. lighttpd
- cat $workdir/{server.key,server.crt,ca.crt} > "${apath}/full.pem"
- # Give key acme permissions
- chown '${data.user}:${data.group}' "${apath}/"{key,fullchain,full}.pem
- chmod ${fileMode} "${apath}/"{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 = "!${apath}/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 =
- map (data: "d ${data.webroot}/.well-known/acme-challenge - ${data.user} ${data.group}") (filter (data: data.webroot != null) (attrValues cfg.certs));
-
- systemd.timers = let
- # Allow systemd to pick a convenient time within the day
- # to run the check.
- # This allows the coalescing of multiple timer jobs.
- # We divide by the number of certificates so that if you
- # have many certificates, the renewals are distributed over
- # the course of the day to avoid rate limits.
- numCerts = length (attrNames cfg.certs);
- _24hSecs = 60 * 60 * 24;
- AccuracySec = "${toString (_24hSecs / numCerts)}s";
- in 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";
- inherit AccuracySec;
- # Skew randomly within the day, per https://letsencrypt.org/docs/integration-guide/.
- RandomizedDelaySec = "24h";
- };
- })
- );
-
- systemd.targets.acme-selfsigned-certificates = mkIf cfg.preliminarySelfsigned {};
- systemd.targets.acme-certificates = {};
- })
-
- ];
-
- meta = {
- maintainers = with lib.maintainers; [ abbradar fpletz globin m1cr0man ];
- # doc = ./acme.xml; # MODIFIED
- };
-}
diff --git a/default.nix b/default.nix
index 78432c959..8ffe945a8 100644
--- a/default.nix
+++ b/default.nix
@@ -202,20 +202,6 @@ in rec {
(module { inherit exe hostName adminEmail routeHost enableHttps version; nixosPkgs = pkgs; })
(serverModules.mkDefaultNetworking args)
(serverModules.mkObeliskApp args)
- ./acme.nix # Backport of ACME upgrades from 20.03
- ];
-
- # Backport of ACME upgrades from 20.03
- disabledModules = [
- (pkgs.path + /nixos/modules/security/acme.nix)
- ];
- nixpkgs.overlays = [
- (self: super: {
- lego = (import (builtins.fetchTarball {
- url = https://github.com/NixOS/nixpkgs-channels/archive/70717a337f7ae4e486ba71a500367cad697e5f09.tar.gz;
- sha256 = "1sbmqn7yc5iilqnvy9nvhsa9bx6spfq1kndvvis9031723iyymd1";
- }) {}).lego;
- })
];
};
};