depot/cluster/services/acme-client/client.nix

83 lines
2.3 KiB
Nix

{ cluster, config, depot, lib, pkgs, ... }:
let
authoritativeServers = map
(node: cluster.config.hostLinks.${node}.dnsAuthoritative.tuple)
cluster.config.services.dns.nodes.authoritative;
execScript = pkgs.writeShellScript "acme-dns-exec" ''
action="$1"
subdomain="''${2%.${depot.lib.meta.domain}.}"
key="$3"
umask 77
source "$EXEC_ENV_FILE"
headersFile="$(mktemp)"
echo "X-Direct-Key: $ACME_DNS_DIRECT_STATIC_KEY" > "$headersFile"
case "$action" in
present)
for i in {1..5}; do
${pkgs.curl}/bin/curl -X POST -s -f -H "@$headersFile" \
"${cluster.config.links.acmeDnsApi.url}/update" \
--data '{"subdomain":"'"$subdomain"'","txt":"'"$key"'"}' && break
sleep 5
done
;;
esac
'';
in
{
age.secrets.acmeDnsApiKey = {
file = ../dns/acme-dns-direct-key.age;
owner = "acme";
};
security.acme.acceptTerms = true;
security.acme.maxConcurrentRenewals = 0;
security.acme.defaults = {
email = depot.lib.meta.adminEmail;
extraLegoFlags = lib.flatten [
(map (x: [ "--dns.resolvers" x ]) authoritativeServers)
"--dns-timeout" "30"
];
credentialsFile = pkgs.writeText "acme-exec-config" ''
EXEC_PATH=${execScript}
EXEC_ENV_FILE=${config.age.secrets.acmeDnsApiKey.path}
'';
};
systemd.services = lib.mapAttrs' (name: value: {
name = "acme-${name}";
value = {
distributed.enable = value.dnsProvider != null;
preStart = let
serverList = lib.pipe authoritativeServers [
(map (x: "@${x}"))
(map (lib.replaceStrings [":53"] [""]))
lib.escapeShellArgs
];
domainList = lib.pipe ([ value.domain ] ++ value.extraDomainNames) [
(map (x: "${x}."))
(map (lib.replaceStrings ["*"] ["x"]))
lib.unique
lib.escapeShellArgs
];
in ''
echo Testing availability of authoritative DNS servers
for i in {1..60}; do
${pkgs.dig}/bin/dig +short ${serverList} ${domainList} >/dev/null && break
echo Retry [$i/60]
sleep 10
done
echo Available
'';
serviceConfig = {
Restart = "on-failure";
RestartMaxDelaySec = 30;
RestartStesp = 5;
RestartMode = "direct";
};
};
}) config.security.acme.certs;
}