diff --git a/nixos/doc/manual/release-notes/rl-1803.xml b/nixos/doc/manual/release-notes/rl-1803.xml index b755245a69f..e67f1448466 100644 --- a/nixos/doc/manual/release-notes/rl-1803.xml +++ b/nixos/doc/manual/release-notes/rl-1803.xml @@ -322,6 +322,38 @@ following incompatible changes: external module. + + + The Prosody XMPP server has received a major update. The following modules were renamed: + + + + is now + + + + + is now + + + + + + + Many new modules are now core modules, most notably + and . + + + + The better-performing libevent backend is now enabled by default. + + + + withCommunityModules now passes through the modules to . + Use withOnlyInstalledCommunityModules for modules that should not be enabled directly, e.g lib_ldap. + + + diff --git a/nixos/modules/services/networking/prosody.nix b/nixos/modules/services/networking/prosody.nix index 9d7e6d6018a..d57ebb61f63 100644 --- a/nixos/modules/services/networking/prosody.nix +++ b/nixos/modules/services/networking/prosody.nix @@ -15,6 +15,7 @@ let description = "Path to the key file."; }; + # TODO: rename to certificate to match the prosody config cert = mkOption { type = types.path; description = "Path to the certificate file."; @@ -30,7 +31,7 @@ let }; moduleOpts = { - + # Generally required roster = mkOption { type = types.bool; default = true; @@ -61,12 +62,38 @@ let description = "Service discovery"; }; - legacyauth = mkOption { + # Not essential, but recommended + carbons = mkOption { type = types.bool; default = true; - description = "Legacy authentication. Only used by some old clients and bots"; + description = "Keep multiple clients in sync"; }; + pep = mkOption { + type = types.bool; + default = true; + description = "Enables users to publish their mood, activity, playing music and more"; + }; + + private = mkOption { + type = types.bool; + default = true; + description = "Private XML storage (for room bookmarks, etc.)"; + }; + + blocklist = mkOption { + type = types.bool; + default = true; + description = "Allow users to block communications with other users"; + }; + + vcard = mkOption { + type = types.bool; + default = true; + description = "Allow users to set vCards"; + }; + + # Nice to have version = mkOption { type = types.bool; default = true; @@ -91,36 +118,112 @@ let description = "Replies to XMPP pings with pongs"; }; - console = mkOption { + register = mkOption { type = types.bool; - default = false; - description = "telnet to port 5582"; + default = true; + description = "Allow users to register on this server using a client and change passwords"; }; + mam = mkOption { + type = types.bool; + default = false; + description = "Store messages in an archive and allow users to access it"; + }; + + # Admin interfaces + admin_adhoc = mkOption { + type = types.bool; + default = true; + description = "Allows administration via an XMPP client that supports ad-hoc commands"; + }; + + admin_telnet = mkOption { + type = types.bool; + default = false; + description = "Opens telnet console interface on localhost port 5582"; + }; + + # HTTP modules bosh = mkOption { type = types.bool; default = false; description = "Enable BOSH clients, aka 'Jabber over HTTP'"; }; - httpserver = mkOption { - type = types.bool; - default = false; - description = "Serve static files from a directory over HTTP"; - }; - websocket = mkOption { type = types.bool; default = false; description = "Enable WebSocket support"; }; + http_files = mkOption { + type = types.bool; + default = false; + description = "Serve static files from a directory over HTTP"; + }; + + # Other specific functionality + limits = mkOption { + type = types.bool; + default = false; + description = "Enable bandwidth limiting for XMPP connections"; + }; + + groups = mkOption { + type = types.bool; + default = false; + description = "Shared roster support"; + }; + + server_contact_info = mkOption { + type = types.bool; + default = false; + description = "Publish contact information for this service"; + }; + + announce = mkOption { + type = types.bool; + default = false; + description = "Send announcement to all online users"; + }; + + welcome = mkOption { + type = types.bool; + default = false; + description = "Welcome users who register accounts"; + }; + + watchregistrations = mkOption { + type = types.bool; + default = false; + description = "Alert admins of registrations"; + }; + + motd = mkOption { + type = types.bool; + default = false; + description = "Send a message to users when they log in"; + }; + + legacyauth = mkOption { + type = types.bool; + default = false; + description = "Legacy authentication. Only used by some old clients and bots"; + }; + + proxy65 = mkOption { + type = types.bool; + default = false; + description = "Enables a file transfer proxy service which clients behind NAT can use"; + }; + }; toLua = x: if builtins.isString x then ''"${x}"'' - else if builtins.isBool x then toString x + else if builtins.isBool x then (if x == true then "true" else "false") else if builtins.isInt x then toString x + else if builtins.isList x then ''{ ${lib.concatStringsSep ", " (map (n: toLua n) x) } }'' else throw "Invalid Lua value"; createSSLOptsStr = o: '' @@ -198,6 +301,59 @@ in description = "Allow account creation"; }; + c2sRequireEncryption = mkOption { + type = types.bool; + default = true; + description = '' + Force clients to use encrypted connections? This option will + prevent clients from authenticating unless they are using encryption. + ''; + }; + + s2sRequireEncryption = mkOption { + type = types.bool; + default = true; + description = '' + Force servers to use encrypted connections? This option will + prevent servers from authenticating unless they are using encryption. + Note that this is different from authentication. + ''; + }; + + s2sSecureAuth = mkOption { + type = types.bool; + default = false; + description = '' + Force certificate authentication for server-to-server connections? + This provides ideal security, but requires servers you communicate + with to support encryption AND present valid, trusted certificates. + For more information see https://prosody.im/doc/s2s#security + ''; + }; + + s2sInsecureDomains = mkOption { + type = types.listOf types.str; + default = []; + example = [ "insecure.example.com" ]; + description = '' + Some servers have invalid or self-signed certificates. You can list + remote domains here that will not be required to authenticate using + certificates. They will be authenticated using DNS instead, even + when s2s_secure_auth is enabled. + ''; + }; + + s2sSecureDomains = mkOption { + type = types.listOf types.str; + default = []; + example = [ "jabber.org" ]; + description = '' + Even if you leave s2s_secure_auth disabled, you can still require valid + certificates for some domains by specifying a list here. + ''; + }; + + modules = moduleOpts; extraModules = mkOption { @@ -206,6 +362,12 @@ in description = "Enable custom modules"; }; + extraPluginPaths = mkOption { + type = types.listOf types.path; + default = []; + description = "Addtional path in which to look find plugins/modules"; + }; + virtualHosts = mkOption { description = "Define the virtual hosts"; @@ -255,38 +417,48 @@ in config = mkIf cfg.enable { - environment.systemPackages = [ pkgs.prosody ]; + environment.systemPackages = [ cfg.package ]; environment.etc."prosody/prosody.cfg.lua".text = '' pidfile = "/var/lib/prosody/prosody.pid" - log = "*syslog" data_path = "/var/lib/prosody" - - allow_registration = ${boolToString cfg.allowRegistration}; - - ${ optionalString cfg.modules.console "console_enabled = true;" } + plugin_paths = { + ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.extraPluginPaths) } + } ${ optionalString (cfg.ssl != null) (createSSLOptsStr cfg.ssl) } - admins = { ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.admins) } }; + admins = ${toLua cfg.admins} + + -- we already build with libevent, so we can just enable it for a more performant server + use_libevent = true modules_enabled = { ${ lib.concatStringsSep "\n\ \ " (lib.mapAttrsToList - (name: val: optionalString val ''"${name}";'') + (name: val: optionalString val "${toLua name};") cfg.modules) } - - ${ optionalString cfg.allowRegistration "\"register\"\;" } - - ${ lib.concatStringsSep "\n" (map (x: "\"${x}\";") cfg.extraModules)} - - "posix"; + ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.package.communityModules)} + ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.extraModules)} }; + allow_registration = ${toLua cfg.allowRegistration} + + c2s_require_encryption = ${toLua cfg.c2sRequireEncryption} + + s2s_require_encryption = ${toLua cfg.s2sRequireEncryption} + + s2s_secure_auth = ${toLua cfg.s2sSecureAuth} + + s2s_insecure_domains = ${toLua cfg.s2sInsecureDomains} + + s2s_secure_domains = ${toLua cfg.s2sSecureDomains} + + ${ cfg.extraConfig } ${ lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: '' diff --git a/nixos/release.nix b/nixos/release.nix index 0b8d7318cd8..9b4aa4b0399 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -344,6 +344,7 @@ in rec { tests.predictable-interface-names = callSubTests tests/predictable-interface-names.nix {}; tests.printing = callTest tests/printing.nix {}; tests.prometheus = callTest tests/prometheus.nix {}; + tests.prosody = callTest tests/prosody.nix {}; tests.proxy = callTest tests/proxy.nix {}; # tests.quagga = callTest tests/quagga.nix {}; tests.quake3 = callTest tests/quake3.nix {}; diff --git a/nixos/tests/prosody.nix b/nixos/tests/prosody.nix new file mode 100644 index 00000000000..fcebfaf74e1 --- /dev/null +++ b/nixos/tests/prosody.nix @@ -0,0 +1,75 @@ +import ./make-test.nix { + name = "prosody"; + + machine = { config, pkgs, ... }: { + services.prosody = { + enable = true; + # TODO: use a self-signed certificate + c2sRequireEncryption = false; + }; + environment.systemPackages = let + sendMessage = pkgs.writeScriptBin "send-message" '' + #!/usr/bin/env python3 + # Based on the sleekxmpp send_client example, look there for more details: + # https://github.com/fritzy/SleekXMPP/blob/develop/examples/send_client.py + import sleekxmpp + + class SendMsgBot(sleekxmpp.ClientXMPP): + """ + A basic SleekXMPP bot that will log in, send a message, + and then log out. + """ + def __init__(self, jid, password, recipient, message): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + + self.recipient = recipient + self.msg = message + + self.add_event_handler("session_start", self.start, threaded=True) + + def start(self, event): + self.send_presence() + self.get_roster() + + self.send_message(mto=self.recipient, + mbody=self.msg, + mtype='chat') + + self.disconnect(wait=True) + + + if __name__ == '__main__': + xmpp = SendMsgBot("test1@localhost", "test1", "test2@localhost", "Hello World!") + xmpp.register_plugin('xep_0030') # Service Discovery + xmpp.register_plugin('xep_0199') # XMPP Ping + + # TODO: verify certificate + # If you want to verify the SSL certificates offered by a server: + # xmpp.ca_certs = "path/to/ca/cert" + + if xmpp.connect(('localhost', 5222)): + xmpp.process(block=True) + else: + print("Unable to connect.") + sys.exit(1) + ''; + in [ (pkgs.python3.withPackages (ps: [ ps.sleekxmpp ])) sendMessage ]; + }; + + testScript = '' + $machine->waitForUnit('prosody.service'); + $machine->succeed('prosodyctl status') =~ /Prosody is running/; + + # set password to 'test' (it's asked twice) + $machine->succeed('yes test1 | prosodyctl adduser test1@localhost'); + # set password to 'y' + $machine->succeed('yes | prosodyctl adduser test2@localhost'); + # correct password to 'test2' + $machine->succeed('yes test2 | prosodyctl passwd test2@localhost'); + + $machine->succeed("send-message"); + + $machine->succeed('prosodyctl deluser test1@localhost'); + $machine->succeed('prosodyctl deluser test2@localhost'); + ''; +} diff --git a/pkgs/servers/xmpp/prosody/default.nix b/pkgs/servers/xmpp/prosody/default.nix index b0e3492c0da..2d0e1a529da 100644 --- a/pkgs/servers/xmpp/prosody/default.nix +++ b/pkgs/servers/xmpp/prosody/default.nix @@ -1,14 +1,13 @@ { stdenv, fetchurl, libidn, openssl, makeWrapper, fetchhg , lua5, luasocket, luasec, luaexpat, luafilesystem, luabitop , withLibevent ? true, luaevent ? null -, withZlib ? true, luazlib ? null , withDBI ? true, luadbi ? null # use withExtraLibs to add additional dependencies of community modules , withExtraLibs ? [ ] +, withOnlyInstalledCommunityModules ? [ ] , withCommunityModules ? [ ] }: assert withLibevent -> luaevent != null; -assert withZlib -> luazlib != null; assert withDBI -> luadbi != null; with stdenv.lib; @@ -16,7 +15,6 @@ with stdenv.lib; let libs = [ luasocket luasec luaexpat luafilesystem luabitop ] ++ optional withLibevent luaevent - ++ optional withZlib luazlib ++ optional withDBI luadbi ++ withExtraLibs; getPath = lib : type : "${lib}/lib/lua/${lua5.luaversion}/?.${type};${lib}/share/lua/${lua5.luaversion}/?.${type}"; @@ -27,21 +25,22 @@ let in stdenv.mkDerivation rec { - version = "0.9.12"; + version = "0.10.0"; name = "prosody-${version}"; src = fetchurl { url = "http://prosody.im/downloads/source/${name}.tar.gz"; - sha256 = "139yxqpinajl32ryrybvilh54ddb1q6s0ajjhlcs4a0rnwia6n8s"; + sha256 = "1644jy5dk46vahmh6nna36s79k8k668sbi3qamjb4q3c4m3y853l"; }; communityModules = fetchhg { url = "https://hg.prosody.im/prosody-modules"; - rev = "9a3e51f348fe"; - sha256 = "09g4vi52rv0r3jzcm0bsgp4ngqq6iapfbxfh0l7qj36qnajp4vm6"; + rev = "150a7bd59043"; + sha256 = "0nfx3lngcy88nd81gb7v4kh3nz1bzsm67bxgpd2lprk54diqcrz1"; }; - buildInputs = [ lua5 makeWrapper libidn openssl ]; + buildInputs = [ lua5 makeWrapper libidn openssl ] + ++ optional withDBI luadbi; configureFlags = [ "--ostype=linux" @@ -52,7 +51,7 @@ stdenv.mkDerivation rec { postInstall = '' ${concatMapStringsSep "\n" (module: '' cp -r $communityModules/mod_${module} $out/lib/prosody/modules/ - '') withCommunityModules} + '') (withCommunityModules ++ withOnlyInstalledCommunityModules)} wrapProgram $out/bin/prosody \ --set LUA_PATH '${luaPath};' \ --set LUA_CPATH '${luaCPath};' @@ -62,11 +61,13 @@ stdenv.mkDerivation rec { --set LUA_CPATH '${luaCPath};' ''; + passthru.communityModules = withCommunityModules; + meta = { description = "Open-source XMPP application server written in Lua"; license = licenses.mit; - homepage = http://www.prosody.im; + homepage = https://prosody.im; platforms = platforms.linux; - maintainers = [ ]; + maintainers = with maintainers; [ fpletz globin ]; }; } diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index ae0d11474b6..1936a57dded 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -12269,8 +12269,9 @@ with pkgs; hyp = callPackage ../servers/http/hyp/default.nix { }; prosody = callPackage ../servers/xmpp/prosody { - lua5 = lua5_1; - inherit (lua51Packages) luasocket luasec luaexpat luafilesystem luabitop luaevent luazlib luadbi; + # _compat can probably be removed on next minor version after 0.10.0 + lua5 = lua5_2_compat; + inherit (lua52Packages) luasocket luasec luaexpat luafilesystem luabitop luaevent luadbi; }; biboumi = callPackage ../servers/xmpp/biboumi { }; diff --git a/pkgs/top-level/lua-packages.nix b/pkgs/top-level/lua-packages.nix index 0cc4482f402..a23de670836 100644 --- a/pkgs/top-level/lua-packages.nix +++ b/pkgs/top-level/lua-packages.nix @@ -167,15 +167,14 @@ let }; luaevent = buildLuaPackage rec { - version = "0.4.3"; + version = "0.4.4"; name = "luaevent-${version}"; - disabled = isLua52; src = fetchFromGitHub { owner = "harningt"; repo = "luaevent"; rev = "v${version}"; - sha256 = "1c1n2zqx5rwfwkqaq1jj8gvx1vswvbihj2sy445w28icz1xfhpik"; + sha256 = "1krzxr0jkv3gmhpckp02byhdd9s5dd0hpyqc8irc8i79dd8x0p53"; }; preBuild = ''