ssl: make self-signed certs idempotent

This commit is contained in:
ibizaman 2026-03-15 15:10:39 +01:00
parent 9d7e93b70d
commit 9bfd954c1f
2 changed files with 109 additions and 32 deletions

View file

@ -399,21 +399,25 @@ in
crl_signing_key crl_signing_key
EOF EOF
mkdir -p "$(dirname -- "${caCfg.paths.key}")" keyfile="${caCfg.paths.key}"
${pkgs.gnutls}/bin/certtool \ keydir="$(dirname -- "$keyfile")"
--generate-privkey \ mkdir -p "$keydir"
--key-type rsa \ [ -f "$keyfile" ] || ${pkgs.gnutls}/bin/certtool \
--sec-param High \ --generate-privkey \
--outfile ${caCfg.paths.key} --key-type rsa \
chmod 666 ${caCfg.paths.key} --sec-param High \
--outfile "$keyfile"
chmod 666 "$keyfile"
mkdir -p "$(dirname -- "${caCfg.paths.cert}")" certfile="${caCfg.paths.cert}"
${pkgs.gnutls}/bin/certtool \ certdir="$(dirname -- "$certfile")"
--generate-self-signed \ mkdir -p "$certdir"
--load-privkey ${caCfg.paths.key} \ [ -f "$certfile" ] || ${pkgs.gnutls}/bin/certtool \
--template ca.template \ --generate-self-signed \
--outfile ${caCfg.paths.cert} --load-privkey "$keyfile" \
chmod 666 ${caCfg.paths.cert} --template ca.template \
--outfile "$certfile"
chmod 666 "$certfile"
''; '';
} }
) cfg.cas.selfsigned; ) cfg.cas.selfsigned;
@ -427,6 +431,8 @@ in
script = '' script = ''
mkdir -p /etc/ssl/certs mkdir -p /etc/ssl/certs
# This file is automatically idempotent since the source files used are idempotent.
rm -f /etc/ssl/certs/ca-bundle.crt rm -f /etc/ssl/certs/ca-bundle.crt
rm -f /etc/ssl/certs/ca-certificates.crt rm -f /etc/ssl/certs/ca-certificates.crt
@ -456,8 +462,8 @@ in
let let
extraDnsNames = lib.strings.concatStringsSep "\n" (map (n: "dns_name = ${n}") certCfg.extraDomains); extraDnsNames = lib.strings.concatStringsSep "\n" (map (n: "dns_name = ${n}") certCfg.extraDomains);
chmod = cert: '' chmod = cert: ''
chown root:${certCfg.group} ${cert} chown root:${certCfg.group} "${cert}"
chmod 640 ${cert} chmod 640 "${cert}"
''; '';
in in
'' ''
@ -474,23 +480,27 @@ in
signing_key signing_key
EOF EOF
mkdir -p "$(dirname -- "${certCfg.paths.key}")" keyfile="${certCfg.paths.key}"
${pkgs.gnutls}/bin/certtool \ keydir="$(dirname -- "$keyfile")"
--generate-privkey \ mkdir -p "$keydir"
--key-type rsa \ [ -f "$keyfile" ] || ${pkgs.gnutls}/bin/certtool \
--sec-param High \ --generate-privkey \
--outfile ${certCfg.paths.key} --key-type rsa \
${chmod certCfg.paths.key} --sec-param High \
--outfile "$keyfile"
${chmod "$keyfile"}
mkdir -p "$(dirname -- "${certCfg.paths.cert}")" certfile="${certCfg.paths.cert}"
${pkgs.gnutls}/bin/certtool \ certdir="$(dirname -- "$certfile")"
--generate-certificate \ mkdir -p "$certdir"
--load-privkey ${certCfg.paths.key} \ [ -f "$certfile" ] || ${pkgs.gnutls}/bin/certtool \
--load-ca-privkey ${certCfg.ca.paths.key} \ --generate-certificate \
--load-ca-certificate ${certCfg.ca.paths.cert} \ --load-privkey "$keyfile" \
--template server.template \ --load-ca-privkey "${certCfg.ca.paths.key}" \
--outfile ${certCfg.paths.cert} --load-ca-certificate "${certCfg.ca.paths.cert}" \
${chmod certCfg.paths.cert} --template server.template \
--outfile "$certfile"
${chmod "$certfile"}
''; '';
postStart = lib.optionalString (certCfg.reloadServices != [ ]) '' postStart = lib.optionalString (certCfg.reloadServices != [ ]) ''

View file

