guix-epistemia

Personal Guix channel
Log | Files | Refs

age-secret.scm (3951B)


      1 (define-module (epistemia services age-secret)
      2   #:use-module (guix gexp)
      3   #:use-module (guix records)
      4   #:use-module (gnu services)
      5   #:use-module (gnu services shepherd)
      6   #:use-module (gnu packages golang-crypto)
      7   #:use-module (ice-9 match)
      8   #:export (age-secret-service-type
      9             age-secret-configuration
     10             age-secret))
     11 
     12 (define-record-type* <age-secret-configuration>
     13   age-secret-configuration make-age-secret-configuration
     14   age-secret-configuration?
     15   (secrets age-secret-configuration-secrets
     16            (default '())))
     17 
     18 (define-record-type* <age-secret>
     19   age-secret make-age-secret
     20   age-secret?
     21   (file   age-secret-file)
     22   (name   age-secret-name)
     23   (owner  age-secret-owner (default "root"))
     24   (group  age-secret-group (default "root"))
     25   (mode   age-secret-mode  (default #o400)))
     26 
     27 (define (age-secret-activation config)
     28   (let ((secrets (age-secret-configuration-secrets config)))
     29     (with-imported-modules '((guix build utils))
     30       #~(begin
     31           (use-modules (guix build utils)
     32                        (ice-9 match)
     33                        (ice-9 format))
     34 
     35 	  ;; TODO: make the let vars configurable if I ever upstream this
     36           (let ((secret-dir "/run/secrets")
     37                 (key-path "/etc/ssh/ssh_host_ed25519_key")
     38                 (age-bin #$(file-append age "/bin/age")))
     39 
     40             (mkdir-p secret-dir)
     41             (chmod secret-dir #o711)
     42 
     43             (for-each
     44              (match-lambda
     45                ((source output-name owner-name group-name mode)
     46                 (let ((dest (string-append secret-dir "/" output-name)))
     47                   ;; decrypt if missing
     48                   (unless (file-exists? dest)
     49                     (if (file-exists? key-path)
     50                         (begin
     51                           (format #t "Decrypting secret: ~a -> ~a...~%" source dest)
     52                           (let ((status (system* age-bin "-d" "-i" key-path "-o" dest source)))
     53                             (unless (zero? status)
     54                               (format (current-error-port) "Failed to decrypt ~a.~%" output-name))))
     55                         (format (current-error-port) "Missing host key: ~a~%" key-path)))
     56 
     57                   ;; apply permissions and ownership
     58                   (when (file-exists? dest)
     59                     (catch 'system-error
     60                       (lambda ()
     61                         (let ((uid (passwd:uid (getpwnam owner-name)))
     62                               (gid (group:gid (getgrnam group-name))))
     63                           (chown dest uid gid)
     64                           (chmod dest mode)))
     65                       (lambda args
     66                         (format (current-error-port)
     67                                 "Failed to set owner ~a:~a on ~a: ~a~%"
     68                                 owner-name group-name dest (strerror (system-error-errno args)))))))))
     69              
     70              ;; serialize record list for gexp
     71              '#$(map (lambda (s)
     72                        (list (age-secret-file s)
     73                              (age-secret-name s)
     74                              (age-secret-owner s)
     75                              (age-secret-group s)
     76                              (age-secret-mode s)))
     77                      secrets)))))))
     78 
     79 (define (age-secret-shepherd-service config)
     80   (list (shepherd-service
     81           (provision '(age-secret))
     82           (requirement '())
     83           (one-shot? #t)
     84           (start #~(lambda () #t))
     85           (documentation "Age secret decryption marker service."))))
     86 
     87 (define age-secret-service-type
     88   (service-type
     89     (name 'age-secret)
     90     (extensions
     91      (list (service-extension activation-service-type
     92                               age-secret-activation)
     93            (service-extension shepherd-root-service-type
     94                               age-secret-shepherd-service)))
     95     (description
     96      "Decrypts age-encrypted files at boot using host keys and assigns ownership.")
     97     (default-value (age-secret-configuration))))