Commit c98cf683 authored by Bjørn Forsman's avatar Bjørn Forsman
Browse files

nixos/acme: fix ReadWritePaths for acme-${domain}.service

Currently ReadWritePaths is only sufficiently specificed for
acme-order-renew-${domain}.service, and not acme-${domain}.service. This
results in service failure if specifying the webroot outside of
/var/lib/acme, for example /var/www/challenges:

  acme-example.com-start[1379]: + mkdir -p /var/www/challenges//.well-known/acme-challenge
  acme-example.com-start[1382]: mkdir: cannot create directory ‘/var/www/challenges//.well-known’: Read-only file system
  systemd[1]: acme-example.com.service: Main process exited, code=exited, status=1/FAILURE

Fix it by adding the webroots to ReadWritePaths in the common
serviceConfig, where it can affect both acme-order-renew-${domain}.service
AND acme-${domain}.service.

Avoid adding subdirs of existing ReadWritePaths entries, because
otherwise systemd will fail to set up the services, for example:

  acme-zeroconf.example.test.service: Failed to set up mount namespacing: /run/acme: No such file or directory

(Confusingly, the path shown in the error message isn't necessarily
related to the problematic path.)
parent c1aa7496
Loading
Loading
Loading
Loading
+9 −9
Original line number Diff line number Diff line
@@ -51,6 +51,12 @@ let
    ''
    + script;

  # We need to collect all the ACME webroots to grant them write
  # access in the systemd service.
  webroots = lib.remove null (
    lib.unique (map (certAttrs: certAttrs.webroot) (lib.attrValues config.security.acme.certs))
  );

  # There are many services required to make cert renewals work.
  # They all follow a common structure:
  #   - They inherit this commonServiceConfig
@@ -69,7 +75,9 @@ let
    ReadWritePaths = [
      "/var/lib/acme"
      lockdir
    ];
    ]
    # Prevent runtime breakage by only adding non-overlapping paths.
    ++ (lib.filter (x: !(lib.strings.hasPrefix "/var/lib/acme/" x)) webroots);
    PrivateTmp = true;

    WorkingDirectory = "/tmp";
@@ -299,12 +307,6 @@ let
        ++ data.extraLegoRenewFlags
      );

      # We need to collect all the ACME webroots to grant them write
      # access in the systemd service.
      webroots = lib.remove null (
        lib.unique (map (certAttrs: certAttrs.webroot) (lib.attrValues config.security.acme.certs))
      );

      certificateKey = if data.csrKey != null then "${data.csrKey}" else "certificates/${keyName}.key";
    in
    {
@@ -469,8 +471,6 @@ let
              "acme/.lego/accounts/${accountHash}"
            ];

            ReadWritePaths = commonServiceConfig.ReadWritePaths ++ webroots;

            # Needs to be space separated, but can't use a multiline string because that'll include newlines
            BindPaths = [
              "${accountDir}:/tmp/accounts"