← Към блога

Как да защитите XMPP сървър: Практическо ръководство стъпка по стъпка

Практическо ръководство за защита на self-hosted XMPP сървър, включващо OS hardening, firewall правила, TLS конфигурация, настройки на ejabberd и защита на PostgreSQL.

Как да защитите XMPP сървър: Практическо ръководство стъпка по стъпка

Да поддържате собствен XMPP сървър е едно от най-добрите решения, които можете да вземете, ако държите на личната комуникация. Няма реклами, няма телеметрия, няма компания, която да чете съобщенията ви. Но лошо конфигуриран сървър може да е по-опасен от комерсиална услуга, защото вече Вие носите отговорност за сигурността.

Това ръководство покрива нещата, които наистина имат значение при защитата на XMPP сървър. Базирано е на реален одит на production ejabberd сървър върху Ubuntu 22.04 с nginx, PostgreSQL и Let’s Encrypt сертификати. Стъпките са подредени по важност, така че ако нямате време за всичко, поне критичните неща ще бъдат направени първо.

Преди да започнете

Ще Ви трябва root или sudo достъп до сървъра. Работете от две SSH сесии едновременно, когато правите промени по firewall-а или SSH. Ако едната сесия прекъсне, ще имате резервна за възстановяване.

Винаги правете backup на конфигурационните файлове преди редакция:

cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

Стъпка 1: Заключете SSH

SSH е входната врата към сървъра ви. Всичко останало зависи от това той да е добре защитен.

Изключете password authentication

Ако атакуващ може да пробва пароли през SSH, сървърът Ви постоянно ще бъде подложен на brute-force опити. Използвайте само key-based authentication.

# /etc/ssh/sshd_config
PasswordAuthentication no
KbdInteractiveAuthentication no

Задайте PermitRootLogin изрично

Стойността по подразбиране варира между различните дистрибуции. Не разчитайте на нея. Задайте я ръчно.

PermitRootLogin prohibit-password

Това позволява root login чрез SSH ключове, но не и с парола. Ако никога не влизате директно като root, задайте no.

Намалете MaxAuthTries

По подразбиране има 6 опита на връзка. Намалете ги, за да забавите brute-force атаките.

MaxAuthTries 3

Изключете X11 forwarding

X11 forwarding не е нужен на сървър и може да бъде използван за кражба на credentials чрез xauth cookie hijacking.

X11Forwarding no

Валидирайте и презаредете

Винаги тествайте конфигурацията преди reload, за да не се заключите навън заради печатна грешка.

sshd -t && systemctl reload ssh

След това потвърдете, че все още можете да влезете от втори терминал, преди да затворите текущата сесия.

Стъпка 2: Защитете firewall-а

Добрият firewall има default deny policy и отваря само това, което реално е нужно. Всичко останало се блокира.

Задайте default DROP policy

Разликата между DROP и ACCEPT като default policy е разликата между заключена и отворена врата.

iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

Разрешете само необходимото

За типичен ejabberd сървър:

# Loopback (винаги разрешен)
iptables -A INPUT -i lo -j ACCEPT

# Established connections
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

# ICMP с rate limiting
iptables -A INPUT -p icmp --icmp-type any -m limit --limit 10/s -j ACCEPT

# SSH - само от trusted IP
iptables -A INPUT -s YOUR_IP/32 -p tcp --dport 22 -j ACCEPT

# Web
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT

# XMPP client-to-server
iptables -A INPUT -p tcp -m multiport --dports 5222,5223 -j ACCEPT

# XMPP server-to-server (federation)
iptables -A INPUT -p tcp -m multiport --dports 5269,5270 -j ACCEPT

# XMPP HTTPS API и file upload
iptables -A INPUT -p tcp --dport 5443 -j ACCEPT

# STUN/TURN за voice и video
iptables -A INPUT -p udp --dport 3478 -j ACCEPT
iptables -A INPUT -p tcp --dport 5349 -j ACCEPT
iptables -A INPUT -p udp --dport 5349 -j ACCEPT

# TURN relay ports
iptables -A INPUT -p tcp -m multiport --dports 49152:65535 -j ACCEPT
iptables -A INPUT -p udp -m multiport --dports 49152:65535 -j ACCEPT

# Финален deny
iptables -A INPUT -j REJECT --reject-with icmp-host-prohibited

Блокирайте опасните портове изрично

Дори default policy-то Ви да е DROP, добавете изрични блокове за портове, които никога не трябва да бъдат достъпни. Това Ви пази, ако policy-то случайно бъде reset-нато.

# Erlang Port Mapper - никога не го expose-вайте
iptables -A INPUT -p tcp --dport 4369 -j DROP

# MQTT plaintext - блокирайте го, освен ако не Ви трябва
iptables -A INPUT -p tcp --dport 1883 -j DROP

