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+ÍI�Ç3Ï÷÷¼u3ËÏ\PÀÔ6‡ë¼Ì‡æ—»ÈÅ«ó:ÀP–r¨v¾H¥zÍ.4ëNq@É¿ñ
-÷È›¸VÊ«f‘R•V¦2
-\ 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·ÁÄÇ!ªy›Öâ‚DzŽ�¡·Jòž˜j÷“SB`ŸºB\€÷µãáÔ¾FáÀl{
+L%‡=et˜-ä¿�Üî—]ë1€Îu_‘T½,…¢öxÜB¬µ¹ßqü«
+\ 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‘¶…}ÃÐ7ƒíGìQû“E¾¯~ÄN"“X}r9Cš.¶q¨ÝŒéJÙMVÓ-åg7ßÿ¦áÇ*úDí‚à1�nô‹†ž�A(-±‰ô—
+-> ssh-ed25519 KXd7pg tUohFOSCPlfHTBOKGtiMtzzsKECiGZTWFfvZDgC77xc
+lSJPK6pIzYrX7SOdNKLMNckjQcdNt51XdD1mwSPNNAE
+-> ssh-ed25519 AaF/mQ 3VNOi3vM4l7GArgCG257ysdBRoNKlxo1WlMt3X343nw
+SRKfvfM4LyGzPUKG5O5xAtsuUdXiA/xo4BBTk6qz+Gk
+--- Q6rWlMQRuHZmFr7Ijx1h4NYdBmdI6R/q2jRnxxiC7tM
+õ‹&¬ùkÝ^HÌ©�Z‚]Õ®¦JeäÕdª�·V m©$Á_ÊU¨öçO¾�Š¿¥£"Úƒ>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ãÈI‘AvåÅvEƒ™ØTUL³ïè^Jt·~ iìµ,ž"wÑUxX9²ÍžÑž²·…勎–ÈtyøåÔy1%dÍh"Aÿ9Ÿ@Eá…Yí
-\ 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
+IöCf,üCA¯sàOL%7ÊìµÁ”7WÁ0îê8[Ùw¦!Vb ŽÏÐfçoRBå¥#oÍ//ÌÖ¨Qó)7œT0aìÛLMñSHjú5‡Qiì2jc
+\ 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ݶ1ŒhææjI£=^uÌ_8FFük]ÿ
-)ØmÞÉd‡‚#©>,¦l*œ,- z€CÑßg
-\ No newline at end of file
+-> ssh-ed25519 947m3A MZJNQdBmczFVatS246wLAJvj6ZojkugRszDRInvO3Gw
+FvxGl/0+DjU5UAlE7w3g6laQqEOTMHDt8tjMEGT7CBE
+-> ssh-ed25519 AaF/mQ Hn/rf5RG5TTQdOACU45UAjHCVFZgqH+XHNZ8tivszEo
+evddhLmWuD6tYZ6umIHla2Gl2Afnl4XUSVX3ge7eslI
+--- IXQPgY+qi5IRb5NXOmXdOkISv1lGaWpiv8xmkdf54A4
+WAOÔÑÄH÷[T ÝAvqÍŠ…¥OpcÍV@„©
+þéÝä\Grjí/K¤ è»ZÂ8Ÿ0Ð`ø�ÐÝ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�˜\ˆê“g›²æ�clÛK訣B`qŒ²$<SâãZã0mCžný§dÅaÝ%„
-.�K-Xwîcê‰}¸�
-\ 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>1ÍJÁ¸Cë‡êŸÆ¯ì¶<@ÿ²î¡A¥mŽ`h$5)Ô”mô’=Tð\_s®úÝK@º¯®Ö‡¡þç/ïLë~Úï2ò4ÅŠ!Á·
+\ 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
-e‹ì¼íø¿1Œúr—ãEïàͰ/¶#ìME¬qv7}}’–NÍ…ZË!±S§È°ì˜‘MÝsFãHiìfG“ŽNmS;˜(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ñšö{€Pqð€1óXQö¥Lxÿ’Ó·[|[w²8ñ;°q®Æ˜S¸PІi*éñý„’.pnXаkjãLÕ1EèÉ©w…r0Nc
+\ 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`Q50ép_§Ñ6M6}p‘‹ÚÊ2äW–¯c°3*úô™Õ†#\lR…z•h!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²°&;Ï!¨k6ÙUô�eÇVð¯—ÓU5®WЉ¥à�üï
+\ 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È–èNþ¨1vuL¤å¶+ký‡8RËâè~¯¥±Tù6Ñ{4|½9Wªfƒ˜_Ë”Š…:ô…Ë[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:>°SŠÌxåJ0 ‹ãJ,öß^âlíá²ÐÓs‡•‚Á¶ÈnÙlÏBc(ÏŠ¡¤ªÝ—^¤³’‰|øÉï 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