Merge pull request #86067 from NinjaTrappeur/nin-sane-prosody-defaults
nixos/prosody: make module defaults comply with XEP-0423
This commit is contained in:
commit
e148a72377
@ -1,9 +1,7 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
cfg = config.services.prosody;
|
||||
|
||||
sslOpts = { ... }: {
|
||||
@ -30,8 +28,21 @@ let
|
||||
};
|
||||
};
|
||||
|
||||
discoOpts = {
|
||||
options = {
|
||||
url = mkOption {
|
||||
type = types.str;
|
||||
description = "URL of the endpoint you want to make discoverable";
|
||||
};
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
description = "A short description of the endpoint you want to advertise";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
moduleOpts = {
|
||||
# Generally required
|
||||
# Required for compliance with https://compliance.conversations.im/about/
|
||||
roster = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
@ -69,6 +80,18 @@ let
|
||||
description = "Keep multiple clients in sync";
|
||||
};
|
||||
|
||||
csi = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Implements the CSI protocol that allows clients to report their active/inactive state to the server";
|
||||
};
|
||||
|
||||
cloud_notify = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Push notifications to inform users of new messages or other pertinent information even when they have no XMPP clients online";
|
||||
};
|
||||
|
||||
pep = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
@ -89,10 +112,22 @@ let
|
||||
|
||||
vcard = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
default = false;
|
||||
description = "Allow users to set vCards";
|
||||
};
|
||||
|
||||
vcard_legacy = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Converts users profiles and Avatars between old and new formats";
|
||||
};
|
||||
|
||||
bookmarks = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Allows interop between older clients that use XEP-0048: Bookmarks in its 1.0 version and recent clients which use it in PEP";
|
||||
};
|
||||
|
||||
# Nice to have
|
||||
version = mkOption {
|
||||
type = types.bool;
|
||||
@ -126,10 +161,16 @@ let
|
||||
|
||||
mam = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
default = true;
|
||||
description = "Store messages in an archive and allow users to access it";
|
||||
};
|
||||
|
||||
smacks = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Allow a client to resume a disconnected session, and prevent message loss";
|
||||
};
|
||||
|
||||
# Admin interfaces
|
||||
admin_adhoc = mkOption {
|
||||
type = types.bool;
|
||||
@ -137,6 +178,18 @@ let
|
||||
description = "Allows administration via an XMPP client that supports ad-hoc commands";
|
||||
};
|
||||
|
||||
http_files = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Serve static files from a directory over HTTP";
|
||||
};
|
||||
|
||||
proxy65 = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enables a file transfer proxy service which clients behind NAT can use";
|
||||
};
|
||||
|
||||
admin_telnet = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
@ -156,12 +209,6 @@ let
|
||||
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;
|
||||
@ -210,13 +257,6 @@ let
|
||||
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:
|
||||
@ -235,6 +275,153 @@ let
|
||||
};
|
||||
'';
|
||||
|
||||
mucOpts = { ... }: {
|
||||
options = {
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
description = "Domain name of the MUC";
|
||||
};
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
description = "The name to return in service discovery responses for the MUC service itself";
|
||||
default = "Prosody Chatrooms";
|
||||
};
|
||||
restrictRoomCreation = mkOption {
|
||||
type = types.enum [ true false "admin" "local" ];
|
||||
default = false;
|
||||
description = "Restrict room creation to server admins";
|
||||
};
|
||||
maxHistoryMessages = mkOption {
|
||||
type = types.int;
|
||||
default = 20;
|
||||
description = "Specifies a limit on what each room can be configured to keep";
|
||||
};
|
||||
roomLocking = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Enables room locking, which means that a room must be
|
||||
configured before it can be used. Locked rooms are invisible
|
||||
and cannot be entered by anyone but the creator
|
||||
'';
|
||||
};
|
||||
roomLockTimeout = mkOption {
|
||||
type = types.int;
|
||||
default = 300;
|
||||
description = ''
|
||||
Timout after which the room is destroyed or unlocked if not
|
||||
configured, in seconds
|
||||
'';
|
||||
};
|
||||
tombstones = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
When a room is destroyed, it leaves behind a tombstone which
|
||||
prevents the room being entered or recreated. It also allows
|
||||
anyone who was not in the room at the time it was destroyed
|
||||
to learn about it, and to update their bookmarks. Tombstones
|
||||
prevents the case where someone could recreate a previously
|
||||
semi-anonymous room in order to learn the real JIDs of those
|
||||
who often join there.
|
||||
'';
|
||||
};
|
||||
tombstoneExpiry = mkOption {
|
||||
type = types.int;
|
||||
default = 2678400;
|
||||
description = ''
|
||||
This settings controls how long a tombstone is considered
|
||||
valid. It defaults to 31 days. After this time, the room in
|
||||
question can be created again.
|
||||
'';
|
||||
};
|
||||
|
||||
vcard_muc = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Adds the ability to set vCard for Multi User Chat rooms";
|
||||
};
|
||||
|
||||
# Extra parameters. Defaulting to prosody default values.
|
||||
# Adding them explicitly to make them visible from the options
|
||||
# documentation.
|
||||
#
|
||||
# See https://prosody.im/doc/modules/mod_muc for more details.
|
||||
roomDefaultPublic = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "If set, the MUC rooms will be public by default.";
|
||||
};
|
||||
roomDefaultMembersOnly = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "If set, the MUC rooms will only be accessible to the members by default.";
|
||||
};
|
||||
roomDefaultModerated = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "If set, the MUC rooms will be moderated by default.";
|
||||
};
|
||||
roomDefaultPublicJids = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "If set, the MUC rooms will display the public JIDs by default.";
|
||||
};
|
||||
roomDefaultChangeSubject = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "If set, the rooms will display the public JIDs by default.";
|
||||
};
|
||||
roomDefaultHistoryLength = mkOption {
|
||||
type = types.int;
|
||||
default = 20;
|
||||
description = "Number of history message sent to participants by default.";
|
||||
};
|
||||
roomDefaultLanguage = mkOption {
|
||||
type = types.str;
|
||||
default = "en";
|
||||
description = "Default room language.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
uploadHttpOpts = { ... }: {
|
||||
options = {
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = "Domain name for the http-upload service";
|
||||
};
|
||||
uploadFileSizeLimit = mkOption {
|
||||
type = types.str;
|
||||
default = "50 * 1024 * 1024";
|
||||
description = "Maximum file size, in bytes. Defaults to 50MB.";
|
||||
};
|
||||
uploadExpireAfter = mkOption {
|
||||
type = types.str;
|
||||
default = "60 * 60 * 24 * 7";
|
||||
description = "Max age of a file before it gets deleted, in seconds.";
|
||||
};
|
||||
userQuota = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
example = 1234;
|
||||
description = ''
|
||||
Maximum size of all uploaded files per user, in bytes. There
|
||||
will be no quota if this option is set to null.
|
||||
'';
|
||||
};
|
||||
httpUploadPath = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
Directory where the uploaded files will be stored. By
|
||||
default, uploaded files are put in a sub-directory of the
|
||||
default Prosody storage path (usually /var/lib/prosody).
|
||||
'';
|
||||
default = "/var/lib/prosody";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
vHostOpts = { ... }: {
|
||||
|
||||
options = {
|
||||
@ -283,6 +470,27 @@ in
|
||||
description = "Whether to enable the prosody server";
|
||||
};
|
||||
|
||||
xmppComplianceSuite = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
The XEP-0423 defines a set of recommended XEPs to implement
|
||||
for a server. It's generally a good idea to implement this
|
||||
set of extensions if you want to provide your users with a
|
||||
good XMPP experience.
|
||||
|
||||
This NixOS module aims to provide a "advanced server"
|
||||
experience as per defined in the XEP-0423[1] specification.
|
||||
|
||||
Setting this option to true will prevent you from building a
|
||||
NixOS configuration which won't comply with this standard.
|
||||
You can explicitely decide to ignore this standard if you
|
||||
know what you are doing by setting this option to false.
|
||||
|
||||
[1] https://xmpp.org/extensions/xep-0423.html
|
||||
'';
|
||||
};
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
description = "Prosody package to use";
|
||||
@ -302,6 +510,12 @@ in
|
||||
default = "/var/lib/prosody";
|
||||
};
|
||||
|
||||
disco_items = mkOption {
|
||||
type = types.listOf (types.submodule discoOpts);
|
||||
default = [];
|
||||
description = "List of discoverable items you want to advertise.";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "prosody";
|
||||
@ -320,6 +534,31 @@ in
|
||||
description = "Allow account creation";
|
||||
};
|
||||
|
||||
# HTTP server-related options
|
||||
httpPorts = mkOption {
|
||||
type = types.listOf types.int;
|
||||
description = "Listening HTTP ports list for this service.";
|
||||
default = [ 5280 ];
|
||||
};
|
||||
|
||||
httpInterfaces = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ "*" "::" ];
|
||||
description = "Interfaces on which the HTTP server will listen on.";
|
||||
};
|
||||
|
||||
httpsPorts = mkOption {
|
||||
type = types.listOf types.int;
|
||||
description = "Listening HTTPS ports list for this service.";
|
||||
default = [ 5281 ];
|
||||
};
|
||||
|
||||
httpsInterfaces = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ "*" "::" ];
|
||||
description = "Interfaces on which the HTTPS server will listen on.";
|
||||
};
|
||||
|
||||
c2sRequireEncryption = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
@ -387,6 +626,26 @@ in
|
||||
description = "Addtional path in which to look find plugins/modules";
|
||||
};
|
||||
|
||||
uploadHttp = mkOption {
|
||||
description = ''
|
||||
Configures the Prosody builtin HTTP server to handle user uploads.
|
||||
'';
|
||||
type = types.nullOr (types.submodule uploadHttpOpts);
|
||||
default = null;
|
||||
example = {
|
||||
domain = "uploads.my-xmpp-example-host.org";
|
||||
};
|
||||
};
|
||||
|
||||
muc = mkOption {
|
||||
type = types.listOf (types.submodule mucOpts);
|
||||
default = [ ];
|
||||
example = [ {
|
||||
domain = "conference.my-xmpp-example-host.org";
|
||||
} ];
|
||||
description = "Multi User Chat (MUC) configuration";
|
||||
};
|
||||
|
||||
virtualHosts = mkOption {
|
||||
|
||||
description = "Define the virtual hosts";
|
||||
@ -443,9 +702,44 @@ in
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
assertions = let
|
||||
genericErrMsg = ''
|
||||
|
||||
Having a server not XEP-0423-compliant might make your XMPP
|
||||
experience terrible. See the NixOS manual for further
|
||||
informations.
|
||||
|
||||
If you know what you're doing, you can disable this warning by
|
||||
setting config.services.prosody.xmppComplianceSuite to false.
|
||||
'';
|
||||
errors = [
|
||||
{ assertion = (builtins.length cfg.muc > 0) || !cfg.xmppComplianceSuite;
|
||||
message = ''
|
||||
You need to setup at least a MUC domain to comply with
|
||||
XEP-0423.
|
||||
'' + genericErrMsg;}
|
||||
{ assertion = cfg.uploadHttp != null || !cfg.xmppComplianceSuite;
|
||||
message = ''
|
||||
You need to setup the uploadHttp module through
|
||||
config.services.prosody.uploadHttp to comply with
|
||||
XEP-0423.
|
||||
'' + genericErrMsg;}
|
||||
];
|
||||
in errors;
|
||||
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
|
||||
environment.etc."prosody/prosody.cfg.lua".text = ''
|
||||
environment.etc."prosody/prosody.cfg.lua".text =
|
||||
let
|
||||
httpDiscoItems = if (cfg.uploadHttp != null)
|
||||
then [{ url = cfg.uploadHttp.domain; description = "HTTP upload endpoint";}]
|
||||
else [];
|
||||
mucDiscoItems = builtins.foldl'
|
||||
(acc: muc: [{ url = muc.domain; description = "${muc.domain} MUC endpoint";}] ++ acc)
|
||||
[]
|
||||
cfg.muc;
|
||||
discoItems = cfg.disco_items ++ httpDiscoItems ++ mucDiscoItems;
|
||||
in ''
|
||||
|
||||
pidfile = "/run/prosody/prosody.pid"
|
||||
|
||||
@ -472,6 +766,10 @@ in
|
||||
${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.extraModules)}
|
||||
};
|
||||
|
||||
disco_items = {
|
||||
${ lib.concatStringsSep "\n" (builtins.map (x: ''{ "${x.url}", "${x.description}"};'') discoItems)}
|
||||
};
|
||||
|
||||
allow_registration = ${toLua cfg.allowRegistration}
|
||||
|
||||
c2s_require_encryption = ${toLua cfg.c2sRequireEncryption}
|
||||
@ -486,6 +784,42 @@ in
|
||||
|
||||
authentication = ${toLua cfg.authentication}
|
||||
|
||||
http_interfaces = ${toLua cfg.httpInterfaces}
|
||||
|
||||
https_interfaces = ${toLua cfg.httpsInterfaces}
|
||||
|
||||
http_ports = ${toLua cfg.httpPorts}
|
||||
|
||||
https_ports = ${toLua cfg.httpsPorts}
|
||||
|
||||
${lib.concatMapStrings (muc: ''
|
||||
Component ${toLua muc.domain} "muc"
|
||||
modules_enabled = { "muc_mam"; ${optionalString muc.vcard_muc ''"vcard_muc";'' } }
|
||||
name = ${toLua muc.name}
|
||||
restrict_room_creation = ${toLua muc.restrictRoomCreation}
|
||||
max_history_messages = ${toLua muc.maxHistoryMessages}
|
||||
muc_room_locking = ${toLua muc.roomLocking}
|
||||
muc_room_lock_timeout = ${toLua muc.roomLockTimeout}
|
||||
muc_tombstones = ${toLua muc.tombstones}
|
||||
muc_tombstone_expiry = ${toLua muc.tombstoneExpiry}
|
||||
muc_room_default_public = ${toLua muc.roomDefaultPublic}
|
||||
muc_room_default_members_only = ${toLua muc.roomDefaultMembersOnly}
|
||||
muc_room_default_moderated = ${toLua muc.roomDefaultModerated}
|
||||
muc_room_default_public_jids = ${toLua muc.roomDefaultPublicJids}
|
||||
muc_room_default_change_subject = ${toLua muc.roomDefaultChangeSubject}
|
||||
muc_room_default_history_length = ${toLua muc.roomDefaultHistoryLength}
|
||||
muc_room_default_language = ${toLua muc.roomDefaultLanguage}
|
||||
|
||||
'') cfg.muc}
|
||||
|
||||
${ lib.optionalString (cfg.uploadHttp != null) ''
|
||||
Component ${toLua cfg.uploadHttp.domain} "http_upload"
|
||||
http_upload_file_size_limit = ${cfg.uploadHttp.uploadFileSizeLimit}
|
||||
http_upload_expire_after = ${cfg.uploadHttp.uploadExpireAfter}
|
||||
${lib.optionalString (cfg.uploadHttp.userQuota != null) "http_upload_quota = ${toLua cfg.uploadHttp.userQuota}"}
|
||||
http_upload_path = ${toLua cfg.uploadHttp.httpUploadPath}
|
||||
''}
|
||||
|
||||
${ cfg.extraConfig }
|
||||
|
||||
${ lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: ''
|
||||
@ -522,9 +856,22 @@ in
|
||||
PIDFile = "/run/prosody/prosody.pid";
|
||||
ExecStart = "${cfg.package}/bin/prosodyctl start";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
|
||||
MemoryDenyWriteExecute = true;
|
||||
PrivateDevices = true;
|
||||
PrivateMounts = true;
|
||||
PrivateTmp = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
meta.doc = ./prosody.xml;
|
||||
}
|
||||
|
88
nixos/modules/services/networking/prosody.xml
Normal file
88
nixos/modules/services/networking/prosody.xml
Normal file
@ -0,0 +1,88 @@
|
||||
<chapter xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
version="5.0"
|
||||
xml:id="module-services-prosody">
|
||||
<title>Prosody</title>
|
||||
<para>
|
||||
<link xlink:href="https://prosody.im/">Prosody</link> is an open-source, modern XMPP server.
|
||||
</para>
|
||||
<section xml:id="module-services-prosody-basic-usage">
|
||||
<title>Basic usage</title>
|
||||
|
||||
<para>
|
||||
A common struggle for most XMPP newcomers is to find the right set
|
||||
of XMPP Extensions (XEPs) to setup. Forget to activate a few of
|
||||
those and your XMPP experience might turn into a nightmare!
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The XMPP community tackles this problem by creating a meta-XEP
|
||||
listing a decent set of XEPs you should implement. This meta-XEP
|
||||
is issued every year, the 2020 edition being
|
||||
<link xlink:href="https://xmpp.org/extensions/xep-0423.html">XEP-0423</link>.
|
||||
</para>
|
||||
<para>
|
||||
The NixOS Prosody module will implement most of these recommendend XEPs out of
|
||||
the box. That being said, two components still require some
|
||||
manual configuration: the
|
||||
<link xlink:href="https://xmpp.org/extensions/xep-0045.html">Multi User Chat (MUC)</link>
|
||||
and the <link xlink:href="https://xmpp.org/extensions/xep-0363.html">HTTP File Upload</link> ones.
|
||||
You'll need to create a DNS subdomain for each of those. The current convention is to name your
|
||||
MUC endpoint <literal>conference.example.org</literal> and your HTTP upload domain <literal>upload.example.org</literal>.
|
||||
</para>
|
||||
<para>
|
||||
A good configuration to start with, including a
|
||||
<link xlink:href="https://xmpp.org/extensions/xep-0045.html">Multi User Chat (MUC)</link>
|
||||
endpoint as well as a <link xlink:href="https://xmpp.org/extensions/xep-0363.html">HTTP File Upload</link>
|
||||
endpoint will look like this:
|
||||
<programlisting>
|
||||
services.prosody = {
|
||||
<link linkend="opt-services.prosody.enable">enable</link> = true;
|
||||
<link linkend="opt-services.prosody.admins">admins</link> = [ "root@example.org" ];
|
||||
<link linkend="opt-services.prosody.ssl.cert">ssl.cert</link> = "/var/lib/acme/example.org/fullchain.pem";
|
||||
<link linkend="opt-services.prosody.ssl.key">ssl.key</link> = "/var/lib/acme/example.org/key.pem";
|
||||
<link linkend="opt-services.prosody.virtualHosts">virtualHosts</link>."example.org" = {
|
||||
<link linkend="opt-services.prosody.virtualHosts._name__.enabled">enabled</link> = true;
|
||||
<link linkend="opt-services.prosody.virtualHosts._name__.domain">domain</link> = "example.org";
|
||||
<link linkend="opt-services.prosody.virtualHosts._name__.ssl.cert">ssl.cert</link> = "/var/lib/acme/example.org/fullchain.pem";
|
||||
<link linkend="opt-services.prosody.virtualHosts._name__.ssl.key">ssl.key</link> = "/var/lib/acme/example.org/key.pem";
|
||||
};
|
||||
<link linkend="opt-services.prosody.muc">muc</link> = [ {
|
||||
<link linkend="opt-services.prosody.muc">domain</link> = "conference.example.org";
|
||||
} ];
|
||||
<link linkend="opt-services.prosody.uploadHttp">uploadHttp</link> = {
|
||||
<link linkend="opt-services.prosody.uploadHttp.domain">domain</link> = "upload.example.org";
|
||||
};
|
||||
};</programlisting>
|
||||
</para>
|
||||
</section>
|
||||
<section xml:id="module-services-prosody-letsencrypt">
|
||||
<title>Let's Encrypt Configuration</title>
|
||||
<para>
|
||||
As you can see in the code snippet from the
|
||||
<link linkend="module-services-prosody-basic-usage">previous section</link>,
|
||||
you'll need a single TLS certificate covering your main endpoint,
|
||||
the MUC one as well as the HTTP Upload one. We can generate such a
|
||||
certificate by leveraging the ACME
|
||||
<link linkend="opt-security.acme.certs._name_.extraDomains">extraDomains</link> module option.
|
||||
</para>
|
||||
<para>
|
||||
Provided the setup detailed in the previous section, you'll need the following acme configuration to generate
|
||||
a TLS certificate for the three endponits:
|
||||
<programlisting>
|
||||
security.acme = {
|
||||
<link linkend="opt-security.acme.email">email</link> = "root@example.org";
|
||||
<link linkend="opt-security.acme.acceptTerms">acceptTerms</link> = true;
|
||||
<link linkend="opt-security.acme.certs">certs</link> = {
|
||||
"example.org" = {
|
||||
<link linkend="opt-security.acme.certs._name_.webroot">webroot</link> = "/var/www/example.org";
|
||||
<link linkend="opt-security.acme.certs._name_.email">email</link> = "root@example.org";
|
||||
<link linkend="opt-security.acme.certs._name_.extraDomains">extraDomains."conference.example.org"</link> = null;
|
||||
<link linkend="opt-security.acme.certs._name_.extraDomains">extraDomains."upload.example.org"</link> = null;
|
||||
};
|
||||
};
|
||||
};</programlisting>
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
@ -1,27 +1,80 @@
|
||||
import ../make-test-python.nix {
|
||||
name = "prosody";
|
||||
let
|
||||
cert = pkgs: pkgs.runCommandNoCC "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
|
||||
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=example.com/CN=uploads.example.com/CN=conference.example.com'
|
||||
mkdir -p $out
|
||||
cp key.pem cert.pem $out
|
||||
'';
|
||||
createUsers = pkgs: pkgs.writeScriptBin "create-prosody-users" ''
|
||||
#!${pkgs.bash}/bin/bash
|
||||
set -e
|
||||
|
||||
# Creates and set password for the 2 xmpp test users.
|
||||
#
|
||||
# Doing that in a bash script instead of doing that in the test
|
||||
# script allow us to easily provision the users when running that
|
||||
# test interactively.
|
||||
|
||||
prosodyctl register cthon98 example.com nothunter2
|
||||
prosodyctl register azurediamond example.com hunter2
|
||||
'';
|
||||
delUsers = pkgs: pkgs.writeScriptBin "delete-prosody-users" ''
|
||||
#!${pkgs.bash}/bin/bash
|
||||
set -e
|
||||
|
||||
# Deletes the test users.
|
||||
#
|
||||
# Doing that in a bash script instead of doing that in the test
|
||||
# script allow us to easily provision the users when running that
|
||||
# test interactively.
|
||||
|
||||
prosodyctl deluser cthon98@example.com
|
||||
prosodyctl deluser azurediamond@example.com
|
||||
'';
|
||||
in import ../make-test-python.nix {
|
||||
name = "prosody";
|
||||
nodes = {
|
||||
client = { nodes, pkgs, ... }: {
|
||||
client = { nodes, pkgs, config, ... }: {
|
||||
security.pki.certificateFiles = [ "${cert pkgs}/cert.pem" ];
|
||||
console.keyMap = "fr-bepo";
|
||||
networking.extraHosts = ''
|
||||
${nodes.server.config.networking.primaryIPAddress} example.com
|
||||
${nodes.server.config.networking.primaryIPAddress} conference.example.com
|
||||
${nodes.server.config.networking.primaryIPAddress} uploads.example.com
|
||||
'';
|
||||
environment.systemPackages = [
|
||||
(pkgs.callPackage ./xmpp-sendmessage.nix { connectTo = nodes.server.config.networking.primaryIPAddress; })
|
||||
];
|
||||
};
|
||||
server = { config, pkgs, ... }: {
|
||||
security.pki.certificateFiles = [ "${cert pkgs}/cert.pem" ];
|
||||
console.keyMap = "fr-bepo";
|
||||
networking.extraHosts = ''
|
||||
${config.networking.primaryIPAddress} example.com
|
||||
${config.networking.primaryIPAddress} conference.example.com
|
||||
${config.networking.primaryIPAddress} uploads.example.com
|
||||
'';
|
||||
networking.firewall.enable = false;
|
||||
environment.systemPackages = [
|
||||
(createUsers pkgs)
|
||||
(delUsers pkgs)
|
||||
];
|
||||
services.prosody = {
|
||||
enable = true;
|
||||
# TODO: use a self-signed certificate
|
||||
c2sRequireEncryption = false;
|
||||
extraConfig = ''
|
||||
storage = "sql"
|
||||
'';
|
||||
virtualHosts.test = {
|
||||
ssl.cert = "${cert pkgs}/cert.pem";
|
||||
ssl.key = "${cert pkgs}/key.pem";
|
||||
virtualHosts.example = {
|
||||
domain = "example.com";
|
||||
enabled = true;
|
||||
ssl.cert = "${cert pkgs}/cert.pem";
|
||||
ssl.key = "${cert pkgs}/key.pem";
|
||||
};
|
||||
muc = [
|
||||
{
|
||||
domain = "conference.example.com";
|
||||
}
|
||||
];
|
||||
uploadHttp = {
|
||||
domain = "uploads.example.com";
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -31,16 +84,8 @@ import ../make-test-python.nix {
|
||||
server.wait_for_unit("prosody.service")
|
||||
server.succeed('prosodyctl status | grep "Prosody is running"')
|
||||
|
||||
# set password to 'nothunter2' (it's asked twice)
|
||||
server.succeed("yes nothunter2 | prosodyctl adduser cthon98@example.com")
|
||||
# set password to 'y'
|
||||
server.succeed("yes | prosodyctl adduser azurediamond@example.com")
|
||||
# correct password to "hunter2"
|
||||
server.succeed("yes hunter2 | prosodyctl passwd azurediamond@example.com")
|
||||
|
||||
client.succeed("send-message")
|
||||
|
||||
server.succeed("prosodyctl deluser cthon98@example.com")
|
||||
server.succeed("prosodyctl deluser azurediamond@example.com")
|
||||
server.succeed("create-prosody-users")
|
||||
client.succeed('send-message 2>&1 | grep "XMPP SCRIPT TEST SUCCESS"')
|
||||
server.succeed("delete-prosody-users")
|
||||
'';
|
||||
}
|
||||
|
@ -1,46 +1,61 @@
|
||||
{ writeScriptBin, python3, connectTo ? "localhost" }:
|
||||
writeScriptBin "send-message" ''
|
||||
#!${(python3.withPackages (ps: [ ps.sleekxmpp ])).interpreter}
|
||||
# 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
|
||||
{ writeScriptBin, writeText, python3, connectTo ? "localhost" }:
|
||||
let
|
||||
dummyFile = writeText "dummy-file" ''
|
||||
Dear dog,
|
||||
|
||||
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)
|
||||
Please find this *really* important attachment.
|
||||
|
||||
self.recipient = recipient
|
||||
self.msg = message
|
||||
Yours truly,
|
||||
John
|
||||
'';
|
||||
in writeScriptBin "send-message" ''
|
||||
#!${(python3.withPackages (ps: [ ps.slixmpp ])).interpreter}
|
||||
import logging
|
||||
import sys
|
||||
from types import MethodType
|
||||
|
||||
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)
|
||||
from slixmpp import ClientXMPP
|
||||
from slixmpp.exceptions import IqError, IqTimeout
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
xmpp = SendMsgBot("cthon98@example.com", "nothunter2", "azurediamond@example.com", "hey, if you type in your pw, it will show as stars")
|
||||
xmpp.register_plugin('xep_0030') # Service Discovery
|
||||
xmpp.register_plugin('xep_0199') # XMPP Ping
|
||||
class CthonTest(ClientXMPP):
|
||||
|
||||
# TODO: verify certificate
|
||||
# If you want to verify the SSL certificates offered by a server:
|
||||
# xmpp.ca_certs = "path/to/ca/cert"
|
||||
def __init__(self, jid, password):
|
||||
ClientXMPP.__init__(self, jid, password)
|
||||
self.add_event_handler("session_start", self.session_start)
|
||||
|
||||
if xmpp.connect(('${connectTo}', 5222)):
|
||||
xmpp.process(block=True)
|
||||
else:
|
||||
print("Unable to connect.")
|
||||
sys.exit(1)
|
||||
async def session_start(self, event):
|
||||
log = logging.getLogger(__name__)
|
||||
self.send_presence()
|
||||
self.get_roster()
|
||||
# Sending a test message
|
||||
self.send_message(mto="azurediamond@example.com", mbody="Hello, this is dog.", mtype="chat")
|
||||
log.info('Message sent')
|
||||
|
||||
# Test http upload (XEP_0363)
|
||||
def timeout_callback(arg):
|
||||
log.error("ERROR: Cannot upload file. XEP_0363 seems broken")
|
||||
sys.exit(1)
|
||||
url = await self['xep_0363'].upload_file("${dummyFile}",timeout=10, timeout_callback=timeout_callback)
|
||||
log.info('Upload success!')
|
||||
# Test MUC
|
||||
self.plugin['xep_0045'].join_muc('testMucRoom', 'cthon98', wait=True)
|
||||
log.info('MUC join success!')
|
||||
log.info('XMPP SCRIPT TEST SUCCESS')
|
||||
self.disconnect(wait=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format='%(levelname)-8s %(message)s')
|
||||
|
||||
ct = CthonTest('cthon98@example.com', 'nothunter2')
|
||||
ct.register_plugin('xep_0071')
|
||||
ct.register_plugin('xep_0128')
|
||||
# HTTP Upload
|
||||
ct.register_plugin('xep_0363')
|
||||
# MUC
|
||||
ct.register_plugin('xep_0045')
|
||||
ct.connect(("server", 5222))
|
||||
ct.process(forever=False)
|
||||
''
|
||||
|
@ -1,4 +1,4 @@
|
||||
{ stdenv, fetchurl, libidn, openssl, makeWrapper, fetchhg
|
||||
{ stdenv, fetchurl, lib, libidn, openssl, makeWrapper, fetchhg
|
||||
, lua5, luasocket, luasec, luaexpat, luafilesystem, luabitop
|
||||
, withLibevent ? true, luaevent ? null
|
||||
, withDBI ? true, luadbi ? null
|
||||
@ -16,7 +16,16 @@ with stdenv.lib;
|
||||
stdenv.mkDerivation rec {
|
||||
version = "0.11.5"; # also update communityModules
|
||||
pname = "prosody";
|
||||
|
||||
# The following community modules are necessary for the nixos module
|
||||
# prosody module to comply with XEP-0423 and provide a working
|
||||
# default setup.
|
||||
nixosModuleDeps = [
|
||||
"bookmarks"
|
||||
"cloud_notify"
|
||||
"vcard_muc"
|
||||
"smacks"
|
||||
"http_upload"
|
||||
];
|
||||
src = fetchurl {
|
||||
url = "https://prosody.im/downloads/source/${pname}-${version}.tar.gz";
|
||||
sha256 = "12s0hn6hvjbi61cdw3165l6iw0878971dmlvfg663byjsmjvvy2m";
|
||||
@ -52,7 +61,7 @@ stdenv.mkDerivation rec {
|
||||
postInstall = ''
|
||||
${concatMapStringsSep "\n" (module: ''
|
||||
cp -r $communityModules/mod_${module} $out/lib/prosody/modules/
|
||||
'') (withCommunityModules ++ withOnlyInstalledCommunityModules)}
|
||||
'') (lib.lists.unique(nixosModuleDeps ++ withCommunityModules ++ withOnlyInstalledCommunityModules))}
|
||||
wrapProgram $out/bin/prosody \
|
||||
--prefix LUA_PATH ';' "$LUA_PATH" \
|
||||
--prefix LUA_CPATH ';' "$LUA_CPATH"
|
||||
|
Loading…
Reference in New Issue
Block a user