From 8490a8346b5c8207f5798be55bea1de865b0bd42 Mon Sep 17 00:00:00 2001 From: Oleg Pykhalov Date: Fri, 13 Jul 2018 11:49:13 +0300 Subject: services: Add ddclient service. * gnu/services/dns.scm (ddclient-configuration, ddclient-service-type): New variables. (uglify-field-name, serialize-field, serialize-boolean, serialize-integer, serialize-string, serialize-list, serialize-extra-options, ddclient-activation, ddclient-shepherd-service, generate-ddclient-documentation): New procedures. * doc/guix.texi (DNS Services): Document it. --- doc/guix.texi | 108 +++++++++++++++++++++++++++++++++ gnu/services/dns.scm | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 275 insertions(+), 1 deletion(-) diff --git a/doc/guix.texi b/doc/guix.texi index cd0e74a2d7..97631d52e4 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -17263,6 +17263,114 @@ When false, disable negative caching. @end table @end deftp +@subsubheading ddclient Service + +@cindex ddclient +The ddclient service described below runs the ddclient daemon, which takes +care of automatically updating DNS entries for service providers such as +@uref{https://dyn.com/dns/, Dyn}. + +The following example show instantiates the service with its default +configuration: + +@example +(service ddclient-service-type) +@end example + +Note that ddclient needs to access credentials that are stored in a +@dfn{secret file}, by default @file{/etc/ddclient/secrets} (see +@code{secret-file} below.) You are expected to create this file manually, in +an ``out-of-band'' fashion (you @emph{could} make this file part of the +service configuration, for instance by using @code{plain-file}, but it will be +world-readable @i{via} @file{/gnu/store}.) See the examples in the +@file{share/ddclient} directory of the @code{ddclient} package. + +@c %start of fragment + +Available @code{ddclient-configuration} fields are: + +@deftypevr {@code{ddclient-configuration} parameter} package ddclient +The ddclient package. + +@end deftypevr + +@deftypevr {@code{ddclient-configuration} parameter} integer daemon +The period after which ddclient will retry to check IP and domain name. + +Defaults to @samp{300}. + +@end deftypevr + +@deftypevr {@code{ddclient-configuration} parameter} boolean syslog +Use syslog for the output. + +Defaults to @samp{#t}. + +@end deftypevr + +@deftypevr {@code{ddclient-configuration} parameter} string mail +Mail to user. + +Defaults to @samp{"root"}. + +@end deftypevr + +@deftypevr {@code{ddclient-configuration} parameter} string mail-failure +Mail failed update to user. + +Defaults to @samp{"root"}. + +@end deftypevr + +@deftypevr {@code{ddclient-configuration} parameter} string pid +The ddclient PID file. + +Defaults to @samp{"/var/run/ddclient/ddclient.pid"}. + +@end deftypevr + +@deftypevr {@code{ddclient-configuration} parameter} boolean ssl +Enable SSL support. + +Defaults to @samp{#t}. + +@end deftypevr + +@deftypevr {@code{ddclient-configuration} parameter} string user +Specifies the user name or ID that is used when running ddclient +program. + +Defaults to @samp{"ddclient"}. + +@end deftypevr + +@deftypevr {@code{ddclient-configuration} parameter} string group +Group of the user who will run the ddclient program. + +Defaults to @samp{"ddclient"}. + +@end deftypevr + +@deftypevr {@code{ddclient-configuration} parameter} string secret-file +Secret file which will be appended to @file{ddclient.conf} file. This +file contains credentials for use by ddclient. You are expected to +create it manually. + +Defaults to @samp{"/etc/ddclient/secrets.conf"}. + +@end deftypevr + +@deftypevr {@code{ddclient-configuration} parameter} list extra-options +Extra options will be appended to @file{ddclient.conf} file. + +Defaults to @samp{()}. + +@end deftypevr + + +@c %end of fragment + + @node VPN Services @subsubsection VPN Services @cindex VPN (virtual private network) diff --git a/gnu/services/dns.scm b/gnu/services/dns.scm index 2c57a36b84..16bd039f59 100644 --- a/gnu/services/dns.scm +++ b/gnu/services/dns.scm @@ -1,5 +1,6 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2017 Julien Lepiller +;;; Copyright © 2018 Oleg Pykhalov ;;; ;;; This file is part of GNU Guix. ;;; @@ -45,7 +46,10 @@ zone-entry dnsmasq-service-type - dnsmasq-configuration)) + dnsmasq-configuration + + ddclient-service-type + ddclient-configuration)) ;;; ;;; Knot DNS. @@ -670,3 +674,165 @@ (compose list dnsmasq-shepherd-service)))) (default-value (dnsmasq-configuration)) (description "Run the dnsmasq DNS server."))) + + +;;; +;;; ddclient +;;; + +(define (uglify-field-name field-name) + (string-delete #\? (symbol->string field-name))) + +(define (serialize-field field-name val) + (format #t "~a=~a\n" (uglify-field-name field-name) val)) + +(define (serialize-boolean field-name val) + (serialize-field field-name (if val "yes" "no"))) + +(define (serialize-integer field-name val) + (serialize-field field-name (number->string val))) + +(define (serialize-string field-name val) + (if (and (string? val) (string=? val "")) + "" + (serialize-field field-name val))) + +(define (serialize-list field-name val) + (if (null? val) "" (serialize-field field-name (string-join val)))) + +(define (serialize-extra-options extra-options) + (string-join extra-options "\n" 'suffix)) + +(define-configuration ddclient-configuration + (ddclient + (package ddclient) + "The ddclient package.") + (daemon + (integer 300) + "The period after which ddclient will retry to check IP and domain name.") + (syslog + (boolean #t) + "Use syslog for the output.") + (mail + (string "root") + "Mail to user.") + (mail-failure + (string "root") + "Mail failed update to user.") + (pid + (string "/var/run/ddclient/ddclient.pid") + "The ddclient PID file.") + (ssl + (boolean #t) + "Enable SSL support.") + (user + (string "ddclient") + "Specifies the user name or ID that is used when running ddclient +program.") + (group + (string "ddclient") + "Group of the user who will run the ddclient program.") + (secret-file + (string "/etc/ddclient/secrets.conf") + "Secret file which will be appended to @file{ddclient.conf} file. This +file contains credentials for use by ddclient. You are expected to create it +manually.") + (extra-options + (list '()) + "Extra options will be appended to @file{ddclient.conf} file.")) + +(define (ddclient-account config) + "Return the user accounts and user groups for CONFIG." + (let ((ddclient-user (ddclient-configuration-user config)) + (ddclient-group (ddclient-configuration-group config))) + (list (user-group + (name ddclient-group) + (system? #t)) + (user-account + (name ddclient-user) + (system? #t) + (group ddclient-group) + (comment "ddclientd privilege separation user") + (home-directory (string-append "/var/run/" ddclient-user)))))) + +(define (ddclient-activation config) + "Return the activation GEXP for CONFIG." + (with-imported-modules '((guix build utils) + (ice-9 rdelim)) + #~(begin + (use-modules (guix build utils) + (ice-9 rdelim)) + (let ((ddclient-user + #$(passwd:uid (getpw (ddclient-configuration-user config)))) + (ddclient-group + #$(passwd:gid (getpw (ddclient-configuration-group config)))) + (ddclient-secret-file + #$(ddclient-configuration-secret-file config))) + ;; 'ddclient' complains about ddclient.conf file permissions, which + ;; rules out /gnu/store. Thus we copy the ddclient.conf to /etc. + (for-each (lambda (dir) + (mkdir-p dir) + (chmod dir #o700) + (chown dir ddclient-user ddclient-group)) + '("/var/cache/ddclient" "/var/run/ddclient" + "/etc/ddclient")) + (with-output-to-file "/etc/ddclient/ddclient.conf" + (lambda () + (display + (string-append + "# Generated by 'ddclient-service'.\n\n" + #$(with-output-to-string + (lambda () + (serialize-configuration config + ddclient-configuration-fields))) + (if (string-null? ddclient-secret-file) + "" + (format #f "\n\n# Appended from '~a'.\n\n~a" + ddclient-secret-file + (with-input-from-file ddclient-secret-file + read-string))))))) + (chmod "/etc/ddclient/ddclient.conf" #o600) + (chown "/etc/ddclient/ddclient.conf" + ddclient-user ddclient-group))))) + +(define (ddclient-shepherd-service config) + "Return a for ddclient with CONFIG." + (let ((ddclient (ddclient-configuration-ddclient config)) + (ddclient-pid (ddclient-configuration-pid config)) + (ddclient-user (ddclient-configuration-user config)) + (ddclient-group (ddclient-configuration-group config))) + (list (shepherd-service + (provision '(ddclient)) + (documentation "Run ddclient daemon.") + (start #~(make-forkexec-constructor + (list #$(file-append ddclient "/bin/ddclient") + "-foreground" + "-file" "/etc/ddclient/ddclient.conf") + #:pid-file #$ddclient-pid + #:environment-variables + (list "SSL_CERT_DIR=/run/current-system/profile\ +/etc/ssl/certs" + "SSL_CERT_FILE=/run/current-system/profile\ +/etc/ssl/certs/ca-certificates.crt") + #:user #$ddclient-user + #:group #$ddclient-group)) + (stop #~(make-kill-destructor)))))) + +(define ddclient-service-type + (service-type + (name 'ddclient) + (extensions + (list (service-extension account-service-type + ddclient-account) + (service-extension shepherd-root-service-type + ddclient-shepherd-service) + (service-extension activation-service-type + ddclient-activation))) + (default-value (ddclient-configuration)) + (description "Configure address updating utility for dynamic DNS services, +ddclient."))) + +(define (generate-ddclient-documentation) + (generate-documentation + `((ddclient-configuration ,ddclient-configuration-fields)) + 'ddclient-configuration)) -- cgit 1.4.1