Песочница
В этой статье приводится пример конфигурирования ejabberd с поддержкой актуальных и популярных функций. В качестве основы взят конфиг-файл с работающего сервера версии 21.12, а в качестве сертификатов используется Let's Encrypt - и все это установлено на Debian GNU/Linux.
Рассмотрим особенности данной конфигурации.
DNS-записи
Хотя для базового варианта своего jabber-сервера вполне достаточно бесплатного DynDNS-домена, для полноценной конфигурации все же нужно обзавестись собственным доменом, где можно будет добавить все необходимые DNS-записи и сделать субдомены для сервисов. Выбор регистратора и покупка домена остается за рамками данной статьи, тут только приводятся шаги, которые надо сделать после покупки домена в его админке.
Всего понадобится около 15 записей в DNS:
- Для удобства, чтобы в случае миграции сервера с одного IP на другой не приходилось переписывать все записи, стоит завести для него субдомен, который будет ссылаться на нужный IP с помощью А-записи (или 2 таких субдомена в случае использования IPv4+IPv6 - один с A-записью, а второй с AAAA), а все остальные записи сделать CNAME-алиасами для него. Например, для данного сервера (jabberworld.info) сделаны 2 субдомена xmpp.jabberworld.info, ссылающиеся на IPv4 и IPv6-адреса.
- Создаем субдомены для необходимых сервисов сервера - conference (для конференций), proxy (прокси для прямой передачи файлов), pubsub (сервисы типа "Публикация/подписка", где может сохраняться самая разная информация) и upload (сервис для передачи файлов через HTTP Upload). Все эти записи создаем как CNAME на наш субдомен из 1-го пункта.
- Создаем ряд SRV-записей для нашего основного домена - они показывают, где именно какой сервис находится. Конечно, если у вас все на одном адресе, то будет работать и так, но мы ведь решили все делать правильно, верно? Итак, нужны такие SRV-записи:
- _xmpp-client._tcp.EXAMPLE.COM (порт 5222) - чтобы указать, куда подключаться jabber-клиенту
- _xmpps-client._tcp.EXAMPLE.COM (порт 5223) - аналогичная запись для TLS-подключений клиентов
- _xmpp-server._tcp.EXAMPLE.COM (порт 5269) - чтобы указать, куда подключаться jabber-серверу
- _xmpps-server._tcp.EXAMPLE.COM (порт 5270) - аналогичная запись для TLS-подключений серверов.
- Записи ниже служат для работы сервиса аудио/видеозвонков через jabber:
- _stun._tcp.EXAMPLE.COM (порт 3478) - STUN через TCP
- _stun._udp.EXAMPLE.COM (порт 3478) - STUN через UDP
- _stuns._tcp.EXAMPLE.COM (порт 5349) - шифрованный STUN через TCP
- _turn._tcp.EXAMPLE.COM (порт 3478) - TURN через TCP
- _turn._udp.EXAMPLE.COM (порт 3478) - TURN через UDP
- _turns._tcp.EXAMPLE.COM (порт 5349) - шифрованный TURN через TCP
- Во всех случаях target'ом для записей служит наш алиас из первого пункта.
Вот как выглядят записи для данного сервера в админке бесплатного DNS-провайдера Hurricane Electric:
Сертификаты
В современном мире стандартной практикой является шифрование соединений, а для этого, в свою очередь, требуются сертификаты от доверенных центров сертификации. Можно, конечно, использовать самоподписанный сертификат, но, во-первых, не все серверы будут их принимать - а значит, к ним не получится подключиться и общаться с их пользователями, а во-вторых, в подключающихся клиентах будут появляться сообщения о недоверенном сертификате, что тоже будет доставлять определенные неудобства. Поэтому стоит воспользоваться услугами одного из популярных бесплатных центров сертификации - Let's Encrypt.
Для работы с сертификатами от Let's Encrypt в Debian есть специальный пакет - certbot, поэтому установите его следующей командой:
sudo apt-get install certbot
Сертификаты от Let's Encrypt выдаются сроком на 3 месяца, поэтому в конце этого срока их нужно обновлять. Для подтверждения владения доменом, а также для автоматизации обновления сертификатов в дальнейшем со стороны Let's Encrypt делается запрос на веб-сервер для указанного домена, поэтому для полноценной работы стоит установить какой-нибудь популярный вариант и прописать там виртуальные хосты для нашего jabber-сервера, а также сервисов на нем (сертификаты будут делаться в том числе для сервисов). Пример конфигурации веб-сервера Apache для данного сервера:
<VirtualHost 185.161.208.229:80 [2a07:c801:0:5::]:80> ServerAdmin webmaster@jabberworld.info DocumentRoot /var/www/jabberworld.info/htdocs ServerName jabberworld.info </VirtualHost> <VirtualHost 185.161.208.229:80 [2a07:c801:0:5::]:80> ServerAdmin webmaster@upload.jabberworld.info DocumentRoot /var/www/upload.jabberworld.info/htdocs ServerName upload.jabberworld.info </VirtualHost> <VirtualHost 185.161.208.229:80 [2a07:c801:0:5::]:80> ServerAdmin webmaster@pubsub.jabberworld.info DocumentRoot /var/www/pubsub.jabberworld.info/htdocs ServerName pubsub.jabberworld.info </VirtualHost> <VirtualHost 185.161.208.229:80 [2a07:c801:0:5::]:80> ServerAdmin webmaster@conference.jabberworld.info DocumentRoot /var/www/conference.jabberworld.info/htdocs ServerName conference.jabberworld.info </VirtualHost> <VirtualHost 185.161.208.229:80 [2a07:c801:0:5::]:80> ServerAdmin webmaster@proxy.jabberworld.info DocumentRoot /var/www/proxy.jabberworld.info/htdocs ServerName proxy.jabberworld.info </VirtualHost>
В каталогах сервера не обязательно должен быть какой-то контент, хотя, например, на основном домене можно разместить какую-то страничку, посвященную jabber-серверу, а на поддомене conference разместить чат-логи конференций. Субдомен upload можно приспособить под загружаемые через HTTP Upload файлы, чтобы в дальнейшем их раздавал веб-сервер.
После всех приготовлений создайте все необходимые сертификаты. Для этого воспользуйтесь следующими командами (выполнять надо от пользователя root):
certbot certonly --webroot --webroot-path /var/www/EXAMPLE.COM/htdocs/ -d EXAMPLE.COM certbot certonly --webroot --webroot-path /var/www/conference.EXAMPLE.COM/htdocs/ -d conference.EXAMPLE.COM certbot certonly --webroot --webroot-path /var/www/upload.EXAMPLE.COM/htdocs/ -d upload.EXAMPLE.COM certbot certonly --webroot --webroot-path /var/www/pubsub.EXAMPLE.COM/htdocs/ -d pubsub.EXAMPLE.COM certbot certonly --webroot --webroot-path /var/www/proxy.EXAMPLE.COM/htdocs/ -d proxy.EXAMPLE.COM
В параметрах после --webroot-path указывается каталог веб-сервера для данного домена, а после -d - сам домен.
После успешного выполнения команд будет выдана информация о созданном сертификате, в том числе 2 файла - цепочка ключей и приватный ключ - сохраните эти пути для дальнейшего использования, они нам еще пригодятся. Всего получится 10 файлов. certbot обновляет сертификаты автоматически, если срок их истечения составляет менее 30 дней - т.е., обновление происходит раз в 2 месяца.
Так как ejabberd работает от своего пользователя и не имеет доступа к сертификатам, созданным certbot от рута, то надо каким-то образом предоставить доступ ejabberd'у к сертификатам. Делать это можно по-разному - кто-то, например, меняет права на созданные сертификаты и добавляет возможность ejabberd'у получать доступ к нужным файлам - правда, эти права сбрасываются после каждого обновления сертификатов. В моем случае я создал для ejabberd отдельный каталог - /etc/ejabberd/certs, куда копируются по cron'у созданные сертификаты и уже там даются права для jabber-сервера.
Настройка ejabberd
Все подготовительные процедуры завершены, теперь можно приступать непосредственно к установке и настройке своего jabber-сервера. В Debian установить ejabberd можно командой
sudo apt-get install ejabberd
Зачастую самая свежая версия может находиться в backports, поэтому при необходимости можно указать конкретный репозиторий из вашего /etc/apt/sources.list с помощью ключа -t - например, -t bullseye-backports. Кроме того, с недавних пор у ejabberd появился официальный DEB и RPM-репозиторий - подробнее об этом можно почитать по этой ссылке.
Штатно ejabberd использует собственную базу данных - Mnesia. Ее объем ограничен 2 ГБ, поэтому если планируется большое количество пользователей, большие объемы сообщений или просто хочется использовать удобные популярные инструменты для работы с привычной вам базой - я бы рекомендовал использовать внешнюю базу данных - например, MySQL или MariaDB. Я так и поступил, тем более сделать это весьма просто - кроме ejabberd просто установите еще пакет erlang-p1-mysql командой
sudo apt-get install erlang-p1-mysql
Создайте базу для jabber-сервера, после чего импортируйте туда схему базы из /usr/share/ejabberd/sql/mysql.sql.
Ниже приводится пример конфигурации jabber-сервера. Рассмотрим его подробнее.
### ### 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? ### # --- ## loglevel: Verbosity of log files generated by ejabberd ## 0: No ejabberd log at all (not recommended) ## 1: Critical ## 2: Error ## 3: Warning ## 4: Info ## 5: Debug loglevel: 4 ## rotation: Disable ejabberd's internal log rotation, as the Debian package ## uses logrotate(8). log_rotate_count: 0 #log_rotate_date: "" ## hosts: Domains served by ejabberd. ## You can define one or several, for example: ## hosts: ## - "example.net" ## - "example.com" ## - "example.org" hosts: - "EXAMPLE.COM" certfiles: - "/etc/ejabberd/certs/conference.EXAMPLE.COM.fullchain.pem" - "/etc/ejabberd/certs/conference.EXAMPLE.COM.privkey.pem" - "/etc/ejabberd/certs/EXAMPLE.COM.fullchain.pem" - "/etc/ejabberd/certs/EXAMPLE.COM.privkey.pem" - "/etc/ejabberd/certs/pubsub.EXAMPLE.COM.fullchain.pem" - "/etc/ejabberd/certs/pubsub.EXAMPLE.COM.privkey.pem" - "/etc/ejabberd/certs/upload.EXAMPLE.COM.fullchain.pem" - "/etc/ejabberd/certs/upload.EXAMPLE.COM.privkey.pem" - "/etc/ejabberd/certs/proxy.EXAMPLE.COM.fullchain.pem" - "/etc/ejabberd/certs/proxy.EXAMPLE.COM.privkey.pem" ## TLS configuration define_macro: 'TLS_CIPHERS': "HIGH:!aNULL:!eNULL:!3DES:@STRENGTH" 'TLS_OPTIONS': - "no_sslv3" # - "no_tlsv1" # - "no_tlsv1_1" - "cipher_server_preference" - "no_compression" ## generated with: openssl dhparam -out dhparams.pem 2048 c2s_ciphers: 'TLS_CIPHERS' s2s_ciphers: 'TLS_CIPHERS' c2s_protocol_options: 'TLS_OPTIONS' s2s_protocol_options: 'TLS_OPTIONS' ## c2s_dhfile: 'DH_FILE' s2s_dhfile: "/etc/ejabberd/dhparams.pem" listen: - port: 3478 ip: "::" transport: udp module: ejabberd_stun auth_realm: "@HOST@" use_turn: true ## The server's public IPv4 address: turn_ipv4_address: "123.123.123.123" ## The server's public IPv6 address: turn_ipv6_address: "2a01:0123:0123:0123::" - port: 3478 ip: "::" transport: tcp module: ejabberd_stun auth_realm: "@HOST@" use_turn: true ## The server's public IPv4 address: turn_ipv4_address: "123.123.123.123" ## The server's public IPv6 address: turn_ipv6_address: "2a01:0123:0123:0123::" - port: 5349 transport: tcp module: ejabberd_stun use_turn: true tls: true ip: "::" ## The server's public IPv4 address: turn_ipv4_address: "123.123.123.123" ## The server's public IPv6 address: turn_ipv6_address: "2a01:0123:0123:0123::" - port: 5000 ip: "::" module: ejabberd_http tls: true request_handlers: /conversejs: mod_conversejs /: mod_http_fileserver - port: 5222 ip: "::" module: ejabberd_c2s max_stanza_size: 262144 shaper: c2s_shaper access: c2s zlib: true starttls_required: true protocol_options: 'TLS_OPTIONS' - port: 5223 ip: "::" module: ejabberd_c2s max_stanza_size: 262144 shaper: c2s_shaper access: c2s tls: true zlib: true starttls_required: true protocol_options: 'TLS_OPTIONS' - port: 5269 ip: "::" module: ejabberd_s2s_in max_stanza_size: 524288 - port: 5270 ip: "::" tls: true module: ejabberd_s2s_in max_stanza_size: 524288 - port: 5280 ip: "::" module: ejabberd_http request_handlers: "/captcha": ejabberd_captcha tls: true - port: 5281 ip: "::" module: ejabberd_http request_handlers: "/admin": ejabberd_web_admin tls: true - port: 5282 ip: "::" module: ejabberd_http request_handlers: "/captcha": ejabberd_captcha "/register": mod_register_web tls: true - port: 5283 ip: "::" module: ejabberd_http request_handlers: "/api": mod_http_api "/bosh": mod_bosh "/upload": mod_http_upload "/ws": ejabberd_http_ws "/captcha": ejabberd_captcha tls: true ## Disabling digest-md5 SASL authentication. digest-md5 requires plain-text ## password storage (see auth_password_format option). disable_sasl_mechanisms: - "digest-md5" - "X-OAUTH2" s2s_use_starttls: required ## Store the plain passwords or hashed for SCRAM: auth_password_format: scram ## Full path to a script that generates the image. captcha_cmd: "/usr/share/ejabberd/captcha.sh" #captcha_url: "https://xmpp.EXAMPLE.COM:5282" captcha_host: "https://EXAMPLE.COM:5282" acl: admin: user: - "ADMIN": "EXAMPLE.COM" 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_create: - allow: local muc: - allow pubsub_createnode: - allow: local register: - allow trusted_network: - allow: loopback #webadmin_view: # - viewers: allow api_permissions: "console commands": from: - ejabberd_ctl who: all what: "*" "admin access": who: - access: - allow: - acl: loopback - acl: admin - oauth: - scope: "ejabberd:admin" - access: - allow: - acl: loopback - acl: admin what: - "*" - "!stop" - "!start" "public commands": who: - ip: "127.0.0.1/8" what: - "status" - "connected_users_number" shaper: normal: 2500 fast: 50000 shaper_rules: max_user_sessions: 25 max_user_offline_messages: - 5000: admin - 200 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_delegation: {} # for xep0356 mod_disco: server_info: - modules: all name: "abuse-addresses" urls: - "xmpp:ADMIN@EXAMPLE.COM" - "mailto:ADMIN@EXAMPLE.COM" - modules: [mod_muc] name: "Web chatroom logs" urls: ["https://chatlogs.EXAMPLE.COM"] - modules: all name: "support-addresses" urls: - "xmpp:ADMIN@EXAMPLE.COM" - "xmpp:support@conference.EXAMPLE.COM?join" - "https://my.cool.site" mod_fail2ban: {} mod_http_api: {} mod_http_upload: put_url: "https://@HOST@:5283/upload" thumbnail: false jid_in_url: sha1 custom_headers: "Access-Control-Allow-Origin": "*" "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS" "Access-Control-Allow-Headers": "Content-Type" mod_http_upload_quota: max_days: 180 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: roster compress_xml: true use_cache: true cache_life_time: 86400 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_muc_log: outdir: "/var/www/chatlogs" access_log: muc cssfile: /var/www/chatlogs.EXAMPLE.COM/htdocs/muc.css #cssfile: http://chatlogs.EXAMPLE.COM/muc.css mod_ping: {} mod_pres_counter: count: 5 interval: 60 mod_privacy: {} mod_private: {} mod_proxy65: # remove ip? ip: 123.123.123.123 access: local max_connections: 10 mod_pubsub: access_createnode: pubsub_createnode plugins: - "flat" - "pep" force_node_config: "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 ip_access: all captcha_protected: true registration_watchers: - "ADMIN@EXAMPLE.COM" welcome_message: subject: "Добро пожаловать на Jabber-сервер!" body: "Приветствую. Ведите себя хорошо. Доступен веб-клиент ConverseJS по адресу https://xmpp.EXAMPLE.COM. Лимит на загружаемые файлы - 100 МБ. Срок хранения - 180 дней." mod_roster: versioning: true mod_s2s_dialback: {} mod_shared_roster: {} mod_sic: {} mod_stream_mgmt: resend_on_timeout: if_offline mod_vcard: search: false mod_stun_disco: services: - host: 123.123.123.123 port: 3478 type: stun transport: udp restricted: false - host: 123.123.123.123 port: 3478 type: turn transport: udp restricted: true - host: 123.123.123.123 port: 3478 type: stun transport: tcp restricted: false - host: 123.123.123.123 port: 3478 type: turn transport: tcp restricted: true - host: "2a01:0123:0123:0123::" port: 3478 type: stun transport: udp restricted: false - host: "2a01:0123:0123:0123::" port: 3478 type: turn transport: udp restricted: true - host: "2a01:0123:0123:0123::" port: 3478 type: stun transport: tcp restricted: false - host: "2a01:0123:0123:0123::" port: 3478 type: turn transport: tcp restricted: true - host: EXAMPLE.COM port: 5349 type: stuns transport: tcp restricted: false - host: EXAMPLE.COM port: 5349 type: turns transport: tcp restricted: true mod_vcard_xupdate: {} mod_version: show_os: false mod_stats: {} mod_last: {} # mod_time: {} mod_conversejs: websocket_url: "wss://EXAMPLE.COM:5283/ws" conversejs_script: "https://EXAMPLE.COM:5000/converse.min.js" conversejs_css: "https://EXAMPLE.COM:5000/converse.min.css" # bosh_service_url: "https://EXAMPLE.COM:5283/bosh" default_domain: "EXAMPLE.COM" mod_http_fileserver: docroot: "/var/www/ejabberd/package/dist" accesslog: "/var/log/ejabberd/fileserver-access.log" custom_headers: "Access-Control-Allow-Origin": "*" "Access-Control-Allow-Methods": "GET,HEAD,OPTIONS" "Access-Control-Allow-Headers": "Content-Type" default_db: sql sql_type: mysql sql_server: "localhost" host_config: "EXAMPLE.COM": sql_database: "JABBERDB" sql_username: "DBUSER" sql_password: "SUPERPASSWORD" sql_port: 3306 language: "ru" ### Local Variables: ### mode: yaml ### End: ### vim: set filetype=yaml tabstop=8
- Первым делом указывается один или несколько хостов, которые будет обслуживать сервер - в конфигурационном файле выше он указан как EXAMPLE.COM.
- Далее надо указать перечень файлов сертификатов, которые мы создали раньше.
- Секция Listen описывает порты, которые слушает сервер. Тут перечисляются порты для STUN/TURN (3478) и их шифрованных аналогов (5349); порты для подключения клиентов (5222 и 5223) и серверов (5269 и 5270), а также ряд дополнительных портов. К ним относятся порты для админ-панели сервера (5281), веб-страницы для регистрации пользователей (5282), а также подключений к серверу через Websocket или BOSH и загрузка файлов через HTTP Upload (5283) - я постарался разнести различные сервисы по разным портам, чтобы в дальнейшем было проще, например, ограничить доступ к админ-панели определенными адресами, не блокируя при этом доступ к остальным функциям. Еще одним полезным сервисом является веб-клиент ConverseJS - под него выделен порт 5000 - о нем будет написано чуть ниже. Для некоторых описанных портов необходимо указывать внешний IP-адрес сервера - это нужно, чтобы сервис знал, какой именно адрес отдавать клиентам - это относится к настройкам STUN/TURN и Proxy. В качестве шаблона в показанном конфиге указаны адреса 123.123.123.123 для IPv4 и 2a01:0123:0123:0123:: для IPv6 - если не используете IPv6, то просто закомментируйте соответствующие строки.
- Далее необходимо указать хост, с которого будет отдаваться картинка CAPTCHA.
- Далее указывается один или несколько администраторских аккаунтов - с помощью них можно будет попасть в веб-интерфейс администрирования, зайти в любую конференцию, а также в браузере сервисов будут доступны дополнительные административные пункты.
- После настроек шейпера идет довольно объемное описание различных модулей. Из интересного:
- В server-info для mod_disco можно указать контакты администратора сервера, техподдержки или просто дополнительную информацию.
- В mod_http_upload можно указать адрес, с которого будут отдаваться загруженные на сервер через HTTP Upload файлы. При желании можно использовать не встроенный веб-сервер, а тот веб-сервер, что был настроен для получения сертификатов. Чуть ниже этой опции есть настройка времени устаревания файлов - в примере выше это 180 дней. Если у вас мало места на сервере, то можно поставить это значение поменьше.
- Аналогичным образом можно поступить с mod_muc_log - модулем, позволяющим вести логи конференций (при включении соответствующей опции в настройках этой конференции). В примере выше файлы просто складываются в определенный каталог, а дальше их уже раздает веб-сервер.
- Для модуля proxy65 не забудьте указать внешний адрес сервера.
- Если хотите получать уведомления о новых регистрациях - обратите внимание на параметр registration_watchers в секции mod_register. Тут в том числе можно запретить регистрацию пользователей вообще или ограничить ее определенными адресами (например, локальной сетью), а также задать приветственное сообщение, отправляемое каждому новому пользователю.
- Ниже идет большой блок параметров STUN/TURN-сервера. Укажите внешние IP-адреса сервера и обслуживаемый домен; если у сервера нет IPv6-адреса - закомментируйте соответствующие блоки.