nixos/acme: Restructure module

- Use an acme user and group, allow group override only
- Use hashes to determine when certs actually need to regenerate
- Avoid running lego more than necessary
- Harden permissions
- Support "systemctl clean" for cert regeneration
- Support reuse of keys between some configuration changes
- Permissions fix services solves for previously root owned certs
- Add a note about multiple account creation and emails
- Migrate extraDomains to a list
- Deprecate user option
- Use minica for self-signed certs
- Rewrite all tests

I thought of a few more cases where things may go wrong,
and added tests to cover them. In particular, the web server
reload services were depending on the target - which stays alive,
meaning that the renewal timer wouldn't be triggering a reload
and old certs would stay on the web servers.

I encountered some problems ensuring that the reload took place
without accidently triggering it as part of the test. The sync
commands I added ended up being essential and I'm not sure why,
it seems like either node.succeed ends too early or there's an
oddity of the vm's filesystem I'm not aware of.

- Fix duplicate systemd rules on reload services

Since useACMEHost is not unique to every vhost, if one cert
was reused many times it would create duplicate entries in
${server}-config-reload.service for wants, before and
ConditionPathExists
This commit is contained in:
Lucas Savva
2020-09-02 19:22:43 +01:00
parent 6ab387699a
commit 982c5a1f0e
15 changed files with 839 additions and 729 deletions
+207 -134
View File
@@ -1,29 +1,43 @@
let
commonConfig = ./common/acme/client;
dnsScript = {writeScript, dnsAddress, bash, curl}: writeScript "dns-hook.sh" ''
#!${bash}/bin/bash
dnsServerIP = nodes: nodes.dnsserver.config.networking.primaryIPAddress;
dnsScript = {pkgs, nodes}: let
dnsAddress = dnsServerIP nodes;
in pkgs.writeShellScript "dns-hook.sh" ''
set -euo pipefail
echo '[INFO]' "[$2]" 'dns-hook.sh' $*
if [ "$1" = "present" ]; then
${curl}/bin/curl --data '{"host": "'"$2"'", "value": "'"$3"'"}' http://${dnsAddress}:8055/set-txt
${pkgs.curl}/bin/curl --data '{"host": "'"$2"'", "value": "'"$3"'"}' http://${dnsAddress}:8055/set-txt
else
${curl}/bin/curl --data '{"host": "'"$2"'"}' http://${dnsAddress}:8055/clear-txt
${pkgs.curl}/bin/curl --data '{"host": "'"$2"'"}' http://${dnsAddress}:8055/clear-txt
fi
'';
documentRoot = pkgs: pkgs.runCommand "docroot" {} ''
mkdir -p "$out"
echo hello world > "$out/index.html"
'';
vhostBase = pkgs: {
forceSSL = true;
locations."/".root = documentRoot pkgs;
};
in import ./make-test-python.nix ({ lib, ... }: {
name = "acme";
meta.maintainers = lib.teams.acme.members;
nodes = rec {
nodes = {
# The fake ACME server which will respond to client requests
acme = { nodes, lib, ... }: {
imports = [ ./common/acme/server ];
networking.nameservers = lib.mkForce [
nodes.dnsserver.config.networking.primaryIPAddress
];
networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ];
};
# A fake DNS server which can be configured with records as desired
# Used to test DNS-01 challenge
dnsserver = { nodes, pkgs, ... }: {
networking.firewall.allowedTCPPorts = [ 8055 53 ];
networking.firewall.allowedUDPPorts = [ 53 ];
@@ -39,112 +53,87 @@ in import ./make-test-python.nix ({ lib, ... }: {
};
};
acmeStandalone = { nodes, lib, config, pkgs, ... }: {
imports = [ commonConfig ];
networking.nameservers = lib.mkForce [
nodes.dnsserver.config.networking.primaryIPAddress
];
networking.firewall.allowedTCPPorts = [ 80 ];
security.acme.certs."standalone.test" = {
webroot = "/var/lib/acme/acme-challenges";
};
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" = {
locations."/.well-known/acme-challenge".root = "/var/lib/acme/acme-challenges";
};
};
webserver = { nodes, config, pkgs, lib, ... }: {
# A web server which will be the node requesting certs
webserver = { pkgs, nodes, lib, config, ... }: {
imports = [ commonConfig ];
networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ];
networking.firewall.allowedTCPPorts = [ 80 443 ];
networking.nameservers = lib.mkForce [
nodes.dnsserver.config.networking.primaryIPAddress
];
# 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" = {
after = [ "acme-a.example.test.service" ];
wantedBy = [ "acme-a.example.test.service" ];
};
# OpenSSL will be used for more thorough certificate validation
environment.systemPackages = [ pkgs.openssl ];
# First tests configure a basic cert and run a bunch of openssl checks
services.nginx.enable = true;
services.nginx.virtualHosts."a.example.test" = {
services.nginx.virtualHosts."a.example.test" = (vhostBase pkgs) // {
enableACME = true;
forceSSL = true;
locations."/".root = pkgs.runCommand "docroot" {} ''
mkdir -p "$out"
echo hello world > "$out/index.html"
'';
};
security.acme.server = "https://acme.test/dir";
# Used to determine if service reload was triggered
systemd.targets.test-renew-nginx = {
wants = [ "acme-a.example.test.service" ];
after = [ "acme-a.example.test.service" "nginx-config-reload.service" ];
};
specialisation.second-cert.configuration = {pkgs, ...}: {
systemd.targets."acme-finished-b.example.test" = {
after = [ "acme-b.example.test.service" ];
wantedBy = [ "acme-b.example.test.service" ];
# Cert config changes will not cause the nginx configuration to change.
# This tests that the reload service is correctly triggered.
specialisation.cert-change.configuration = { pkgs, ... }: {
security.acme.certs."a.example.test".keyType = "ec384";
};
# Now adding an alias to ensure that the certs are updated
specialisation.nginx-aliases.configuration = { pkgs, ... }: {
services.nginx.virtualHosts."a.example.test" = {
serverAliases = [ "b.example.test" ];
};
services.nginx.virtualHosts."b.example.test" = {
enableACME = true;
};
# Test using Apache HTTPD
specialisation.httpd-aliases.configuration = { pkgs, config, lib, ... }: {
services.nginx.enable = lib.mkForce false;
services.httpd.enable = true;
services.httpd.adminAddr = config.security.acme.email;
services.httpd.virtualHosts."c.example.test" = {
serverAliases = [ "d.example.test" ];
forceSSL = true;
locations."/".root = pkgs.runCommand "docroot" {} ''
mkdir -p "$out"
echo hello world > "$out/index.html"
'';
enableACME = true;
documentRoot = documentRoot pkgs;
};
# Used to determine if service reload was triggered
systemd.targets.test-renew-httpd = {
wants = [ "acme-c.example.test.service" ];
after = [ "acme-c.example.test.service" "httpd-config-reload.service" ];
};
};
specialisation.dns-01.configuration = {pkgs, config, nodes, lib, ...}: {
# Validation via DNS-01 challenge
specialisation.dns-01.configuration = { pkgs, config, nodes, ... }: {
security.acme.certs."example.test" = {
domain = "*.example.test";
group = config.services.nginx.group;
dnsProvider = "exec";
dnsPropagationCheck = false;
credentialsFile = with pkgs; writeText "wildcard.env" ''
EXEC_PATH=${dnsScript { inherit writeScript bash curl; dnsAddress = nodes.dnsserver.config.networking.primaryIPAddress; }}
credentialsFile = pkgs.writeText "wildcard.env" ''
EXEC_PATH=${dnsScript { inherit pkgs nodes; }}
'';
user = config.services.nginx.user;
group = config.services.nginx.group;
};
systemd.targets."acme-finished-example.test" = {
after = [ "acme-example.test.service" ];
wantedBy = [ "acme-example.test.service" ];
};
systemd.services."acme-example.test" = {
before = [ "nginx.service" ];
wantedBy = [ "nginx.service" ];
};
services.nginx.virtualHosts."c.example.test" = {
forceSSL = true;
sslCertificate = config.security.acme.certs."example.test".directory + "/cert.pem";
sslTrustedCertificate = config.security.acme.certs."example.test".directory + "/full.pem";
sslCertificateKey = config.security.acme.certs."example.test".directory + "/key.pem";
locations."/".root = pkgs.runCommand "docroot" {} ''
mkdir -p "$out"
echo hello world > "$out/index.html"
'';
services.nginx.virtualHosts."dns.example.test" = (vhostBase pkgs) // {
useACMEHost = "example.test";
};
};
# 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, ...}: {
# Validate service relationships by adding a slow start service to nginx' wants.
# 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" = {
services.nginx.virtualHosts."slow.example.com" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://localhost:8000";
@@ -152,11 +141,13 @@ in import ./make-test-python.nix ({ lib, ... }: {
};
};
client = {nodes, lib, ...}: {
# The client will be used to curl the webserver to validate configuration
client = {nodes, lib, pkgs, ...}: {
imports = [ commonConfig ];
networking.nameservers = lib.mkForce [
nodes.dnsserver.config.networking.primaryIPAddress
];
networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ];
# OpenSSL will be used for more thorough certificate validation
environment.systemPackages = [ pkgs.openssl ];
};
};
@@ -167,73 +158,155 @@ in import ./make-test-python.nix ({ lib, ... }: {
in
# Note, wait_for_unit 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
# reaches the active state. Targets do not have this issue.
''
has_switched = False
def switch_to(node, name):
global has_switched
if has_switched:
node.succeed(
"${switchToNewServer}"
)
has_switched = True
node.succeed(
"/run/current-system/specialisation/{}/bin/switch-to-configuration test".format(
name
)
)
# Ensures the issuer of our cert matches the chain
# and matches the issuer we expect it to be.
# It's a good validation to ensure the cert.pem and fullchain.pem
# are not still selfsigned afer verification
def check_issuer(node, cert_name, issuer):
for fname in ("cert.pem", "fullchain.pem"):
node.succeed(
(
"""openssl x509 -noout -issuer -in /var/lib/acme/{cert_name}/{fname} \
| tee /proc/self/fd/2 \
| cut -d'=' -f2- \
| grep "$(openssl x509 -noout -subject -in /var/lib/acme/{cert_name}/chain.pem \
| cut -d'=' -f2-)\" \
| grep -i '{issuer}'
"""
).format(cert_name=cert_name, issuer=issuer, fname=fname)
)
# Ensure cert comes before chain in fullchain.pem
def check_fullchain(node, cert_name):
node.succeed(
(
"""openssl crl2pkcs7 -nocrl -certfile /var/lib/acme/{cert_name}/fullchain.pem \
| tee /proc/self/fd/2 \
| openssl pkcs7 -print_certs -noout | head -1 | grep {cert_name}
"""
).format(cert_name=cert_name)
)
def check_connection(node, domain):
node.succeed(
(
"""openssl s_client -brief -verify 2 -verify_return_error -CAfile /tmp/ca.crt \
-servername {domain} -connect {domain}:443 < /dev/null 2>&1 \
| tee /proc/self/fd/2
"""
).format(domain=domain)
)
client.start()
dnsserver.start()
acme.wait_for_unit("default.target")
dnsserver.wait_for_unit("pebble-challtestsrv.service")
client.wait_for_unit("default.target")
client.succeed(
'curl --data \'{"host": "acme.test", "addresses": ["${nodes.acme.config.networking.primaryIPAddress}"]}\' http://${nodes.dnsserver.config.networking.primaryIPAddress}:8055/add-a'
)
client.succeed(
'curl --data \'{"host": "standalone.test", "addresses": ["${nodes.acmeStandalone.config.networking.primaryIPAddress}"]}\' http://${nodes.dnsserver.config.networking.primaryIPAddress}:8055/add-a'
'curl --data \'{"host": "acme.test", "addresses": ["${nodes.acme.config.networking.primaryIPAddress}"]}\' http://${dnsServerIP nodes}:8055/add-a'
)
acme.start()
acmeStandalone.start()
webserver.start()
acme.wait_for_unit("default.target")
acme.wait_for_unit("pebble.service")
with subtest("can request certificate with HTTPS-01 challenge"):
acmeStandalone.wait_for_unit("default.target")
acmeStandalone.succeed("systemctl start acme-standalone.test.service")
acmeStandalone.wait_for_unit("acme-finished-standalone.test.target")
client.wait_for_unit("default.target")
client.succeed("curl https://acme.test:15000/roots/0 > /tmp/ca.crt")
client.succeed("curl https://acme.test:15000/intermediate-keys/0 >> /tmp/ca.crt")
with subtest("Can request certificate for nginx service"):
with subtest("Can request certificate with HTTPS-01 challenge"):
webserver.wait_for_unit("acme-finished-a.example.test.target")
check_fullchain(webserver, "a.example.test")
check_issuer(webserver, "a.example.test", "pebble")
check_connection(client, "a.example.test")
with subtest("Can generate valid selfsigned certs"):
webserver.succeed("systemctl clean acme-a.example.test.service --what=state")
webserver.succeed("systemctl start acme-selfsigned-a.example.test.service")
check_fullchain(webserver, "a.example.test")
check_issuer(webserver, "a.example.test", "minica")
# Will succeed if nginx can load the certs
webserver.succeed("systemctl start nginx-config-reload.service")
with subtest("Can reload nginx when timer triggers renewal"):
# These syncs are required because of weird scenarios where the cert files
# were not actually changed when the checks run.
webserver.succeed("sync")
webserver.succeed("systemctl start test-renew-nginx.target")
webserver.succeed("sync")
check_issuer(webserver, "a.example.test", "pebble")
check_connection(client, "a.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")
client.succeed(
"curl --cacert /tmp/ca.crt https://a.example.test/ | grep -qF 'hello world'"
"""openssl s_client -CAfile /tmp/ca.crt -connect a.example.test:443 < /dev/null \
| openssl x509 -noout -text | grep -i Public-Key | grep 384
"""
)
with subtest("Can add another certificate for nginx service"):
webserver.succeed(
"/run/current-system/specialisation/second-cert/bin/switch-to-configuration test"
)
webserver.wait_for_unit("acme-finished-b.example.test.target")
client.succeed(
"curl --cacert /tmp/ca.crt https://b.example.test/ | grep -qF 'hello world'"
)
with subtest("Can request certificate with HTTPS-01 when nginx startup is delayed"):
switch_to(webserver, "slow-startup")
webserver.wait_for_unit("acme-finished-slow.example.com.target")
check_issuer(webserver, "slow.example.com", "pebble")
check_connection(client, "slow.example.com")
with subtest("Can request certificate for vhost + aliases (nginx)"):
switch_to(webserver, "nginx-aliases")
webserver.wait_for_unit("acme-finished-a.example.test.target")
check_issuer(webserver, "a.example.test", "pebble")
check_connection(client, "a.example.test")
check_connection(client, "b.example.test")
with subtest("Can request certificates for vhost + aliases (apache-httpd)"):
switch_to(webserver, "httpd-aliases")
webserver.wait_for_unit("acme-finished-c.example.test.target")
check_issuer(webserver, "c.example.test", "pebble")
check_connection(client, "c.example.test")
check_connection(client, "d.example.test")
with subtest("Can reload httpd when timer triggers renewal"):
# Switch to selfsigned first
webserver.succeed("systemctl clean acme-c.example.test.service --what=state")
webserver.succeed("systemctl start acme-selfsigned-c.example.test.service")
webserver.succeed("sync")
check_issuer(webserver, "c.example.test", "minica")
webserver.succeed("systemctl start httpd-config-reload.service")
webserver.succeed("systemctl start test-renew-httpd.target")
webserver.succeed("sync")
check_issuer(webserver, "c.example.test", "pebble")
check_connection(client, "c.example.test")
with subtest("Can request wildcard certificates using DNS-01 challenge"):
webserver.succeed(
"${switchToNewServer}"
)
webserver.succeed(
"/run/current-system/specialisation/dns-01/bin/switch-to-configuration test"
)
switch_to(webserver, "dns-01")
webserver.wait_for_unit("acme-finished-example.test.target")
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/")
check_issuer(webserver, "example.test", "pebble")
check_connection(client, "dns.example.test")
'';
})
+5 -6
View File
@@ -1,15 +1,14 @@
{ lib, nodes, pkgs, ... }:
let
acme-ca = nodes.acme.config.test-support.acme.caCert;
in
caCert = nodes.acme.config.test-support.acme.caCert;
caDomain = nodes.acme.config.test-support.acme.caDomain;
{
in {
security.acme = {
server = "https://acme.test/dir";
server = "https://${caDomain}/dir";
email = "hostmaster@example.test";
acceptTerms = true;
};
security.pki.certificateFiles = [ acme-ca ];
security.pki.certificateFiles = [ caCert ];
}
+38 -34
View File
@@ -3,7 +3,7 @@
# config.test-support.acme.caCert
#
# This value can be used inside the configuration of other test nodes to inject
# the snakeoil certificate into security.pki.certificateFiles or into package
# the test certificate into security.pki.certificateFiles or into package
# overlays.
#
# Another value that's needed if you don't use a custom resolver (see below for
@@ -50,19 +50,13 @@
# Also make sure that whenever you use a resolver from a different test node
# that it has to be started _before_ the ACME service.
{ config, pkgs, lib, ... }:
let
snakeOilCerts = import ./snakeoil-certs.nix;
testCerts = import ./snakeoil-certs.nix {
minica = pkgs.minica;
mkDerivation = pkgs.stdenv.mkDerivation;
};
domain = testCerts.domain;
wfeDomain = "acme.test";
wfeCertFile = snakeOilCerts.${wfeDomain}.cert;
wfeKeyFile = snakeOilCerts.${wfeDomain}.key;
siteDomain = "acme.test";
siteCertFile = snakeOilCerts.${siteDomain}.cert;
siteKeyFile = snakeOilCerts.${siteDomain}.key;
pebble = pkgs.pebble;
resolver = let
message = "You need to define a resolver for the acme test module.";
firstNS = lib.head config.networking.nameservers;
@@ -71,8 +65,9 @@ let
pebbleConf.pebble = {
listenAddress = "0.0.0.0:443";
managementListenAddress = "0.0.0.0:15000";
certificate = snakeOilCerts.${wfeDomain}.cert;
privateKey = snakeOilCerts.${wfeDomain}.key;
# These certs and keys are used for the Web Front End (WFE)
certificate = testCerts.${domain}.cert;
privateKey = testCerts.${domain}.key;
httpPort = 80;
tlsPort = 443;
ocspResponderURL = "http://0.0.0.0:4002";
@@ -80,18 +75,30 @@ let
};
pebbleConfFile = pkgs.writeText "pebble.conf" (builtins.toJSON pebbleConf);
pebbleDataDir = "/root/pebble";
in {
imports = [ ../../resolver.nix ];
options.test-support.acme.caCert = lib.mkOption {
type = lib.types.path;
description = ''
A certificate file to use with the <literal>nodes</literal> attribute to
inject the snakeoil CA certificate used in the ACME server into
<option>security.pki.certificateFiles</option>.
'';
options.test-support.acme = with lib; {
caDomain = mkOption {
type = types.str;
readOnly = true;
default = domain;
description = ''
A domain name to use with the <literal>nodes</literal> attribute to
identify the CA server.
'';
};
caCert = mkOption {
type = types.path;
readOnly = true;
default = testCerts.ca.cert;
description = ''
A certificate file to use with the <literal>nodes</literal> attribute to
inject the test CA certificate used in the ACME server into
<option>security.pki.certificateFiles</option>.
'';
};
};
config = {
@@ -99,35 +106,32 @@ in {
resolver.enable = let
isLocalResolver = config.networking.nameservers == [ "127.0.0.1" ];
in lib.mkOverride 900 isLocalResolver;
acme.caCert = snakeOilCerts.ca.cert;
};
# This has priority 140, because modules/testing/test-instrumentation.nix
# already overrides this with priority 150.
networking.nameservers = lib.mkOverride 140 [ "127.0.0.1" ];
networking.firewall.enable = false;
networking.firewall.allowedTCPPorts = [ 80 443 15000 4002 ];
networking.extraHosts = ''
127.0.0.1 ${wfeDomain}
${config.networking.primaryIPAddress} ${wfeDomain} ${siteDomain}
127.0.0.1 ${domain}
${config.networking.primaryIPAddress} ${domain}
'';
systemd.services = {
pebble = {
enable = true;
description = "Pebble ACME server";
requires = [ ];
wantedBy = [ "network.target" ];
preStart = ''
mkdir ${pebbleDataDir}
'';
script = ''
cd ${pebbleDataDir}
${pebble}/bin/pebble -config ${pebbleConfFile}
'';
serviceConfig = {
RuntimeDirectory = "pebble";
WorkingDirectory = "/run/pebble";
# Required to bind on privileged ports.
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
ExecStart = "${pkgs.pebble}/bin/pebble -config ${pebbleConfFile}";
};
};
};
@@ -1,69 +0,0 @@
{ pkgs ? import <nixpkgs> {}
, lib ? pkgs.lib
, domains ? [ "acme.test" ]
}:
pkgs.runCommand "acme-snakeoil-ca" {
nativeBuildInputs = [ pkgs.openssl ];
} ''
addpem() {
local file="$1"; shift
local storeFileName="$(IFS=.; echo "$*")"
echo -n " " >> "$out"
# Every following argument is an attribute, so let's recurse and check
# every attribute whether it must be quoted and write it into $out.
while [ -n "$1" ]; do
if expr match "$1" '^[a-zA-Z][a-zA-Z0-9]*$' > /dev/null; then
echo -n "$1" >> "$out"
else
echo -n '"' >> "$out"
echo -n "$1" | sed -e 's/["$]/\\&/g' >> "$out"
echo -n '"' >> "$out"
fi
shift
[ -z "$1" ] || echo -n . >> "$out"
done
echo " = builtins.toFile \"$storeFileName\" '''" >> "$out"
sed -e 's/^/ /' "$file" >> "$out"
echo " ''';" >> "$out"
}
echo '# Generated via mkcert.sh in the same directory.' > "$out"
echo '{' >> "$out"
openssl req -newkey rsa:4096 -x509 -sha256 -days 36500 \
-subj '/CN=Snakeoil CA' -nodes -out ca.pem -keyout ca.key
addpem ca.key ca key
addpem ca.pem ca cert
${lib.concatMapStrings (fqdn: let
opensslConfig = pkgs.writeText "snakeoil.cnf" ''
[req]
default_bits = 4096
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[dn]
CN = ${fqdn}
[req_ext]
subjectAltName = DNS:${fqdn}
'';
in ''
export OPENSSL_CONF=${lib.escapeShellArg opensslConfig}
openssl genrsa -out snakeoil.key 4096
openssl req -new -key snakeoil.key -out snakeoil.csr
openssl x509 -req -in snakeoil.csr -sha256 -set_serial 666 \
-CA ca.pem -CAkey ca.key -out snakeoil.pem -days 36500 \
-extfile "$OPENSSL_CONF" -extensions req_ext
addpem snakeoil.key ${lib.escapeShellArg fqdn} key
addpem snakeoil.pem ${lib.escapeShellArg fqdn} cert
'') domains}
echo '}' >> "$out"
''
@@ -1,6 +0,0 @@
#!/usr/bin/env nix-shell
#!nix-shell -p nix bash -i bash
set -e
cd "$(dirname "$0")"
storepath="$(nix-build --no-out-link mkcerts.nix)"
cat "$storepath" > snakeoil-certs.nix
+36 -171
View File
@@ -1,172 +1,37 @@
# Generated via mkcert.sh in the same directory.
{
ca.key = builtins.toFile "ca.key" ''
-----BEGIN PRIVATE KEY-----
MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDd1G7OFpXIoHnr
rxdw+hiJVDY6nQDDKFt9FBKwlv7x2hCvX7bnyvHaL7H61c+80McGPISrQn3+MjuR
Zuqwax49DddNXbGt4WqGlx4LAeI37OgNUUz9foNr2rDDV744vwp14/PD1f3nqpWf
Ogzzsh8rxac0mZ5Se9HxOIpI7NRNuHJjj7HWZ4YxeOvi289rmpu0JPcp25njw7h6
FNfHu8GGp34Uj6wAxubdRyfViV8z9FMfbglLuA9i1OiSy3NQpq8VwBG+u/0iC7PQ
sQjxSragQu25sfATYIrFJQ4ZCvh0nxqKMeyPPBi6dAcMpa2AZAqtqv+CwWdo36Bt
S5XiC7rApgYn+yteKQHSbnCiG2W/boSbfg9lRk3w41dESENCADVajLb3Eovvp0tB
O/BALudvWjzAPbpXleVNr6ngWtGlsZTC7LXDgBqdW2KlzpZGcz+PW3ATlwip/ZFR
t7A15u5dVkWPVoPuQ0w1Tw+g9dxWFTNk3h+2d7K87IxQbcvqxeIDSEVFMrxo0e4C
G2udMcelZwARl6iNTAETa2zJW0XtAdGVM+HY1S/kU6U9J3nubDttAkAMABjPwyjL
G7hfyWqUHf9yPs49GsftAVvIy8XIeu0shD1BG11/VzvwpUCiRc+btuWi2erZ4ZfP
oQ5YoS9gt4S+Ipz7TPGBl+AUk9HO2QIDAQABAoICAGW+aLAXxc2GZUVHQp4r55Md
T94kYtQgL4435bafGwH8vchiQzcfazxiweRFqwl0TMS8fzE5xyYPDilLpfsStoTU
U1sFzVfuWviuWTY9P+/ctjZdgs2F+GtAm/CMzw+h9/9IdWbuQI3APO4SJxyjJw7h
kiZbCzXT2uAjybFXBq07GyQ1JSEszGzmhHLB1OoKuL2wcrj9IyFHhNZhtvLCWCoV
qotttjuI/xyg5VFYt5TRzEpPIu5a1pvDAYVK0XI9cXKtbLYp7RlveqMOgAaD+S2a
ZQTV60JH9n4j18p+sKR00SxvZ4vuyXzDePRBDUolGIy9MIJdiLueTiuzDmTmclnM
8Yy7oliawW2Bn+1gaWpqmgzEUw9bXRSqIp2zGZ7HaQ+5c/MhS002+/i8WQyssfeg
9EfI+Vl0D2avTxCECmsfjUxtkhzMYPVNbRPjt0QBEM+s8lDoNsP2zhMO441+TKpe
/5KZHIW+Y0US6GMIUs1o1byKfNz8Nj5HjEKO9CMyK6SBMJnCMroPD4H6opqk3lw9
4mk04BdN556EzyJDT0a5/VpXG2DUYwFaNwE1ZPMu3Yx6IBoM1xx8mR80vHQCddmF
NP+BzkpUiHf0Txyy0YQWECZ/anTt0Bo0XqY5tirIM2dkG0ngNl9tGlw6gVAY1ky8
+cr7qKmhhwMWojaX/L+9AoIBAQD/BZAeF3l9I5RBh6ktWA+opzVyd6ejdLpc2Q1z
fmSmtUKRsEe51sWaIf6Sez408UaCMT2IQuppPgMnV8xfMM1/og75Cs8aPyAohwKo
IbOenXhLfFZiYB4y/Pac3F+FzNKsTT6n+fsE+82UHafY5ZI2FlPb2L0lfyx09zXv
fBYhcXgwSx5ymJLJSl8zFaEGn9qi3UB5ss44SaNM0n8SFGUQUk3PR7SFWSWgNxtl
CP7LWTsjXYoC/qBMe7b8JieK5aFk1EkkG1EkJvdiMnulMcMJzl+kj6LqVPmVDoZS
mMGvgKGJPpFgrbJ5wlA7uOhucGmMpFWP9RCav66DY4GHrLJPAoIBAQDerkZQ03AN
i2iJVjtL97TvDjrE8vtNFS/Auh8JyDIW4GGK3Y/ZoMedQpuu3e6NYM9aMjh+QJoA
kqhaiZ/tMXjEXJByglpc3a43g2ceWtJg5yLgexGgRtegbA57PRCo35Vhc6WycD1l
6FZNxpTkd2BXX/69KWZ6PpSiLYPvdzxP5ZkYqoWRQIa4ee4orHfz/lUXJm1XwmyG
mx3hN9Z9m8Q/PGMGfwrorcp4DK53lmmhTZyPh+X5T5/KkVmrw/v5HEEB3JsknStR
3DAqp2XZcRHsGQef9R7H+PINJm9nebjCraataaE4gr76znXKT23P80Ce5Lw6OQUW
XHhoL16gS+pXAoIBADTuz6ofTz01PFmZsfjSdXWZN1PKGEaqPOB2wP7+9h9QMkAR
KeId/Sfv9GotII1Woz70v4Pf983ebEMnSyla9NyQI7F3l+MnxSIEW/3P+PtsTgLF
DR0gPERzEzEd4Mnh6LyQz/eHwJ2ZMmOTADrZ8848Ni3EwAXfbrfcdBqAVAufBMZp
YSmCF72mLTpqO+EnHvd9GxvnjDxMtJOGgY+cIhoQK0xh4stm5JNrvMjs5A4LOGYv
zSyv80/Mwf92X/DJlwVZttDCxsXNPL3qIpX4TTZk2p9KnRMsjh1tRV4xjMpD1cOp
8/zwMMJrHcI3sC70MERb+9KEmGy2ap+k8MbbhqsCggEAUAqqocDupR+4Kq2BUPQv
6EHgJA0HAZUc/hSotXZtcsWiqiyr2Vkuhzt7BGcnqU/kGJK2tcL42D3fH/QaNUM0
Grj+/voWCw1v4uprtYCF4GkUo0X5dvgf570Pk4LGqzz6z/Wm2LX5i9jwtLItsNWs
HpwVz97CxCwcdxMPOpNMbZek6TXaHvTnuAWz8pDT6TNBWLnqUcJECjpVii/s/Gdy
KhzFp38g57QYdABy8e9x9pYUMY9yvaO+VyzZ46DlwIxEXavzZDzOZnVUJvDW7krz
Wz8/+2I7dzvnnYx0POiG3gtXPzwZxFtS1IpD0r2sRjQ0xSiI9BCs4HXKngBw7gN7
rwKCAQEAloJOFw4bafVXZVXuQVnLDm0/MNTfqxUzFE6V2WkMVkJqcpKt+ndApM8P
MJvojHWw1fmxDzIAwqZ9rXgnwWKydjSZBDYNjhGFUACVywHe5AjC4PPMUdltGptU
lY0BjC7qtwkVugr65goQkEzU61y9JgTqKpYsr3D+qXcoiDvWRuqk5Q0WfYJrUlE0
APWaqbxmkqUVDRrXXrifiluupk+BCV7cFSnnknSYbd9FZd9DuKaoNBlkp2J9LZE+
Ux74Cfro8SHINHmvqL+YLFUPVDWNeuXh5Kl6AaJ7yclCLXLxAIix3/rIf6mJeIGc
s9o9Sr49cibZ3CbMjCSNE3AOeVE1/Q==
-----END PRIVATE KEY-----
'';
ca.cert = builtins.toFile "ca.cert" ''
-----BEGIN CERTIFICATE-----
MIIFDzCCAvegAwIBAgIUX0P6NfX4gRUpFz+TNV/f26GHokgwDQYJKoZIhvcNAQEL
BQAwFjEUMBIGA1UEAwwLU25ha2VvaWwgQ0EwIBcNMjAwODI0MDc0MjEyWhgPMjEy
MDA3MzEwNzQyMTJaMBYxFDASBgNVBAMMC1NuYWtlb2lsIENBMIICIjANBgkqhkiG
9w0BAQEFAAOCAg8AMIICCgKCAgEA3dRuzhaVyKB5668XcPoYiVQ2Op0AwyhbfRQS
sJb+8doQr1+258rx2i+x+tXPvNDHBjyEq0J9/jI7kWbqsGsePQ3XTV2xreFqhpce
CwHiN+zoDVFM/X6Da9qww1e+OL8KdePzw9X956qVnzoM87IfK8WnNJmeUnvR8TiK
SOzUTbhyY4+x1meGMXjr4tvPa5qbtCT3KduZ48O4ehTXx7vBhqd+FI+sAMbm3Ucn
1YlfM/RTH24JS7gPYtTokstzUKavFcARvrv9Iguz0LEI8Uq2oELtubHwE2CKxSUO
GQr4dJ8aijHsjzwYunQHDKWtgGQKrar/gsFnaN+gbUuV4gu6wKYGJ/srXikB0m5w
ohtlv26Em34PZUZN8ONXREhDQgA1Woy29xKL76dLQTvwQC7nb1o8wD26V5XlTa+p
4FrRpbGUwuy1w4AanVtipc6WRnM/j1twE5cIqf2RUbewNebuXVZFj1aD7kNMNU8P
oPXcVhUzZN4ftneyvOyMUG3L6sXiA0hFRTK8aNHuAhtrnTHHpWcAEZeojUwBE2ts
yVtF7QHRlTPh2NUv5FOlPSd57mw7bQJADAAYz8Moyxu4X8lqlB3/cj7OPRrH7QFb
yMvFyHrtLIQ9QRtdf1c78KVAokXPm7blotnq2eGXz6EOWKEvYLeEviKc+0zxgZfg
FJPRztkCAwEAAaNTMFEwHQYDVR0OBBYEFNhBZxryvykCjfPO85xB3wof2enAMB8G
A1UdIwQYMBaAFNhBZxryvykCjfPO85xB3wof2enAMA8GA1UdEwEB/wQFMAMBAf8w
DQYJKoZIhvcNAQELBQADggIBAEZwlsQ+3yd1MVxLRy9RjoA8hI7iWBNmvPUyNjlb
l/L9N+dZgdx9G5h/KPRUyzvUc/uk/ZxTWVPIOp13WI65qwsBKrwvYKiXiwzjt+9V
CKDRc1sOghTSXk4FD3L5UcKvTQ2lRcFsqxbkopEwQWhoCuhe4vFyt3Nx8ZGLCBUD
3I5zMHtO8FtpZWKJPw46Yc1kasv0nlfly/vUbnErYfgjWX1hgWUcRgYdKwO4sOZ7
KbNma0WUsX5mWhXo4Kk7D15wATHO+j9s+j8m86duBL3A4HzpTo1DhHvBi0dkg0CO
XuSdByIzVLIPh3yhCHN1loRCP2rbzKM8IQeU/X5Q4UJeC/x9ew8Kk+RKXoHc8Y2C
JQO1DxuidyDJRhbb98wZo2YfIsdWQGjYZBe1XQRwBD28JnB+Rb9shml6lORWQn9y
P/STo9uWm5zvOCfqwbnCoetljDweItINx622G9SafBwPZc3o79oL7QSl8DgCtN6g
p0wGIlIBx+25w/96PqZcrYb8B7/uBHJviiKjBXDoIJWNiNRhW5HaFjeJdSKq7KIL
I/PO9KuHafif36ksG69X02Rio2/cTjgjEW1hGHcDRyyJWWaj7bd2eWuouh6FF22b
PA6FGY4vewDPnbLKLaix2ZIKxtedUDOH/qru3Mv58IFXmQ4iyM8oC8aOxYSQLZDn
1yJD
-----END CERTIFICATE-----
'';
"acme.test".key = builtins.toFile "acme.test.key" ''
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----
'';
"acme.test".cert = builtins.toFile "acme.test.cert" ''
-----BEGIN CERTIFICATE-----
MIIEwDCCAqigAwIBAgICApowDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLU25h
a2VvaWwgQ0EwIBcNMjAwODI0MDc0MjEzWhgPMjEyMDA3MzEwNzQyMTNaMBQxEjAQ
BgNVBAMMCWFjbWUudGVzdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
AN3SZeAchx0XKm2c25bMxyEuXhFt03F075ZagtZ3nRixlPXZwGG3aQ1GIb1s/8vQ
H0WdXfI2M9NY2bbecVmBtEKEFHZAnPUDz4dAMa5d/HBpYgeiGSGNKn7Vvc6y0oAj
lJ1y/+OSakqQ+ewxzEkoiZd4aDqOJHgWjETf1Cuhdw8Q/52RLIfsgQYdo7N6sZXh
MapF/CJ5oyvaRvI+6muAXuS4eIiCwR2xgDGsTO1YjQIOCoFsoHefuKgfOiRbRP3C
fq9w+tgEY1WsVOnjzi9AMEv78eZbOgxUAu/tQqRgjXpoCz83HO9LNFdEgJxe98R2
KkVoyRbbpsr8k5IkFi4NMw0DroFndwfQA7miBMdp74j8qYQLYzN2Sqy9szdWiH6s
28Sfy/E0BWtlj7D1Nvos2v5vZDHy12eFzxf0icUPuclR/q8ceTlq12dd5wjMnlaG
l0KH5hu6GbzkG7bk/a+Q0GXfVjdD9xt7MtP3TZ5TVZ/Lggb1yU0IUSrMtRREmXYH
BTbaSRBuwURvRnOotFd/Wkkc0oWciiUgUIZXNBTE5V+OORGLITyoxAv/krqvcMvy
7i4yJJKgW1YOOteyJYmy89Hw2P8q2apH0qoQlbFp62ttU8tzKbw/sJ7JE87hq7wl
U3tynFqfsJHh9Mb4a/BV1+p5+PdmAn72UiEztxRfqlrfAgMBAAGjGDAWMBQGA1Ud
EQQNMAuCCWFjbWUudGVzdDANBgkqhkiG9w0BAQsFAAOCAgEAM5WrCpBOmLrZ1QX8
l6vxVXwoI8pnqyy3cbAm3aLRPbw4gb0Ot90Pv/LoMhP0fkrNOKwH/FGRjSXyti0X
TheKrP7aEf6XL2/Xnb8rK2jYMQo6YJU9T+wBJA6Q+GBrc8SE75KfOi5NWJr8T4Ju
Etb+G05hXClrN19VFzIoz3L4kRV+xNMialcOT3xQfHtXCQUgwAWpPlwcJA/Jf60m
XsfwQwk2Ir16wq+Lc3y+mQ7d/dbG+FVrngFk4qN2B9M/Zyv4N9ZBbqeDUn3mYtJE
FeJrwHgmwH6slf1gBN3gxUKRW7Bvzxk548NdmLOyN+Y4StsqbOaYGtShUJA7f1Ng
qQqdgvxZ9MNwwMv9QVDZEnaaew3/oWOSmQGAai4hrc7gLMLJmIxzgfd5P6Dr06e4
2zwsMuI8Qh/IDqu/CfmFYvaua0FEeyAtpoID9Y/KPM7fu9bJuxjZ6kqLVFkEi9nF
/rCMchcSA8N2z/vLPabpNotO7OYH3VD7aQGTfCL82dMlp1vwZ39S3Z1TFLLh3MZ+
BYcAv8kUvCV6kIdPAXvJRSQOJUlJRV7XiI2mwugdDzMx69wQ0Zc1e4WyGfiSiVYm
ckSJ/EkxuwT/ZYLqCAKSFGMlFhad9g1Zyvd67XgfZq5p0pJTtGxtn5j8QHy6PM6m
NbjvWnP8lDU8j2l3eSG58S14iGs=
-----END CERTIFICATE-----
'';
# Minica can provide a CA key and cert, plus a key
# and cert for our fake CA server's Web Front End (WFE).
{ minica, mkDerivation }:
let
domain = "acme.test";
selfSignedCertData = mkDerivation {
name = "test-certs";
buildInputs = [ minica ];
phases = [ "buildPhase" "installPhase" ];
buildPhase = ''
mkdir ca
minica \
--ca-key ca/key.pem \
--ca-cert ca/cert.pem \
--domains ${domain}
chmod 600 ca/*
chmod 640 ${domain}/*.pem
'';
installPhase = ''
mkdir -p $out
mv ${domain} ca $out/
'';
};
in {
inherit domain;
ca = {
cert = "${selfSignedCertData}/ca/cert.pem";
key = "${selfSignedCertData}/ca/key.pem";
};
"${domain}" = {
cert = "${selfSignedCertData}/${domain}/cert.pem";
key = "${selfSignedCertData}/${domain}/key.pem";
};
}
@@ -1,6 +1,3 @@
let
certs = import ./common/acme/server/snakeoil-certs.nix;
in
import ./make-test-python.nix {
name = "postfix";
+7 -6
View File
@@ -1,5 +1,6 @@
let
certs = import ./common/acme/server/snakeoil-certs.nix;
domain = certs.domain;
in
import ./make-test-python.nix {
name = "postfix";
@@ -11,8 +12,8 @@ import ./make-test-python.nix {
enableSubmission = true;
enableSubmissions = true;
sslCACert = certs.ca.cert;
sslCert = certs."acme.test".cert;
sslKey = certs."acme.test".key;
sslCert = certs.${domain}.cert;
sslKey = certs.${domain}.key;
submissionsOptions = {
smtpd_sasl_auth_enable = "yes";
smtpd_client_restrictions = "permit";
@@ -25,7 +26,7 @@ import ./make-test-python.nix {
];
networking.extraHosts = ''
127.0.0.1 acme.test
127.0.0.1 ${domain}
'';
environment.systemPackages = let
@@ -33,7 +34,7 @@ import ./make-test-python.nix {
#!${pkgs.python3.interpreter}
import smtplib
with smtplib.SMTP('acme.test') as smtp:
with smtplib.SMTP('${domain}') as smtp:
smtp.sendmail('root@localhost', 'alice@localhost', 'Subject: Test\n\nTest data.')
smtp.quit()
'';
@@ -45,7 +46,7 @@ import ./make-test-python.nix {
ctx = ssl.create_default_context()
with smtplib.SMTP('acme.test') as smtp:
with smtplib.SMTP('${domain}') as smtp:
smtp.ehlo()
smtp.starttls(context=ctx)
smtp.ehlo()
@@ -60,7 +61,7 @@ import ./make-test-python.nix {
ctx = ssl.create_default_context()
with smtplib.SMTP_SSL(host='acme.test', context=ctx) as smtp:
with smtplib.SMTP_SSL(host='${domain}', context=ctx) as smtp:
smtp.sendmail('root@localhost', 'alice@localhost', 'Subject: Test SMTPS\n\nTest data.')
smtp.quit()
'';