# RPC portmapper - ненужен за XMPP сървър
iptables -A INPUT -p tcp --dport 111 -j DROP
iptables -A INPUT -p udp --dport 111 -j DROP

Проверете IPv6

Дори да нямате публичен IPv6 адрес, проверете дали ip6tables правилата са настроени. Честа грешка е да има добри IPv4 правила и IPv6 да бъде забравен напълно.

ip -6 addr show
ip6tables -L INPUT -n

Ако имате публични IPv6 адреси, приложете еквивалентни правила с ip6tables. Ако имате само link-local адреси (fe80::), нямате публичен IPv6 exposure.

Запазете и направете правилата persistent

Правилата в паметта се губят след reboot. Запазете ги и ги restore-вайте при стартиране.

iptables-save > /opt/iptables-rules

# В root crontab:
@reboot /usr/sbin/iptables-restore < /opt/iptables-rules

Стъпка 3: Защитете операционната система

Тези промени отнемат няколко минути и значително намаляват attack surface-а.

Изключете ненужните услуги

Услугите, които не използвате, са услуги, които могат да бъдат exploit-нати. На XMPP сървър обикновено не са Ви нужни:

systemctl disable --now rpcbind rpcbind.socket
systemctl disable --now ModemManager
systemctl disable --now avahi-daemon

Проверете какво работи:

systemctl list-units --type=service --state=running

Поставяйте под въпрос всичко непознато.

Kernel hardening параметри

Добавете това в /etc/sysctl.d/99-hardening.conf:

# Изключва IP source routing - предотвратява path manipulation атаки
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# Изключва ICMP redirects - предотвратява man-in-the-middle чрез фалшиви router съобщения
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0

# Логва пакети с невъзможни source адреси
net.ipv4.conf.all.log_martians = 1

# SYN flood защита
net.ipv4.tcp_syncookies = 1

# Игнорира broadcast ping - предотвратява използване като DDoS amplifier
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Пълен ASLR
kernel.randomize_va_space = 2

# Без core dumps от SUID програми - предотвратява изтичане на credentials от паметта
fs.suid_dumpable = 0

# Ограничен ptrace - предотвратява процесите да четат паметта си взаимно
kernel.yama.ptrace_scope = 1

Приложете настройките:

sysctl -p /etc/sysctl.d/99-hardening.conf

Проверете потребителите и sudo достъпа

Проверете кой има login shell и кой има sudo:

grep -v "nologin\|false" /etc/passwd | grep -v "^#"
getent group sudo

Премахнете sudo от акаунти, които не се нуждаят от него. Заключете неизползваните акаунти:

usermod -s /usr/sbin/nologin unused_account
passwd -l unused_account

Проверете за файлове без валиден owner:

find / -xdev -nouser 2>/dev/null

Активирайте автоматичните security updates

apt install unattended-upgrades
systemctl enable --now unattended-upgrades

Стъпка 4: Конфигурирайте TLS правилно

Използвайте ECC сертификати

Elliptic curve (ECC/ECDSA) сертификатите са по-малки, по-бързи и също толкова сигурни спрямо RSA при еквивалентна сила. Използвайте acme.sh или certbot за EC-384 сертификат.

acme.sh --issue -d yourdomain.tld --keylength ec-384 \
  -d conference.yourdomain.tld \
  -d upload.yourdomain.tld \
  -d proxy.yourdomain.tld

Ако издавате и RSA сертификат за съвместимост, 2048-bit е минимумът, а 4096-bit е за предпочитане. Но за нов сървър ECC-only е по-чистият вариант.

Генерирайте силни DH параметри

openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096

Това отнема няколко минути. Прави се веднъж и файлът може да се използва както от nginx, така и от ejabberd.

nginx TLS конфигурация

ssl_certificate     /path/to/fullchain_ecc.pem;
ssl_certificate_key /path/to/privkey_ecc.pem;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers   ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;

ssl_ecdh_curve secp384r1;
ssl_dhparam /etc/ssl/certs/dhparam.pem;

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;

ssl_prefer_server_ciphers off е правилната настройка за TLS 1.3. ssl_session_tickets off е важно за forward secrecy.

Стъпка 5: Защитете nginx

Security headers

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "no-referrer" always;
add_header Permissions-Policy "interest-cohort=()" always;
add_header Content-Security-Policy "default-src 'self'; ..." always;

Не добавяйте X-XSS-Protection. Той беше deprecated преди години и премахнат от Chrome. Модерните браузъри го игнорират, а старите версии на IE дори могат да се държат по-зле с него. Реалната защита идва от Content-Security-Policy.

Скрийте nginx версията

server_tokens off;

Пренасочете HTTP към HTTPS

