nixpkgs/nixos/tests/acme.nix
Félix Baylac-Jacqué 0c0af28cd5 nixos/tests/letsencrypt: use Pebble instead of Boulder
Let's encrypt bumped ACME to V2. We need to update our nixos test to
be compatible with this new protocol version.

We decided to drop the Boulder ACME server in favor of the more
integration test friendly Pebble.

- overriding cacert not necessary
- this avoids rebuilding lots of packages needlessly
- nixos/tests/acme: use pebble's ca for client tests
- pebble always generates its own ca which has to be fetched

TODO: write proper commit msg :)
2019-10-23 21:17:17 +02:00

124 lines
4.6 KiB
Nix

let
commonConfig = ./common/letsencrypt/common.nix;
in import ./make-test.nix {
name = "acme";
nodes = rec {
letsencrypt = ./common/letsencrypt;
acmeStandalone = { config, pkgs, ... }: {
imports = [ commonConfig ];
networking.firewall.allowedTCPPorts = [ 80 ];
networking.extraHosts = ''
${config.networking.primaryIPAddress} standalone.com
'';
security.acme.certs."standalone.com" = {
webroot = "/var/lib/acme/acme-challenges";
};
systemd.targets."acme-finished-standalone.com" = {};
systemd.services."acme-standalone.com" = {
wants = [ "acme-finished-standalone.com.target" ];
before = [ "acme-finished-standalone.com.target" ];
};
services.nginx.enable = true;
services.nginx.virtualHosts."standalone.com" = {
locations."/.well-known/acme-challenge".root = "/var/lib/acme/acme-challenges";
};
};
webserver = { config, pkgs, ... }: {
imports = [ commonConfig ];
networking.firewall.allowedTCPPorts = [ 80 443 ];
networking.extraHosts = ''
${config.networking.primaryIPAddress} a.example.com
${config.networking.primaryIPAddress} b.example.com
'';
# 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.com" = {};
systemd.services."acme-a.example.com" = {
wants = [ "acme-finished-a.example.com.target" ];
before = [ "acme-finished-a.example.com.target" ];
};
services.nginx.enable = true;
services.nginx.virtualHosts."a.example.com" = {
enableACME = true;
forceSSL = true;
locations."/".root = pkgs.runCommand "docroot" {} ''
mkdir -p "$out"
echo hello world > "$out/index.html"
'';
};
nesting.clone = [
({pkgs, ...}: {
networking.extraHosts = ''
${config.networking.primaryIPAddress} b.example.com
'';
systemd.targets."acme-finished-b.example.com" = {};
systemd.services."acme-b.example.com" = {
wants = [ "acme-finished-b.example.com.target" ];
before = [ "acme-finished-b.example.com.target" ];
};
services.nginx.virtualHosts."b.example.com" = {
enableACME = true;
forceSSL = true;
locations."/".root = pkgs.runCommand "docroot" {} ''
mkdir -p "$out"
echo hello world > "$out/index.html"
'';
};
})
];
};
client = commonConfig;
};
testScript = {nodes, ...}:
let
newServerSystem = nodes.webserver2.config.system.build.toplevel;
switchToNewServer = "${newServerSystem}/bin/switch-to-configuration test";
in
# Note, waitForUnit does not work for oneshot services that do not have RemainAfterExit=true,
# this is because a oneshot goes from inactive => activating => inactive, and never
# reaches the active state. To work around this, we create some mock target units which
# get pulled in by the oneshot units. The target units linger after activation, and hence we
# can use them to probe that a oneshot fired. It is a bit ugly, but it is the best we can do
''
$client->start;
$letsencrypt->start;
$acmeStandalone->start;
$letsencrypt->waitForUnit("default.target");
$letsencrypt->waitForUnit("pebble.service");
subtest "can request certificate with HTTPS-01 challenge", sub {
$acmeStandalone->waitForUnit("default.target");
$acmeStandalone->succeed("systemctl start acme-standalone.com.service");
$acmeStandalone->waitForUnit("acme-finished-standalone.com.target");
};
$client->waitForUnit("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');
subtest "Can request certificate for nginx service", sub {
$webserver->waitForUnit("acme-finished-a.example.com.target");
$client->succeed('curl --cacert /tmp/ca.crt https://a.example.com/ | grep -qF "hello world"');
};
subtest "Can add another certificate for nginx service", sub {
$webserver->succeed("/run/current-system/fine-tune/child-1/bin/switch-to-configuration test");
$webserver->waitForUnit("acme-finished-b.example.com.target");
$client->succeed('curl --cacert /tmp/ca.crt https://b.example.com/ | grep -qF "hello world"');
};
'';
}