commit f0ca500f274a41cf21b5966be82bf7892ce70c16
parent 0b090e3fb2331efe940e2e56680e3a6d2888f127
Author: Vineet Kumar <git@vineetk.net>
Date: Sun, 1 Feb 2026 22:09:09 -0500
make equivalent of agenix but for guix
Diffstat:
16 files changed, 185 insertions(+), 76 deletions(-)
diff --git a/epistemia/secrets/email_13f0.age b/epistemia/secrets/email_13f0.age
@@ -1,8 +1,8 @@
age-encryption.org/v1
--> ssh-ed25519 KXd7pg NC24+Ojrbpj4ZCNPsaOxmfjZ12TOkAZj+Ep+5Z24RV8
-VeJTCZDftGOr1lGk3P64Q8WKh0JJeMoANXbfI1xVpC0
--> ssh-ed25519 AaF/mQ iQk+QSLJVX4qUbVc4ipnUAySefXAlMtMRqX+ehn9tA8
-/HIS+hgZkEGzlHZu+qPlJsmZuCG5M3cZDGv2RoFhbTg
---- /vlSUL9zRFQKhDGF+EVNkn6c9aFOn+Vrlo6fJ4gLCC8
-j/=E+I3u3\P6̇旻ū:PrvHz.4Nq@ɭ
-țVʫfRV2
-\ No newline at end of file
+-> ssh-ed25519 KXd7pg +F2yXhae5yj0NLsNOiW1uO7pivHOHBs/mB+gyoJMeyc
+BKlidch3PDLCGyP9XhVGtNf4KIdf2/hogpB2pMA+td4
+-> ssh-ed25519 AaF/mQ XY8cQjrBInYxxvE5WOulENM01ON8r0rMJTDLjV5b+x0
+5qIebghkQsO/fJpFAC8LGj3uNGKoKxDzRPg1XYJC/NE
+--- yN9TRJeI9kDxjDQX7VT3U1fmlcmhKyOmQFwB9fErhoA
+G'm!yDzJjSB`B\ԾFl{
+L%=et-]1u_T,xBq
+\ No newline at end of file
diff --git a/epistemia/secrets/email_vinbiz.age b/epistemia/secrets/email_vinbiz.age
@@ -1,8 +1,7 @@
age-encryption.org/v1
--> ssh-ed25519 KXd7pg SGHGg+SbZe1jFIRXvUK5E1QmyR6doXb5LQ9TSnkgYgM
-JWS0xIhjTojEyOrby9SvJ/xQ0c6yPUwRdY64v7dH3j0
--> ssh-ed25519 AaF/mQ JjFXRIs1rUDGQO53DEVjC15A+8EWC+yn/mjjQLo++i8
-gUlsb8MO6toHIS9igzRRHT+XM58LMIJJ8g/wLYU6REA
---- jfk6HuQV7JwDyV3SAXq9v7TxTvfIoTU6Hpr24uhMNAE
-BĶ
-p?v}7GQE~N"X}r9C.qJMV-g7*D1nA(-
+-> ssh-ed25519 KXd7pg tUohFOSCPlfHTBOKGtiMtzzsKECiGZTWFfvZDgC77xc
+lSJPK6pIzYrX7SOdNKLMNckjQcdNt51XdD1mwSPNNAE
+-> ssh-ed25519 AaF/mQ 3VNOi3vM4l7GArgCG257ysdBRoNKlxo1WlMt3X343nw
+SRKfvfM4LyGzPUKG5O5xAtsuUdXiA/xo4BBTk6qz+Gk
+--- Q6rWlMQRuHZmFr7Ijx1h4NYdBmdI6R/q2jRnxxiC7tM
+&k^H̩Z]ծJedVm$_ʭUO"ڃ>87+S[F{/=a_y'R:ɦ
+\ No newline at end of file
diff --git a/epistemia/secrets/email_vineetk.age b/epistemia/secrets/email_vineetk.age
@@ -1,7 +1,7 @@
age-encryption.org/v1
--> ssh-ed25519 KXd7pg HlY4UPQmA0BiIoATyFzk/9idMKP/NqBUyxA4z2/mQlA
-9XFT2Ede47iwkwAwnP3ib238tSSaNU0Oi6fAWjXaaxY
--> ssh-ed25519 AaF/mQ 63PqLPa6RQJgdZj/B71rfpUf5fJVFZyuVFQ+NPFjRHw
-TUlu9Kpg/Oh3WwzY2RSxBVDSO7GBf7CSuYwpcHtAgj8
---- Lj13oXwXWMQewx/6ay+eFkhXgVaPWDYavRROlgc5Rjc
-,BȭIAvvETUL^Jt~ i,"wUxX9͞ў勎tyy1%dh"A9@EY
-\ No newline at end of file
+-> ssh-ed25519 KXd7pg R5+p2kMgSxnMdpi8IS3nW/9xLSuu1qjsqz4yijKtC1A
+2hdmKFZYX/S55tg2dQjNPRBPsmlkwNwjCr931LojORA
+-> ssh-ed25519 AaF/mQ S3RPT19vbkgu/jFnhb9pcivTv5zVvqOxY5akt2AMU2k
+WcSSP5XciN9bs5/mpR/ze+ofuTMDqF/cQ2fJjs0l2gM
+--- WzqudkfQGAKINq39mpndXRJvDCGP32rGprE87V5WmqQ
+ICf,CAsOL%77W08[w!Vb foRB#o//֨Q)7T0aLMSHj5Qi2jc
+\ No newline at end of file
diff --git a/epistemia/secrets/rekey.scm b/epistemia/secrets/rekey.scm
@@ -18,6 +18,7 @@
("email_vinbiz" . ,saklas)
("email_vineetk" . ,saklas)
("wg1_saklas" . ,saklas)
+ ("saklas_smtpd_dkim" . ,saklas)
("saklas_smtpd_passwd" . ,saklas)
("saklas_smtpd_virtuals" . ,saklas)
("wg0_demiurge" . ,demiurge)
diff --git a/epistemia/secrets/saklas_smtpd_dkim.age b/epistemia/secrets/saklas_smtpd_dkim.age
Binary files differ.
diff --git a/epistemia/secrets/saklas_smtpd_passwd.age b/epistemia/secrets/saklas_smtpd_passwd.age
Binary files differ.
diff --git a/epistemia/secrets/saklas_smtpd_virtuals.age b/epistemia/secrets/saklas_smtpd_virtuals.age
Binary files differ.
diff --git a/epistemia/secrets/wg0_demiurge.age b/epistemia/secrets/wg0_demiurge.age
@@ -1,8 +1,8 @@
age-encryption.org/v1
--> ssh-ed25519 947m3A EzzSrDNUoYSj9hFJoRLIkVeTvwhhfrBiEszh7T2Y3BA
-v1Q5wUaO0jB5FatrQm5xBNYbz+DxQI8zLdqqNqI0T3E
--> ssh-ed25519 AaF/mQ ciS30vz8wKEhpDO9NZHOWJzqv6jiY72NobmkzOhoHDQ
-tw2tLWjoLy+osPhYuAsZeZ/snoW037x6OYl1/NTnGHY
---- 8Tgn/Xk1ih6cvlyLuoKLQhb9QvJfjsUvl1LL9Iz0LHI
-^_ƕsݶ1hjI=^u_8FFk]
-)md#>,l*,- zCg
-\ No newline at end of file
+-> ssh-ed25519 947m3A MZJNQdBmczFVatS246wLAJvj6ZojkugRszDRInvO3Gw
+FvxGl/0+DjU5UAlE7w3g6laQqEOTMHDt8tjMEGT7CBE
+-> ssh-ed25519 AaF/mQ Hn/rf5RG5TTQdOACU45UAjHCVFZgqH+XHNZ8tivszEo
+evddhLmWuD6tYZ6umIHla2Gl2Afnl4XUSVX3ge7eslI
+--- IXQPgY+qi5IRb5NXOmXdOkISv1lGaWpiv8xmkdf54A4
+WAOH[T Avq͊OpcV@
+\Grj/KZ80`eB!\"h&^[V
+\ No newline at end of file
diff --git a/epistemia/secrets/wg0_hastur.age b/epistemia/secrets/wg0_hastur.age
@@ -1,8 +1,7 @@
age-encryption.org/v1
--> ssh-ed25519 u1jj0Q mphW61rtPkXG08C3Z6JPRnE/NqOQKLLHB5DJBTp4IAE
-YURVENV8sjC64KcDM+8lpIHjqq6MNWsdvuc3van33zs
--> ssh-ed25519 AaF/mQ VjH8LCzCONTa1mx3Wa0mQbfsIqjaoIUOjwnw68llYUs
-AWsE19FFC/a84Yivfk50/qOgvekIROxiYJJiSB08MII
---- vJ5mAAjqSmC57qAFuy/KtBPl6GHwoe4alm/PVfHHO/E
-s_f\gclK訣B`q$<SZ0mCnda%
-.K-Xwc}
-\ No newline at end of file
+-> ssh-ed25519 u1jj0Q MGIN6ROiC3rE7ZErJhRH7t4oVLV4VFxRDS0xCY5oB10
+srGbsuqsdyu//8XPbiAaCUJT58l+7IN+ARkFqlkIV6s
+-> ssh-ed25519 AaF/mQ iqyIyroyukF/SWbgztW3+NArH0f1R9Aj79m9Xf3ZT08
+xqjJHiv/GYAohv4gpraGf2EOsFyoWsQ1/TObi04vFJI
+--- dLDpm8m28K0XbsCYr80NCRqu0grE97WfAUocmC/XylE
+M>1JCƯ<@Am`h$5)m=T\_sK@և/L~24Ŋ!
+\ No newline at end of file
diff --git a/epistemia/secrets/wg1_demiurge.age b/epistemia/secrets/wg1_demiurge.age
@@ -1,7 +1,7 @@
age-encryption.org/v1
--> ssh-ed25519 947m3A Pj/LyJ8L4atRQWxR65rrP8dc7RVWXanSYo73IHeVl10
-ynDKYBHehTilfx3NfjFuTz41r9OvnS5uABZoKrIch0A
--> ssh-ed25519 AaF/mQ sfOhQXgUNaNmK3MPQRJBQqrl91SN+lltL92FFeHcjT8
-hkbqyIgMI3qlDMiNm1hgfHZ7f6vCIQn/2LOZl3YcplQ
---- 4FpSJvkKBxQTirJ6gapo/eUu4INP24rHN9slfDmcwBc
-e1rEͰ/#MEqv7}}NͅZ!SȰ옑MsFHifGNmS;(Uq
-\ No newline at end of file
+-> ssh-ed25519 947m3A wSP66asmzFEKBI3ZW6Qj04EQer77GbP65G8Jn2ipbFY
+Uwrhu8BdmHa1Qj4dsY2OP88zR0R+CG6K8UPRtDRWYUc
+-> ssh-ed25519 AaF/mQ VN8rH287lfy2TGK1xVPA5EUknHJAU0AMIjM7lq5OwV4
+UthVsZ8EkKBRkqS8evwQ4JipkMK+XaaeMWEV0C0x+lY
+--- p+XFc58iBH/vM/R1HXGE0NfWNE6DdDBsQmqKFAUehKs
+E{Pq1XQLx[|[w8;qƘSPІi*.pnXаkjL1Eɩwr0Nc
+\ No newline at end of file
diff --git a/epistemia/secrets/wg1_hastur.age b/epistemia/secrets/wg1_hastur.age
@@ -1,7 +1,7 @@
age-encryption.org/v1
--> ssh-ed25519 u1jj0Q 4FWew45tBGHr2xw8qTwPWeKMYfHsA7+zodf0D/DVJx4
-YSmlzSbuf5btGhRvaVqzq5qQjjybH6E2d9JEMGWo7cU
--> ssh-ed25519 AaF/mQ BoVOirxxUVYCBbYhglHAndZBTUPrv+9e0JEH3SKm110
-lmBhSgieUlBr7y4w0YPqjrQKtBakXV/6t+YQSkGbbz4
---- g+oByV+yiFrZUqpuqCkJecGtLw25AHiZ7QUIiRhsVKY
-e]FA53Ġ_z4"Q`Q50p_6M6}p2Wc3*Ն#\lRzh!D7
-\ No newline at end of file
+-> ssh-ed25519 u1jj0Q Pyp2wAZl+sOCwkmOnZpO/dvSn5iHgKe5aYKw2pSe2D8
+OeU8Fb99Im82dq7QPV/LgrWHXUkZJddIGYUSoFXKBgs
+-> ssh-ed25519 AaF/mQ /0u55zKnbUjBjdwzTsoSzEvuQbIhs47yMaIitlTO8Tk
+6O1V+F7O6p6oHcm96sna2YkCl1T3Dk86R61Gzm37A6s
+--- CcJPTC6ff7N1NRIj7SoeojxJETF6BQp3FL6TaNp/4rA
+'W}@Pڲϥ^$I.L&;!k6UeVU5WЉ
+\ No newline at end of file
diff --git a/epistemia/secrets/wg1_saklas.age b/epistemia/secrets/wg1_saklas.age
@@ -1,7 +1,7 @@
age-encryption.org/v1
--> ssh-ed25519 KXd7pg 14/iC0/3YrCbW4qThXzO96JzLdyM7eBYxVrX8ocWhwU
-4eoKcXxg3ohlyWRQb2j84kQwyjBvCDKkA2WT3PTKjcg
--> ssh-ed25519 AaF/mQ OmXRflVw3QgvHdjKG27kIYpHgk7+gWI27YmQjt/S4GI
-PZwKbH+ed56kGOAQqxuMgubqcFx6LgxnJArUiWtWKNw
---- ItcnMiw1eWZyJvxw27RmnJaRwmQiHsTOu6hcLMIdjmI
-n*)LȖN1vuL+k8R~T6{4|9Wf_˔:[bژ
-\ No newline at end of file
+-> ssh-ed25519 KXd7pg ug86AuwHwShDfuSxt6BvxZVJ0+lTwdYrF3X4gFISonU
+PTz6aBjUzAlLhfQJ+40NJUahra3c/SDlA5bGDJyCEek
+-> ssh-ed25519 AaF/mQ RLagOaQ3N9xW4XSJuXT3oIuXPayxCkve00CrscVlCnA
+drWVnMMSn2P4DjnIGHkVPRvTi1rTPrJeeJOqKTS3ZRM
+--- HzborkeCUmEQRRQTk6OQZ+Xl7b8GYSUqIMqKfopkUDY
+8"e:>SxJ0J,^lsnlBc(ϊݗ^| g#
+\ No newline at end of file
diff --git a/epistemia/secrets/wpa_supplicant.age b/epistemia/secrets/wpa_supplicant.age
Binary files differ.
diff --git a/epistemia/services/age-secret.scm b/epistemia/services/age-secret.scm
@@ -0,0 +1,86 @@
+(define-module (epistemia services age-secret)
+ #:use-module (guix gexp)
+ #:use-module (guix records)
+ #:use-module (gnu services)
+ #:use-module (gnu packages golang-crypto)
+ #:use-module (ice-9 match)
+ #:export (age-secret-service-type
+ age-secret-configuration
+ age-secret))
+
+(define-record-type* <age-secret-configuration>
+ age-secret-configuration make-age-secret-configuration
+ age-secret-configuration?
+ (secrets age-secret-configuration-secrets
+ (default '())))
+
+(define-record-type* <age-secret>
+ age-secret make-age-secret
+ age-secret?
+ (file age-secret-file)
+ (name age-secret-name)
+ (owner age-secret-owner (default "root"))
+ (group age-secret-group (default "root"))
+ (mode age-secret-mode (default #o400)))
+
+(define (age-secret-activation config)
+ (let ((secrets (age-secret-configuration-secrets config)))
+ (with-imported-modules '((guix build utils))
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 match)
+ (ice-9 format))
+
+ ;; TODO: make the let vars configurable if I ever upstream this
+ (let ((secret-dir "/run/secrets")
+ (key-path "/etc/ssh/ssh_host_ed25519_key")
+ (age-bin #$(file-append age "/bin/age")))
+
+ (mkdir-p secret-dir)
+ (chmod secret-dir #o711)
+
+ (for-each
+ (match-lambda
+ ((source output-name owner-name group-name mode)
+ (let ((dest (string-append secret-dir "/" output-name)))
+ ;; decrypt if missing
+ (unless (file-exists? dest)
+ (if (file-exists? key-path)
+ (begin
+ (format #t "Decrypting secret: ~a -> ~a...~%" source dest)
+ (let ((status (system* age-bin "-d" "-i" key-path "-o" dest source)))
+ (unless (zero? status)
+ (format (current-error-port) "Failed to decrypt ~a.~%" output-name))))
+ (format (current-error-port) "Missing host key: ~a~%" key-path)))
+
+ ;; apply permissions and ownership
+ (when (file-exists? dest)
+ (catch 'system-error
+ (lambda ()
+ (let ((uid (passwd:uid (getpwnam owner-name)))
+ (gid (group:gid (getgrnam group-name))))
+ (chown dest uid gid)
+ (chmod dest mode)))
+ (lambda args
+ (format (current-error-port)
+ "Failed to set owner ~a:~a on ~a: ~a~%"
+ owner-name group-name dest (strerror (system-error-errno args)))))))))
+
+ ;; serialize record list for gexp
+ '#$(map (lambda (s)
+ (list (age-secret-file s)
+ (age-secret-name s)
+ (age-secret-owner s)
+ (age-secret-group s)
+ (age-secret-mode s)))
+ secrets)))))))
+
+(define age-secret-service-type
+ (service-type
+ (name 'age-secret)
+ (extensions
+ (list (service-extension activation-service-type
+ age-secret-activation)))
+ (description
+ "Decrypts age-encrypted files at boot using host keys and assigns ownership.")
+ (default-value (age-secret-configuration))))
diff --git a/epistemia/systems/demiurge.scm b/epistemia/systems/demiurge.scm
@@ -29,6 +29,7 @@
#:use-module (epistemia bootloader)
#:use-module (epistemia systems base-system)
#:use-module (epistemia systems linux-initrd)
+ #:use-module (epistemia services age-secret)
#:use-module (epistemia services zfs)
#:use-module (epistemia packages zfs))
@@ -108,7 +109,16 @@
zfs-linux)
%base-packages))
- (services (modify-services (append (list (simple-service 'zfs-loader
+ (services (modify-services (append (list (service age-secret-service-type
+ (age-secret-configuration
+ (secrets (list
+ (age-secret
+ (name "wg0_demiurge")
+ (file (local-file "../secrets/wg0_demiurge.age")))
+ (age-secret
+ (name "wg1_demiurge")
+ (file (local-file "../secrets/wg1_demiurge.age")))))))
+ (simple-service 'zfs-loader
kernel-module-loader-service-type
'("zfs"))
(simple-service 'zfs-shepherd-services
@@ -202,11 +212,7 @@ rpz:
(addresses '("10.68.117.111/32" "fc00:bbbb:bbbb:bb01::5:756e/128"))
(port 51821)
(bootstrap-private-key? #f)
- (private-key
- #~(string-append "<("
- #$(file-append age "/bin/age -d -i /etc/ssh/ssh_host_ed25519_key ")
- #$(local-file "../secrets/wg0_demiurge.age")
- ")"))
+ (private-key "/run/secrets/wg0_demiurge")
(pre-up '("ip route replace 79.127.222.207 via 192.168.1.1 dev eth0"))
(post-up
(list "ip rule add fwmark 0x1 table 100 priority 100"
@@ -236,11 +242,7 @@ rpz:
(addresses '("10.0.13.2/24" "fd00:b0ba:cafe:babe::2/64 "))
(port 51820)
(bootstrap-private-key? #f)
- (private-key
- #~(string-append "<("
- #$(file-append age "/bin/age -d -i /etc/ssh/ssh_host_ed25519_key ")
- #$(local-file "../secrets/wg1_demiurge.age")
- ")"))
+ (private-key "/run/secrets/wg1_demiurge")
(table #f)
(post-up
;; IPv4 and IPv6 NAT
diff --git a/epistemia/systems/saklas.scm b/epistemia/systems/saklas.scm
@@ -27,6 +27,7 @@
#:use-module (epistemia bootloader)
#:use-module (epistemia systems base-system)
#:use-module (epistemia systems linux-initrd)
+ #:use-module (epistemia services age-secret)
#:use-module (epistemia services zfs)
#:use-module (epistemia packages zfs))
@@ -223,7 +224,31 @@ table inet filter {
zfs-linux)
%base-packages))
- (services (modify-services (append (list (simple-service 'zfs-loader
+ (services (modify-services (append (list (service age-secret-service-type
+ (age-secret-configuration
+ (secrets (list
+ (age-secret
+ (name "wg1_saklas")
+ (file (local-file "../secrets/wg1_saklas.age")))
+ (age-secret
+ (name "saklas_smtpd_dkim")
+ (file (local-file "../secrets/saklas_smtpd_dkim.age"))
+ (owner "smtpd")
+ (group "nogroup")
+ (mode #o400))
+ (age-secret
+ (name "saklas_smtpd_virtuals")
+ (file (local-file "../secrets/saklas_smtpd_virtuals.age"))
+ (owner "smtpd")
+ (group "nogroup")
+ (mode #o400))
+ (age-secret
+ (name "saklas_smtpd_passwd")
+ (file (local-file "../secrets/saklas_smtpd_passwd.age"))
+ (owner "smtpd")
+ (group "dovecot")
+ (mode #o440))))))
+ (simple-service 'zfs-loader
kernel-module-loader-service-type
'("zfs"))
(simple-service 'zfs-shepherd-services
@@ -338,11 +363,7 @@ rpz:
(addresses '("10.0.13.1/24" "fd00:b0ba:cafe:babe::1/64 "))
(port 51820)
(bootstrap-private-key? #f)
- (private-key
- #~(string-append "<("
- #$(file-append age "/bin/age -d -i /etc/ssh/ssh_host_ed25519_key ")
- #$(local-file "../secrets/wg1_saklas.age")
- ")"))
+ (private-key "/run/secrets/wg1_saklas")
(peers
(list
(wireguard-peer