Сайт NGINX только для VPN: слушатель, межсетевой экран и список разрешений HTTP
Частному сайту NGINX нужен привязанный сокет VPN, ограниченное правило входа на хосте и целый список доступа HTTP. Проверяйте все три, а не один блок сервера.
Между частным сайтом и интернетом стоят три независимых решения: какой сокет принимает соединение, какой путь может его доставить и каким клиентам HTTP отвечает выбранный сервер. Блок сервера принимает только первое решение. Относитесь к двум другим как к отдельным средствам контроля, иначе сайт будет выглядеть частным, пока старый вход остаётся открытым. Большинство сбоев NGINX только для VPN возникает из-за проверки лишь первого уровня.
В примере используется шлюз Linux OpenVPN по адресу 10.90.0.1 на tun0, клиенты в 10.90.0.0/24, сохранённые источники и TCP 8443, зарезервированный для этого входа. Настройте значения, сохраните уровни.
Начните с сокета, а не с имени хоста
Привяжите слушатель HTTPS к адресу туннеля и отдельному порту. Конкретный адрес проще осмыслить, чем универсальный сокет с проверкой имени, потому что ядро не отдаст порт другому локальному адресу.
server {
listen 10.90.0.1:8443 ssl;
server_name admin.corp.example;
ssl_certificate /etc/nginx/tls/admin-fullchain.pem;
ssl_certificate_key /etc/nginx/tls/admin-key.pem;
root /srv/private-site;
allow 10.90.0.0/24;
deny all;
location / {
try_files $uri $uri/ =404;
}
}
Сертификат должен содержать имя хоста службы, которое вводят клиенты, а частное представление DNS должно разрешать этот хост в привязанный адрес. server_name только выбирает виртуальный хост; он никого не аутентифицирует. Отсутствующая запись DNS выдаёт ошибку сертификата, похожую на сбой TLS. Проверьте имя, прежде чем заменять сертификат.
Прочитайте действующую конфигурацию перед добавлением файла. Включения и универсальные слушатели могут обслуживать тот же корень в другом месте, а сервер по умолчанию может отвечать на запросы, которых новый блок вообще не видит.
Закройте каждый другой слушатель для этого содержимого
Перечислите директивы listen и сравните корни, которые они обслуживают. Блок, добавленный для другого приложения, может проксировать на тот же upstream, а старый сервер по умолчанию может продолжать отвечать после установки нового. Этот вывод и есть настоящая инвентаризация, а не файл, который вы только что правили. Сравните каждый корень, обслуживающий то же содержимое.
sudo nginx -T | grep -n "listen\|server_name\|root\|proxy_pass"
Проверьте оба семейства адресов. Привязка IPv4 оставляет сокет IPv6 нетронутым, а новая запись A не отзывает старый ответ AAAA, всё ещё указывающий на публичный путь.
Выведите старый виртуальный хост отдельным изменением. Удаление публичного двойника нового блока обычно и есть шаг, который действительно закрывает вход; частный слушатель лишь добавляет нужный.
Ограничьте входящий путь на хосте
Адрес прослушивания ограничивает сокет, а не входной интерфейс. Пакет, маршрутизированный на этот адрес откуда-то ещё, всё равно приходит как локальный ввод, поэтому межсетевому экрану хоста нужно собственное заявление о том, откуда может приходить соединение. Поэтому одна привязка редко удовлетворяет аудиту.
При политике входящих по умолчанию «запретить» и без более широкого разрешения для этой службы ограниченное правило называет туннель, диапазон источников, назначение и порт вместе:
sudo ufw allow in on tun0 proto tcp from 10.90.0.0/24 to 10.90.0.1 port 8443
Прочитайте действующий порядок межсетевого экрана перед применением. Раннее широкое разрешение может свести ограничение на нет, а раннее окончательное отбрасывание может не дать дойти до этого разрешения. Не очищайте посторонние правила и не отключайте межсетевой экран, чтобы пример подошёл.
Это правило — ввод на хост, а не маршрут к другой машине LAN, и внешний слушатель OpenVPN шлюза остаётся отдельной службой. Если сайт раньше был публичным, спланируйте, как выводится старый вход и как обрабатываются существующие сеансы.
Сохраняйте список HTTP целым при изменении расположений
NGINX оценивает записи allow и deny по порядку, и список на уровне сервера выше пропускает объявленный пул до окончательного deny. Модуль доступа также фиксирует, что расположение, определяющее любую запись доступа, использует собственный список, а не родительский. Порядок важен не меньше самих записей.
Этот сброс — обычная регрессия. Новое расположение загрузки, API или здоровья, добавляющее одну запись, молча заменяет ограничение на уровне сервера, и сайт становится шире на самом недавно добавленном пути.
Просмотрите каждое расположение, псевдоним и альтернативный блок как часть одной политики. Если проверки IP сочетаются с аутентификацией NGINX, разберитесь в satisfy: параметр any может пропустить клиента через один механизм, пока другой не пройден. Включите список доступа в контрольный список расположений.
Отказ HTTP происходит после того, как соединение TCP достигает NGINX. Он дополняет сетевые ограничения, а собственная авторизация приложения по-прежнему действует для каждого пользователя.
Проверьте живой путь и случай отказа
Запустите анализатор конфигурации, осмотрите слушающие сокеты, затем выполните новый именованный запрос от нужного клиента. Подтвердите, что сертификат проходит проверку, имя DNS разрешается в привязанный адрес, а ответ приходит из ожидаемого корня.
Затем проверьте исключённый источник и альтернативный вход. Работающий разрешённый запрос ничего не говорит о путях, которые вы хотели закрыть, и именно на исключённой проверке проявляется оставшийся слушатель или широкое разрешение межсетевого экрана.
Повторите случай загрузки или восстановления VPN. Если NGINX стартует раньше, чем существует адрес туннеля, привязка может не удаться даже с идеальным файлом. Согласуйте готовность адреса и держите локальный путь восстановления, прежде чем менять общий веб-сервер.
Запишите сокет, порт, диапазон источников и список HTTP, которые прошли. Следующее изменение VPN или DNS может сдвинуть любой из них, и четыре именованных значения сравнивать легче, чем работающую вкладку браузера.