Как настроить доменные имена для домашнего сервера в локальной сети без DNS-сервера?

Как настроить доменные имена для домашнего сервера в локальной сети без DNS-сервера? — статья Василия Киреева
Доменные имена для домашнего сервера без DNS: настраиваем адреса .local в локальной сети и подключаем Nginx, чтобы домашние сервисы открывались без портов.

На домашнем сервере часто одновременно работают разные веб-сервисы: сайты, умный дом, облачное хранилище, медиасервер. Пользователям неудобно открывать их по IP-адресу и разным портам. Хочется короткие адреса вроде homeassistant.local вместо 192.168.1.50 и plex.local вместо 192.168.1.50:32400.

Настроить полноценный локальный DNS получается не всегда. Даже если такая возможность есть, самодельные доменные зоны, даже .lan, в современных браузерах могут восприниматься как поисковый запрос, если не указать протокол.

Доменная зона .home.arpa специально выделена для домашних сетей, но она решает задачу только при наличии DNS-сервера на роутере и не понятна тем, кто далек от компьютеров.

Для одного домашнего сервера самый простой вариант без DNS-сервера — Multicast DNS, сокращённо mDNS, и домены в зоне .local. Это способ разрешения имён внутри локального сегмента сети при отсутствии обычного DNS-сервера. Устройства самостоятельно транслируют в локальную сеть свои имена.

Почему не получится публиковать много имен через конфигурацию Avahi

В Linux mDNS обычно обеспечивает Avahi. У Avahi есть конфигурационный файл /etc/avahi/hosts. Это простой текстовый файл, где на каждой строке задаётся соответствие IP-адреса и имени хоста, которое Avahi должен опубликовать через mDNS.

Таким способом можно присваивать доменные имена устройствам, у которых нет возможности самостоятельно транслировать свое локальное имя в сеть. Однако, когда пытаются опубликовать несколько имён для одного IP-адреса через этот механизм, Avahi часто отвечает ошибкой:

Static host name example.local: avahi_server_add_address failure: Local name collision

Это происходит, потому что один IPv4-адрес обычно должен иметь одну основную обратную запись.

Обычная запись отвечает на вопрос: какой IP-адрес у имени example.local. Это A-запись.

Обратная запись отвечает на вопрос: какое имя у IP-адреса 192.168.1.50. В классическом DNS это PTR-запись. Обратные записи используются в диагностике, логах и в ситуациях, когда по IP-адресу нужно получить человекочитаемое имя.

Если попытаться закрепить несколько разных имён за одним адресом через /etc/avahi/hosts, Avahi старается публиковать и обратную часть, а она становится неоднозначной. В результате Avahi фиксирует коллизию и отказывается добавлять адрес.

Практическое решение — публиковать дополнительные имена как A-записи без обратной публикации.

Публикация дополнительных имён через avahi-publish и ключ -R

Для публикации отдельных A-записей подходит утилита avahi-publish из пакета avahi-utils. Она регистрирует имя в mDNS через работающий avahi-daemon.

Базовая команда:

avahi-publish -a -R example.local 192.168.1.50

Пояснения по ключам:

  • -a публикует A-запись, то есть соответствие имени и IPv4-адреса
  • -R соответствует --no-reverse, то есть запрещает публикацию обратной PTR-части и тем самым избегает конфликтов при нескольких именах на одном IP

Так можно объявить несколько имён, которые будут указывать на один и тот же сервер.

Установка Avahi и управление набором имён через systemd

На Raspberry Pi OS 13 Avahi работает из коробки. Если нет — устанавливается и включается следующим образом:

sudo apt update
sudo apt install -y avahi-daemon avahi-utils
sudo systemctl enable avahi-daemon
sudo systemctl start avahi-daemon

Публикация имен avahi-publish работает как отдельный процесс. Удобно оформить каждое имя как systemd-сервис и поднимать весь набор одним target-юнитом.

В примерах ниже используется IP 192.168.1.50. Замените на IP адрес вашего сервера.

Шаблон systemd-юнита

Создайте файл /etc/systemd/system/avahi-publish@.service:

[Unit]
Description=Publish mDNS A record %I for 192.168.1.50 via avahi-publish
After=network-online.target avahi-daemon.service dbus.service
Wants=network-online.target
Requires=avahi-daemon.service

[Service]
Type=simple
# -a публикует A-запись
# -R отключает обратную запись
ExecStart=/usr/bin/avahi-publish -a -R %I 192.168.1.50
Restart=always
RestartSec=2
StartLimitIntervalSec=0

[Install]
WantedBy=multi-user.target