server {
    listen 80 default_server;
    server_name _;
    return 301 https://$host$request_uri;
}

Добавете rate limiting за чувствителни endpoints

Ако имате registration или invite endpoints, защитете ги от ботове с rate limiting в nginx. Това стои пред application logic-а и е много по-евтино като натоварване.

# В http {} блока:
limit_req_zone $binary_remote_addr zone=invites:10m rate=20r/m;

# В location блоковете:
location /invites/ {
    limit_req zone=invites burst=20 nodelay;
    limit_req_status 429;
    ...
}

Не expose-вайте internal портове през nginx

Ако nginx proxy-ва към ejabberd на порт 8444 или подобен, backend портът трябва да бъде bind-нат само към localhost. Всеки порт, към който nginx proxy-ва, не трябва да е публично достъпен.

# ejabberd - bind-вайте internal HTTP listeners само към localhost
port: 8444
ip: "127.0.0.1"

Стъпка 6: Защитете ejabberd

Изисквайте TLS за всички връзки

Client-to-server връзките трябва задължително да използват STARTTLS. Plain връзките трябва да бъдат отказвани.

listen:
  -
    port: 5222
    ip: "::"
    module: ejabberd_c2s
    starttls_required: true
  -
    port: 5269
    ip: "::"
    module: ejabberd_s2s_in
    protocol_options:
      - no_sslv2
      - no_sslv3
      - no_tlsv1
      - no_tlsv1_1

Изисквайте TLS и за server-to-server federation:

s2s_use_starttls: required

Използвайте SCRAM за съхранение на пароли

Никога не съхранявайте пароли в plaintext. Използвайте SCRAM, който пази salted hash, от който оригиналната парола не може да бъде възстановена.

auth_password_format: scram

Изключете слабите SASL механизми

disable_sasl_mechanisms:
  - "digest-md5"
  - "X-OAUTH2"

DIGEST-MD5 изисква reversible password storage, което обезсмисля SCRAM. Изключете го.

Конфигурирайте fail2ban

ejabberd има вграден brute-force protection модул. Използвайте го.

mod_fail2ban:
  c2s_auth_ban_lifetime: 6h
  c2s_max_auth_failures: 5

Пет неуспешни опита водят до шестчасова забрана за свързване.

Bind-нете admin портовете само към internal IP

Admin web panel-ът и API портовете не трябва да бъдат достъпни от публичния интернет, дори firewall-ът да ги ограничава. Bind-нете ги към internal interface-а.

  -
    port: 6443
    ip: "172.16.x.x"   # internal IP на сървъра, не ::
    module: ejabberd_http
    tls: true
    request_handlers:
      /admin: ejabberd_web_admin

Изключете неизползваните модули и listeners

Ако не използвате MQTT, коментирайте го:

# mod_mqtt: {}

Ако не използвате built-in ACME на ejabberd (защото използвате acme.sh например), коментирайте listener-а на порт 5280 изцяло:

# -
#   port: 5280
#   ip: "::"
#   module: ejabberd_http

Всеки зареден модул е код, който се изпълнява. Оставете само това, което реално използвате.

Заключете Erlang distribution порта

Това е един от най-пренебрегваните проблеми при ejabberd сървърите. Erlang използва port mapper daemon (epmd) и отделен distribution порт за inter-node комуникация. По подразбиране слушат на всички interfaces.

В /opt/ejabberd/conf/ejabberdctl.cfg uncomment-нете:

INET_DIST_INTERFACE=127.0.0.1
ERL_DIST_PORT=5210
ERL_EPMD_ADDRESS=127.0.0.1

След това рестартирайте ejabberd, не само reload:

systemctl restart ejabberd

След тази промяна epmd вече няма да е нужен и няма да стартира. Distribution портът ще бъде само на localhost и на фиксиран порт, което е много по-лесно за одит.

Проверете:

ss -tlnp | grep -E "4369|5210"
# Трябва да виждате само 127.0.0.1:5210 или нищо

Стъпка 7: Защитете PostgreSQL

Слушайте само на localhost

По подразбиране PostgreSQL слуша на localhost. Направете го изрично в postgresql.conf:

listen_addresses = 'localhost'

Проверете дали не е expose-нат externally:

ss -tlnp | grep postgres
# Трябва да показва само 127.0.0.1:5432

Използвайте scram-sha-256 authentication

В pg_hba.conf използвайте scram-sha-256 вместо md5 за всички TCP връзки. md5 е слаб и deprecated.

host    all    all    127.0.0.1/32    scram-sha-256
host    all    all    ::1/128         scram-sha-256

Никога не използвайте trust authentication. trust означава, че не се изисква парола.

Дайте на ejabberd минимални database права

Database user-ът на ejabberd трябва да има достъп само до ejabberd базата. Без superuser, без createdb, без replication права.

