summaryrefslogtreecommitdiff
path: root/hosts/surtr/tls
diff options
context:
space:
mode:
Diffstat (limited to 'hosts/surtr/tls')
-rw-r--r--hosts/surtr/tls/default.nix156
-rw-r--r--hosts/surtr/tls/tsig_keys/rheperire.org26
2 files changed, 182 insertions, 0 deletions
diff --git a/hosts/surtr/tls/default.nix b/hosts/surtr/tls/default.nix
new file mode 100644
index 00000000..a1548feb
--- /dev/null
+++ b/hosts/surtr/tls/default.nix
@@ -0,0 +1,156 @@
1{ config, lib, customUtils, pkgs, ... }:
2
3with lib;
4
5let
6 inherit (customUtils) mapFilterAttrs;
7
8 tsigSecretName = domain: "${domain}_tsig-secret";
9
10 cfg = config.security.acme;
11 knotCfg = config.services.knot;
12
13 knotDNSCredentials = domain: let
14 zone = if cfg.domains.${domain}.zone == null then domain else cfg.domains.${domain}.zone;
15 in pkgs.writeText "lego-credentials" ''
16 EXEC_PATH=${knotDNSExec zone}/bin/update-dns.sh
17 EXEC_PROPAGATION_TIMEOUT=300
18 EXEC_POLLING_INTERVAL=5
19 '';
20 knotDNSExec = zone: pkgs.writeScriptBin "update-dns.sh" ''
21 #!${pkgs.zsh}/bin/zsh -xe
22
23 mode=$1
24 fqdn=$2
25 challenge=$3
26
27 owner=''${fqdn%".${zone}."}
28
29 commited=
30 function abort() {
31 [[ -n "''${commited}" ]] || ${knotCfg.cliWrappers}/bin/knotc zone-abort "${zone}"
32 }
33
34 ${knotCfg.cliWrappers}/bin/knotc zone-begin "${zone}"
35 trap abort EXIT
36
37 case "''${mode}" in
38 present)
39 if ${knotCfg.cliWrappers}/bin/knotc zone-get ${zone} "''${owner}" TXT; then
40 ${knotCfg.cliWrappers}/bin/knotc zone-unset ${zone} "''${owner}" TXT '""'
41 fi
42 ${knotCfg.cliWrappers}/bin/knotc zone-set ${zone} "''${owner}" 30 TXT "''${challenge}"
43 ;;
44 cleanup)
45 ${knotCfg.cliWrappers}/bin/knotc zone-unset ${zone} "''${owner}" TXT "''${challenge}"
46 ${knotCfg.cliWrappers}/bin/knotc zone-set ${zone} "''${owner}" 30 TXT '""'
47 ;;
48 *)
49 exit 2
50 ;;
51 esac
52
53 ${knotCfg.cliWrappers}/bin/knotc zone-commit "${zone}"
54 commited=yes
55 '';
56
57 domainOptions = {
58 options = {
59 wildcard = mkOption {
60 type = types.bool;
61 default = false;
62 };
63 zone = mkOption {
64 type = types.nullOr types.str;
65 default = null;
66 };
67 certCfg = mkOption {
68 type = types.attrs;
69 default = {};
70 };
71 };
72 };
73in {
74 options = {
75 security.acme = {
76 domains = mkOption {
77 type = types.attrsOf (types.submodule domainOptions);
78 default = {};
79 };
80 };
81 };
82
83 config = {
84 security.acme.domains = genAttrs ["dirty-haskell.org" "141.li" "xmpp.li" "yggdrasil.li" "praseodym.org" "rheperire.org" "kleen.li" "nights.email"] (domain: { wildcard = true; });
85
86 fileSystems."/var/lib/acme" =
87 { device = "surtr/safe/var-lib-acme";
88 fsType = "zfs";
89 };
90
91 security.acme = {
92 acceptTerms = true;
93 preliminarySelfsigned = true; # DNS challenge is slow
94 defaults = {
95 email = "phikeebaogobaegh@141.li";
96 keyType = "rsa4096"; # we don't like NIST curves
97 # extraLegoFlags = ["--preferred-chain" "ISRG Root X1"];
98 };
99 certs =
100 let
101 domainAttrset = domain: let
102 tsigPath = ./tsig_keys + "/${domain}";
103 tsigSecret = config.sops.secrets.${tsigSecretName domain};
104 isTsig = pathExists tsigPath;
105 shared = {
106 inherit domain;
107 extraDomainNames = optional cfg.domains.${domain}.wildcard "*.${domain}";
108 dnsResolver = "127.0.0.1:5353";
109 };
110 mkKnotc = shared // {
111 dnsProvider = "exec";
112 credentialsFile = knotDNSCredentials domain;
113 };
114 mkRFC2136 = let
115 tsigInfo = readYaml tsigPath;
116 in shared // {
117 dnsProvider = "rfc2136";
118 credentialsFile = pkgs.writeText "${domain}_credentials.env" ''
119 RFC2136_NAMESERVER=127.0.0.1:53
120 RFC2136_TSIG_ALGORITHM=hmac-sha256.
121 RFC2136_TSIG_KEY=${domain}_acme
122 RFC2136_TSIG_SECRET_FILE=${tsigSecret.path}
123 RFC2136_PROPAGATION_TIMEOUT=300
124 RFC2136_POLLING_INTERVAL=5
125 RFC2136_TTL=300
126 '';
127 };
128 in // cfg.domains.${domain}.certCfg;
129 in genAttrs (attrNames cfg.domains) domainAttrset;
130 };
131
132 sops.secrets = let
133 toTSIGSecret = n: v:
134 if v == "regular" || v == "symlink"
135 then nameValuePair (tsigSecretName n) {
136 format = "binary";
137 owner = if config.security.acme.useRoot then "root" else "acme";
138 group = "acme";
139 sopsFile = ./tsig_keys + "/${n}";
140 } else null;
141 in mapFilterAttrs (_: v: v != null) toTSIGSecret (readDir ./tsig_keys);
142
143 systemd.services =
144 let
145 serviceAttrset = domain: {
146 after = [ "knot.service" ];
147 bindsTo = [ "knot.service" ];
148 serviceConfig = {
149 ReadWritePaths = ["/run/knot/knot.sock"];
150 SupplementaryGroups = ["knot"];
151 RestrictAddressFamilies = ["AF_UNIX"];
152 };
153 };
154 in mapAttrs' (domain: nameValuePair "acme-${domain}") (genAttrs (attrNames config.security.acme.certs) serviceAttrset);
155 };
156}
diff --git a/hosts/surtr/tls/tsig_keys/rheperire.org b/hosts/surtr/tls/tsig_keys/rheperire.org
new file mode 100644
index 00000000..1f3dc4a4
--- /dev/null
+++ b/hosts/surtr/tls/tsig_keys/rheperire.org
@@ -0,0 +1,26 @@
1{
2 "data": "ENC[AES256_GCM,data:Lb1IWtwPdBxeXGrOr74MlR7lKBMIg2ix3enRMVN4MPEDh7CIFv1nhAfLCDU=,iv:TcyGtcKEL7yUXgHrfJ5pDjl8r5V7ltYQw2wcnsFN1bc=,tag:gBaYj6MkQKvD/uO+Pudnzg==,type:str]",
3 "sops": {
4 "kms": null,
5 "gcp_kms": null,
6 "azure_kv": null,
7 "hc_vault": null,
8 "age": null,
9 "lastmodified": "2022-02-22T09:46:27Z",
10 "mac": "ENC[AES256_GCM,data:vtymuSymOeh34ZbAumxCEcemtI0UFBoQwj4axpKf7AzVnQOubWYxPI5x23CiOE12Y+FThg1dYEnDWlkkQjMVWQvcJzfP1g25S98MuZ42E9R0nBxGzTrIaKS7kgNAriYDy8ib7Z3DUbajEGfvLLazCdHu3g9gE0dLewKcUTHaXwM=,iv:j8vS2Ej58HLf+CP9fe1rN+4UuniGlv/0M+Zzt3nlk5I=,tag:xn33GGm2knsIdH+EJ1zDcA==,type:str]",
11 "pgp": [
12 {
13 "created_at": "2022-02-22T09:46:26Z",
14 "enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DyFKFNkTVG5oSAQdA1++YsCSiRkVGoVF6uvYeY7JjZjsdjAUwubCF3fLDtS8w\nHFSQltgrqkul1Ei1fUFQqFbSpMSfBQhk0kMH0Xuk5SoDd1lWumqTP9gaJj/CM+yz\n0l4BHiQ4Ktyc01f127Az36nQZGKdJvL9etQAceNtNM7Y4XkLQ78OXFJoERrf81s/\n4wm5za7xqEBE1oE0QWAelzyjBqxI1iCjDl3p6heUdhqlcvAifXgqAk0qxq/4T4FG\n=2Bwl\n-----END PGP MESSAGE-----\n",
15 "fp": "7ED22F4AA7BB55728B643DC5471B7D88E4EF66F8"
16 },
17 {
18 "created_at": "2022-02-22T09:46:26Z",
19 "enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DXxoViZlp6dISAQdA4jg570qc5Zu3yAJPmjf11r20eYMyjAeEWw/RreZga3Uw\nmTy2jo/de11m3r/aIxp0Q/vFZ1m4JGeoIzIgQxGnhX0s0NpfCbCih0P7UMjZRatC\n0l4BCzWq7c0A2GbjPq+aKF2IYfQV5gdwjgCAJO3Oylb/SAltwnyGChANw4ckUCjV\n9DWYIcerMP2scxpjf5nJXpHNMjpGR7fcqS71PoVoJoActMqrG0deOMUSUEOVP8nR\n=+ux+\n-----END PGP MESSAGE-----\n",
20 "fp": "30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51"
21 }
22 ],
23 "unencrypted_suffix": "_unencrypted",
24 "version": "3.7.1"
25 }
26} \ No newline at end of file