Я устал от ситуации с мессенджерами и настроил звонки через Nextcloud Talk для всей семьи. Потому что мама звонит в VK и не понимает, почему звонки не проходят - у меня iPhone, приложение VK удалено из стора. А значит, отозван токен для уведомлений и входящие вызовы проходят только если приложение открыто. А папа звонит в TG по старой привычке - пока включишь VPN, пока дождешься установления соединения...
Задача: дать возможность всей семье общаться через свой self-hosted сервер с Nextcloud Talk. В моём случае это TrueNAS, об этом писал ранее на Хабре. Для этого нужен домен с SSL-сертификатом и доступом к htaccess либо конфигам nginx. Схема выглядит так:

Шаг 1. Настройка хоста
Нам потребуется свой домен example.com. Допустим у него пописаны NS от хостера:
Я использую VPS с HestiaCP вместо виртуального хостинга, но данная схема будет работать с любой панелью. Работать будем дальше с ней. Далее в панели хостера создаём поддомен nextcloud.example.com (хотя можно и на основном домене делать) и меняем A-запись в настройках DNS на IP сервера с HestiaCP. Проверяем через терминал (PowerShell), что резолвится:
nslookup nextcloud.example.com
nslookup nextcloud.example.com 8.8.8.8
Мы должны увидеть IP сервера с HestiaCP. Выпускаем SSL от Let's Encrypt - так SSL-сертификатом будет управлять HestiaCP и отвечать за его автоматический перевыпуск. DNS support для домена не нужен, т к DNS управлеятся хостером.
Enable SSL for this domain - вкл
Use Let's Encrypt to obtain SSL certificate - вкл
Enable automatic HTTPS redirection - вкл

Шаг 2. Настройка Port Forwarding на домашнем роутере
Мне повезло, и провайдер выдал мне статичный IPv4 не за CGNAT, иначе потребовался бы DDNS. Сам Nextcloud у меня уже установлен на TrueNAS в домашней сети по адресу 192.168.0.17:30027 (порт 30027 - дефолтный для Nextcloud). Давайте сгенерируем рандомный порт и настроим Port Forwarding на роутере с этого порта на 30027.
На примере моего старенького роутера Asus RT-N12 - давайте настроим Port Forwarding c рандомного порта 56070 на 192.168.0.17:30027:
Service Name = Nextcloud # Называете как нравится
Source Target = IP вашего хостинга или VPS. Так доступ будет только у вашего VPS. Можно оставить пустым, но будет менее безопасно
Port Range = 56070
Local IP = 192.168.0.17 # локальный IP TrueNAS
Local Port = 30027
Protocol = TCP
Создаем сразу еще два правила - они понадобятся нам позже:
Service Name = Nextcloud coturn 3478 # Называете как нравится
Source Target = пусто # Если разрешаем подключения с любого IP
Port Range = 3478
Local IP = 192.168.0.17 # локальный IP TrueNAS
Local Port = 3478
Protocol = BOTH
И последнее:
Service Name = Nextcloud coturn relay # Называете как нравится
Source Target = пусто # Если разрешаем подключения с любого IP
Port Range = 49160:49200
Local IP = 192.168.0.17 # локальный IP TrueNAS
Local Port = 49160
Protocol = UDP

