diff --git a/doc/guix.texi b/doc/guix.texi index 30674dab1e..f376fd2c9b 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -28452,6 +28452,8 @@ Local accounts with lower values will silently fail to authenticate. @node LDAP Services @subsection LDAP Services @cindex LDAP + +@subsubheading Authentication against LDAP with nslcd @cindex nslcd, LDAP service The @code{(gnu services authentication)} module provides the @@ -28928,6 +28930,189 @@ Defaults to @samp{()}. @c %end of generated documentation for nslcd-configuration +@subsubheading LDAP Directory Server +@cindex LDAP, server + +The @code{(gnu services ldap)} module provides the +@code{directory-server-service-type}, which can be used to create and +launch an LDAP server instance. + +Here is an example configuration of the +@code{directory-server-service-type}: + +@lisp +(use-service-modules ldap) + +... +(operating-system + ... + (services + (cons + (service directory-server-service-type + (directory-server-instance-configuration + (slapd + (slapd-configuration + (root-password "@{PBKDF2_SHA256@}AAAgAG@dots{}ABSOLUTELYSECRET"))))) + %base-services))) +@end lisp + +The root password should be generated with the @command{pwdhash} utility +that is provided by the @code{389-ds-base} package. + +Note that changes to the directory server configuration will not be +applied to existing instances. You will need to back up and restore +server data manually. Only new directory server instances will be +created upon system reconfiguration. + +@c %start of generated documentation for directory-server-instance-configuration +@deftp {Data Type} directory-server-instance-configuration +Available @code{directory-server-instance-configuration} fields are: + +@table @asis +@item @code{package} (default: @code{389-ds-base}) (type: file-like) +The @code{389-ds-base} package. + +@item @code{config-version} (default: @code{2}) (type: number) +Sets the format version of the configuration file. To use the INF file +with @command{dscreate}, this parameter must be 2. + +@item @code{full-machine-name} (default: @code{"localhost"}) (type: string) +Sets the fully qualified hostname (FQDN) of this system. + +@item @code{selinux} (default: @code{#false}) (type: boolean) +Enables SELinux detection and integration during the installation of +this instance. If set to @code{#true}, @command{dscreate} auto-detects +whether SELinux is enabled. + +@item @code{strict-host-checking} (default: @code{#true}) (type: boolean) +Sets whether the server verifies the forward and reverse record set in +the @code{full-machine-name} parameter. When installing this instance with +GSSAPI authentication behind a load balancer, set this parameter to +@code{#false}. + +@item @code{systemd} (default: @code{#false}) (type: boolean) +Enables systemd platform features. If set to @code{#true}, +@command{dscreate} auto-detects whether systemd is installed. + +@item @code{slapd} (type: slapd-configuration) +Configuration of slapd. + +@deftp {Data Type} slapd-configuration +Available @code{slapd-configuration} fields are: + +@table @asis +@item @code{instance-name} (default: @code{"localhost"}) (type: string) +Sets the name of the instance. You can refer to this value in other +parameters of this INF file using the @code{@{instance_name@}} variable. +Note that this name cannot be changed after the installation! + +@item @code{user} (default: @code{"dirsrv"}) (type: string) +Sets the user name the ns-slapd process will use after the service +started. + +@item @code{group} (default: @code{"dirsrv"}) (type: string) +Sets the group name the ns-slapd process will use after the service +started. + +@item @code{port} (default: @code{389}) (type: number) +Sets the TCP port the instance uses for LDAP connections. + +@item @code{secure-port} (default: @code{636}) (type: number) +Sets the TCP port the instance uses for TLS-secured LDAP connections +(LDAPS). + +@item @code{root-dn} (default: @code{"cn=Directory Manager"}) (type: string) +Sets the @dfn{Distinquished Name} (DN) of the administrator account for this +instance. + +@item @code{root-password} (default: @code{"@{invalid@}YOU-SHOULD-CHANGE-THIS"}) (type: string) +Sets the password of the account specified in the @code{root-dn} +parameter. You can either set this parameter to a plain text password +@command{dscreate} hashes during the installation or to a +"@{algorithm@}hash" string generated by the @command{pwdhash} utility. +Note that setting a plain text password can be a security risk if +unprivileged users can read this INF file! + +@item @code{self-sign-cert} (default: @code{#true}) (type: boolean) +Sets whether the setup creates a self-signed certificate and enables TLS +encryption during the installation. This is not suitable for +production, but it enables administrators to use TLS right after the +installation. You can replace the self-signed certificate with a +certificate issued by a certificate authority. + +@item @code{self-sign-cert-valid-months} (default: @code{24}) (type: number) +Set the number of months the issued self-signed certificate will be +valid. + +@item @code{backup-dir} (default: @code{"/var/lib/dirsrv/slapd-@{instance_name@}/bak"}) (type: string) +Set the backup directory of the instance. + +@item @code{cert-dir} (default: @code{"/etc/dirsrv/slapd-@{instance_name@}"}) (type: string) +Sets the directory of the instance's Network Security Services (NSS) +database. + +@item @code{config-dir} (default: @code{"/etc/dirsrv/slapd-@{instance_name@}"}) (type: string) +Sets the configuration directory of the instance. + +@item @code{db-dir} (default: @code{"/var/lib/dirsrv/slapd-@{instance_name@}/db"}) (type: string) +Sets the database directory of the instance. + +@item @code{initconfig-dir} (default: @code{"/etc/dirsrv/registry"}) (type: string) +Sets the directory of the operating system's rc configuration directory. + +@item @code{ldif-dir} (default: @code{"/var/lib/dirsrv/slapd-@{instance_name@}/ldif"}) (type: string) +Sets the LDIF export and import directory of the instance. + +@item @code{lock-dir} (default: @code{"/var/lock/dirsrv/slapd-@{instance_name@}"}) (type: string) +Sets the lock directory of the instance. + +@item @code{log-dir} (default: @code{"/var/log/dirsrv/slapd-@{instance_name@}"}) (type: string) +Sets the log directory of the instance. + +@item @code{run-dir} (default: @code{"/var/run/dirsrv"}) (type: string) +Sets PID directory of the instance. + +@item @code{schema-dir} (default: @code{"/etc/dirsrv/slapd-@{instance_name@}/schema"}) (type: string) +Sets schema directory of the instance. + +@item @code{tmp-dir} (default: @code{"/tmp"}) (type: string) +Sets the temporary directory of the instance. +@end table +@end deftp + +@item @code{backend-userroot} (type: backend-userroot-configuration) +Configuration of the userroot backend. + +@deftp {Data Type} backend-userroot-configuration +Available @code{backend-userroot-configuration} fields are: + +@table @asis +@item @code{create-suffix-entry?} (default: @code{#false}) (type: boolean) +Set this parameter to @code{#true} to create a generic root node entry +for the suffix in the database. + +@item @code{require-index?} (default: @code{#false}) (type: boolean) +Set this parameter to @code{#true} to refuse unindexed searches in this +database. + +@item @code{sample-entries} (default: @code{"no"}) (type: string) +Set this parameter to @code{"yes"} to add latest version of sample +entries to this database. Or, use @code{"001003006"} to use the 1.3.6 +version sample entries. Use this option, for example, to create a +database for testing purposes. + +@item @code{suffix} (type: maybe-string) +Sets the root suffix stored in this database. If you do not set the +suffix attribute the install process will not create the backend/suffix. +You can also create multiple backends/suffixes by duplicating this +section. + +@end table +@end deftp +@end table +@end deftp +@c end of generated documentation for directory-server + @node Web Services @subsection Web Services diff --git a/gnu/local.mk b/gnu/local.mk index 60a422fd7e..aae6e23e12 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -671,6 +671,7 @@ GNU_SYSTEM_MODULES = \ %D%/services/guix.scm \ %D%/services/hurd.scm \ %D%/services/kerberos.scm \ + %D%/services/ldap.scm \ %D%/services/lightdm.scm \ %D%/services/linux.scm \ %D%/services/lirc.scm \ diff --git a/gnu/services/ldap.scm b/gnu/services/ldap.scm new file mode 100644 index 0000000000..35f2735d40 --- /dev/null +++ b/gnu/services/ldap.scm @@ -0,0 +1,317 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2018, 2019, 2022 Ricardo Wurmus +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of thye GNU General Public License +;;; along with GNU Guix. If not, see . + +(define-module (gnu services ldap) + #:use-module (gnu packages admin) + #:use-module (gnu packages openldap) + #:use-module (gnu services) + #:use-module (gnu services configuration) + #:use-module (gnu services shepherd) + #:use-module (gnu system shadow) + #:use-module (guix gexp) + #:use-module (srfi srfi-1) + #:use-module (ice-9 match) + #:use-module (ice-9 string-fun) + #:export (directory-server-service-type + directory-server-shepherd-service + + directory-server-instance-configuration + slapd-configuration + backend-configuration)) + +(define (uglify-field-name name) + (let ((str (string-map (match-lambda + (#\- #\_) + (chr chr)) + (symbol->string name)))) + (if (string-suffix? "?" str) + (substring str 0 (1- (string-length str))) + str))) +(define (serialize-field field-name val) + (format #t "~a = ~a\n" (uglify-field-name field-name) val)) +(define serialize-string serialize-field) +(define-maybe string) +(define (serialize-boolean field-name val) + (serialize-field field-name (if val "True" "False"))) +(define (serialize-number field-name val) + (serialize-field field-name (number->string val))) + + +(define-configuration slapd-configuration + (instance-name + (string "localhost") + "Sets the name of the instance. You can refer to this value in other +parameters of this INF file using the \"{instance_name}\" variable. Note that +this name cannot be changed after the installation!") + (user + (string "dirsrv") + "Sets the user name the ns-slapd process will use after the service +started.") + (group + (string "dirsrv") + "Sets the group name the ns-slapd process will use after the service +started.") + (port + (number 389) + "Sets the TCP port the instance uses for LDAP connections.") + (secure-port + (number 636) + "Sets the TCP port the instance uses for TLS-secured LDAP +connections (LDAPS).") + (root-dn + (string "cn=Directory Manager") + "Sets the Distinquished Name (DN) of the administrator account for this +instance.") + (root-password + (string "{invalid}YOU-SHOULD-CHANGE-THIS") + "Sets the password of the account specified in the \"root-dn\" parameter. +You can either set this parameter to a plain text password dscreate hashes +during the installation or to a \"{algorithm}hash\" string generated by the +pwdhash utility. Note that setting a plain text password can be a security +risk if unprivileged users can read this INF file!") + (self-sign-cert + (boolean #t) + "Sets whether the setup creates a self-signed certificate and enables TLS +encryption during the installation. This is not suitable for production, but +it enables administrators to use TLS right after the installation. You can +replace the self-signed certificate with a certificate issued by a certificate +authority.") + (self-sign-cert-valid-months + (number 24) + "Set the number of months the issued self-signed certificate will be valid.") + (backup-dir + (string "/var/lib/dirsrv/slapd-{instance_name}/bak") + "Set the backup directory of the instance.") + (cert-dir + (string "/etc/dirsrv/slapd-{instance_name}") + "Sets the directory of the instance's Network Security Services (NSS) +database.") + (config-dir + (string "/etc/dirsrv/slapd-{instance_name}") + "Sets the configuration directory of the instance.") + (db-dir + (string "/var/lib/dirsrv/slapd-{instance_name}/db") + "Sets the database directory of the instance.") + (initconfig-dir + (string "/etc/dirsrv/registry") + "Sets the directory of the operating system's rc configuration directory.") + (ldif-dir + (string "/var/lib/dirsrv/slapd-{instance_name}/ldif") + "Sets the LDIF export and import directory of the instance.") + (lock-dir + (string "/var/lock/dirsrv/slapd-{instance_name}") + "Sets the lock directory of the instance.") + (log-dir + (string "/var/log/dirsrv/slapd-{instance_name}") + "Sets the log directory of the instance.") + (run-dir + (string "/var/run/dirsrv") + "Sets PID directory of the instance.") + (schema-dir + (string "/etc/dirsrv/slapd-{instance_name}/schema") + "Sets schema directory of the instance.") + (tmp-dir + (string "/tmp") + "Sets the temporary directory of the instance.")) + +(define (serialize-slapd-configuration field-name val) + #t) + + +(define-configuration backend-userroot-configuration + (create-suffix-entry? + (boolean #false) + "Set this parameter to #true to create a generic root node entry for the +suffix in the database.") + (require-index? + (boolean #false) + "Set this parameter to #true to refuse unindexed searches in this +database.") + (sample-entries + (string "no") + "Set this parameter to \"yes\" to add latest version of sample entries to +this database. Or, use \"001003006\" to use the 1.3.6 version sample entries. +Use this option, for example, to create a database for testing purposes.") + (suffix + maybe-string + "Sets the root suffix stored in this database. If you do not set the +suffix attribute the install process will not create the backend/suffix. You +can also create multiple backends/suffixes by duplicating this section.")) + +(define (serialize-backend-userroot-configuration field-name val) + #t) + + +(define-configuration directory-server-instance-configuration + (package + (file-like 389-ds-base) + "The 389-ds-base package.") + ;; General settings + (config-version + (number 2) + "Sets the format version of the configuration file. To use the INF file +with dscreate, this parameter must be 2.") + (full-machine-name + (string "localhost") + "Sets the fully qualified hostname (FQDN) of this system.") + (selinux + (boolean #false) + "Enables SELinux detection and integration during the installation of this +instance. If set to #T, dscreate auto-detects whether SELinux is enabled.") + (strict-host-checking + (boolean #t) + "Sets whether the server verifies the forward and reverse record set in the +\"full-machine-name\" parameter. When installing this instance with GSSAPI +authentication behind a load balancer, set this parameter to #F.") + (systemd + (boolean #false) + "Enables systemd platform features. If set to #T, dscreate auto-detects +whether systemd is installed.") + (slapd + (slapd-configuration (slapd-configuration)) + "Configuration of slapd.") + (backend-userroot + (backend-userroot-configuration (backend-userroot-configuration)) + "Configuration of the userroot backend.")) + +(define (serialize-directory-server-instance-configuration x) + (format #t "[general]\n") + (serialize-configuration + x + (filter (lambda (field) + (not (member (configuration-field-name field) + '(package slapd backend-userroot)))) + directory-server-instance-configuration-fields)) + ;; Do not start instance while running dscreate. Do this later with + ;; shepherd. + (format #t "start = False\n") + (format #t "\n[slapd]\n") + (serialize-configuration + (directory-server-instance-configuration-slapd x) + slapd-configuration-fields) + (format #t "\n[backend-userroot]\n") + (serialize-configuration + (directory-server-instance-configuration-backend-userroot x) + backend-userroot-configuration-fields)) + +(define (directory-server-instance-config-file config) + "Return an LDAP directory server instance configuration file." + (let* ((slapd (directory-server-instance-configuration-slapd config)) + (instance-name (slapd-configuration-instance-name slapd))) + (plain-file + (string-append "dirsrv-" instance-name ".inf") + (with-output-to-string + (lambda () + (serialize-directory-server-instance-configuration config)))))) + +(define (directory-server-shepherd-service config) + "Return a shepherd service for an LDAP directory server with CONFIG." + (let* ((389-ds-base (directory-server-instance-configuration-package config)) + (slapd (directory-server-instance-configuration-slapd config)) + (instance-name + (slapd-configuration-instance-name slapd))) + (list (shepherd-service + (documentation "Run an 389 directory server instance.") + (provision (list (symbol-append 'directory-server- + (string->symbol instance-name)))) + (requirement '()) + (start #~(make-forkexec-constructor + (list #$(file-append 389-ds-base "/sbin/dsctl") + #$instance-name "start") + #:pid-file + (string-append + #$(slapd-configuration-run-dir slapd) + "/slapd-" #$instance-name ".pid"))) + (stop #~(make-kill-destructor)))))) + +(define (directory-server-accounts config) + (let* ((slapd (directory-server-instance-configuration-slapd config)) + (user (slapd-configuration-user slapd)) + (group (slapd-configuration-group slapd))) + (list (user-group + (name group) + (system? #true)) + (user-account + (name user) + (group group) + (system? #true) + (comment "System user for the 389 directory server") + (home-directory "/var/empty") + (shell (file-append shadow "/sbin/nologin")))))) + +(define (directory-server-activation config) + (let* ((389-ds-base (directory-server-instance-configuration-package config)) + (config-file (directory-server-instance-config-file config)) + (slapd (directory-server-instance-configuration-slapd config)) + (instance-name (slapd-configuration-instance-name slapd)) + (user (slapd-configuration-user slapd)) + (group (slapd-configuration-group slapd)) + (instantiate (lambda (proc) + (string-replace-substring + (proc slapd) "{instance_name}" instance-name))) + (config-dir (instantiate slapd-configuration-config-dir)) + (all-dirs (delete-duplicates + (map (compose dirname instantiate) + (list slapd-configuration-backup-dir + slapd-configuration-cert-dir + slapd-configuration-db-dir + slapd-configuration-ldif-dir + slapd-configuration-lock-dir + slapd-configuration-log-dir + slapd-configuration-run-dir + slapd-configuration-schema-dir))))) + ;; 389-ds-base doesn't let us update an instance configuration, so bail + ;; out when the configuration directory already exists. + #~(begin + (use-modules (ice-9 match) + (guix build utils)) + (if (file-exists? #$config-dir) + (format #t + "directory-server: Instance configuration for `~a' already exists. Skipping.\n" + #$instance-name) + (let ((owner (getpwnam #$user))) + (for-each (lambda (dir) + (mkdir-p dir) + (chown dir (passwd:uid owner) (passwd:gid owner))) + (sort '#$all-dirs string<=)) + (system* #$(file-append 389-ds-base "/sbin/dscreate") + "from-file" #$config-file)))))) + +(define directory-server-service-type + (service-type (name 'directory-server) + (extensions + (list (service-extension shepherd-root-service-type + directory-server-shepherd-service) + (service-extension activation-service-type + directory-server-activation) + (service-extension account-service-type + directory-server-accounts))) + (default-value (directory-server-instance-configuration)) + (description + "Run a directory server instance."))) + +(define (generate-directory-server-documentation) + (generate-documentation + `((directory-server-instance-configuration + ,directory-server-instance-configuration-fields + (slapd slapd-configuration) + (backend-userroot backend-userroot-configuration)) + (slapd-configuration ,slapd-configuration-fields) + (backend-userroot-configuration + ,backend-userroot-configuration-fields)) + 'directory-server-instance-configuration))