From f9535a0ab75ba2124926449d912c7e522734b710 Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Sun, 13 Dec 2020 20:22:33 +0000 Subject: [PATCH 1/9] nixos/acme: prevent mass account creation Closes #106565 When generating multiple certificates which all share the same server + email, lego will attempt to create an account multiple times. By adding an account creation target certificates which share an account will wait for one service (chosen at config build time) to complete first. (cherry picked from commit 85769a8cd8012e5dfb907f49555ccc7c3c5a9d35) --- nixos/modules/security/acme.nix | 46 +++++++++++++++++++++++---------- nixos/tests/acme.nix | 32 ++++++++++++++++++++++- 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index 4e597eedfad2..8e3b9caf05ac 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -7,6 +7,11 @@ let numCerts = length (builtins.attrNames cfg.certs); _24hSecs = 60 * 60 * 24; + # Used to make unique paths for each cert/account config set + mkHash = with builtins; val: substring 0 20 (hashString "sha256" val); + mkAccountHash = acmeServer: data: mkHash "${toString acmeServer} ${data.keyType} ${data.email}"; + accountDirRoot = "/var/lib/acme/.lego/accounts/"; + # There are many services required to make cert renewals work. # They all follow a common structure: # - They inherit this commonServiceConfig @@ -101,11 +106,10 @@ let ${toString acmeServer} ${toString data.dnsProvider} ${toString data.ocspMustStaple} ${data.keyType} ''; - mkHash = with builtins; val: substring 0 20 (hashString "sha256" val); certDir = mkHash hashData; domainHash = mkHash "${concatStringsSep " " extraDomains} ${data.domain}"; - othersHash = mkHash "${toString acmeServer} ${data.keyType} ${data.email}"; - accountDir = "/var/lib/acme/.lego/accounts/" + othersHash; + accountHash = (mkAccountHash acmeServer data); + accountDir = accountDirRoot + accountHash; protocolOpts = if useDns then ( [ "--dns" data.dnsProvider ] @@ -141,7 +145,7 @@ let ); in { - inherit accountDir selfsignedDeps; + inherit accountHash accountDir cert selfsignedDeps; webroot = data.webroot; group = data.group; @@ -252,8 +256,7 @@ let echo '${domainHash}' > domainhash.txt # Check if we can renew - # Certificates and account credentials must exist - if [ -e 'certificates/${keyName}.key' -a -e 'certificates/${keyName}.crt' -a "$(ls -1 accounts)" ]; then + if [ -e 'certificates/${keyName}.key' -a -e 'certificates/${keyName}.crt' -a -n "$(ls -1 accounts)" ]; then # When domains are updated, there's no need to do a full # Lego run, but it's likely renew won't work if days is too low. @@ -658,15 +661,32 @@ in { "d /var/lib/acme/.lego/accounts - acme acme" ] ++ (unique (concatMap (conf: [ "d ${conf.accountDir} - acme acme" - ] ++ (optional (conf.webroot != null) "d ${conf.webroot}/.well-known/acme-challenge - acme ${conf.group}") + ] ++ (optionals (conf.webroot != null) [ + "d ${conf.webroot} - acme ${conf.group}" + "d ${conf.webroot}/.well-known - acme ${conf.group}" + "d ${conf.webroot}/.well-known/acme-challenge - acme ${conf.group}" + ]) ) (attrValues certConfigs))); - # Create some targets which can be depended on to be "active" after cert renewals - systemd.targets = mapAttrs' (cert: conf: nameValuePair "acme-finished-${cert}" { - wantedBy = [ "default.target" ]; - requires = [ "acme-${cert}.service" ] ++ conf.selfsignedDeps; - after = [ "acme-${cert}.service" ] ++ conf.selfsignedDeps; - }) certConfigs; + systemd.targets = let + # Create some targets which can be depended on to be "active" after cert renewals + finishedTargets = mapAttrs' (cert: conf: nameValuePair "acme-finished-${cert}" { + wantedBy = [ "default.target" ]; + requires = [ "acme-${cert}.service" ] ++ conf.selfsignedDeps; + after = [ "acme-${cert}.service" ] ++ conf.selfsignedDeps; + }) certConfigs; + + # Create targets to limit the number of simultaneous account creations + accountTargets = mapAttrs' (hash: confs: let + leader = "acme-${(builtins.head confs).cert}.service"; + dependantServices = map (conf: "acme-${conf.cert}.service") (builtins.tail confs); + in nameValuePair "acme-account-${hash}" { + requiredBy = dependantServices; + before = dependantServices; + requires = [ leader ]; + after = [ leader ]; + }) (groupBy (conf: conf.accountHash) (attrValues certConfigs)); + in finishedTargets // accountTargets; }) ]; diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix index eb152cf51a6a..503d77f24f93 100644 --- a/nixos/tests/acme.nix +++ b/nixos/tests/acme.nix @@ -77,6 +77,27 @@ in import ./make-test-python.nix ({ lib, ... }: { after = [ "acme-a.example.test.service" "nginx-config-reload.service" ]; }; + # Test that account creation is collated into one service + specialisation.account-creation.configuration = { nodes, pkgs, lib, ... }: let + email = "newhostmaster@example.test"; + caDomain = nodes.acme.config.test-support.acme.caDomain; + # Exit 99 to make it easier to track if this is the reason a renew failed + testScript = '' + test -e accounts/${caDomain}/${email}/account.json || exit 99 + ''; + in { + security.acme.email = lib.mkForce email; + systemd.services."b.example.test".serviceConfig.preStart = testScript; + systemd.services."c.example.test".serviceConfig.preStart = testScript; + + services.nginx.virtualHosts."b.example.test" = (vhostBase pkgs) // { + enableACME = true; + }; + services.nginx.virtualHosts."c.example.test" = (vhostBase pkgs) // { + enableACME = true; + }; + }; + # Cert config changes will not cause the nginx configuration to change. # This tests that the reload service is correctly triggered. # It also tests that postRun is exec'd as root @@ -289,7 +310,7 @@ in import ./make-test-python.nix ({ lib, ... }: { acme.start() webserver.start() - acme.wait_for_unit("default.target") + acme.wait_for_unit("network-online.target") acme.wait_for_unit("pebble.service") client.succeed("curl https://${caDomain}:15000/roots/0 > /tmp/ca.crt") @@ -314,6 +335,15 @@ in import ./make-test-python.nix ({ lib, ... }: { check_issuer(webserver, "a.example.test", "pebble") check_connection(client, "a.example.test") + with subtest("Runs 1 cert for account creation before others"): + switch_to(webserver, "account-creation") + webserver.wait_for_unit("acme-finished-a.example.test.target") + check_connection(client, "a.example.test") + webserver.wait_for_unit("acme-finished-b.example.test.target") + webserver.wait_for_unit("acme-finished-c.example.test.target") + check_connection(client, "b.example.test") + check_connection(client, "c.example.test") + with subtest("Can reload web server when cert configuration changes"): switch_to(webserver, "cert-change") webserver.wait_for_unit("acme-finished-a.example.test.target") From cd73f6dbef8aa38da01f2dcf311e6cf04d4c5240 Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Sun, 13 Dec 2020 22:19:53 +0000 Subject: [PATCH 2/9] nixos/acme: reduce dependency on tmpfiles systemd-tmpfiles is no longer required for most of the critical paths in the module. The only one that remains is the webroot acme-challenge directory since there's no other good place for this to live and forcing users to do the right thing alone will only create more issues. (cherry picked from commit 351065f9705c856ffc695af290a7c7aa2b57be6b) --- nixos/modules/security/acme.nix | 49 ++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index 8e3b9caf05ac..a72e7b130291 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -59,9 +59,9 @@ let ''; }; - # Previously, all certs were owned by whatever user was configured in - # config.security.acme.certs..user. Now everything is owned by and - # run by the acme user. + # Ensures that directories which are shared across all certs + # exist and have the correct user and group, since group + # is configurable on a per-cert basis. userMigrationService = { description = "Fix owner and group of all ACME certificates"; @@ -74,8 +74,13 @@ let done '') certConfigs); - # We don't want this to run every time a renewal happens - serviceConfig.RemainAfterExit = true; + serviceConfig = { + # We don't want this to run every time a renewal happens + RemainAfterExit = true; + + # These StateDirectory entries negate the need for tmpfiles + StateDirectory = "acme acme/.lego acme/.lego/accounts"; + }; }; certToConfig = cert: data: let @@ -145,7 +150,7 @@ let ); in { - inherit accountHash accountDir cert selfsignedDeps; + inherit accountHash cert selfsignedDeps; webroot = data.webroot; group = data.group; @@ -225,10 +230,14 @@ let serviceConfig = commonServiceConfig // { Group = data.group; - # AccountDir dir will be created by tmpfiles to ensure correct permissions - # And to avoid deletion during systemctl clean - # acme/.lego/${cert} is listed so that it is deleted during systemctl clean - StateDirectory = "acme/${cert} acme/.lego/${cert} acme/.lego/${cert}/${certDir}"; + # Keep in mind that these directories will be deleted if the user runs + # systemctl clean --what=state + # acme/.lego/${cert} is listed for this reason. + StateDirectory = + "acme/${cert} " + + "acme/.lego/${cert} " + + "acme/.lego/${cert}/${certDir} " + + "acme/.lego/accounts/${accountHash} "; # Needs to be space separated, but can't use a multiline string because that'll include newlines BindPaths = @@ -655,18 +664,14 @@ in { systemd.timers = mapAttrs' (cert: conf: nameValuePair "acme-${cert}" conf.renewTimer) certConfigs; - # .lego and .lego/accounts specified to fix any incorrect permissions - systemd.tmpfiles.rules = [ - "d /var/lib/acme/.lego - acme acme" - "d /var/lib/acme/.lego/accounts - acme acme" - ] ++ (unique (concatMap (conf: [ - "d ${conf.accountDir} - acme acme" - ] ++ (optionals (conf.webroot != null) [ - "d ${conf.webroot} - acme ${conf.group}" - "d ${conf.webroot}/.well-known - acme ${conf.group}" - "d ${conf.webroot}/.well-known/acme-challenge - acme ${conf.group}" - ]) - ) (attrValues certConfigs))); + systemd.tmpfiles.rules = unique ( + flatten ( + mapAttrsToList ( + cert: conf: + optional (conf.webroot != null) "d ${conf.webroot}/.well-known/acme-challenge - acme ${conf.group}" + ) certConfigs + ) + ); systemd.targets = let # Create some targets which can be depended on to be "active" after cert renewals From a72d434f888c0e9b163873f2d62b0e908c7fc704 Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Sun, 13 Dec 2020 22:33:27 +0000 Subject: [PATCH 3/9] nixos/acme: change service umask to 0023 Closes #106603 Some webservers (lighttpd) require that the files they are serving are world readable. We do our own chmods in the scripts anyway, and lego has sensible permissions on its output files, so this change is safe enough. (cherry picked from commit f670e1dc23bab2af7fdd8d7121d9f281d744ed1b) --- nixos/modules/security/acme.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index a72e7b130291..238d2696df16 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -24,7 +24,7 @@ let Type = "oneshot"; User = "acme"; Group = mkDefault "acme"; - UMask = 0027; + UMask = 0023; StateDirectoryMode = 750; ProtectSystem = "full"; PrivateTmp = true; From 1803de501ab04f181a7b8b9bae35722e0ef1d884 Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Sun, 13 Dec 2020 23:09:23 +0000 Subject: [PATCH 4/9] nixos/acme: update documentation and release notes The instructions on recreating the cert were missing --what=state. Also added a note on ensuring the group of manual certs is correct. (cherry picked from commit e5913db0c946b0d3408fc902858cdc2a26f7ad36) --- nixos/modules/security/acme.xml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/nixos/modules/security/acme.xml b/nixos/modules/security/acme.xml index f24811291728..b34cbdafb2d3 100644 --- a/nixos/modules/security/acme.xml +++ b/nixos/modules/security/acme.xml @@ -162,6 +162,9 @@ services.httpd = { ."foo.example.com" = { webroot = "/var/lib/acme/.challenges"; email = "foo@example.com"; + # Ensure that the web server you use can read the generated certs + # Take a look at the group option for the web server you choose. + group = "nginx"; # Since we have a wildcard vhost to handle port 80, # we can generate certs for anything! # Just make sure your DNS resolves them. @@ -257,10 +260,11 @@ chmod 400 /var/lib/secrets/certs.secret Should you need to regenerate a particular certificate in a hurry, such as when a vulnerability is found in Let's Encrypt, there is now a convenient - mechanism for doing so. Running systemctl clean acme-example.com.service - will remove all certificate files for the given domain, allowing you to then - systemctl start acme-example.com.service to generate fresh - ones. + mechanism for doing so. Running + systemctl clean --what=state acme-example.com.service + will remove all certificate files and the account data for the given domain, + allowing you to then systemctl start acme-example.com.service + to generate fresh ones.
From 6d3d8e6262aef083682acbf70649052516ab2bc7 Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Mon, 14 Dec 2020 19:40:28 +0000 Subject: [PATCH 5/9] nixos/acme: fix test config (cherry picked from commit bfe07e21795685d023b0595d9305071f30e3d448) --- nixos/tests/acme.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix index 503d77f24f93..c6d393d91963 100644 --- a/nixos/tests/acme.nix +++ b/nixos/tests/acme.nix @@ -87,8 +87,8 @@ in import ./make-test-python.nix ({ lib, ... }: { ''; in { security.acme.email = lib.mkForce email; - systemd.services."b.example.test".serviceConfig.preStart = testScript; - systemd.services."c.example.test".serviceConfig.preStart = testScript; + systemd.services."b.example.test".preStart = testScript; + systemd.services."c.example.test".preStart = testScript; services.nginx.virtualHosts."b.example.test" = (vhostBase pkgs) // { enableACME = true; From e5af62882660d9fa9e6154a96051cb5a3d82c2a0 Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Tue, 29 Dec 2020 15:01:08 +0000 Subject: [PATCH 6/9] nixos/acme: Remove all systemd-tmpfiles usage - Added an ExecPostStart to acme-$cert.service when webroot is defined to create the acme-challenge directory and fix required permissions. Lego always tries to create .well-known and acme-challenge, thus if any permissions in that tree are wrong it will crash and break cert renewal. - acme-fixperms now configured with acme User and Group, however the script still runs as root. This ensures the StateDirectories are owned by the acme user. - Switched to list syntax for systemd options where multiple values are specified. (cherry picked from commit 92a3a37153b159951d027a77cbb7b1ee7f92bde6) --- nixos/modules/security/acme.nix | 64 +++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index 238d2696df16..071d8ba8cf27 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -62,24 +62,30 @@ let # Ensures that directories which are shared across all certs # exist and have the correct user and group, since group # is configurable on a per-cert basis. - userMigrationService = { - description = "Fix owner and group of all ACME certificates"; - + userMigrationService = let script = with builtins; concatStringsSep "\n" (mapAttrsToList (cert: data: '' - for fixpath in /var/lib/acme/${escapeShellArg cert} /var/lib/acme/.lego/${escapeShellArg cert}; do + chown -R acme .lego/accounts + for fixpath in ${escapeShellArg cert} .lego/${escapeShellArg cert}; do if [ -d "$fixpath" ]; then chmod -R 750 "$fixpath" chown -R acme:${data.group} "$fixpath" fi done '') certConfigs); + in { + description = "Fix owner and group of all ACME certificates"; - serviceConfig = { + serviceConfig = commonServiceConfig // { # We don't want this to run every time a renewal happens RemainAfterExit = true; # These StateDirectory entries negate the need for tmpfiles - StateDirectory = "acme acme/.lego acme/.lego/accounts"; + StateDirectory = [ "acme" "acme/.lego" "acme/.lego/accounts" ]; + StateDirectoryMode = 755; + WorkingDirectory = "/var/lib/acme"; + + # Run the start script as root + ExecStart = "+" + (pkgs.writeShellScript "acme-fixperms" script); }; }; @@ -152,7 +158,6 @@ let in { inherit accountHash cert selfsignedDeps; - webroot = data.webroot; group = data.group; renewTimer = { @@ -192,7 +197,10 @@ let StateDirectory = "acme/${cert}"; - BindPaths = "/var/lib/acme/.minica:/tmp/ca /var/lib/acme/${cert}:/tmp/${keyName}"; + BindPaths = [ + "/var/lib/acme/.minica:/tmp/ca" + "/var/lib/acme/${cert}:/tmp/${keyName}" + ]; }; # Working directory will be /tmp @@ -233,17 +241,19 @@ let # Keep in mind that these directories will be deleted if the user runs # systemctl clean --what=state # acme/.lego/${cert} is listed for this reason. - StateDirectory = - "acme/${cert} " + - "acme/.lego/${cert} " + - "acme/.lego/${cert}/${certDir} " + - "acme/.lego/accounts/${accountHash} "; + StateDirectory = [ + "acme/${cert}" + "acme/.lego/${cert}" + "acme/.lego/${cert}/${certDir}" + "acme/.lego/accounts/${accountHash}" + ]; # Needs to be space separated, but can't use a multiline string because that'll include newlines - BindPaths = - "${accountDir}:/tmp/accounts " + - "/var/lib/acme/${cert}:/tmp/out " + - "/var/lib/acme/.lego/${cert}/${certDir}:/tmp/certificates "; + BindPaths = [ + "${accountDir}:/tmp/accounts" + "/var/lib/acme/${cert}:/tmp/out" + "/var/lib/acme/.lego/${cert}/${certDir}:/tmp/certificates" + ]; # Only try loading the credentialsFile if the dns challenge is enabled EnvironmentFile = mkIf useDns data.credentialsFile; @@ -256,7 +266,16 @@ let ${data.postRun} fi ''); - }; + + } // (optionalAttrs (data.webroot != null) { + # Lego always tries to create .well-known/acme-challenge, but if webroot is owned + # by the wrong user then it will crash and break cert renewal. + ExecStartPre = "+" + pkgs.writeShellScript "acme-${cert}-make-webroot" '' + mkdir -p '${data.webroot}/.well-known/acme-challenge' + cd '${data.webroot}' + chown 'acme:${data.group}' . .well-known .well-known/acme-challenge + ''; + }); # Working directory will be /tmp script = '' @@ -664,15 +683,6 @@ in { systemd.timers = mapAttrs' (cert: conf: nameValuePair "acme-${cert}" conf.renewTimer) certConfigs; - systemd.tmpfiles.rules = unique ( - flatten ( - mapAttrsToList ( - cert: conf: - optional (conf.webroot != null) "d ${conf.webroot}/.well-known/acme-challenge - acme ${conf.group}" - ) certConfigs - ) - ); - systemd.targets = let # Create some targets which can be depended on to be "active" after cert renewals finishedTargets = mapAttrs' (cert: conf: nameValuePair "acme-finished-${cert}" { From 96508ce26ff2c3dd929efc8ba23ad6097849e618 Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Sat, 9 Jan 2021 19:15:03 +0000 Subject: [PATCH 7/9] nixos/acme: Incorporate review suggestions (cherry picked from commit a01df7dc4663650146cba65d25aaf225391f22ce) --- nixos/modules/security/acme.nix | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index 071d8ba8cf27..1892244aab30 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -63,15 +63,16 @@ let # exist and have the correct user and group, since group # is configurable on a per-cert basis. userMigrationService = let - script = with builtins; concatStringsSep "\n" (mapAttrsToList (cert: data: '' + script = with builtins; '' chown -R acme .lego/accounts + '' + (concatStringsSep "\n" (mapAttrsToList (cert: data: '' for fixpath in ${escapeShellArg cert} .lego/${escapeShellArg cert}; do if [ -d "$fixpath" ]; then chmod -R 750 "$fixpath" chown -R acme:${data.group} "$fixpath" fi done - '') certConfigs); + '') certConfigs)); in { description = "Fix owner and group of all ACME certificates"; @@ -692,6 +693,14 @@ in { }) certConfigs; # Create targets to limit the number of simultaneous account creations + # How it works: + # - Pick a "leader" cert service, which will be in charge of creating the account, + # and run first (requires + after) + # - Make all other cert services sharing the same account wait for the leader to + # finish before starting (requiredBy + before). + # Using a target here is fine - account creation is a one time event. Even if + # systemd clean --what=state is used to delete the account, so long as the user + # then runs one of the cert services, there won't be any issues. accountTargets = mapAttrs' (hash: confs: let leader = "acme-${(builtins.head confs).cert}.service"; dependantServices = map (conf: "acme-${conf.cert}.service") (builtins.tail confs); From 93ac91d31b6268736784fec93ca88f1aa95d462e Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Sat, 9 Jan 2021 19:34:54 +0000 Subject: [PATCH 8/9] nixos/acme: Set up webroot as non-root user (cherry picked from commit 5b4f9c42442545ea0352e4cb776a0f94074fca5b) --- nixos/modules/security/acme.nix | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index 1892244aab30..742255800ba8 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -267,21 +267,19 @@ let ${data.postRun} fi ''); - - } // (optionalAttrs (data.webroot != null) { - # Lego always tries to create .well-known/acme-challenge, but if webroot is owned - # by the wrong user then it will crash and break cert renewal. - ExecStartPre = "+" + pkgs.writeShellScript "acme-${cert}-make-webroot" '' - mkdir -p '${data.webroot}/.well-known/acme-challenge' - cd '${data.webroot}' - chown 'acme:${data.group}' . .well-known .well-known/acme-challenge - ''; - }); + }; # Working directory will be /tmp script = '' set -euo pipefail + ${optionalString (data.webroot != null) '' + # Ensure the webroot exists + mkdir -p '${data.webroot}/.well-known/acme-challenge' + chown 'acme:${data.group}' ${data.webroot}/{.well-known,.well-known/acme-challenge} \ + || echo "Please fix the permissions under ${data.webroot}/.well-known/acme-challenge" && exit 1 + ''} + echo '${domainHash}' > domainhash.txt # Check if we can renew From 1f054921b5cd7b09cd1c2dd29283b67e5eaddf8a Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Tue, 12 Jan 2021 19:11:50 +0000 Subject: [PATCH 9/9] nixos/acme: Fix bash issue, enable debug I found a logical error in the bash script, but during debugging I enabled command echoing and realised it would be a good idea to have it enabled all the time for ease of bug reporting. (cherry picked from commit 514a0b6d8adf9fa181549dd0ae5c52ee04846975) --- nixos/modules/security/acme.nix | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index 742255800ba8..8bbf83bf21cd 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -271,13 +271,12 @@ let # Working directory will be /tmp script = '' - set -euo pipefail + set -euxo pipefail ${optionalString (data.webroot != null) '' # Ensure the webroot exists mkdir -p '${data.webroot}/.well-known/acme-challenge' - chown 'acme:${data.group}' ${data.webroot}/{.well-known,.well-known/acme-challenge} \ - || echo "Please fix the permissions under ${data.webroot}/.well-known/acme-challenge" && exit 1 + chown 'acme:${data.group}' ${data.webroot}/{.well-known,.well-known/acme-challenge} ''} echo '${domainHash}' > domainhash.txt