Шаг 3. Настройка реверс-прокси на хостинге
Наша задача в том, чтобы при открытии https://nextcloud.example.com/ открывалась страница логина. Есть несколько вариантов:
Вариант 1. Через htaccess (apache)
Создаем файл .htaccess в корне public_html сайта и прописываем туда 2 строчки:
# Проксирование всех запросов на домашний Nextcloud RewriteEngine OnRewriteRule (.*) http://<ВНЕШНИЙ_IP_РОУТЕРА>:56070/$1 [P,L]
Эта история будет работать, если на хостинге включено 2 модуля:
proxy_module
proxy_http_module
Проверить можно подключившись к терминалу хостинга и ввести команду:
apachectl -M | grep proxy
Если этих 2х модулей не видно - то можно доустановить, например у меня не хватало одного модуля:
a2enmod proxy_http systemctl restart apache2
Я также попробовал данную схему с обычным виртуальным хостингом у одного из популярных хостеров в РФ - у него хоть и доступен терминал из браузера, но установить недостающие модули он не дал. Возможно стоит обратиться с вопросом к техподдержке.
Вариант 2. Через nginx
Создаем новый шаблон для Proxy Template и применяем его к nextcloud.example.com в HestiaCP. Признаюсь честно - код помогал писать Claude:
cat > /usr/local/hestia/data/templates/web/nginx/nextcloud_proxy.tpl << 'EOF' #=========================================================================# # Nextcloud External Proxy Web Domain Template # # Custom template, proxies all requests including static assets # # to an external backend, without local try_files fallback. # #=========================================================================# server { listen %ip%:%proxy_port%; server_name %domain_idn% %alias_idn%; error_log /var/log/%web_system%/domains/%domain%.error.log error; include %home%/%user%/conf/web/%domain%/nginx.forcessl.conf*; location ~ /\.(?!well-known\/|file) { deny all; return 404; } location / { proxy_pass http://<ВНЕШНИЙ_IP_РОУТЕРА>:56070; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 3600s; proxy_send_timeout 3600s; client_max_body_size 10G; } location /error/ { alias %home%/%user%/web/%domain%/document_errors/; } include %home%/%user%/conf/web/%domain%/nginx.conf_*; } EOF
И повторяем тоже самое для HTTPS (stpl):
cat > /usr/local/hestia/data/templates/web/nginx/nextcloud_proxy.stpl << 'EOF' #=========================================================================# # Nextcloud External Proxy Web Domain Template (SSL) # # Custom template, proxies all requests including static assets # # to an external backend, without local try_files fallback. # #=========================================================================# server { listen %ip%:%proxy_ssl_port% ssl; server_name %domain_idn% %alias_idn%; error_log /var/log/%web_system%/domains/%domain%.error.log error; ssl_certificate %ssl_pem%; ssl_certificate_key %ssl_key%; ssl_stapling on; ssl_stapling_verify on; if ($anti_replay = 307) { return 307 https://$host$request\_uri; } if ($anti_replay = 425) { return 425; } include %home%/%user%/conf/web/%domain%/nginx.hsts.conf*; location ~ /\.(?!well-known\/|file) { deny all; return 404; } location / { proxy_pass http://<ВНЕШНИЙ_IP_РОУТЕРА>:56070; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 3600s; proxy_send_timeout 3600s; client_max_body_size 10G; } location /error/ { alias %home%/%user%/web/%domain%/document_errors/; } proxy_hide_header Upgrade; include %home%/%user%/conf/web/%domain%/nginx.ssl.conf_*; } EOF
Затем проверяем:
# должно быть без ошибок nginx -t # ребутаем nginx systemctl reload nginx # проверяем, что статика грузится (должно быть 200) curl -I https://nextcloud.example.com/core/css/server.css
Вариант 3. Для тех, кто понимает что делает
Не повторяйте это дома!
A-запись nextcloud.example.com указывает прямо на домашний IP.
На роутере открыть порты 80 и 443 на TrueNAS.
В каталоге приложений TrueNAS ставится обратный прокси с Let's Encrypt, например Nginx Proxy Manager, и проксирует домен на Nextcloud.
---
После выполнения одного из вариантов - уже можно попробовать открыть https://nextcloud.example.com/ в браузере и мы увидим заглушку от Nextcloud.
Шаг 4. Отключить DoS protection на роутере
Со включеным DoS protection роутер пропускает без задержек лишь несколько запросов, а всем последующим даёт таймаут и ставит в очередь. Выглядит это так, что вы открываете страницу логина в Nextcloud по адресу https://nextcloud.example.com/, а там простая текстовая заглушка и статика загружается несколько минут. В Dev Tools браузера видно, как во вкладке Network запросы к статике летят с большой задержкой друг за другом.


