From 61f834833b0ad688817c6f26201aa12a2b26872e Mon Sep 17 00:00:00 2001 From: Arian van Putten Date: Tue, 14 Apr 2020 14:34:46 +0200 Subject: [PATCH 1/5] nixos/acme: turn around test probes' dependencies Reads a bit more naturally, and now the changes to the acme-${cert}.service actually reflect what would be needed were you to do the same in production. e.g. "for dns-01, your service that needs the cert needs to pull in the cert" --- nixos/tests/acme.nix | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix index fc41dc1eb5f..f871d0ea5fc 100644 --- a/nixos/tests/acme.nix +++ b/nixos/tests/acme.nix @@ -48,10 +48,9 @@ in import ./make-test-python.nix ({ lib, ... }: { security.acme.certs."standalone.test" = { webroot = "/var/lib/acme/acme-challenges"; }; - systemd.targets."acme-finished-standalone.test" = {}; - systemd.services."acme-standalone.test" = { - wants = [ "acme-finished-standalone.test.target" ]; - before = [ "acme-finished-standalone.test.target" ]; + systemd.targets."acme-finished-standalone.test" = { + after = [ "acme-standalone.test.service" ]; + wantedBy = [ "acme-standalone.test.service" ]; }; services.nginx.enable = true; services.nginx.virtualHosts."standalone.test" = { @@ -68,10 +67,11 @@ in import ./make-test-python.nix ({ lib, ... }: { # A target remains active. Use this to probe the fact that # a service fired eventhough it is not RemainAfterExit - systemd.targets."acme-finished-a.example.test" = {}; + systemd.targets."acme-finished-a.example.test" = { + after = [ "acme-a.example.test.service" ]; + wantedBy = [ "acme-a.example.test.service" ]; + }; systemd.services."acme-a.example.test" = { - wants = [ "acme-finished-a.example.test.target" ]; - before = [ "acme-finished-a.example.test.target" ]; after = [ "nginx.service" ]; }; @@ -89,10 +89,11 @@ in import ./make-test-python.nix ({ lib, ... }: { security.acme.server = "https://acme.test/dir"; specialisation.second-cert.configuration = {pkgs, ...}: { - systemd.targets."acme-finished-b.example.test" = {}; + systemd.targets."acme-finished-b.example.test" = { + after = [ "acme-b.example.test.service" ]; + wantedBy = [ "acme-b.example.test.service" ]; + }; systemd.services."acme-b.example.test" = { - wants = [ "acme-finished-b.example.test.target" ]; - before = [ "acme-finished-b.example.test.target" ]; after = [ "nginx.service" ]; }; services.nginx.virtualHosts."b.example.test" = { @@ -115,10 +116,12 @@ in import ./make-test-python.nix ({ lib, ... }: { user = config.services.nginx.user; group = config.services.nginx.group; }; - systemd.targets."acme-finished-example.test" = {}; + systemd.targets."acme-finished-example.test" = { + after = [ "acme-example.test.service" ]; + wantedBy = [ "acme-example.test.service" ]; + }; systemd.services."acme-example.test" = { - wants = [ "acme-finished-example.test.target" ]; - before = [ "acme-finished-example.test.target" "nginx.service" ]; + before = [ "nginx.service" ]; wantedBy = [ "nginx.service" ]; }; services.nginx.virtualHosts."c.example.test" = { From 60247e8560c22695cd21addd92ffe16d75fc7401 Mon Sep 17 00:00:00 2001 From: Arian van Putten Date: Tue, 14 Apr 2020 15:00:50 +0200 Subject: [PATCH 2/5] nixos/acme: Add regression test for #81842 --- nixos/tests/acme-issue-81842.nix | 61 ++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 nixos/tests/acme-issue-81842.nix diff --git a/nixos/tests/acme-issue-81842.nix b/nixos/tests/acme-issue-81842.nix new file mode 100644 index 00000000000..be843600938 --- /dev/null +++ b/nixos/tests/acme-issue-81842.nix @@ -0,0 +1,61 @@ +# When nginx depends on a service that is slow to start up, requesting +# certificates fail. Reproducer for +# https://github.com/NixOS/nixpkgs/issues/81842 +import ./make-test-python.nix { + name = "acme-issue-81842"; + nodes = { + letsencrypt = { nodes, lib, ... }: { + imports = [ ./common/letsencrypt ]; + # TODO: Move out to common ? + }; + webserver = { nodes, config, pkgs, lib, ... }: { + imports = [ ./common/letsencrypt/common.nix ]; + + # TODO move to common? + security.acme.server = "https://acme-v02.api.letsencrypt.org/dir"; + + systemd.services.my-slow-service = { + wantedBy = [ "multi-user.target" "nginx.service" ]; + before = [ "nginx.service" ]; + preStart = "sleep 5"; + script = "${pkgs.python3}/bin/python -m http.server"; + }; + + # Probe to measure that acme-a.example.com.service fired + systemd.targets."acme-finished-a.example.com" = { + after = [ "acme-a.example.com.service" ]; + wantedBy = [ "acme-a.example.com.service" ]; + }; + + # TODO: Move to pebble dns server. get rid of the resolver.nix hacks + networking.extraHosts = '' + ${config.networking.primaryIPAddress} a.example.com + ''; + + + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + services.nginx = { + enable = true; + virtualHosts."a.example.com" = { + forceSSL = true; + enableACME = true; + locations."/".proxyPass = "http://localhost:8000"; + }; + }; + }; + client = { nodes, ... }: { imports = [ ./common/letsencrypt/common.nix ]; }; + }; + testScript = { nodes, ... }: + '' + letsencrypt.wait_for_unit("default.target") + letsencrypt.wait_for_unit("pebble.service") + client.wait_for_unit("default.target") + client.succeed("curl https://acme-v02.api.letsencrypt.org:15000/roots/0 > /tmp/ca.crt") + client.succeed( + "curl https://acme-v02.api.letsencrypt.org:15000/intermediate-keys/0 >> /tmp/ca.crt" + ) + webserver.wait_for_unit("acme-finished-a.example.com.target") + client.succeed("curl --cacert /tmp/ca.crt https://a.example.com/") + ''; +} From 681cc105ceaad05b23d483b3b40abc32364e3062 Mon Sep 17 00:00:00 2001 From: Arian van Putten Date: Tue, 14 Apr 2020 14:55:05 +0200 Subject: [PATCH 3/5] nixos/acme: Make sure nginx is running before certs are requested This fixes https://github.com/NixOS/nixpkgs/issues/81842 We should probably also fix this for Apache, which recently also learned to use ACME. --- nixos/modules/services/web-servers/nginx/default.nix | 4 ++++ nixos/tests/acme.nix | 6 ------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index 8a015bb3556..4c4b7f39e6b 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -693,6 +693,10 @@ in wantedBy = [ "multi-user.target" ]; wants = concatLists (map (vhostConfig: ["acme-${vhostConfig.serverName}.service" "acme-selfsigned-${vhostConfig.serverName}.service"]) acmeEnabledVhosts); after = [ "network.target" ] ++ map (vhostConfig: "acme-selfsigned-${vhostConfig.serverName}.service") acmeEnabledVhosts; + # Nginx needs to be started in order to be able to request certificates + # (it's hosting the acme challenge after all) + # This fixes https://github.com/NixOS/nixpkgs/issues/81842 + before = map (vhostConfig: "acme-${vhostConfig.serverName}.service") acmeEnabledVhosts; stopIfChanged = false; preStart = '' ${cfg.preStart} diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix index f871d0ea5fc..826dd8f97d1 100644 --- a/nixos/tests/acme.nix +++ b/nixos/tests/acme.nix @@ -71,9 +71,6 @@ in import ./make-test-python.nix ({ lib, ... }: { after = [ "acme-a.example.test.service" ]; wantedBy = [ "acme-a.example.test.service" ]; }; - systemd.services."acme-a.example.test" = { - after = [ "nginx.service" ]; - }; services.nginx.enable = true; @@ -93,9 +90,6 @@ in import ./make-test-python.nix ({ lib, ... }: { after = [ "acme-b.example.test.service" ]; wantedBy = [ "acme-b.example.test.service" ]; }; - systemd.services."acme-b.example.test" = { - after = [ "nginx.service" ]; - }; services.nginx.virtualHosts."b.example.test" = { enableACME = true; forceSSL = true; From cfd672a94d3a5a1505871658bbceb06344dfe80f Mon Sep 17 00:00:00 2001 From: Arian van Putten Date: Tue, 14 Apr 2020 15:05:02 +0200 Subject: [PATCH 4/5] nixos/acme: Also fix ordering for apache --- nixos/modules/services/web-servers/apache-httpd/default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixos/modules/services/web-servers/apache-httpd/default.nix index 8abee7130d7..e1d1217943b 100644 --- a/nixos/modules/services/web-servers/apache-httpd/default.nix +++ b/nixos/modules/services/web-servers/apache-httpd/default.nix @@ -708,6 +708,7 @@ in wantedBy = [ "multi-user.target" ]; wants = concatLists (map (hostOpts: [ "acme-${hostOpts.hostName}.service" "acme-selfsigned-${hostOpts.hostName}.service" ]) vhostsACME); after = [ "network.target" "fs.target" ] ++ map (hostOpts: "acme-selfsigned-${hostOpts.hostName}.service") vhostsACME; + before = map (hostOpts: "acme-${hostOpts.hostName}.service") vhostsACME; path = [ pkg pkgs.coreutils pkgs.gnugrep ]; From 0952336d1d048617b9c976e90c4078ebe0c7ab46 Mon Sep 17 00:00:00 2001 From: Arian van Putten Date: Tue, 14 Apr 2020 20:04:44 +0200 Subject: [PATCH 5/5] nixos/acme: Move regression test into acme.nix --- nixos/tests/acme-issue-81842.nix | 61 -------------------------------- nixos/tests/acme.nix | 31 ++++++++++++++++ 2 files changed, 31 insertions(+), 61 deletions(-) delete mode 100644 nixos/tests/acme-issue-81842.nix diff --git a/nixos/tests/acme-issue-81842.nix b/nixos/tests/acme-issue-81842.nix deleted file mode 100644 index be843600938..00000000000 --- a/nixos/tests/acme-issue-81842.nix +++ /dev/null @@ -1,61 +0,0 @@ -# When nginx depends on a service that is slow to start up, requesting -# certificates fail. Reproducer for -# https://github.com/NixOS/nixpkgs/issues/81842 -import ./make-test-python.nix { - name = "acme-issue-81842"; - nodes = { - letsencrypt = { nodes, lib, ... }: { - imports = [ ./common/letsencrypt ]; - # TODO: Move out to common ? - }; - webserver = { nodes, config, pkgs, lib, ... }: { - imports = [ ./common/letsencrypt/common.nix ]; - - # TODO move to common? - security.acme.server = "https://acme-v02.api.letsencrypt.org/dir"; - - systemd.services.my-slow-service = { - wantedBy = [ "multi-user.target" "nginx.service" ]; - before = [ "nginx.service" ]; - preStart = "sleep 5"; - script = "${pkgs.python3}/bin/python -m http.server"; - }; - - # Probe to measure that acme-a.example.com.service fired - systemd.targets."acme-finished-a.example.com" = { - after = [ "acme-a.example.com.service" ]; - wantedBy = [ "acme-a.example.com.service" ]; - }; - - # TODO: Move to pebble dns server. get rid of the resolver.nix hacks - networking.extraHosts = '' - ${config.networking.primaryIPAddress} a.example.com - ''; - - - networking.firewall.allowedTCPPorts = [ 80 443 ]; - - services.nginx = { - enable = true; - virtualHosts."a.example.com" = { - forceSSL = true; - enableACME = true; - locations."/".proxyPass = "http://localhost:8000"; - }; - }; - }; - client = { nodes, ... }: { imports = [ ./common/letsencrypt/common.nix ]; }; - }; - testScript = { nodes, ... }: - '' - letsencrypt.wait_for_unit("default.target") - letsencrypt.wait_for_unit("pebble.service") - client.wait_for_unit("default.target") - client.succeed("curl https://acme-v02.api.letsencrypt.org:15000/roots/0 > /tmp/ca.crt") - client.succeed( - "curl https://acme-v02.api.letsencrypt.org:15000/intermediate-keys/0 >> /tmp/ca.crt" - ) - webserver.wait_for_unit("acme-finished-a.example.com.target") - client.succeed("curl --cacert /tmp/ca.crt https://a.example.com/") - ''; -} diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix index 826dd8f97d1..a8188473721 100644 --- a/nixos/tests/acme.nix +++ b/nixos/tests/acme.nix @@ -99,6 +99,7 @@ in import ./make-test-python.nix ({ lib, ... }: { ''; }; }; + specialisation.dns-01.configuration = {pkgs, config, nodes, lib, ...}: { security.acme.certs."example.test" = { domain = "*.example.test"; @@ -129,6 +130,26 @@ in import ./make-test-python.nix ({ lib, ... }: { ''; }; }; + + # When nginx depends on a service that is slow to start up, requesting used to fail + # certificates fail. Reproducer for https://github.com/NixOS/nixpkgs/issues/81842 + specialisation.slow-startup.configuration = { pkgs, config, nodes, lib, ...}: { + systemd.services.my-slow-service = { + wantedBy = [ "multi-user.target" "nginx.service" ]; + before = [ "nginx.service" ]; + preStart = "sleep 5"; + script = "${pkgs.python3}/bin/python -m http.server"; + }; + systemd.targets."acme-finished-d.example.com" = { + after = [ "acme-d.example.com.service" ]; + wantedBy = [ "acme-d.example.com.service" ]; + }; + services.nginx.virtualHosts."d.example.com" = { + forceSSL = true; + enableACME = true; + locations."/".proxyPass = "http://localhost:8000"; + }; + }; }; client = {nodes, lib, ...}: { @@ -204,5 +225,15 @@ in import ./make-test-python.nix ({ lib, ... }: { client.succeed( "curl --cacert /tmp/ca.crt https://c.example.test/ | grep -qF 'hello world'" ) + + with subtest("Can request certificate of nginx when startup is delayed"): + webserver.succeed( + "${switchToNewServer}" + ) + webserver.succeed( + "/run/current-system/specialisation/slow-startup/bin/switch-to-configuration test" + ) + webserver.wait_for_unit("acme-finished-d.example.com.target") + client.succeed("curl --cacert /tmp/ca.crt https://d.example.com/") ''; })