XMPP
We are using XMPP for real-time communication. This page guides you through setting up the ejabberd XMPP Server.
Jail creation
As with most other services. Let’s start by creating a jail for ejabberd, install the ejabberd package and enabling the service.
bastille create ejabberd 14.2-RELEASE 10.0.0.30/8 bastille0
bastille pkg ejabberd install ejabberd
bastille sysrc ejabberd ejabberd_enable=YESMake a copy of the example config files
cp /usr/local/bastille/jails/ejabberd/root/usr/local/etc/ejabberd/ejabberd.yml.example /usr/local/bastille/jails/ejabberd/root/usr/local/etc/ejabberd/ejabberd.yml
cp /usr/local/bastille/jails/ejabberd/root/usr/local/etc/ejabberd/ejabberdctl.cfg.example /usr/local/bastille/jails/ejabberd/root/usr/local/etc/ejabberd/ejabberdctl.cfgProxy & Certificates
Similar to our mailserver, ejabberd also requires access to the TLS certificates stored by caddy. The process will be similar. First, create a DNS A record for xmpp.<yourdomain.tld> pointing to the host. Then edit the caddy config:
micro /usr/local/bastille/jails/caddy/root/usr/local/etc/caddy/CaddyfileAdd the following entries to the Caddyfile
xmpp.ezdk.org {
reverse_proxy 10.0.0.30:5280
}Reload the caddy config:
bastille service caddy caddy reloadAnd from your local workstation send a request to the subdomain:
curl xmpp.ezdk.orgNow we can mount the directory containing the certificate and key for xmpp.<yourdomain.tld> into the ejabberd jail.
bastille mount ejabberd /usr/local/certificates/acme-v02.api.letsencrypt.org-directory/xmpp.ezdk.org /usr/local/certs nullfs ro 0 0ejabberd configuration
First, we need to change the user under which ejabberd runs. For this, we need to edit the service file
micro /usr/local/bastille/jails/ejabberd/root/usr/local/etc/rc.d/ejabberdand change the variable EJABBERDUSER to root
EJABBERDUSER=rootCaution
It is specifically not recommended to run the ejabberd service as root. But we have to use the same user which runs caddy (to read the certificates). NOTE FROM/TO AUTHORS: At least change the user for caddy/stalwart/ejabberd/etc to www
Edit the ejabberd config file:
micro /usr/local/bastille/jails/ejabberd/root/usr/local/etc/ejabberd/ejabberd.ymlHere we want to do the following adjustments:
- Set our xmpp.* subdomain to host.
- Set the paths to the certificates
- Set the ip for all listeners to the jails internal ip address
- Put all ejabberd_http listeners on port 5280 and disable TLS (since caddy handles it for us)
- Add the internal jail ip to acl.loopback.ip and to api_permissions.‘public commands’.who.ip
- Add ACL Rules for our admin account (that we will create afterwards)
This should result in a config file like this:
###
### 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.
###
hosts:
- xmpp.ezdk.org
loglevel: info
## If you already have certificates, list them here
certfiles:
- /usr/local/certs/xmpp.ezdk.org.crt
- /usr/local/certs/xmpp.ezdk.org.key
listen:
-
port: 5222
ip: "10.0.0.30"
module: ejabberd_c2s
max_stanza_size: 262144
shaper: c2s_shaper
access: c2s
starttls_required: true
-
port: 5223
ip: "10.0.0.30"
module: ejabberd_c2s
max_stanza_size: 262144
shaper: c2s_shaper
access: c2s
tls: true
-
port: 5269
ip: "10.0.0.30"
module: ejabberd_s2s_in
max_stanza_size: 524288
shaper: s2s_shaper
-
port: 5280
ip: "10.0.0.30"
module: ejabberd_http
tls: false
request_handlers:
/admin: ejabberd_web_admin
/api: mod_http_api
/bosh: mod_bosh
/captcha: ejabberd_captcha
/upload: mod_http_upload
/ws: ejabberd_http_ws
-
port: 5478
ip: "10.0.0.30"
transport: udp
module: ejabberd_stun
use_turn: true
## The server's public IPv4 address:
turn_ipv4_address: "152.53.126.184"
## The server's public IPv6 address:
# turn_ipv6_address: "2001:db8::3"
-
port: 1883
ip: "10.0.0.30"
module: mod_mqtt
backlog: 1000
s2s_use_starttls: optional
acl:
local:
user_regexp: ""
loopback:
ip:
- 10.0.0.30/8
- 127.0.0.0/8
- ::1/128
admin:
user: admin@xmpp.ezdk.org
access_rules:
local:
allow: local
c2s:
deny: blocked
allow: all
announce:
allow: admin
configure:
allow: admin
muc_create:
allow: local
pubsub_createnode:
allow: local
trusted_network:
allow: loopback
api_permissions:
"console commands":
from: ejabberd_ctl
who: all
what: "*"
"webadmin commands":
from: ejabberd_web_admin
who: admin
what: "*"
"adhoc commands":
from: mod_adhoc_api
who: admin
what: "*"
"http access":
from: mod_http_api
who:
access:
allow:
- acl: loopback
- acl: admin
oauth:
scope: "ejabberd:admin"
access:
allow:
- acl: loopback
- acl: admin
what:
- "*"
- "!stop"
- "!start"
"public commands":
who:
ip:
- 10.0.0.30/8
- 127.0.0.1/8
what:
- status
- connected_users_number
shaper:
normal:
rate: 3000
burst_size: 20000
fast: 100000
shaper_rules:
max_user_sessions: 10
max_user_offline_messages:
5000: admin
100: all
c2s_shaper:
none: admin
normal: all
s2s_shaper: fast
modules:
mod_adhoc: {}
mod_adhoc_api: {}
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://@HOST@:5443/upload
custom_headers:
"Access-Control-Allow-Origin": "https://@HOST@"
"Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
"Access-Control-Allow-Headers": "Content-Type"
mod_last: {}
mod_mam:
## Mnesia is limited to 2GB, better to use an SQL backend
## For small servers SQLite is a good fit and is very easy
## to configure. Uncomment this when you have SQL configured:
## db_type: sql
assume_mam_usage: true
default: always
mod_mqtt: {}
mod_muc:
access:
- allow
access_admin:
- allow: admin
access_create: muc_create
access_persistent: muc_create
access_mam:
- allow
default_room_options:
mam: true
mod_muc_admin: {}
mod_muc_occupantid: {}
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
- pep
force_node_config:
## 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
mod_roster:
versioning: true
mod_s2s_bidi: {}
mod_s2s_dialback: {}
mod_shared_roster: {}
mod_stream_mgmt:
resend_on_timeout: if_offline
mod_stun_disco: {}
mod_vcard: {}
mod_vcard_xupdate: {}
mod_version:
show_os: false
### Local Variables:
### mode: yaml
### End:
### vim: set filetype=yaml tabstop=8
root@ez1:~ #Next, we need to setup a admin account for ejabberd.
bastille cmd ejabberd ejabberdctl register admin ezdk.org <a_very_strong_password>Now we can start the ejabberd service
bastille service ejabberd ejabberd startOpen xmpp.<yourdomain.tld>/admin in your browser and log in with admin@xmpp.<yourdomain.tld> and the password you’ve set in the previous step. If you gain access to the ejabberd WebAdmn site, all went fine. Proceed to the next steps.
Port fowarding
All connections to ejabberd not through https (which also means most xmpp traffic), will be handled by ejabberd directly and not through caddy. Which means, we need to forward all other ports on which we have defined ejabberd listeners.
bastille rdr ejabberd tcp 5222 5222
bastille rdr ejabberd tcp 5223 5223
bastille rdr ejabberd tcp 5269 5269
bastille rdr ejabberd udp 5478 5478
bastille rdr ejabberd tcp 1883 1883LDAP Integration
We want any user which is part of a specific group to be able to authenticate on the xmpp server (e.g. use the IM service). First, create the group “xmpp_users” though the lldap administration interface.
Then create a user ro_ejabberd. We will use this as the user ejabberd uses to query the directory. Add it to the group lldap_strict_readonly.
You can also set up a testuser. E.g. xmpptest and add it to the xmpp_users group.
Then, edit the ejabberd config micro /usr/local/bastille/jails/ejabberd/root/usr/local/etc/ejabberd/ejabberd.yml and add the following lines:
## Authentication method
auth_method: [internal, ldap]
## DNS name of our LDAP server
ldap_servers: [10.0.0.10]
## Bind to LDAP server as "cn=Manager,dc=example,dc=org" with password "secret"
ldap_rootdn: "uid=ro_ejabberd,ou=people,dc=example,dc=com"
ldap_password: <password_of_ro_ejabberd>
ldap_port: 3890
## Define the user's base
ldap_base: "ou=people,dc=example,dc=com"
## We want to authorize users from 'xmpp_users' group only
ldap_filter: "(&(objectclass=person)(memberOf=cn=xmpp_users,ou=groups,dc=example,dc=com))"Note
We could reuse the ro_admin account we used for nextclour earlier. But since the password is part of the config in cleartext, we better have an individual user which we can easily delete/recreate should something leak.
Note
One could also disable the internal user auth completely and just use auth_method: [ldap] but in this case, another admin user needs to be defined in the config file. Once which is present in the lldap directory.
Restart the ejabberd service:
bastille service ejabberd ejabberd restartNow you can use @xmpp.ezdk.org with the users password to log in from any xmpp/jabber client - as long as the user is part of the xmpp_users group.
Frontend
We also want to serve a webclient from the host, so people can use the service without looking for a client first. We choose xmpp-web for this. It is a simple collection of html, js and css. So we can serve it with caddy directly.
Let’s grab the release from https://github.com/nioc/xmpp-web/releases and extract it in the roots /usr/local/www dir. This will create a new directory /usr/local/www/xmpp-web/ containing the content of the application.
fetch https://github.com/nioc/xmpp-web/releases/download/0.10.6/xmpp-web-0.10.6.tar.gz
tar xf xmpp-web-0.10.6.tar.gz -C /usr/local/www/Now mount this directory into the caddy jail
bastille mount caddy /usr/local/www/xmpp-web /usr/local/www/xmpp-web nullfs ro 0 0Modify the local.js config file of xmpp-web:
micro /usr/local/xmpp-web/local.jsChange the domains and websocket transport to point to our xmpp server
// eslint-disable-next-line no-unused-vars, no-var
var config = {
name: 'Echtzeit Chat',
transports: {
websocket: 'wss://xmpp.ezdk.org/ws',
},
hasGuestAccess: false,
hasRegisteredAccess: true,
anonymousHost: null,
// anonymousHost: 'anon.domain-xmpp.ltd',
isTransportsUserAllowed: false,
hasHttpAutoDiscovery: false,
resource: 'EZ Web XMPP',
defaultDomain: 'xmpp.ezdk.org',
defaultMuc: 'conference.xmpp.ezdk.org',
// defaultMuc: 'conference.domain-xmpp.ltd',
isStylingDisabled: false,
hasSendingEnterKey: false,
connectTimeout: 5000,
pinnedMucs: [],
logoUrl: '',
sso: {
endpoint: false,
jidHeader: 'jid',
passwordHeader: 'password',
},
guestDescription: '',
}Edit the Caddyfile and add the following block to the config:
chat.ezdk.org {
root * /usr/local/www/xmpp-web
file_server
}And reload the config
bastille service caddy caddy reload