(use-modules (guix gexp) (guix modules) (guix records) (gnu home services) (gnu home services mcron) (gnu services) (gnu services mcron) (ice-9 match) (ice-9 receive) (ice-9 textual-ports) (json) (rnrs bytevectors) (srfi srfi-1) (web client) (web uri)) (define dnsimple-url "https://api.dnsimple.com/v2") (define (dnsimple-whoami headers) (receive (header body) (http-get (string->uri (string-append dnsimple-url "/whoami")) #:headers headers) (fold (lambda (a b) (assoc-ref b a)) (json-string->scm (utf8->string body)) '("data" "account" "id")))) (define (get-root-url domain headers) (string-join (list dnsimple-url (number->string (dnsimple-whoami headers)) "zones" domain "records") "/")) (define (dnsimple-get-records root-url headers ignore-patterns) (filter-map (lambda (record) (and (not (any (lambda (ignore-pattern) (match ignore-pattern ((key . value) (equal? value (assoc-ref record key))))) ignore-patterns)) (map (lambda (ass) (if (and (equal? (car ass) "content") (equal? (assoc-ref record "type") "TXT")) (cons k (read (open-input-string (assoc-ref record "content")))) ass)) record))) (receive (header body) (http-get (string->uri root-url) #:headers headers) (vector->list (assoc-ref (json-string->scm (utf8->string body)) "data"))))) (define (duplicate-record-pred record-list) (lambda (record) (any (lambda (record2) (every (lambda (field) (equal? (assoc-ref record1 field) (assoc-ref record2 field))) '("type" "content" "name"))) record-list))) (define (dnsimple-delete-record root-url headers record) (http-delete (string->uri (string-append root-url "/" (number->string (assoc-ref record "id")))) #:headers headers)) (define (dnsimple-post-record root-url headers record) (http-post (string->uri root-url) #:headers headers #:body (string->utf8 (scm->json-string record)))) (define (dnsimple-update-records api-key-file domain get-records ignore-patterns) (let* ((headers (list (cons 'Authorization (string-append "Bearer " (get-line (open-input-file api-key-file)))) (cons 'Content-Type "application/json"))) (root-url (get-root-url domain headers)) (new-records (get-records)) (old-records (dnsimple-get-records root-url headers ignore-patterns))) (for-each (lambda (record) (dnsimple-post-record root-url headers record)) (remove (duplicate-record-pred old-records) new-records)) (display (remove (duplicate-record-pred new-records) old-records)) (for-each (lambda (record) (dnsimple-delete-record root-url headers record)) (remove (duplicate-record-pred new-records) old-records)))) (define-record-type* ddns-configuration make-ddns-configuration ddns-configuration? (api-key-file ddns-configuration-api-key-file) (domain ddns-configuration-domain) (get-records ddns-configuration-get-records) (filter ddns-configuration-filter)) (define ddns-mcron-service (match-record-lambda (api-key-file domain get-records filter) (list #~(job '(next-minute (range 0 60 10)) (lambda () (#$dnsimple-update-records #$api-key-file #$domain get-records #$filter)))))) (define ddns-service-type (service-type (name 'ddns) (description "automatically update dns entries") (extensions (list (service-extension mcron-service-type ddns-mcron-service))))) (define home-ddns-service-type (service-type (inherit(system->home-service-type ddns-service-type)) (extensions (list (service-extension home-mcron-service-type ddns-mcron-service))))) ; library side ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; application side (define (get-records) `((("type" . "A") ("name" . "") ("content" . ,(get-line (open-input-file "/home/"))) ("ttl" . 600)) (("type" . "TXT") ("name" . "_dmarc.cchae.us") ("content" . "v=DMARC1;p=reject;pct=100;rua=mailto:postmaster@zacchae.us") ("ttl" . 3600)) (("type" . "TXT") ("name" . "my_ed_sel._domainkey.zacchae.us") ("content" . "k=ed25519 p=e4sV1Nfd1IVpVD6LSn7AbFczV0N6geZ6ALMgh18jjog=") ("ttl" . 3600)) (("type" . "TXT") ("name" . "my_rsa_sel._domainkey.zacchae.us") ("content" . "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu2FyBwMXSINLrLbKRz+cMWE42hFBA1u4+IVLkg6LJd5xQpegOd7XOxnZ1/f29UGc+M23lkUmrVaicSujEkky4noks0gg9yruHFAUI/hocgrJN8SMsTTF/X1J9hdVKf2MZ8BW6cqZPhTEFUuZzNJiSWl8HWjLr2RMWKlm+8L71yrzuk5tnMUhLg7l4/kfThvHTIP/nlOitDw+B/erEhg1zy915hdyq/rBlgqp3D+wnXFHeVFwGZ1ekx+XAjorIH2XvXypTmxPpZN13lqvo4jxJRADeFHNv3RRxH7hxEuy9t6U2oxDkT4ETVKWGuuEuHrI4Lu0Gc8wQkB0kSFibY9K5QIDAQAB") ("ttl" . 3600)) (("type" . "TXT") ("name" . "") ("content" . "v=spf1 a -all") ("ttl" . 3600)))) ;(dnsimple-update-records "/home/zacchae/.config/guix/dnsimple" ; "cchae.us" ; get-records ; '(("type" . "NS") ("type" . "SOA"))) (use-modules (gnu home) (gnu home services shells)) (home-environment (services (list (service home-ddns-service-type (ddns-configuration (api-key-file "/home//.config/guix/dnsimple") (domain "") (get-records get-records) (filter '(("type" . "NS") ("type" . "SOA"))))) (service home-zsh-service-type))))