diasporaTool = ./websites/tools/diaspora;
etherTool = ./websites/tools/ether;
gitTool = ./websites/tools/git;
+ imTool = ./websites/tools/im;
mastodonTool = ./websites/tools/mastodon;
mgoblinTool = ./websites/tools/mgoblin;
peertubeTool = ./websites/tools/peertube;
dns = ./dns.nix;
ftp = ./ftp.nix;
mpd = ./mpd.nix;
+ ejabberd = ./ejabberd;
ssh = ./ssh;
monitoring = ./monitoring;
--- /dev/null
+{ lib, pkgs, config, ... }:
+let
+ cfg = config.myServices.ejabberd;
+in
+{
+ options.myServices = {
+ ejabberd.enable = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Whether to enable ejabberd service.
+ '';
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ security.acme.certs = {
+ "ejabberd" = config.myServices.certificates.certConfig // {
+ user = "ejabberd";
+ group = "ejabberd";
+ domain = "eldiron.immae.eu";
+ postRun = ''
+ systemctl restart ejabberd.service
+ '';
+ extraDomains = {
+ "immae.fr" = null;
+ "conference.immae.fr" = null;
+ "proxy.immae.fr" = null;
+ "pubsub.immae.fr" = null;
+ "upload.immae.fr" = null;
+ };
+ };
+ };
+ networking.firewall.allowedTCPPorts = [ 5222 5269 ];
+ myServices.websites.tools.im.enable = true;
+ systemd.services.ejabberd.postStop = ''
+ rm /var/log/ejabberd/erl_crash*.dump
+ '';
+ secrets.keys = [
+ {
+ dest = "ejabberd/psql.yml";
+ permissions = "0400";
+ user = "ejabberd";
+ group = "ejabberd";
+ text = ''
+ sql_type: pgsql
+ sql_server: "localhost"
+ sql_database: "${config.myEnv.jabber.postgresql.database}"
+ sql_username: "${config.myEnv.jabber.postgresql.user}"
+ sql_password: "${config.myEnv.jabber.postgresql.password}"
+ '';
+ }
+ {
+ dest = "ejabberd/host.yml";
+ permissions = "0400";
+ user = "ejabberd";
+ group = "ejabberd";
+ text = ''
+ host_config:
+ "immae.fr":
+ domain_certfile: "${config.security.acme.directory}/ejabberd/full.pem"
+ auth_method: [ldap]
+ ldap_servers: ["${config.myEnv.jabber.ldap.host}"]
+ ldap_encrypt: tls
+ ldap_rootdn: "${config.myEnv.jabber.ldap.dn}"
+ ldap_password: "${config.myEnv.jabber.ldap.password}"
+ ldap_base: "${config.myEnv.jabber.ldap.base}"
+ ldap_uids:
+ - "uid": "%u"
+ - "immaeXmppUid": "%u"
+ ldap_filter: "${config.myEnv.jabber.ldap.filter}"
+ '';
+ }
+ ];
+ users.users.ejabberd.extraGroups = [ "keys" ];
+ services.ejabberd = {
+ package = pkgs.ejabberd.override { withPgsql = true; };
+ imagemagick = true;
+ enable = true;
+ ctlConfig = ''
+ ERLANG_NODE=ejabberd@localhost
+ '';
+ configFile = pkgs.runCommand "ejabberd.yml" {
+ certificatePrivateKeyAndFullChain = "${config.security.acme.directory}/ejabberd/full.pem";
+ certificateCA = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
+ sql_config_file = config.secrets.fullPaths."ejabberd/psql.yml";
+ host_config_file = config.secrets.fullPaths."ejabberd/host.yml";
+ } ''
+ substituteAll ${./ejabberd.yml} $out
+ '';
+ };
+ };
+}
--- /dev/null
+###
+### ejabberd configuration file
+###
+### The parameters used in this configuration file are explained at
+###
+### https://docs.ejabberd.im/admin/configuration
+###
+### The configuration file is written in YAML.
+### *******************************************************
+### ******* !!! WARNING !!! *******
+### ******* YAML IS INDENTATION SENSITIVE *******
+### ******* MAKE SURE YOU INDENT SECTIONS CORRECTLY *******
+### *******************************************************
+### Refer to http://en.wikipedia.org/wiki/YAML for the brief description.
+### However, ejabberd treats different literals as different types:
+###
+### - unquoted or single-quoted strings. They are called "atoms".
+### Example: dog, 'Jupiter', '3.14159', YELLOW
+###
+### - numeric literals. Example: 3, -45.0, .0
+###
+### - quoted or folded strings.
+### Examples of quoted string: "Lizzard", "orange".
+### Example of folded string:
+### > Art thou not Romeo,
+### and a Montague?
+###
+
+hosts:
+ - "immae.fr"
+
+loglevel: 4
+log_rotate_size: 10485760
+log_rotate_date: ""
+log_rotate_count: 1
+log_rate_limit: 100
+
+certfiles:
+ - "@certificatePrivateKeyAndFullChain@"
+
+listen:
+ -
+ port: 5222
+ ip: "::"
+ module: ejabberd_c2s
+ max_stanza_size: 262144
+ shaper: c2s_shaper
+ access: c2s
+ starttls_required: true
+ -
+ port: 5269
+ ip: "::"
+ module: ejabberd_s2s_in
+ max_stanza_size: 524288
+ -
+ port: 5280
+ ip: "127.0.0.1"
+ module: ejabberd_http
+ request_handlers:
+ "/admin": ejabberd_web_admin
+ "/api": mod_http_api
+ "/bosh": mod_bosh
+ "/captcha": ejabberd_captcha
+ "/upload": mod_http_upload
+ "/ws": ejabberd_http_ws
+ tls: false
+
+s2s_use_starttls: optional
+s2s_cafile: "@certificateCA@"
+
+default_db: sql
+sql_type: pgsql
+include_config_file: @sql_config_file@
+include_config_file: @host_config_file@
+new_sql_schema: true
+
+acl:
+ admin:
+ - user: "ismael@immae.fr"
+ local:
+ user_regexp: ""
+ loopback:
+ ip:
+ - "127.0.0.0/8"
+ - "::1/128"
+ - "::FFFF:127.0.0.1/128"
+
+access_rules:
+ local:
+ - allow: local
+ c2s:
+ - deny: blocked
+ - allow
+ announce:
+ - allow: admin
+ configure:
+ - allow: admin
+ muc_admin:
+ - allow: admin
+ muc_create:
+ - allow: local
+ muc:
+ - allow
+ pubsub_createnode:
+ - allow: local
+ register:
+ - deny
+ trusted_network:
+ - allow: loopback
+
+api_permissions:
+ "console commands":
+ from:
+ - ejabberd_ctl
+ who: all
+ what: "*"
+ "admin access":
+ who:
+ - acl: admin
+ - oauth:
+ - scope: "ejabberd:admin"
+ - acl: admin
+ what:
+ - "*"
+ - "!stop"
+ - "!start"
+ "public commands":
+ who:
+ - ip:
+ - "0.0.0.0"
+ - "::"
+ what:
+ - "status"
+ - "connected_users_number"
+
+shaper:
+ normal: 1000
+ fast: 50000
+
+shaper_rules:
+ max_user_sessions: 10
+ max_user_offline_messages:
+ - 5000: admin
+ - 100
+ c2s_shaper:
+ - none: admin
+ - normal
+ s2s_shaper: fast
+
+modules:
+ mod_adhoc: {}
+ mod_admin_extra: {}
+ mod_announce:
+ access: announce
+ mod_avatar: {}
+ mod_blocking: {}
+ mod_bosh: {}
+ mod_caps: {}
+ mod_carboncopy: {}
+ mod_client_state: {}
+ mod_configure: {}
+ mod_disco: {}
+ mod_fail2ban: {}
+ mod_http_api: {}
+ mod_http_upload:
+ put_url: "https://im.immae.fr/upload"
+ custom_headers:
+ "Access-Control-Allow-Origin": "*"
+ "Access-Control-Allow-Methods": "OPTIONS, HEAD, GET, PUT, POST"
+ "Access-Control-Allow-Headers": "Content-Type"
+ mod_last: {}
+ mod_mam:
+ default: always
+ mod_muc:
+ access:
+ - allow
+ access_admin:
+ - allow: admin
+ access_create: muc_create
+ access_persistent: muc_create
+ default_room_options:
+ mam: true
+ mod_muc_admin: {}
+ mod_offline:
+ access_max_user_messages: max_user_offline_messages
+ mod_ping: {}
+ mod_privacy: {}
+ mod_private: {}
+ mod_proxy65:
+ access: local
+ max_connections: 5
+ mod_pubsub:
+ access_createnode: pubsub_createnode
+ plugins:
+ - "flat"
+ - "hometree"
+ - "pep"
+ force_node_config:
+ ## Change from "whitelist" to "open" to enable OMEMO support
+ ## See https://github.com/processone/ejabberd/issues/2425
+ "eu.siacs.conversations.axolotl.*":
+ access_model: open
+ ## Avoid buggy clients to make their bookmarks public
+ "storage:bookmarks":
+ access_model: whitelist
+ mod_push: {}
+ mod_push_keepalive: {}
+ mod_register:
+ ## Only accept registration requests from the "trusted"
+ ## network (see access_rules section above).
+ ## Think twice before enabling registration from any
+ ## address. See the Jabber SPAM Manifesto for details:
+ ## https://github.com/ge0rg/jabber-spam-fighting-manifesto
+ ip_access: trusted_network
+ access: register
+ mod_roster:
+ versioning: true
+ mod_s2s_dialback: {}
+ mod_shared_roster: {}
+ mod_stats: {}
+ mod_stream_mgmt:
+ resend_on_timeout: if_offline
+ mod_time: {}
+ mod_vcard: {}
+ mod_vcard_xupdate: {}
+ mod_version:
+ show_os: false
+
+### Local Variables:
+### mode: yaml
+### End:
+### vim: set filetype=yaml tabstop=8
+
myServices.certificates.enable = true;
myServices.websites.enable = true;
myServices.mail.enable = true;
+ myServices.ejabberd.enable = true;
services.pure-ftpd.enable = true;
services.duplyBackup.enable = true;
--- /dev/null
+{ config, lib, ... }:
+let
+ cfg = config.myServices.websites.tools.im;
+in
+{
+ options.myServices.websites.tools.im = {
+ enable = lib.mkEnableOption "enable im website";
+ };
+
+ config = lib.mkIf cfg.enable {
+ services.websites.env.tools.vhostConfs.im = {
+ certName = "eldiron";
+ addToCerts = true;
+ hosts = ["im.immae.fr"];
+ root = ./www;
+ extraConfig = [
+ ''
+ Alias /converse ${./www}/converse.html
+ ProxyPreserveHost On
+ <Location "/bosh">
+ ProxyPass http://localhost:5280/bosh
+ ProxyPassReverse http://localhost:5280/bosh
+ </Location>
+ <Location "/ws">
+ ProxyPass ws://localhost:5280/ws
+ </Location>
+ ProxyPass /upload http://localhost:5280/upload
+ ProxyPassReverse /upload http://localhost:5280/upload
+ ProxyPass /admin http://localhost:5280/admin
+ ProxyPassReverse /admin http://localhost:5280/admin
+ ProxyPass /api http://localhost:5280/api
+ ProxyPassReverse /api http://localhost:5280/api
+ ''
+ ];
+ };
+ };
+}
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <title>Converse</title>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta name="description" content="Converse: An XMPP chat client which can be integrated into any website" />
+ <meta name="keywords" content="xmpp chat webchat converse.js Converse" />
+
+ <link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/5.0.4/dist/converse.min.css" />
+ <style type="text/css">
+ #conversejs div.chat-msg__text pre {
+ background-color: #eee;
+ padding: 10px;
+ }
+ </style>
+ <script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>
+ <script src="https://cdn.conversejs.org/5.0.4/dist/converse.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/10.0.0/markdown-it.min.js"></script>
+ <script>
+ var md = window.markdownit({
+ html: true,
+ });
+ converse.plugins.add('markdown', {
+ //initialize () {
+ // const { _converse } = this;
+ // _converse.api.listen.on('afterMessageBodyTransformed', (view, text) => {
+ // let newtext = text.replace(/<br\/>/g, '\n').replace(/```/g, "\n```\n");
+ // newtext = md.render(newtext).replace(/^<p>/, "").replace(/<\/p>\s*$/, "");
+ // view.model.save({'message': newtext}, {'silent': true });
+ // });
+ //}
+ overrides: {
+ MessageView: {
+ transformBodyText: async function(text) {
+ let newtext = await this.__super__.transformBodyText.apply(this, arguments);
+ newtext = newtext.replace(/<br\/>/g, '\n').replace(/```/g, "\n```\n");
+ return md.render(newtext).replace(/^<p>/, "").replace(/<\/p>\s*$/, "");
+ }
+ }
+ }
+ });
+ converse.initialize({
+ //bosh_service_url: 'https://im.immae.fr/bosh',
+ websocket_url: 'wss://im.immae.fr/ws',
+ view_mode: 'fullscreen',
+ show_controlbox_by_default: true,
+ whitelisted_plugins: ['markdown'],
+ });
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
+
--- /dev/null
+<!doctype html>
+<html lang="fr">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Instant messaging configuration (Jabber/XMPP)</title>
+ <style type="text/css">
+ body {
+ padding-top: 1em;
+ padding-left: 5px;
+ padding-right: 5px;
+ text-align: left;
+ margin: auto;
+ font: 20px Helvetica, sans-serif;
+ color: #333;
+ height: 100%;
+ min-height: 100%;
+ }
+ article {
+ text-align: justify;
+ display: block;
+ max-width: 850px;
+ margin: 0 auto;
+ padding-top: 30px;
+ }
+ span.code {
+ font-family: monospace;
+ }
+ </style>
+ </head>
+ <body>
+ <p>
+ Pre-configured clients:
+ <ul>
+ <li><a href="converse">Converse client</a></li>
+ </ul>
+ </p>
+ <p>
+ Technical details:
+ <ul>
+ <li>HTTP-bind/BOSH access (XEP-0124 / XEP-0206): <span class="code">https://im.immae.fr/bosh</span></li>
+ <li>WS access (RFC 7395): <span class="code">wss://im.immae.fr/ws</span></li>
+ </ul>
+ </p>
+ </body>
+</html>
extraConfig = [
''
RedirectMatch 301 ^/roundcube(.*)$ https://mail.immae.eu/roundcube$1
+ RedirectMatch 301 ^/jappix(.*)$ https://im.immae.fr/converse
<Directory "/var/lib/ftp/tools.immae.eu">
DirectoryIndex index.php index.htm index.html
services.websites.env.tools.vhostConfs.outils = {
certName = "eldiron";
addToCerts = true;
- hosts = [ "outils.immae.eu" ];
+ hosts = [ "outils.immae.eu" "outils.immae.fr" ];
root = null;
extraConfig = [
''
RedirectMatch 301 ^/roundcube(.*)$ https://mail.immae.eu/roundcube$1
+ RedirectMatch 301 ^/jappix(.*)$ https://im.immae.fr/converse
+
RedirectMatch 301 ^/(.*)$ https://tools.immae.eu/$1
''
];