Шаг 5. Настройка разрешенных хостов в Nextcloud
Открываем Shell Nextcloud в Truenas и добавляем наш домен в список разрешенных:
php occ config:system:get trusted_domains
У меня показало 4 хоста:
127.0.0.1 192.168.0.17 localhost nextcloud
Значит добавляем новый хост с индексом 4:
php occ config:system:set trusted_domains 4 --value=nextcloud.example.com
Дальше добавляем:
php occ config:system:set overwrite.cli.url --value="https://nextcloud.example.com" php occ config:system:set overwriteprotocol --value="https" php occ config:system:set overwritehost --value="nextcloud.example.com"
Перезагружаем приложение Nextcloud в TrueNAS. Теперь при запросе https://nextcloud.example.com/ должен происходить редирект на /login и открываться страница логина в Nextcloud. Проверяем статику в Dev Tools, если открылась почти пустая заглушка. Если все сделано верно - мы сможем авторизоваться.
Теперь уже можно попробовать создать пару тестовых учеток и обменяться сообщениями. Но для звонков надо сделать еще кое-что.
Шаг 6. Поднимаем coturn-сервер
Далее надо поднять свой Turn (coturn) сервер. Без него работают только сообщения, со звонками нормально работать не будет. К сожалению в каталоге приложений TrueNAS нет готового решения, как в случае с RustDesk Relay например. Для этого:
Создаём датасет на TrueNAS: main_pool/coturn (Owner = root)
Через Shell (или SSH) кладём файл конфига:
mkdir -p /mnt/main_pool/coturn/conf nano /mnt/main_pool/coturn/conf/turnserver.conf
Вставляем конфиг, меняем пароль:
listening-port=3478 tls-listening-port=5349 listening-ip=192.168.0.17 external-ip=<ВНЕШНИЙ_IP_РОУТЕРА> relay-ip=192.168.0.17 min-port=49160 max-port=49200 verbose fingerprint lt-cred-mech user=talkuser:<СЛОЖНЫЙ_ПАРОЛЬ> realm=example.com no-tlsv1 no-tlsv1_1
Выставляем права:
chown -R root:root /mnt/main_pool/coturn
Создаем кастомное приложение в TrueNAS: Apps - Discover Apps - Custom App с таким конфигом:
Application Name* = coturn Repository* = coturn/coturn Pull Policy* = Pull the image if it is not already present on the host. Command turnserver -c /etc/coturn/turnserver.conf Restart Policy* = Unless Stopped - Restarts the container irrespective of the exit code but stops restarting when the service is stopped or removed. Host Network = On Mount Path* = /etc/coturn/turnserver.conf Host Path* = /mnt/main_pool/coturn/conf/turnserver.conf
Деплоим приложение и запускаем.
Шаг 7. Финальная настройка Nextcloud
Открываем с ПК Nextcloud - Administration settings - Talk - TURN servers, добавить сервер с префиксом turn:, адресом <ВНЕШНИЙ_IP_РОУТЕРА>:3478, секретом и протоколом UDP and TCP. Без этого шага звонки не смогут использовать ваш TURN. Там же стоит оставить стандартный STUN stun.nextcloud.com:443.
Дальше делаем тестовый звонок вне домашней сети. Всё должно работать.
Потом была проблема с тем, что нет уведомления о входящем звонке на айфоне (call kit не заработал) - перелогинился, выкинул приложение из памяти и всё заработало. Проверить доставку пуш-уведомлений можно командой (вводить в Shell приложения Nextcloud):
php occ notification:test-push --talk <USER>
Чем порадовал Nextcloud как мессенджер
Есть Screen Sharing для iPhone. Очень удобно объяснять родителям куда и что нажимать.
Есть подключение по ссылке к миту для рандомных гостей.
Есть отдельное приложение Talk без лишних функций облака, кроссплатформенное (любая ОС, на ПК сделано на базе Electron).
Уведомелния приходят на Apple Watch.
Есть реакции на сообщения, редактирование отправленных, онлайн-статусы, избранное, видео-звонки. Что еще надо для счастья?
Ну и конечно же приложение есть во всех сторах (работают уведомления), не страшны случайные блокировки РКН.
Вместо вывода
Теперь можно со спокойной душой отключить все уведомления в Telegram, чтобы не отвлекали от работы. А близкие достанут тебя через Nextcloud. Плюсом у вас появится возможность шарить файлы наружу из основного приложения Nextcloud - теперь он будет иметь основной адрес вашего хоста вместо IP.
Можно еще поработать над дополнительной безопасностью - у нас остался незащищенный участок между доменом и домашним роутером, а также стоит подумать над защитой нашего домена от посторонних глаз. Первое, что приходит на ум - посмотреть основные Request Headers от браузера и клиента Nextcloud и сделать их минимальную проверку в htaccess, остальным отдавать 403 код ответа.
Мой канал в Telegram — @barbolin, там я делюсь своими открытиями во всём, что касается работы.
warlinx
айти которое мы заслужили - мамы папы школьные пет проекты в сми