Snikket XMPP
1. Introduction
Snikket is a chat application based on XMPP. A Snikket server allows you to chat securely via XMPP, create chat groups, make one-to-one audio and video calls, and much more!
The basic installation steps are described at the Snikket quick-start guide. We will install it in an Incus container.
The Snikket docker-compose.yml
config file uses network_mode: host
, and this makes it difficult to install Snikket alongside other
Dockerized applications. That's why we are installing it inside an
Incus container, where Docker is installed as well, instead of
installing it on the host.
The deployment diagram looks like this:
2. Set the DNS records
For Snikket we need these three DNS records:
chat.user1.fs.al. 300 IN A 188.245.242.143
groups.chat.user1.fs.al. 300 IN CNAME chat.user1.fs.al.
share.chat.user1.fs.al. 300 IN CNAME chat.user1.fs.al.
The subdomains groups.
and share.
provide group chat functionality
and file-sharing.
cd /var/ds/nsd/
ls zones/
nano zones/user1.fs.al.db
ds restart
Besides adding the DNS records listed above, don't forget to update the serial number as well.
Check the DNS records
dig +short chat.user1.fs.al
dig +short groups.chat.user1.fs.al
dig +short share.chat.user1.fs.al
3. Create the container
In order to create and setup an Indus container for Snikket, we should
follow the same steps that we did for the edu
container. However we can automate these steps with a
script like create-container.sh
:
create-container.sh
#!/bin/bash
usage() {
cat <<EOF
Usage: $0 <name> [<fixed-ip>]
EOF
}
name=$1
fixed_ip=$2
[[ -z $name ]] && usage && exit 1
### create the container
incus launch images:debian/12 $name \
-c security.nesting=true \
-c security.syscalls.intercept.mknod=true \
-c security.syscalls.intercept.setxattr=true
### create a configuration script
cat <<'__EOF__' > /tmp/config.sh
#!/bin/bash -x
fixed_ip=$1
main() {
update
customize_bashrc
install_docker
install_docker_scripts
set_an_ip $fixed_ip
}
update() {
# update and upgrade
export DEBIAN_FRONTEND=noninteractive
apt update
apt upgrade --yes
# enable automatic security updates
apt install --yes unattended-upgrades
# install some tools
apt install --yes nano curl wget
}
customize_bashrc() {
# uncomment some aliases
sed -i ~/.bashrc \
-e 's/# export LS_OPTIONS=/export LS_OPTIONS=/' \
-e 's/# alias ls=/alias ls=/' \
-e 's/# alias ll=/alias ll=/' \
-e 's/# alias l=/alias l=/'
# customize bashrc
echo 'source ~/.bashrc_custom' >> ~/.bashrc
cat <<'EOF' > ~/.bashrc_custom
# set a better prompt
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;31m\]\u\[\033[01;33m\]@\[\033[01;36m\]\h \[\033[01;33m\]\w \[\033[01;35m\]\$ \[\033[00m\]'
# enable programmable completion features
if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
source /etc/bash_completion
fi
EOF
apt install --yes bash-completion
# in case of using vim, uncomment its dark background setting
apt install --yes vim
sed -i /etc/vim/vimrc \
-e 's/^"set background=dark/set background=dark/'
}
install_docker() {
wget https://download.docker.com/linux/ubuntu/gpg \
-O /etc/apt/keyrings/docker.asc
cat <<EOF > /etc/apt/sources.list.d/docker.sources
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: bookworm
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF
apt update
apt install --yes \
docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin
}
install_docker_scripts() {
apt install --yes git make m4 highlight tree
git clone \
https://gitlab.com/docker-scripts/ds \
/opt/docker-scripts/ds
cd /opt/docker-scripts/ds/
make install
}
set_an_ip() {
local fixed_ip=$1
[[ -z $fixed_ip ]] && return
local gateway=${fixed_ip%.*}.1
cat <<EOF > /etc/systemd/network/eth0.network
[Match]
Name=eth0
[Address]
Address=$fixed_ip
[Route]
Gateway=$gateway
[Network]
DHCP=no
DNS=8.8.8.8
EOF
systemctl restart systemd-networkd
}
### call main
main "$@"
__EOF__
### execute config script
chmod +x /tmp/config.sh
incus file push /tmp/config.sh $name/tmp/
incus exec $name -- /tmp/config.sh $fixed_ip
### remove config script
rm /tmp/config.sh
### restart container
incus restart $name
wget https://linux-cli.fs.al/apps/part6/create-container.sh
chmod +x create-container.sh
./create-container.sh
incus ls
./create-container.sh snikket 10.31.96.202
incus ls
4. Forward ports
The required ports are:
-
80
/443
-- for the web interface, and group file sharing -
5222
,5269
,5000
-- XMPP ports and file transfer proxy -
For audio and video communication (STUN/TURN server) these ports are needed as well:
- TCP and UDP:
3478
,3479
,5349
,5350
- UDP only:
49152-65535
- TCP and UDP:
4.1 HTTP/HTTPS ports
For forwarding these ports we use SNI Proxy:
cd /var/ds/sniproxy/
nano etc/sniproxy.conf
ds restart
Add these lines to the table of sniproxy
:
table {
# . . . . .
# container: snikket
chat\.user1\.fs\.al 10.31.96.202
.*\.chat\.user1\.fs\.al 10.31.96.202
# . . . . .
}
4.2 XMPP ports
We can use the command incus network forward
to forward the TCP
ports 5222
, 5269
, 5000
from the host to the IP of the snikket
container:
incus network --help | less
incus network forward --help | less
HOST_IP=188.245.242.143 # the public IP of the host
CONTAINER_IP=10.31.96.202
incus network forward list incusbr0
incus network forward create incusbr0 $HOST_IP
incus network forward list incusbr0
incus network forward show incusbr0 $HOST_IP
incus network forward \
port add incusbr0 $HOST_IP \
tcp 5222,5269,5000 \
$CONTAINER_IP
incus network forward show incusbr0 $HOST_IP
How to test port forwarding
We can use netcat
to test that ports are forwarded correctly. On the
server (VPS) run:
incus exec snikket -- apt install --yes netcat-openbsd
incus exec snikket -- nc -l 5222
Outside the server run:
nc chat.user1.fs.al 5222
Every line that is typed outside the server should be displayed inside the server, and vice-versa.
4.3 TURN ports
Snikket has a built-in STUN/TURN server, to support audio/video calls.
If you want to use Snikket only for instant messaging (XMPP), not for audio/video calls, you can skip this part.
We need to forward these ports to the container:
# tcp ports
incus network forward \
port add incusbr0 $HOST_IP \
tcp 3478,3479,5349,5350 \
$CONTAINER_IP
# udp ports
incus network forward \
port add incusbr0 $HOST_IP \
udp 3478,3479,5349,5350,49152-65535 \
$CONTAINER_IP
incus network forward show incusbr0 $HOST_IP
Test UDP port forwarding
For testing UDP port forwarding, use the option -u
of the command
nc
. On the server run:
incus exec snikket -- nc -u -l 60000
Outside the server run:
nc -u chat.user1.fs.al 60000
5. Install Snikket
We are now ready to install Snikket inside the container.
-
Get a shell inside the container:
incus shell snikket
-
Download the file
docker-compose.yml
:mkdir -p /root/snikket
cd /root/snikket/
curl -o docker-compose.yml \
https://snikket.org/service/resources/docker-compose.yml -
Customize a bit
docker-compose.yml
:sed -i docker-compose.yml \
-e '/^version:/ d' \
-e '/^volumes:/,$ d' \
-e 's%snikket_data:%./data:%' \
-e 's%acme_challenges:%./data/acme_challenges:%'
nano docker-compose.yml- Remove the line
version:
(at the top of the file). - Remove the section
volumes:
(at the end of the file). - Replace the data volume
snikket_data
with the local directory./data
. - Replace the data volume
acme_challenges
with the local directory./data/acme_challenges
.
These modifications ensure that everything about snikket is contained in the directory
/root/snikket/
. This way it is easier to make a backup/restore. - Remove the line
-
Create a configuration file, called
snikket.conf
, in the same directory:cat <<EOF > snikket.conf
# The primary domain of your Snikket instance
SNIKKET_DOMAIN=chat.user1.fs.al
# An email address where the admin can be contacted
# (also used to get a Let's Encrypt certificate)
SNIKKET_ADMIN_EMAIL=dashohoxha@gmail.com
EOF
nano snikket.conf -
Launch it:
docker compose up -d
-
As soon as Snikket is running, you can create an admin account:
docker exec snikket \
create-invite --admin --group defaultFollow the link to open the invitation, and follow the instructions to get signed in.
-
Once you’ve created your admin account, you can log in to the web dashboard by visiting https://chat.user1.fs.al/ in your browser. From there you can create more invitation links to share with your family, friends and anyone else you want to join your Snikket instance.
See also this workshop: https://peertube.debian.social/w/jo4ENEJJjDX1s3tcbJXwNm
6. Maintenance
For backup and restore, it is enough to backup and restore the
directory /root/snikket/
.
For update:
cd /root/snikket/
docker-compose pull
docker-compose up -d
See also these scripts: https://github.com/snikket-im/snikket-selfhosted/tree/main/scripts