%I — имя экземпляра шаблона. Если запустить avahi-publish@example.local.service, то %I будет example.local.

Target-юнит для запуска всех имён

Создайте файл /etc/systemd/system/avahi-publish.target:

[Unit]
Description=Publish all mDNS A records via avahi-publish@.service

# Записи для сайта на Битрикс (bitrix.local), Home Assistant (homeassistant.local), ownCloud (owncloud.local) и Plex (plex.loca)
Wants=avahi-publish@bitrix.local.service
Wants=avahi-publish@homeassistant.local.service
Wants=avahi-publish@owncloud.local.service
Wants=avahi-publish@plex.local.service

After=avahi-daemon.service dbus.service network-online.target
Requires=avahi-daemon.service

[Install]
WantedBy=multi-user.target

Wants= перечисляет экземпляры шаблона, которые должны стартовать вместе с target-юнитом.

Включение и запуск

Примените новые unit-файлы:

sudo systemctl daemon-reload

Включите автозапуск набора имён и запустите его:

sudo systemctl enable avahi-publish.target
sudo systemctl start avahi-publish.target

Проверка разрешения имён:

getent hosts homeassistant.local
avahi-resolve -n owncloud.local

Один nginx как точка входа для всех сервисов

mDNS решает только задачу разрешения имён в .local в IP-адрес сервера. Чтобы разные имена открывали разные сервисы, нужен веб-сервер на 80 порту, который маршрутизирует запросы по заголовку Host. Для этого подходит nginx.

Установка Nginx на Raspberry Pi OS 13

sudo apt update
sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx

Один конфигурационный файл

Используем один файл /etc/nginx/conf.d/server.conf.

После изменений проверяйте конфигурацию и перечитывайте её:

sudo nginx -t
sudo systemctl reload nginx

Пример конфигурации Nginx

Файл /etc/nginx/conf.d/server.conf.

Общие настройки для прокси и WebSocket

map $http_upgrade $connection_upgrade {
default upgrade;
''      close;
}

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;

Прямой доступ к сайту на 80 порту

server {
  listen 80 default_server;
  server_name server.local;

  root /var/www/html;
  index index.html;

  location / {
    try_files $uri $uri/ =404;
  }
}

Проксирование обычного веб-сервиса

nginx принимает запрос на 80 порту и проксирует его на порт, который опубликован локально. Пример для тестовой Docker-среды для Bitrix.

server {
  listen 80;
  server_name bitrix.local;

  location / {
    proxy_pass http://127.0.0.1:8588;
    proxy_http_version 1.1;
  }
}

Home Assistant

Home Assistant использует WebSocket в интерфейсе, поэтому нужны заголовки Upgrade и Connection. Также полезно увеличить таймауты для длительных подключений.

server {
  listen 80;
  server_name homeassistant.local;

  location / {
    proxy_pass http://127.0.0.1:8123;

    proxy_http_version 1.1;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    proxy_buffering off;
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
  }
}

Если Home Assistant стоит за обратным прокси, в configuration.yaml нужно включить use_x_forwarded_for и указать trusted_proxies, иначе запросы от прокси будут блокироваться.

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 127.0.0.1

ownCloud

Для ownCloud часто требуется увеличить допустимый размер тела запроса, иначе крупные загрузки будут ограничены на уровне nginx. Также полезно увеличить таймауты чтения.

server {
  listen 80;
  server_name owncloud.local;

  client_max_body_size 2G;

  location / {
    proxy_pass http://127.0.0.1:8880;
    proxy_http_version 1.1;
    proxy_read_timeout 3600s;
  }
}

Plex

При доступе к Plex по доменному имени, сервис обычно требует авторизацию, даже если в локальной сети вы настроили доступ без входа. На практике это означает, что сценарий гостевого входа, который работает по IP-адресу, при входе по доменному имени перестаёт быть прозрачным. В этой схеме используется редирект.

Поэтому остаётся простой и предсказуемый вариант: редирект на IP-адрес и порт Plex Web.

server {
  listen 80;
  server_name plex.local;

  return 301 http://192.168.1.50:32400/web/;
}

Что получится в итоге

После настройки пользователи в домашней сети смогут открывать сервисы по понятным адресам без запоминания портов:

  • server.local открывает сайт на 80 порту
  • bitrix.local открывает тестовую среду через прокси
  • homeassistant.local открывает Home Assistant с поддержкой WebSocket
  • owncloud.local открывает ownCloud с корректными лимитами загрузки
  • plex.local перенаправляет в Plex Web по IP-адресу и порту, чтобы не упираться в требования авторизации при доступе по доменному имени