@ -172,6 +172,32 @@ in
if perm != want_perm: if perm != want_perm:
raise Exception('Unexpected perm for {}: wanted "{}", got: "{}"'.format(path, want_perm, perm)) raise Exception('Unexpected perm for {}: wanted "{}", got: "{}"'.format(path, want_perm, perm))
with subtest("Certificates content seem correct"):
myca_key = server.succeed("cat {}".format("${myca.paths.key}")).strip();
myca_cert = server.succeed("cat {}".format("${myca.paths.cert}")).strip();
cert1_key = server.succeed("cat {}".format("${cert1.paths.key}")).strip();
cert1_cert = server.succeed("cat {}".format("${cert1.paths.cert}")).strip();
cert2_key = server.succeed("cat {}".format("${cert2.paths.key}")).strip();
cert2_cert = server.succeed("cat {}".format("${cert2.paths.cert}")).strip();
ca_bundle = server.succeed("cat /etc/ssl/certs/ca-bundle.crt").strip();
if myca_cert == "":
raise Exception("CA cert was empty")
if cert1_key == "":
raise Exception("Cert1 key was empty")
if cert1_cert == "":
raise Exception("Cert1 cert was empty")
if cert2_key == "":
raise Exception("Cert2 key was empty")
if cert2_cert == "":
raise Exception("Cert2 cert was empty")
if cert1_key == cert2_key:
raise Exception("Cert1 key and cert2 key are the same")
if cert1_cert == cert2_cert:
raise Exception("Cert1 cert and cert2 cert are the same")
if ca_bundle == "":
raise Exception("CA bundle was empty")
with subtest("Certificate is trusted in curl"): with subtest("Certificate is trusted in curl"):
resp = server.succeed("curl --fail-with-body -v https://example.com") resp = server.succeed("curl --fail-with-body -v https://example.com")
if resp != "Top domain": if resp != "Top domain":
@ -204,11 +230,52 @@ in
assert_perm("${cert2.paths.key}", "640") assert_perm("${cert2.paths.key}", "640")
assert_perm("${cert2.paths.cert}", "640") assert_perm("${cert2.paths.cert}", "640")
with subtest("Certificates content seem correct"):
if cert1_key == "":
raise Exception("Cert1 key was empty")
if cert1_cert == "":
raise Exception("Cert1 cert was empty")
if cert2_key == "":
raise Exception("Cert2 key was empty")
if cert2_cert == "":
raise Exception("Cert2 cert was empty")
if cert1_key == cert2_key:
raise Exception("Cert1 key and cert2 key are the same")
if cert1_cert == cert2_cert:
raise Exception("Cert1 cert and cert2 cert are the same")
with subtest("Fail if certificate is not in CA bundle"): with subtest("Fail if certificate is not in CA bundle"):
server.fail("curl --cacert /etc/static/ssl/certs/ca-bundle.crt --fail-with-body -v https://example.com") server.fail("curl --cacert /etc/static/ssl/certs/ca-bundle.crt --fail-with-body -v https://example.com")
server.fail("curl --cacert /etc/static/ssl/certs/ca-bundle.crt --fail-with-body -v https://subdomain.example.com") server.fail("curl --cacert /etc/static/ssl/certs/ca-bundle.crt --fail-with-body -v https://subdomain.example.com")
server.fail("curl --cacert /etc/static/ssl/certs/ca-certificates.crt --fail-with-body -v https://example.com") server.fail("curl --cacert /etc/static/ssl/certs/ca-certificates.crt --fail-with-body -v https://example.com")
server.fail("curl --cacert /etc/static/ssl/certs/ca-certificates.crt --fail-with-body -v https://subdomain.example.com") server.fail("curl --cacert /etc/static/ssl/certs/ca-certificates.crt --fail-with-body -v https://subdomain.example.com")
with subtest("Idempotency"):
server.succeed("systemctl restart shb-certs-ca-myca")
server.succeed("systemctl restart shb-certs-cert-selfsigned-cert1")
server.succeed("systemctl restart shb-certs-cert-selfsigned-cert2")
new_myca_key = server.succeed("cat {}".format("${myca.paths.key}")).strip();
new_myca_cert = server.succeed("cat {}".format("${myca.paths.cert}")).strip();
new_cert1_key = server.succeed("cat {}".format("${cert1.paths.key}")).strip();
new_cert1_cert = server.succeed("cat {}".format("${cert1.paths.cert}")).strip();
new_cert2_key = server.succeed("cat {}".format("${cert2.paths.key}")).strip();
new_cert2_cert = server.succeed("cat {}".format("${cert2.paths.cert}")).strip();
new_ca_bundle = server.succeed("cat /etc/ssl/certs/ca-bundle.crt").strip();
if new_myca_key != myca_key:
raise Exception("New CA key is different from old one.")
if new_myca_cert != myca_cert:
raise Exception("New CA cert is different from old one.")
if new_cert1_key != cert1_key:
raise Exception("New Cert1 key is different from old one.")
if new_cert1_cert != cert1_cert:
raise Exception("New Cert1 cert is different from old one.")
if new_cert2_key != cert2_key:
raise Exception("New Cert2 key is different from old one.")
if new_cert2_cert != cert2_cert:
raise Exception("New Cert2 cert is different from old one.")
if new_ca_bundle != ca_bundle:
raise Exception("New CA bundle is different from old one.")
''; '';
}; };
} }