From mboxrd@z Thu Jan 1 00:00:00 1970 From: =?UTF-8?q?Cl=C3=A9ment=20Lassieur?= Subject: [PATCH] gnu: Add Prosody service. Date: Wed, 4 Jan 2017 22:55:59 +0100 Message-ID: <20170104215559.3295-1-clement@lassieur.org> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:47148) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cOtYy-000091-UZ for guix-devel@gnu.org; Wed, 04 Jan 2017 16:57:45 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cOtYt-00058S-Ub for guix-devel@gnu.org; Wed, 04 Jan 2017 16:57:41 -0500 Received: from mail.lassieur.org ([83.152.10.219]:44020) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1cOtYs-00057j-O7 for guix-devel@gnu.org; Wed, 04 Jan 2017 16:57:35 -0500 Received: from localhost.localdomain (rke75-1-81-57-64-9.fbx.proxad.net [81.57.64.9]) by mail.lassieur.org (Postfix) with ESMTPSA id 12019640116 for ; Wed, 4 Jan 2017 22:57:28 +0100 (CET) List-Id: "Development of GNU Guix and the GNU System distribution." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-devel-bounces+gcggd-guix-devel=m.gmane.org@gnu.org Sender: "Guix-devel" To: guix-devel@gnu.org * gnu/services/messaging.scm: New file. * gnu/services/configuration.scm: New exported procedures. * gnu/packages/messaging.scm (prosody)[arguments]: Add 'fix-makefile' phase. In 'wrap-programs' phase, add 'openssl' and 'coreutils' to 'PATH'. * gnu/local.mk (GNU_SYSTEM_MODULES): Add gnu/services/messaging.scm. * doc/guix.texi (Messaging Services): New section. --- doc/guix.texi | 393 +++++++++++++++++++++- gnu/local.mk | 1 + gnu/packages/messaging.scm | 48 ++- gnu/services/configuration.scm | 3 + gnu/services/messaging.scm | 728 +++++++++++++++++++++++++++++++++++= ++++++ 5 files changed, 1157 insertions(+), 16 deletions(-) create mode 100644 gnu/services/messaging.scm diff --git a/doc/guix.texi b/doc/guix.texi index adc7fefca..1a092d169 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -29,7 +29,8 @@ Copyright @copyright{} 2016 John Darrington@* Copyright @copyright{} 2016 ng0@* Copyright @copyright{} 2016 Jan Nieuwenhuizen@* Copyright @copyright{} 2016 Julien Lepiller@* -Copyright @copyright{} 2016 Alex ter Weele +Copyright @copyright{} 2016 Alex ter Weele@* +Copyright @copyright{} 2017 Cl=C3=A9ment Lassieur =20 Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or @@ -204,6 +205,7 @@ Services * Desktop Services:: D-Bus and desktop services. * Database Services:: SQL databases. * Mail Services:: IMAP, POP3, SMTP, and all that. +* Messaging Services:: Messaging services. * Kerberos Services:: Kerberos services. * Web Services:: Web servers. * Network File System:: NFS related services. @@ -8094,6 +8096,7 @@ declaration. * Desktop Services:: D-Bus and desktop services. * Database Services:: SQL databases. * Mail Services:: IMAP, POP3, SMTP, and all that. +* Messaging Services:: Messaging services. * Kerberos Services:: Kerberos services. * Web Services:: Web servers. * Network File System:: NFS related services. @@ -11708,6 +11711,394 @@ remote servers. Run @command{man smtpd.conf} f= or more information. @end table @end deftp =20 +@node Messaging Services +@subsubsection Messaging Services + +@cindex messaging +@cindex jabber +@cindex XMPP +The @code{(gnu services messaging)} module provides Guix service +definitions for messaging services: currently only Prosody is supported. + +@subsubheading Prosody Service + +@deffn {Scheme Variable} prosody-service-type +This is the type for the @uref{http://prosody.im, Prosody XMPP +communication server}. Its value must be a @code{prosody-configuration} +record as in this example: + +@example +(service prosody-service-type + (prosody-configuration + (modules-enabled (cons "groups" %default-modules-enabled)) + (int-components + (list + (int-component-configuration + (hostname "conference.example.net") + (plugin "muc") + (mod-muc (mod-muc-configuration))))) + (virtualhosts + (list + (virtualhost-configuration + (domain "example.net")))))) +@end example + +See below for details about @code{prosody-configuration}. + +@end deffn + +By default, Prosody does not need much configuration. Only one +@code{virtualhosts} field is needed: it specifies the domain you wish +Prosody to serve. + +Prosodyctl will help you generate X.509 certificates and keys: + +@example +prosodyctl cert request example.net +@end example + +The available configuration parameters follow. Each parameter +definition is preceded by its type; for example, @samp{string-list foo} +indicates that the @code{foo} parameter should be specified as a list of +strings. Types starting with @code{maybe-} denote parameters that won't +show up in @code{prosody.cfg.lua} when their value is @code{'disabled}. + +There is also a way to specify the configuration as a string, if you +have an old @code{prosody.cfg.lua} file that you want to port over from +some other system; see the end for more details. + +@c The following documentation was initially generated by +@c (generate-documentation) in (gnu services messaging). Manually maint= ained +@c documentation is better, so we shouldn't hesitate to edit below as +@c needed. However if the change you want to make to this documentation +@c can be done in an automated way, it's probably easier to change +@c (generate-documentation) than to make it below and have to deal with +@c the churn as Prosody updates. + +Available @code{prosody-configuration} fields are: + +@deftypevr {@code{prosody-configuration} parameter} package prosody +The Prosody package. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} file-name data-path +Location of the Prosody data storage directory. See +@url{http://prosody.im/doc/configure}. +Defaults to @samp{"/var/lib/prosody"}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} file-name-list plugi= n-paths +Additional plugin directories. They are searched in all the specified +paths in order. See @url{http://prosody.im/doc/plugins_directory}. +Defaults to @samp{()}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} string-list admins +This is a list of accounts that are admins for the server. Note that yo= u +must create the accounts separately. See @url{http://prosody.im/doc/adm= ins} and +@url{http://prosody.im/doc/creating_accounts}. +Example: @code{(admins '("user1@@example.com" "user2@@example.net"))} +Defaults to @samp{()}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} boolean use-libevent= ? +Enable use of libevent for better performance under high load. See +@url{http://prosody.im/doc/libevent}. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} module-list modules-= enabled +This is the list of modules Prosody will load on startup. It looks for +@code{mod_modulename.lua} in the plugins folder, so make sure that exist= s too. +Documentation on modules can be found at: @url{http://prosody.im/doc/mod= ules}. +Defaults to @samp{%default-modules-enabled}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} string-list modules-= disabled +@samp{"offline"}, @samp{"c2s"} and @samp{"s2s"} are auto-loaded, but +should you want to disable them then add them to this list. +Defaults to @samp{()}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} file-name groups-fil= e +Path to a text file where the shared groups are defined. If this path i= s +empty then @samp{mod_groups} does nothing. See +@url{http://prosody.im/doc/modules/mod_groups}. +Defaults to @samp{"/var/lib/prosody/sharedgroups.txt"}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} boolean allow-regist= ration? +Disable account creation by default, for security. See +@url{http://prosody.im/doc/creating_accounts}. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} maybe-ssl-configurat= ion ssl +These are the SSL/TLS-related settings. Most of them are disabled so to +use Prosody's defaults. If you do not completely understand these optio= ns, do +not add them to your config, it is easy to lower the security of your se= rver +using them. See @url{http://prosody.im/doc/advanced_ssl_config}. + +Available @code{ssl-configuration} fields are: + +@deftypevr {@code{ssl-configuration} parameter} maybe-string protocol +This determines what handshake to use. +@end deftypevr + +@deftypevr {@code{ssl-configuration} parameter} file-name key +Path to your private key file, relative to @code{/etc/prosody}. +Defaults to @samp{"/etc/prosody/certs/key.pem"}. +@end deftypevr + +@deftypevr {@code{ssl-configuration} parameter} file-name certificate +Path to your certificate file, relative to @code{/etc/prosody}. +Defaults to @samp{"/etc/prosody/certs/cert.pem"}. +@end deftypevr + +@deftypevr {@code{ssl-configuration} parameter} file-name capath +Path to directory containing root certificates that you wish Prosody to +trust when verifying the certificates of remote servers. +Defaults to @samp{"/etc/ssl/certs"}. +@end deftypevr + +@deftypevr {@code{ssl-configuration} parameter} maybe-file-name cafile +Path to a file containing root certificates that you wish Prosody to tru= st. +Similar to @code{capath} but with all certificates concatenated together= . +@end deftypevr + +@deftypevr {@code{ssl-configuration} parameter} maybe-string-list verify +A list of verification options (these mostly map to OpenSSL's +@code{set_verify()} flags). +@end deftypevr + +@deftypevr {@code{ssl-configuration} parameter} maybe-string-list option= s +A list of general options relating to SSL/TLS. These map to OpenSSL's +@code{set_options()}. For a full list of options available in LuaSec, s= ee the +LuaSec source. +@end deftypevr + +@deftypevr {@code{ssl-configuration} parameter} maybe-non-negative-integ= er depth +How long a chain of certificate authorities to check when looking for a +trusted root certificate. +@end deftypevr + +@deftypevr {@code{ssl-configuration} parameter} maybe-string ciphers +An OpenSSL cipher string. This selects what ciphers Prosody will offer = to +clients, and in what order. +@end deftypevr + +@deftypevr {@code{ssl-configuration} parameter} maybe-file-name dhparam +A path to a file containing parameters for Diffie-Hellman key exchange. = You +can create such a file with: +@code{openssl dhparam -out /etc/prosody/certs/dh-2048.pem 2048} +@end deftypevr + +@deftypevr {@code{ssl-configuration} parameter} maybe-string curve +Curve for Elliptic curve Diffie-Hellman. Prosody's default is +@samp{"secp384r1"}. +@end deftypevr + +@deftypevr {@code{ssl-configuration} parameter} maybe-string-list verify= ext +A list of "extra" verification options. +@end deftypevr + +@deftypevr {@code{ssl-configuration} parameter} maybe-string password +Password for encrypted private keys. +@end deftypevr + +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} boolean c2s-require-= encryption? +Whether to force all client-to-server connections to be encrypted or not= . +See @url{http://prosody.im/doc/modules/mod_tls}. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} boolean s2s-require-= encryption? +Whether to force all server-to-server connections to be encrypted or not= . +See @url{http://prosody.im/doc/modules/mod_tls}. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} boolean s2s-secure-a= uth? +Whether to require encryption and certificate authentication. This +provides ideal security, but requires servers you communicate with to su= pport +encryption AND present valid, trusted certificates. See +@url{http://prosody.im/doc/s2s#security}. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} string-list s2s-inse= cure-domains +Many servers don't support encryption or have invalid or self-signed +certificates. You can list domains here that will not be required to +authenticate using certificates. They will be authenticated using DNS. = See +@url{http://prosody.im/doc/s2s#security}. +Defaults to @samp{()}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} string-list s2s-secu= re-domains +Even if you leave @code{s2s-secure-auth?} disabled, you can still requir= e +valid certificates for some domains by specifying a list here. See +@url{http://prosody.im/doc/s2s#security}. +Defaults to @samp{()}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} string authenticatio= n +Select the authentication backend to use. The default provider stores +passwords in plaintext and uses Prosody's configured data storage to sto= re the +authentication data. If you do not trust your server please see +@url{http://prosody.im/doc/modules/mod_auth_internal_hashed} for informa= tion +about using the hashed backend. See also +@url{http://prosody.im/doc/authentication} +Defaults to @samp{"internal_plain"}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} maybe-string log +Set logging options. Advanced logging configuration is not yet supporte= d +by the GuixSD Prosody Service. See @url{http://prosody.im/doc/logging}. +Defaults to @samp{"*syslog"}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} file-name pidfile +File to write pid in. See @url{http://prosody.im/doc/modules/mod_posix}= . +Defaults to @samp{"/var/run/prosody/prosody.pid"}. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} virtualhost-configur= ation-list virtualhosts +A host in Prosody is a domain on which user accounts can be created. Fo= r +example if you want your users to have addresses like +@samp{"john.smith@@example.com"} then you need to add a host +@samp{"example.com"}. All options in this list will apply only to this = host. + +Note: the name "virtual" host is used in configuration to avoid confusio= n with +the actual physical host that Prosody is installed on. A single Prosody +instance can serve many domains, each one defined as a VirtualHost entry= in +Prosody's configuration. Conversely a server that hosts a single domain= would +have just one VirtualHost entry. + +See @url{http://prosody.im/doc/configure#virtual_host_settings}. + +Available @code{virtualhost-configuration} fields are: + +all these @code{prosody-configuration} fields: @code{admins}, @code{use-= libevent?}, @code{modules-enabled}, @code{modules-disabled}, @code{groups= -file}, @code{allow-registration?}, @code{ssl}, @code{c2s-require-encrypt= ion?}, @code{s2s-require-encryption?}, @code{s2s-secure-auth?}, @code{s2s= -insecure-domains}, @code{s2s-secure-domains}, @code{authentication}, @co= de{log}, plus: +@deftypevr {@code{virtualhost-configuration} parameter} string domain +Domain you wish Prosody to serve. +@end deftypevr + +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} int-component-config= uration-list int-components +Components are extra services on a server which are available to clients= , +usually on a subdomain of the main server (such as +@samp{"mycomponent.example.com"}). Example components might be chatroom +servers, user directories, or gateways to other protocols. + +Internal components are implemented with Prosody-specific plugins. To a= dd an +internal component, you simply fill the hostname field, and the plugin y= ou wish +to use for the component. + +See @url{http://prosody.im/doc/components}. +Defaults to @samp{()}. + +Available @code{int-component-configuration} fields are: + +all these @code{prosody-configuration} fields: @code{admins}, @code{use-= libevent?}, @code{modules-enabled}, @code{modules-disabled}, @code{groups= -file}, @code{allow-registration?}, @code{ssl}, @code{c2s-require-encrypt= ion?}, @code{s2s-require-encryption?}, @code{s2s-secure-auth?}, @code{s2s= -insecure-domains}, @code{s2s-secure-domains}, @code{authentication}, @co= de{log}, plus: +@deftypevr {@code{int-component-configuration} parameter} string hostnam= e +Hostname of the component. +@end deftypevr + +@deftypevr {@code{int-component-configuration} parameter} string plugin +Plugin you wish to use for the component. +@end deftypevr + +@deftypevr {@code{int-component-configuration} parameter} maybe-mod-muc-= configuration mod-muc +Multi-user chat (MUC) is Prosody's module for allowing you to create +hosted chatrooms/conferences for XMPP users. + +General information on setting up and using MUC chatrooms can be found i= n our +"Chatrooms" documentation (@url{http://prosody.im/doc/chatrooms}), which= you +should read if you are new to XMPP chatrooms. + +See also @url{http://prosody.im/doc/modules/mod_muc}. + +Available @code{mod-muc-configuration} fields are: + +@deftypevr {@code{mod-muc-configuration} parameter} string name +The name to return in service discovery responses. +Defaults to @samp{"Prosody Chatrooms"}. +@end deftypevr + +@deftypevr {@code{mod-muc-configuration} parameter} string-or-boolean re= strict-room-creation +If @samp{#t}, this will only allow admins to create new chatrooms. +Otherwise anyone can create a room. The value @samp{"local"} restricts = room +creation to users on the service's parent domain. E.g. @samp{user@@exam= ple.com} +can create rooms on @samp{rooms.example.com}. The value @samp{"admin"} +restricts to service administrators only. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{mod-muc-configuration} parameter} non-negative-integer= max-history-messages +Maximum number of history messages that will be sent to the member that = has +just joined the room. +Defaults to @samp{20}. +@end deftypevr + +@end deftypevr + +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} ext-component-config= uration-list ext-components +External components use XEP-0114, which most standalone components +support. To add an external component, you simply fill the hostname fie= ld. See +@url{http://prosody.im/doc/components}. +Defaults to @samp{()}. + +Available @code{ext-component-configuration} fields are: + +all these @code{prosody-configuration} fields: @code{admins}, @code{use-= libevent?}, @code{modules-enabled}, @code{modules-disabled}, @code{groups= -file}, @code{allow-registration?}, @code{ssl}, @code{c2s-require-encrypt= ion?}, @code{s2s-require-encryption?}, @code{s2s-secure-auth?}, @code{s2s= -insecure-domains}, @code{s2s-secure-domains}, @code{authentication}, @co= de{log}, plus: +@deftypevr {@code{ext-component-configuration} parameter} string compone= nt-secret +Password which the component will use to log in. +@end deftypevr + +@deftypevr {@code{ext-component-configuration} parameter} string hostnam= e +Hostname of the component. +@end deftypevr + +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} non-negative-integer= -list component-ports +Port(s) Prosody listens on for component connections. +@end deftypevr + +@deftypevr {@code{prosody-configuration} parameter} string component-int= erface +Interface Prosody listens on for component connections. +Defaults to @samp{"127.0.0.1"}. +@end deftypevr + +It could be that you just want to get a @code{prosody.cfg.lua} +up and running. In that case, you can pass an +@code{opaque-prosody-configuration} record as the value of +@code{prosody-service-type}. As its name indicates, an opaque configura= tion +does not have easy reflective capabilities. +Available @code{opaque-prosody-configuration} fields are: + +@deftypevr {@code{opaque-prosody-configuration} parameter} package proso= dy +The prosody package. +@end deftypevr + +@deftypevr {@code{opaque-prosody-configuration} parameter} string prosod= y.cfg.lua +The contents of the @code{prosody.cfg.lua} to use. +@end deftypevr + +For example, if your @code{prosody.cfg.lua} is just the empty +string, you could instantiate a prosody service like this: + +@example +(service prosody-service-type + (opaque-prosody-configuration + (prosody.cfg.lua ""))) +@end example + @node Kerberos Services @subsubsection Kerberos Services @cindex Kerberos diff --git a/gnu/local.mk b/gnu/local.mk index 2e714ac28..86aad8e76 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -414,6 +414,7 @@ GNU_SYSTEM_MODULES =3D \ %D%/services/lirc.scm \ %D%/services/mail.scm \ %D%/services/mcron.scm \ + %D%/services/messaging.scm \ %D%/services/networking.scm \ %D%/services/nfs.scm \ %D%/services/shepherd.scm \ diff --git a/gnu/packages/messaging.scm b/gnu/packages/messaging.scm index 95c265b98..6f1025d36 100644 --- a/gnu/packages/messaging.scm +++ b/gnu/packages/messaging.scm @@ -7,7 +7,7 @@ ;;; Copyright =C2=A9 2015 Efraim Flashner ;;; Copyright =C2=A9 2016 ng0 ;;; Copyright =C2=A9 2016 Andy Patterson -;;; Copyright =C2=A9 2016 Cl=C3=A9ment Lassieur +;;; Copyright =C2=A9 2016, 2017 Cl=C3=A9ment Lassieur ;;; ;;; This file is part of GNU Guix. ;;; @@ -39,6 +39,7 @@ #:use-module (gnu packages aidc) #:use-module (gnu packages autotools) #:use-module (gnu packages avahi) + #:use-module (gnu packages base) #:use-module (gnu packages check) #:use-module (gnu packages crypto) #:use-module (gnu packages cyrus-sasl) @@ -535,6 +536,14 @@ end-to-end encryption support; XML console.") (substitute* "configure" (("exit 1") "")) #t)) + (add-after 'unpack 'fix-makefile + (lambda _ + (substitute* "Makefile" + ;; prosodyctl needs to read the configuration file. + (("^INSTALLEDCONFIG =3D.*") "INSTALLEDCONFIG =3D /etc/pro= sody\n") + ;; prosodyctl needs a place to put auto-generated certifi= cates. + (("^INSTALLEDDATA =3D.*") "INSTALLEDDATA =3D /var/lib/pro= sody\n")) + #t)) (add-after 'install 'wrap-programs (lambda* (#:key inputs outputs #:allow-other-keys) ;; Make sure all executables in "bin" find the required Lua @@ -546,27 +555,36 @@ end-to-end encryption support; XML console.") (if (string-prefix? "lua" = label) directory #f))) inputs))) - (path (string-join - (map (lambda (path) - (string-append path "/share/lua/5.1/?= .lua;" - path "/share/lua/5.1/?= /?.lua")) - (cons out deps)) - ";")) - (cpath (string-join - (map (lambda (path) - (string-append path "/lib/lua/5.1/?.s= o;" - path "/lib/lua/5.1/?/?= .so")) - (cons out deps)) - ";"))) + (lua-path (string-join + (map (lambda (path) + (string-append + path "/share/lua/5.1/?.lua;" + path "/share/lua/5.1/?/?.lua")) + (cons out deps)) + ";")) + (lua-cpath (string-join + (map (lambda (path) + (string-append + path "/lib/lua/5.1/?.so;" + path "/lib/lua/5.1/?/?.so")) + (cons out deps)) + ";")) + (openssl (assoc-ref inputs "openssl")) + (coreutils (assoc-ref inputs "coreutils")) + (path (map (lambda (dir) + (string-append dir "/bin")) + (list openssl coreutils)))) (for-each (lambda (file) (wrap-program file - `("LUA_PATH" ";" =3D (,path)) - `("LUA_CPATH" ";" =3D (,cpath)))) + `("LUA_PATH" ";" =3D (,lua-path)) + `("LUA_CPATH" ";" =3D (,lua-cpath)) + `("PATH" ":" prefix ,path))) (find-files bin ".*")) #t)))))) (inputs `(("libidn" ,libidn) ("openssl" ,openssl) + ("coreutils" ,coreutils) ; For chmod in certificate generation. ("lua" ,lua-5.1) ("lua5.1-expat" ,lua5.1-expat) ("lua5.1-socket" ,lua5.1-socket) diff --git a/gnu/services/configuration.scm b/gnu/services/configuration.= scm index 94c5f2155..a98db64fa 100644 --- a/gnu/services/configuration.scm +++ b/gnu/services/configuration.scm @@ -28,10 +28,13 @@ #:use-module (srfi srfi-35) #:export (configuration-field configuration-field-name + configuration-field-type configuration-missing-field configuration-field-error configuration-field-serializer configuration-field-getter + configuration-field-default-value-thunk + configuration-field-documentation serialize-configuration define-configuration validate-configuration diff --git a/gnu/services/messaging.scm b/gnu/services/messaging.scm new file mode 100644 index 000000000..833b64514 --- /dev/null +++ b/gnu/services/messaging.scm @@ -0,0 +1,728 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright =C2=A9 2017 Cl=C3=A9ment Lassieur +;;; +;;; 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 (a= t +;;; 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 the GNU General Public License +;;; along with GNU Guix. If not, see . + +(define-module (gnu services messaging) + #:use-module (gnu packages messaging) + #:use-module (gnu packages admin) + #:use-module (gnu services) + #:use-module (gnu services shepherd) + #:use-module (gnu services configuration) + #:use-module (gnu system shadow) + #:use-module (guix gexp) + #:use-module (guix records) + #:use-module (guix packages) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-35) + #:use-module (ice-9 match) + #:export (prosody-service-type + prosody-configuration + opaque-prosody-configuration + + virtualhost-configuration + int-component-configuration + ext-component-configuration + + mod-muc-configuration + ssl-configuration + + %default-modules-enabled)) + +;;; Commentary: +;;; +;;; Messaging services. +;;; +;;; Code: + +(define (id ctx . parts) + (datum->syntax ctx (apply symbol-append (map syntax->datum parts)))) + +(define-syntax define-maybe + (lambda (x) + (syntax-case x () + ((_ stem) + (with-syntax + ((stem? (id #'stem #'stem #'?)) + (maybe-stem? (id #'stem #'maybe- #'stem #'?)) + (serialize-stem (id #'stem #'serialize- #'stem)) + (serialize-maybe-stem (id #'stem #'serialize-maybe- #'stem))= ) + #'(begin + (define (maybe-stem? val) + (or (eq? val 'disabled) (stem? val))) + (define (serialize-maybe-stem field-name val) + (when (stem? val) (serialize-stem field-name val))))))))) + +(define-syntax define-all-configurations + (lambda (stx) + (define (make-pred arg) + (lambda (field target) + (and (memq (syntax->datum target) `(common ,arg)) field))) + (syntax-case stx () + ((_ stem (field (field-type def) doc target) ...) + (with-syntax (((new-field-type ...) + (map (lambda (field-type target) + (if (and (eq? 'common (syntax->datum target= )) + (not (string-prefix? + "maybe-" + (symbol->string + (syntax->datum field-type))= ))) + (id #'stem #'maybe- field-type) field-t= ype)) + #'(field-type ...) #'(target ...))) + ((new-def ...) + (map (lambda (def target) + (if (eq? 'common (syntax->datum target)) + #''disabled def)) + #'(def ...) #'(target ...))) + ((new-doc ...) + (map (lambda (doc target) + (if (eq? 'common (syntax->datum target)) + "" doc)) + #'(doc ...) #'(target ...)))) + #`(begin + (define common-fields + '(#,@(filter-map (make-pred #f) #'(field ...) #'(target .= ..)))) + (define-configuration prosody-configuration + #,@(filter-map (make-pred 'global) + #'((field (field-type def) doc) ...) + #'(target ...))) + (define-configuration virtualhost-configuration + #,@(filter-map (make-pred 'virtualhost) + #'((field (new-field-type new-def) new-doc= ) ...) + #'(target ...))) + (define-configuration int-component-configuration + #,@(filter-map (make-pred 'int-component) + #'((field (new-field-type new-def) new-doc= ) ...) + #'(target ...))) + (define-configuration ext-component-configuration + #,@(filter-map (make-pred 'ext-component) + #'((field (new-field-type new-def) new-doc= ) ...) + #'(target ...))))))))) + +(define (uglify-field-name field-name) + (let ((str (symbol->string field-name))) + (string-join (string-split (if (string-suffix? "?" str) + (substring str 0 (1- (string-length s= tr))) + str) + #\-) + "_"))) + +(define (serialize-field field-name val) + (format #t "~a =3D ~a;\n" (uglify-field-name field-name) val)) +(define (serialize-field-list field-name val) + (serialize-field field-name + (with-output-to-string + (lambda () + (format #t "{\n") + (for-each (lambda (x) + (format #t "~a;\n" x)) + val) + (format #t "}"))))) + +(define (serialize-boolean field-name val) + (serialize-field field-name (if val "true" "false"))) +(define-maybe boolean) + +(define (string-or-boolean? val) + (or (string? val) (boolean? val))) +(define (serialize-string-or-boolean field-name val) + (if (string? val) + (serialize-string field-name val) + (serialize-boolean field-name val))) + +(define (non-negative-integer? val) + (and (exact-integer? val) (not (negative? val)))) +(define (serialize-non-negative-integer field-name val) + (serialize-field field-name val)) +(define-maybe non-negative-integer) + +(define (non-negative-integer-list? val) + (and (list? val) (and-map non-negative-integer? val))) +(define (serialize-non-negative-integer-list field-name val) + (serialize-field-list field-name val)) +(define-maybe non-negative-integer-list) + +(define (enclose-quotes s) + (format #f "\"~a\"" s)) +(define (serialize-string field-name val) + (serialize-field field-name (enclose-quotes val))) +(define-maybe string) + +(define (string-list? val) + (and (list? val) + (and-map (lambda (x) + (and (string? x) (not (string-index x #\,)))) + val))) +(define (serialize-string-list field-name val) + (serialize-field-list field-name (map enclose-quotes val))) +(define-maybe string-list) + +(define (module-list? val) + (string-list? val)) +(define (serialize-module-list field-name val) + (serialize-string-list field-name (cons "posix" val))) +(define-maybe module-list) + +(define (file-name? val) + (and (string? val) + (string-prefix? "/" val))) +(define (serialize-file-name field-name val) + (serialize-string field-name val)) +(define-maybe file-name) + +(define (file-name-list? val) + (and (list? val) (and-map file-name? val))) +(define (serialize-file-name-list field-name val) + (serialize-string-list field-name val)) +(define-maybe file-name) + +(define-configuration mod-muc-configuration + (name + (string "Prosody Chatrooms") + "The name to return in service discovery responses.") + + (restrict-room-creation + (string-or-boolean #f) + "If @samp{#t}, this will only allow admins to create new chatrooms. +Otherwise anyone can create a room. The value @samp{\"local\"} restrict= s room +creation to users on the service's parent domain. E.g. @samp{user@@exam= ple.com} +can create rooms on @samp{rooms.example.com}. The value @samp{\"admin\"= } +restricts to service administrators only.") + + (max-history-messages + (non-negative-integer 20) + "Maximum number of history messages that will be sent to the member t= hat has +just joined the room.")) +(define (serialize-mod-muc-configuration field-name val) + (serialize-configuration val mod-muc-configuration-fields)) +(define-maybe mod-muc-configuration) + +(define-configuration ssl-configuration + (protocol + (maybe-string 'disabled) + "This determines what handshake to use.") + + (key + (file-name "/etc/prosody/certs/key.pem") + "Path to your private key file, relative to @code{/etc/prosody}.") + + (certificate + (file-name "/etc/prosody/certs/cert.pem") + "Path to your certificate file, relative to @code{/etc/prosody}.") + + (capath + (file-name "/etc/ssl/certs") + "Path to directory containing root certificates that you wish Prosody= to +trust when verifying the certificates of remote servers.") + + (cafile + (maybe-file-name 'disabled) + "Path to a file containing root certificates that you wish Prosody to= trust. +Similar to @code{capath} but with all certificates concatenated together= .") + + (verify + (maybe-string-list 'disabled) + "A list of verification options (these mostly map to OpenSSL's +@code{set_verify()} flags).") + + (options + (maybe-string-list 'disabled) + "A list of general options relating to SSL/TLS. These map to OpenSSL= 's +@code{set_options()}. For a full list of options available in LuaSec, s= ee the +LuaSec source.") + + (depth + (maybe-non-negative-integer 'disabled) + "How long a chain of certificate authorities to check when looking fo= r a +trusted root certificate.") + + (ciphers + (maybe-string 'disabled) + "An OpenSSL cipher string. This selects what ciphers Prosody will of= fer to +clients, and in what order.") + + (dhparam + (maybe-file-name 'disabled) + "A path to a file containing parameters for Diffie-Hellman key exchan= ge. You +can create such a file with: +@code{openssl dhparam -out /etc/prosody/certs/dh-2048.pem 2048}") + + (curve + (maybe-string 'disabled) + "Curve for Elliptic curve Diffie-Hellman. Prosody's default is +@samp{\"secp384r1\"}.") + + (verifyext + (maybe-string-list 'disabled) + "A list of \"extra\" verification options.") + + (password + (maybe-string 'disabled) + "Password for encrypted private keys.")) +(define (serialize-ssl-configuration field-name val) + (format #t "ssl =3D {\n") + (serialize-configuration val ssl-configuration-fields) + (format #t "};\n")) +(define-maybe ssl-configuration) + +(define %default-modules-enabled + '("roster" + "saslauth" + "tls" + "dialback" + "disco" + "private" + "vcard" + "version" + "uptime" + "time" + "ping" + "pep" + "register" + "admin_adhoc")) + +;; Guile bug. Use begin wrapper, because otherwise virtualhost-configur= ation +;; is assumed to be a function. See +;; https://www.gnu.org/software/guile/manual/html_node/R6RS-Incompatibil= ities.html +(begin + (define (virtualhost-configuration-list? val) + (and (list? val) (and-map virtualhost-configuration? val))) + (define (serialize-virtualhost-configuration-list l) + (for-each + (lambda (val) (serialize-virtualhost-configuration val)) l)) + + (define (int-component-configuration-list? val) + (and (list? val) (and-map int-component-configuration? val))) + (define (serialize-int-component-configuration-list l) + (for-each + (lambda (val) (serialize-int-component-configuration val)) l)) + + (define (ext-component-configuration-list? val) + (and (list? val) (and-map ext-component-configuration? val))) + (define (serialize-ext-component-configuration-list l) + (for-each + (lambda (val) (serialize-ext-component-configuration val)) l)) + + (define-all-configurations prosody-configuration + (prosody + (package prosody) + "The Prosody package." + global) + + (data-path + (file-name "/var/lib/prosody") + "Location of the Prosody data storage directory. See +@url{http://prosody.im/doc/configure}." + global) + + (plugin-paths + (file-name-list '()) + "Additional plugin directories. They are searched in all the speci= fied +paths in order. See @url{http://prosody.im/doc/plugins_directory}." + global) + + (admins + (string-list '()) + "This is a list of accounts that are admins for the server. Note t= hat you +must create the accounts separately. See @url{http://prosody.im/doc/adm= ins} and +@url{http://prosody.im/doc/creating_accounts}. +Example: @code{(admins '(\"user1@@example.com\" \"user2@@example.net\"))= }" + common) + + (use-libevent? + (boolean #f) + "Enable use of libevent for better performance under high load. Se= e +@url{http://prosody.im/doc/libevent}." + common) + + (modules-enabled + (module-list %default-modules-enabled) + "This is the list of modules Prosody will load on startup. It look= s for +@code{mod_modulename.lua} in the plugins folder, so make sure that exist= s too. +Documentation on modules can be found at: @url{http://prosody.im/doc/mod= ules}. +Defaults to @samp{%default-modules-enabled}." + common) + + (modules-disabled + (string-list '()) + "@samp{\"offline\"}, @samp{\"c2s\"} and @samp{\"s2s\"} are auto-loa= ded, but +should you want to disable them then add them to this list." + common) + + (groups-file + (file-name "/var/lib/prosody/sharedgroups.txt") + "Path to a text file where the shared groups are defined. If this = path is +empty then @samp{mod_groups} does nothing. See +@url{http://prosody.im/doc/modules/mod_groups}." + common) + + (allow-registration? + (boolean #f) + "Disable account creation by default, for security. See +@url{http://prosody.im/doc/creating_accounts}." + common) + + (ssl + (maybe-ssl-configuration (ssl-configuration)) + "These are the SSL/TLS-related settings. Most of them are disabled= so to +use Prosody's defaults. If you do not completely understand these optio= ns, do +not add them to your config, it is easy to lower the security of your se= rver +using them. See @url{http://prosody.im/doc/advanced_ssl_config}." + common) + + (c2s-require-encryption? + (boolean #f) + "Whether to force all client-to-server connections to be encrypted = or not. +See @url{http://prosody.im/doc/modules/mod_tls}." + common) + + (s2s-require-encryption? + (boolean #f) + "Whether to force all server-to-server connections to be encrypted = or not. +See @url{http://prosody.im/doc/modules/mod_tls}." + common) + + (s2s-secure-auth? + (boolean #f) + "Whether to require encryption and certificate authentication. Thi= s +provides ideal security, but requires servers you communicate with to su= pport +encryption AND present valid, trusted certificates. See +@url{http://prosody.im/doc/s2s#security}." + common) + + (s2s-insecure-domains + (string-list '()) + "Many servers don't support encryption or have invalid or self-sign= ed +certificates. You can list domains here that will not be required to +authenticate using certificates. They will be authenticated using DNS. = See +@url{http://prosody.im/doc/s2s#security}." + common) + + (s2s-secure-domains + (string-list '()) + "Even if you leave @code{s2s-secure-auth?} disabled, you can still = require +valid certificates for some domains by specifying a list here. See +@url{http://prosody.im/doc/s2s#security}." + common) + + (authentication + (string "internal_plain") + "Select the authentication backend to use. The default provider st= ores +passwords in plaintext and uses Prosody's configured data storage to sto= re the +authentication data. If you do not trust your server please see +@url{http://prosody.im/doc/modules/mod_auth_internal_hashed} for informa= tion +about using the hashed backend. See also +@url{http://prosody.im/doc/authentication}" + common) + + ;; TODO: Handle more complicated log structures. + (log + (maybe-string "*syslog") + "Set logging options. Advanced logging configuration is not yet su= pported +by the GuixSD Prosody Service. See @url{http://prosody.im/doc/logging}.= " + common) + + (pidfile + (file-name "/var/run/prosody/prosody.pid") + "File to write pid in. See @url{http://prosody.im/doc/modules/mod_= posix}." + global) + + (virtualhosts + (virtualhost-configuration-list + (list (virtualhost-configuration + (domain "localhost")))) + "A host in Prosody is a domain on which user accounts can be create= d. For +example if you want your users to have addresses like +@samp{\"john.smith@@example.com\"} then you need to add a host +@samp{\"example.com\"}. All options in this list will apply only to thi= s host. + +Note: the name \"virtual\" host is used in configuration to avoid confus= ion with +the actual physical host that Prosody is installed on. A single Prosody +instance can serve many domains, each one defined as a VirtualHost entry= in +Prosody's configuration. Conversely a server that hosts a single domain= would +have just one VirtualHost entry. + +See @url{http://prosody.im/doc/configure#virtual_host_settings}." + global) + + (int-components + (int-component-configuration-list '()) + "Components are extra services on a server which are available to c= lients, +usually on a subdomain of the main server (such as +@samp{\"mycomponent.example.com\"}). Example components might be chatro= om +servers, user directories, or gateways to other protocols. + +Internal components are implemented with Prosody-specific plugins. To a= dd an +internal component, you simply fill the hostname field, and the plugin y= ou wish +to use for the component. + +See @url{http://prosody.im/doc/components}." + global) + + (ext-components + (ext-component-configuration-list '()) + "External components use XEP-0114, which most standalone components +support. To add an external component, you simply fill the hostname fie= ld. See +@url{http://prosody.im/doc/components}." + global) + + (component-secret + (string (configuration-missing-field 'ext-component 'component-secr= et)) + "Password which the component will use to log in." + ext-component) + + (component-ports + (non-negative-integer-list '(5347)) + "Port(s) Prosody listens on for component connections." + global) + + (component-interface + (string "127.0.0.1") + "Interface Prosody listens on for component connections." + global) + + (domain + (string (configuration-missing-field 'virtualhost 'domain)) + "Domain you wish Prosody to serve." + virtualhost) + + (hostname + (string (configuration-missing-field 'int-component 'hostname)) + "Hostname of the component." + int-component) + + (plugin + (string (configuration-missing-field 'int-component 'plugin)) + "Plugin you wish to use for the component." + int-component) + + (mod-muc + (maybe-mod-muc-configuration 'disabled) + "Multi-user chat (MUC) is Prosody's module for allowing you to crea= te +hosted chatrooms/conferences for XMPP users. + +General information on setting up and using MUC chatrooms can be found i= n our +\"Chatrooms\" documentation (@url{http://prosody.im/doc/chatrooms}), whi= ch you +should read if you are new to XMPP chatrooms. + +See also @url{http://prosody.im/doc/modules/mod_muc}." + int-component) + + (hostname + (string (configuration-missing-field 'ext-component 'hostname)) + "Hostname of the component." + ext-component))) + +;; Serialize Virtualhost line first. +(define (serialize-virtualhost-configuration config) + (define (rest? field) + (not (memq (configuration-field-name field) + '(domain)))) + (let ((domain (virtualhost-configuration-domain config)) + (rest (filter rest? virtualhost-configuration-fields))) + (format #t "VirtualHost \"~a\"\n" domain) + (serialize-configuration config rest))) + +;; Serialize Component line first. +(define (serialize-int-component-configuration config) + (define (rest? field) + (not (memq (configuration-field-name field) + '(hostname plugin)))) + (let ((hostname (int-component-configuration-hostname config)) + (plugin (int-component-configuration-plugin config)) + (rest (filter rest? int-component-configuration-fields))) + (format #t "Component \"~a\" \"~a\"\n" hostname plugin) + (serialize-configuration config rest))) + +;; Serialize Component line first. +(define (serialize-ext-component-configuration config) + (define (rest? field) + (not (memq (configuration-field-name field) + '(hostname)))) + (let ((hostname (ext-component-configuration-hostname config)) + (rest (filter rest? ext-component-configuration-fields))) + (format #t "Component \"~a\"\n" hostname) + (serialize-configuration config rest))) + +;; Serialize virtualhosts and components last. +(define (serialize-prosody-configuration config) + (define (rest? field) + (not (memq (configuration-field-name field) + '(virtualhosts int-components ext-components)))) + (let ((rest (filter rest? prosody-configuration-fields))) + (serialize-configuration config rest)) + (serialize-virtualhost-configuration-list + (prosody-configuration-virtualhosts config)) + (serialize-int-component-configuration-list + (prosody-configuration-int-components config)) + (serialize-ext-component-configuration-list + (prosody-configuration-ext-components config))) + +(define-configuration opaque-prosody-configuration + (prosody + (package prosody) + "The prosody package.") + + (prosody.cfg.lua + (string (configuration-missing-field 'opaque-prosody-configuration + 'prosody.cfg.lua)) + "The contents of the @code{prosody.cfg.lua} to use.")) + +(define (prosody-shepherd-service config) + "Return a for Prosody with CONFIG." + (let* ((prosody (if (opaque-prosody-configuration? config) + (opaque-prosody-configuration-prosody config) + (prosody-configuration-prosody config))) + (prosodyctl-bin (file-append prosody "/bin/prosodyctl")) + (prosodyctl-action (lambda args + #~(lambda _ + (zero? (system* #$prosodyctl-bin #$@ar= gs)))))) + (list (shepherd-service + (documentation "Run the Prosody XMPP server") + (provision '(prosody)) + (requirement '(networking syslogd user-processes)) + (start (prosodyctl-action "start")) + (stop (prosodyctl-action "stop")))))) + +(define %prosody-accounts + (list (user-group (name "prosody") (system? #t)) + (user-account + (name "prosody") + (group "prosody") + (system? #t) + (comment "Prosody daemon user") + (home-directory "/var/empty") + (shell (file-append shadow "/sbin/nologin"))))) + +(define (prosody-activation config) + "Return the activation gexp for CONFIG." + (let* ((config-dir "/etc/prosody") + (default-certs-dir "/etc/prosody/certs") + (data-path (prosody-configuration-data-path config)) + (pidfile-dir (dirname (prosody-configuration-pidfile config))) + (config-str + (if (opaque-prosody-configuration? config) + (opaque-prosody-configuration-prosody.cfg.lua config) + (with-output-to-string + (lambda () + (serialize-prosody-configuration config))))) + (config-file (plain-file "prosody.cfg.lua" config-str))) + #~(begin + (define %user (getpw "prosody")) + + (mkdir-p #$config-dir) + (chown #$config-dir (passwd:uid %user) (passwd:gid %user)) + (copy-file #$config-file (string-append #$config-dir + "/prosody.cfg.lua")) + + (mkdir-p #$default-certs-dir) + (chown #$default-certs-dir (passwd:uid %user) (passwd:gid %user)= ) + (chmod #$default-certs-dir #o750) + + (mkdir-p #$data-path) + (chown #$data-path (passwd:uid %user) (passwd:gid %user)) + (chmod #$data-path #o750) + + (mkdir-p #$pidfile-dir) + (chown #$pidfile-dir (passwd:uid %user) (passwd:gid %user))))) + +(define prosody-service-type + (service-type (name 'prosody) + (extensions + (list (service-extension shepherd-root-service-type + prosody-shepherd-service) + (service-extension account-service-type + (const %prosody-accounts)) + (service-extension activation-service-type + prosody-activation))))) + +;; A little helper to make it easier to document all those fields. +(define (generate-documentation) + (define documentation + `((prosody-configuration + ,prosody-configuration-fields + (ssl ssl-configuration) + (virtualhosts virtualhost-configuration) + (int-components int-component-configuration) + (ext-components ext-component-configuration)) + (ssl-configuration ,ssl-configuration-fields) + (int-component-configuration ,int-component-configuration-fields + (mod-muc mod-muc-configuration)) + (ext-component-configuration ,ext-component-configuration-fields) + (mod-muc-configuration ,mod-muc-configuration-fields) + (virtualhost-configuration ,virtualhost-configuration-fields) + (opaque-prosody-configuration ,opaque-prosody-configuration-fields= ))) + (define (generate configuration-name) + (match (assq-ref documentation configuration-name) + ((fields . sub-documentation) + (format #t "\nAvailable @code{~a} fields are:\n\n" configuration-= name) + (when (memq configuration-name + '(virtualhost-configuration + int-component-configuration + ext-component-configuration)) + (format #t "all these @code{prosody-configuration} fields: ~a, = plus:\n" + (string-join (map (lambda (s) + (format #f "@code{~a}" s)) common-f= ields) + ", "))) + (for-each + (lambda (f) + (let ((field-name (configuration-field-name f)) + (field-type (configuration-field-type f)) + (field-docs (string-trim-both + (configuration-field-documentation f))) + (default (catch #t + (configuration-field-default-value-thunk f) + (lambda _ 'nope)))) + (define (escape-chars str chars escape) + (with-output-to-string + (lambda () + (string-for-each (lambda (c) + (when (char-set-contains? chars c) + (display escape)) + (display c)) + str)))) + (define (show-default? val) + (or (string? default) (number? default) (boolean? default) + (and (list? val) (and-map show-default? val)))) + (format #t "@deftypevr {@code{~a} parameter} ~a ~a\n~a\n" + configuration-name field-type field-name field-docs) + (when (show-default? default) + (format #t "Defaults to @samp{~a}.\n" + (escape-chars (format #f "~s" default) + (char-set #\@ #\{ #\}) + #\@))) + (for-each generate (or (assq-ref sub-documentation field-nam= e) '())) + (format #t "@end deftypevr\n\n"))) + (filter (lambda (f) + (not (string=3D? "" (configuration-field-documentation= f)))) + fields))))) + (generate 'prosody-configuration) + (format #t "It could be that you just want to get a @code{prosody.cfg.= lua} +up and running. In that case, you can pass an +@code{opaque-prosody-configuration} record as the value of +@code{prosody-service-type}. As its name indicates, an opaque configura= tion +does not have easy reflective capabilities.") + (generate 'opaque-prosody-configuration) + (format #t "For example, if your @code{prosody.cfg.lua} is just the em= pty +string, you could instantiate a prosody service like this: + +@example +(service prosody-service-type + (opaque-prosody-configuration + (prosody.cfg.lua \"\"))) +@end example")) + +;;; messaging.scm ends here --=20 2.11.0