CREATE USER ejabberd WITH PASSWORD 'strong_password';
GRANT CONNECT ON DATABASE ejabberd TO ejabberd;
GRANT USAGE ON SCHEMA public TO ejabberd;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO ejabberd;

Премахнете неизползваните pg_hba entries

Ако не използвате PostgreSQL replica, премахнете replication entries от pg_hba.conf. Те са там по подразбиране и нямат никаква полза на standalone сървър.

Стъпка 8: Настройте intrusion detection

Инсталирайте CrowdSec

CrowdSec е модерен заместител на fail2ban с community threat intelligence feed. Засича атаки в логовете Ви и автоматично блокира атакуващите, като същевременно споделя анонимизирани данни с CrowdSec мрежата.

curl -s https://install.crowdsec.net | bash
apt install crowdsec-firewall-bouncer-iptables

CrowdSec автоматично ще разпознава често срещани атаки срещу SSH, nginx и ejabberd, ако настроите правилните parsers.

Backup на firewall правилата

Firewall правилата трябва да оцелеят след reboot. Тествайте това като рестартирате сървъра и проверите дали правилата са заредени:

iptables -L INPUT -n | head -5

Неща, които си струва да проверявате периодично

След като сървърът вече работи, отделяйте време на няколко месеца за следните проверки:

Изтичане на сертификати. Дори с автоматично подновяване, проверявайте дали реално работи. Сървър с изтекъл сертификат прекъсва всички client връзки веднага.

acme.sh --list
openssl x509 -in /path/to/fullchain.pem -noout -enddate

Неуспешни login опити. Проверявайте какво блокират CrowdSec или fail2ban. Pattern-ите в source IP адресите и времето могат да покажат дали някой таргетира конкретно вашия сървър.

cscli decisions list
journalctl -u ssh | grep "Failed"

Listening портове. Пускайте ss -tlnp от време на време и сравнявайте резултата с това, което очаквате. Нов порт винаги си струва да бъде проверен.

Потребители и sudo. След промени в екипа проверявайте дали sudo групата все още е коректна. Акаунтите на хора, които вече нямат достъп, трябва да бъдат заключени или изтрити.

OS updates. unattended-upgrades се грижи автоматично за security patches, но все пак проверявайте от време на време дали услугата още работи и дали няма package holds, които блокират критични updates.

Кратка справка: За какво служи всеки порт

Специално за ejabberd, ето за какво служи всеки порт, за да знаете какво да отворите и какво да оставите затворено:

ПортПротоколПредназначениеОтворен за
5222TCPXMPP client връзки (STARTTLS)Всички
5223TCPXMPP client връзки (Direct TLS)Всички
5269TCPFederation inbound (STARTTLS)Всички
5270TCPFederation inbound (Direct TLS)Всички
5443TCPBOSH, WebSocket, file uploadВсички
5280TCPHTTP admin / ACME challengeСамо trusted IP или localhost
6443TCPAdmin web panelСамо trusted IP
8444TCPRegistration, invites (proxied)Само localhost
3478UDPSTUNВсички
5349TCP/UDPTURN over TLSВсички
4369TCPErlang epmdНапълно блокиран
1883TCPMQTT (plaintext)Блокиран, освен ако не е нужен
5210TCPErlang distribution (fixed)Само localhost

Бележка за Defense in Depth

Нито една от тези стъпки не е магическо решение сама по себе си. Целта е всеки layer да бъде защитен независимо, така че ако едно нещо се провали или бъде грешно конфигурирано, друг слой да го компенсира.

Например: bind-нете admin порта на ejabberd към localhost И го ограничете през iptables. Ако някой случайно промени ejabberd конфигурацията, iptables пак ще го блокира. Ако някой случайно премахне iptables правилото, bind address-ът пак ще го ограничава. Две независими защити за един чувствителен порт.

Същото важи и за registration-а. Използвайте hCaptcha на nginx layer-а И настройте rate limiting И използвайте fail2ban модула на ejabberd. Ботовете, които минат през един layer, ще ударят следващия.

Self-hosting на XMPP си заслужава усилието. С правилно конфигуриран сървър получавате истински лична комуникация под ваш контрол. Hardening стъпките по-горе не са optional extras. Те са част от това да поддържате сървър отговорно.

Още за четене

09/05/2026
Как да инсталирам Conversations на Android
Стъпка по стъпка ръководство за начинаещи, показващо как да инсталирате …
04/11/2025
Chat Control - Цената на сигурността
Ще пожертва ли Европа цифровите си свободи? Анализ на Chat Control и рисковете …
22/07/2024
Chatrix.One - доставчик категория A
На 17.07.2024г. Chatrix.One беше включен в списъка на XMPP Providers с …