ocserv+nginx

В данной статье кратко описывается конфигурирование OCserv, сервера VPN-протокола Cisco AnyConnect на базе SSL, и HTTPS-сервера nginx с целью обеспечения их совместной работы на одном IP-адресе в ОС Linux (RHEL/CentOS/Fedora).

warning Эта статья не является инструкцией по настройке OCserv "с нуля". Для такой настройки воспользуйтесь одной из множества имеющихся в Сети инструкций (например, этой).

Рассмотрим случай, когда у нас уже есть сервер nginx, обслуживающий сайт domain.com, и нам необходимо обеспечить подключение VPN-клиентов OCserv к субдомену sub.domain.com. Если оба домена резолвятся на один и тот же IP-адрес, возникает проблема одновременного обслуживания соединений HTTPS и VPN на одном и том же порту (443/tcp). Предлагаемое в данной статье решение позволяет обойтись без дополнительного прокси-сервера типа haproxy, используя только имеющиеся у nginx средства для работы с SSL-соединениями.

Для начала необходимо удостовериться, что nginx скомпилирован с модулями stream_ssl_preread_module/http_realip_module. В nginx из репозитория EPEL в RHEL/CentOS/Fedora данные модули присутствуют "из коробки". В случае иных дистрибутивов Linux, или если nginx был установлен из другого репозитория, убедитесь, что в результате выполнения следующей команды в списке опций компиляции nginx присутствуют оба модуля:

$ nginx -V 2>&1 | tr ' ' '\n' | grep -E 'stream_ssl_preread_module|http_realip_module'
--with-http_realip_module
--with-stream_ssl_preread_module

Далее, добавляем секцию stream в /etc/nginx/nginx.conf:

...

stream {
    map $ssl_preread_server_name $name {
        sub.domain.com ocserv;
        default https;
    }

    upstream https {
        server 127.0.0.1:8443;
    }

    upstream ocserv {
        server 127.0.0.1:9443;
    }

    server {
        listen 443;
        ssl_preread on;
        proxy_protocol on;
        proxy_pass $name;
    }
}

http {
    ...
}

В данной секции определяются два upstream с именами https и ocserv, для подключений HTTPS и VPN соответственно. Модуль stream_ssl_preread_module читает имя сервера из заголовка SSL в переменную $ssl_preread_server_name, после чего в map оно преобразуется в имя апстрима, сохраняемое в переменную $name. Преобразование осуществляется следующим образом: если имя сервера для подключения совпадает с sub.domain.com, то выбирается апстрим ocserv, для всех же остальных имен выбирается апстрим https. После этого входящее подключение перенаправляется в выбранный апстрим посредством директивы proxy_pass, при этом добавляется заголовок proxy_protocol, в который сохраняется реальный IP-адрес клиента.

Далее необходимо поправить конфигурацию /etc/nginx/conf.d/domain.conf:

server {
   listen       127.0.0.1:8443 ssl http2 proxy_protocol;
   listen       [::1]:8443 ssl http2 proxy_protocol;
   server_name  domain.com;

   ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
   ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;

   set_real_ip_from  127.0.0.1;
   real_ip_header    proxy_protocol;
   ...
}

В директиве listen необходимо указать те же адрес и порт, что и в секции upstream https {}. Помимо этого, с помощью опции proxy_protocol включается чтение записанного в секции stream заголовка, из которого получается реальный IP-адрес клиента с помощью директивы real_ip_header. Директива set_real_ip_from задает диапазон доверенных адресов источников, для которых разрешается использование данных из заголовка proxy_protocol.

В файле конфигурации /etc/ocserv/ocserv.conf необходимо поправить (или добавить) следующие директивы:

...

listen-host = 127.0.0.1
listen-proxy-proto = true
tcp-port = 9443
keepalive = 300

server-cert = /etc/letsencrypt/live/domain.com/fullchain.pem
server-key = /etc/letsencrypt/live/domain.com/privkey.pem

...

Директива listen-proxy-proto = true включает чтение реального IP-адреса клиента VPN из заголовка, добавленного опцией proxy_protocol nginx. Значение keepalive (в секундах) должно быть меньше значения proxy_timeout в конфигурации nginx (по умолчанию 10 минут). Директивы server-cert и server-key задают пути к SSL-сертификату. Обратите внимание, что данный сертификат является общим для nginx и OCserv, поэтому он должен включать в себя оба имени, domain.com и sub.domain.com.

Таким образом, описанные выше настройки представляют собой минимальный пример конфигурирования OCserv и nginx для их совместного использования на одном IP-адресе.