Исходная ситуация
На одном физическом сервере, который имеет единственный реальный IP-адрес работают несколько виртуальных HTTPS web-серверов. В том числе и XenApp с Citrix Secure Gateway. У каждого сервера свой сертификат.
Задача - настроить reverse-proxy для этих сервисов, чтобы на real_IP:443 работали все сервисы и терминация SSL трафика происходила не на reverse-proxy, а на web-серверах. Также, мне нужно, чтобы через порт 443 проксировался ssh-трафик на заданный хост.
До этого момента в качестве reverse-proxy использовался nginx. И на первый взгляд он пригоден для этой цели. Например, сайт XenApp открывался. Но как оказалось, для работы с Citrix Secure Gateway он не подходит. Скорее всего не все HTTPS-запросы Citrix Receiver в заголовке имеют информацию об имени сервера (без SNI). В результате - nginx не понимал куда проксировать такой запрос. Однако на последних версиях Citrix Secure Gateway 3.3.5 и Linux Citrix Receiver 13.7 x64, вероятно, все заработает и через nginx.
Видимо нужен reverse-proxy, работающий в режиме tcp и поддерживающий SNI (Server Name Indication), который сможет неидентифицированные с помощью SNI запросы отправлять на дефолтный сервер.
Решение
Решением стало внедрение haproxy (версии не ниже 1.5).
Устанавливать на Ubuntu 16.04 можно из стандартного репозитория.
Устанавливаем haproxy и проверяем версию:
sudo apt-get install haproxy haproxy -vv
В выводе этой команды должно быть:
HA-Proxy version 1.6.3 2015/12/25
и
OpenSSL library supports TLS extensions : yes OpenSSL library supports SNI : yes
Теперь в файл конфигурации /etc/haproxy/haproxy.cfg пишем что-то такое:
global log 127.0.0.1 local5 notice user haproxy group haproxy # Adjust the timeout to your needs defaults timeout client 30000s timeout server 30000s timeout connect 10s ############################################################# ############ Frontend HTTPS in tcp mode ########################################################### frontend https_proxy bind *:443 mode tcp log 127.0.0.1 local6 option tcplog tcp-request inspect-delay 5s tcp-request content accept if { req_ssl_hello_type 1 } use_backend ssh if { payload(0,7) -m bin 5353482d322e30 } use_backend xd_https if { req_ssl_sni -i xd.autosys.tk } use_backend sstp_https if { req_ssl_sni -i sstp.autosys.tk } # default_backend xd_https default_backend sstp_https ########################################################## ###-------------- FrontEnds HTTP ------------######## ###################################################### frontend http # option httplog log 127.0.0.1 local7 debug #/var/log/http.log mode tcp bind *:80 # redirect to HTTPS redirect scheme https if { hdr(Host) -i xenapp.autosys.tk } !{ ssl_fc } use_backend ssh if { payload(0,7) -m bin 5353482d322e30 } ######################################################################### ####----- Backends----------###################### ######################################################################## backend sstp_https mode tcp server sstp_ssl 192.168.77.138:443 backend xd_https mode tcp # option ssl-hello-chk server xdwebi xd.autosys.tk:443 check backend ssh mode tcp server ssh_haproxy 127.0.0.1:22
Старые версии продуктов Citrix (Citrix Secure Gateway и Citrix Receiver) не работали без строки default_backend xd_https (вероятно не в каждом запросе HTTPS был заголовок SNI), однако на последних версиях (Citrix Secure Gateway 3.3.5 и Linux Citrix Receiver 13.7 x64) все работает без этой строки, что позволяет запустить на порте 443 еще и SSTP-VPN на базе SoftEther, указав именно его в качестве дефолтного бекенда для SSL-траффика :).
Также в приведенном конфиге показано как пробросить SSH с портов 80 и 443.
Отказоустойчивая балансировка Secure Gateway
Предположим, у нас работает Citrix SecureGateway, но иногда нам нужно с его хостом производить какие-то манипуляции без остановки сервиса. Выход - настройка балансировки. Поднимаем второй хост с Web-интерфейсом и Secure Gateway и настраиваем также как и первый. Сертификат берем тот же что и на первом хосте. Вся магия будет на haproxy в раздельчике backend. В него нужно добавить balance source, тип хеширования IP-адреса источника hash-type consistent и второй сервер с SecureGateway.
backend xenapp option tcplog log ${LOCAL_SYSLOG}:514 local1 debug hash-type consistent balance source mode tcp option ssl-hello-chk server xenappsrv xenapp.domain.com:443 check server xenappsrv2 xenapp2.domain.com:443 check
В результате у нас будет балансировка типа source, при которой клиент будет работать с тем сервером, к которому он подключился изначально. Тип хеширования можно не задавать (по-умолчанию это map-based), но иногда лучше работает consistent. Также, иногда нужно немного расширить таймаут проверки ssl-hello-check. Для этого перед строкой option ssl-hello-chk добавить timeout check 5000 (ну или сколько надо в миллисекундах).
Включаем логи на для haproxy
HAproxy очень продвинутый балансировщик, поэтому писать логи в просто файлы он не может…
Но может писать логи на удаленный или локальный сервер syslogd.
Сначала нужно разрешить syslogd принимать логи по UDP.
Для этого в файле /etc/default/syslogd нужно заменить SYSLOGD=““ на SYSLOGD=”-r”.
Затем в файле /etc/syslog.conf нужно прописать facilities для логов. Напомню, что логи можно писать как в один файл все, так и в разные файлы и на разные сервера. Прямо в начало /etc/syslog.conf я добавил:
# Log HAproxy events local0.* -/var/log/haproxy.log local1.* -/var/log/xenapp.mydomain.org.log local2.* -/var/log/mydomain.org.log
Тем самым я объявил facilities с именами local10, local1 и local2.
А теперь можно настроить логи в конфигурации haproxy. У меня получилось примерно так:
global log ${LOCAL_SYSLOG}:514 local0 notice defaults option tcplog log global # Adjust the timeout to your needs timeout client 3000s timeout server 3000s timeout connect 10s # Single VIP listen ssl :443 tcp-request inspect-delay 5s tcp-request content accept if { req_ssl_hello_type 1 } use_backend xenapp if { req_ssl_sni -i xenapp.mydomain.org } use_backend mydomain if { req_ssl_sni -i mydomain.org } default_backend xenapp backend xenapp log ${LOCAL_SYSLOG}:514 local1 debug mode tcp server xenappsrv xenapp.local:443 backend mydomain log ${LOCAL_SYSLOG}:514 local2 debug mode tcp server mydomainsrv mydomain.local:443
Всё. Еще может понадобиться создать файлы логов, а то syslogd откажется стартовать.
Перезапускаем haproxy и syslogd и радуемся логам.
Discussion