Как стать автором
Поиск
Написать публикацию
Обновить
72.4
Cloud4Y
#1 Корпоративный облачный провайдер

Балансировка Exchange Server 2019 и корпоративного портала на одном внешнем IP

Уровень сложностиСредний
Время на прочтение7 мин
Количество просмотров1.4K

Привет, Хабр! На связи Алексей Ежков из из Cloud4Y. Один внешний IPv4, десятки пользователей Exchange и растущий трафик портала — звучит как головоломка? В этой статье я покажу, как мы решили её, заведя всё хозяйство за единственным IP и обеспечив максимальную защиту.

Допустим, в компании «Фирмачка» (доменное имя — firmachka.pro) количество пользователей Exchange Server 2019 перевалило за сотню, а трафик к двум фронтендам корпоративного сайта вырос после маркетинговой кампании. У провайдера — только один публичный IPv4.


Требуется:

  1. Один внешний IP-адрес.

  2. SNI-маршрутизация нескольких DNS-имён.

  3. SSL-offload (TLS 1.2 / 1.3).

  4. Защита от DoS, health-checks, отказоустойчивость для DAG-кластера Exchange и двух веб-узлов.

DNS-записи и сама DAG-инфраструктура настроены корректно и в данной статье не обсуждаются.

Перед началом убедитесь, что:

  • Все необходимые DNS-записи (A-записи для mail, autodiscover, portal, lb) указывают на публичный IP-адрес HAProxy.

  • Порты 25, 80, 443 открыты на файрволе до HAProxy.

  • Exchange DAG и веб-портал настроены и работают корректно во внутренней сети.

  • У вас есть sudo-доступ к серверу с HAProxy.

2. Топология и окружение

  • HAProxy ≥ 2.4 из backports/debian-bookworm.

  • Exchange Server 2019 CU13 (две ноды DAG).

  • Портал на IIS 10 / ASP.NET Core.

3. Откуда берутся файлы *.pem в /etc/ssl/private

3.1. Получаем wildcard-сертификат

Сертификат нужен один — *.firmachka.pro, чтобы покрыть mail, autodiscover, portal, lb и любые будущие имена.

ACME-клиент выпускает wildcard через DNS-01-валидацию:

DOMAIN="firmachka.pro"
CERT_DIR="/etc/ssl/private"

certbot certonly \
        --agree-tos \
        --manual \
        --preferred-challenges dns \
        -d "*.${DOMAIN}" -d "${DOMAIN}" \
        --manual-public-ip-logging-ok

# склейка в формат HAProxy
cat /etc/letsencrypt/live/${DOMAIN}/privkey.pem \
    /etc/letsencrypt/live/${DOMAIN}/fullchain.pem \
    > ${CERT_DIR}/wildcard.${DOMAIN}.pem
chmod 600 ${CERT_DIR}/wildcard.${DOMAIN}.pem

Для автоматического продления wildcard-сертификатов через DNS-01 челлендж обычно используются DNS-плагины Certbot

3.2. DH-параметры

openssl dhparam -out /etc/haproxy/certs/dhparam.pem 2048

Файл подключается директивой ssl-dh-param-file. Без собственных DH-параметров часть сканеров ругается на «weak prime reuse».

После автоматического продления сертификата выполняйте systemctl reload haproxy, иначе HAProxy продолжит отдавать старую цепочку

4. Полный haproxy.cfg

4.1. global

global
    log /dev/log    local0 notice
    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

    maxconn 20000

    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets prefer-client-ciphers
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384
    ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384

    ssl-dh-param-file /etc/haproxy/certs/dhparam.pem
    tune.ssl.default-dh-param 2048

Пояснения построчно:

  • log /dev/log local0 notice — журналируем в rsyslog; уровень notice экономит диск.

  • stats socket — пригодится для Runtime API и атомарной перезагрузки.

  • maxconn 20000 — лимит по памяти: примерно 50 КБ/conn ⇒ ≈1 ГБ RSS.

  • ssl-default-bind-options — отключаем TLS 1.0/1.1, запрещаем session tickets (защита от атак resumption-tracking), навязываем клиенту наш порядок шифров.

  • ssl-default-bind-ciphersuites — suite-ы для TLS 1.3.

  • tune.ssl.default-dh-param — fallback-значение, если ssl-dh-param-file недоступен.

На заметку
Частая ошибка — оставить ssl-default-bind-options без prefer-client-ciphers; тогда старые браузеры выберут RC4.

4.2. defaults

defaults
    mode    http
    log     global
    option  httplog
    option  dontlognull
    option  forwardfor
    option  redispatch
    option  http-server-close
    retries 3
    
    http-request set-header X-Forwarded-Proto https if { ssl_fc }

    timeout connect 10s
    timeout client  300s
    timeout server  300s
    errorfile 403 /etc/haproxy/errors/403.http
    errorfile 503 /etc/haproxy/errors/503.http

Ключевые моменты:

  • option http-server-close — клиенту keep-alive, а на внутренние сервера соединение закрываем после ответа; экономия на TIME_WAIT.

  • forwardfor + set-header X-Forwarded-Proto — OWA корректно формирует ссылки вида https://.

  • Таймауты 300 s подобраны под длительные загрузки вложений в OWA.

На заметку
Забытый option httpclose ведёт к обрыву крупных загрузок через OWA/OWA Light.

4.3. frontend ft_http — порт 80

frontend ft_http
    bind *:80
    mode http

    acl host_portal hdr(host) -i portal.firmachka.pro
    use_backend be_portal if host_portal

    redirect scheme https code 301 if !host_portal

Разбираем:

  1. Портал доступен и по HTTP :80 (SEO-требование).

  2. Всё остальное принудительно уходит на HTTPS (301).

4.4. frontend ft_https — порт 443

frontend ft_https
    bind *:443 ssl crt /etc/ssl/private/wildcard.firmachka.pro.pem
    mode http

    http-response set-header X-Frame-Options SAMEORIGIN
    http-response set-header X-Content-Type-Options nosniff
    http-response set-header Strict-Transport-Security "max-age=63072000; includeSubDomains"

    # Защита от DoS
    stick-table type ip size 200k expire 30s store http_req_rate(10s)
    http-request track-sc0 src
    http-request deny status 429 if { sc_http_req_rate(0) gt 100 }

    # ---------- ACL ----------
    acl host_portal        hdr(host) -i portal.firmachka.pro
    acl host_autodiscover  hdr(host) -i autodiscover.firmachka.pro

    acl url_ecp path_beg -i /ecp
     # !!! ВАЖНО: Укажите вашу административную подсеть !!!
    acl admin_network src 192.168.20.0/24
    http-request deny if url_ecp !admin_network

    # ---------- Маршрутизация ----------
    use_backend be_portal_https      if host_portal
    use_backend be_exchange_unified  if host_autodiscover

    default_backend be_exchange_unified

Пояснения:

  • Один wildcard-сертификат обслуживает все имена.

  • autodiscover.firmachka.pro направляется в тот же backend, что и mail.

  • Security-заголовки убирают mixed-content и click-jacking.

  • stick-table фильтрует > 100 запросов/10 s ⇒ HTTP 429 Too Many Requests.

  • Защита ECP: доступ только из админской подсети. Exchange не умеет ограничивать сам.

На заметку
• Таблица 200 k на 30 s ≈ 14 MB RAM. Статус в Runtime API: show table ft_https.
• Если нужен IPv6, заведите отдельную stick-таблицу type ipv6.

4.5. frontend ft_smtp — порт 25

frontend ft_smtp
    bind *:25
    mode tcp
    
    # Ограничение скорости подключений
    stick-table type ip size 50k expire 1m store conn_rate(10s)
    tcp-request connection track-sc0 src
    tcp-request connection reject if { sc_conn_rate(0) gt 10 }
    
    default_backend be_smtp
  • Layer 4 proxy, никаких STARTTLS-offload; Exchange сам отдаёт сертификат.

  • 10 conns / 10 s → reject (борьба с ботнетами, не влияя на легитимный MX-трафик).

На заметку
Серые листы (greylisting) любят быстро пересоединяться. Подберите лимит под вашу почтовую политику.

4.6. backend be_portal

backend be_portal
    mode http
    balance roundrobin
    option httpchk GET /
    http-check expect status 200
    server portal01 192.168.20.10:80  check inter 3s rise 2 fall 3
    server portal02 192.168.20.11:80  check inter 3s rise 2 fall 3
  • rise 2 / fall 3 — узел объявляется «здоровым» после двух успешных чеков и «больным» после трёх неудач.

  • Схема round-robin подходит: сессии портала хранятся в Redis-кластер, нет state-stickiness.

4.7. backend be_portal_https

backend be_portal_https
    mode http
    balance roundrobin
    option httpchk GET /
    http-check expect status 200
    server portal01 192.168.20.10:443 ssl verify none check inter 3s rise 2 fall 3
    server portal02 192.168.20.11:443 ssl verify none check inter 3s rise 2 fall 3
  • Внутренний трафик также шифруется; verify none допустим, так как это изолированная VLAN.

  • Если у портала собственный internal-CA, замените на verify required ca-file /etc/ssl/certs/ca_portal.pem.

4.8. backend be_exchange_unified

backend be_exchange_unified
    mode http
    balance source
    option httpchk GET /owa/healthcheck.htm
    http-check expect status 200
    server ex01 192.168.0.100:443 ssl verify none check inter 3s rise 2 fall 3
    server ex02 192.168.0.101:443 ssl verify none check inter 3s rise 2 fall 3

Почему balance source?

Exchange клеит сессию OWA к конкретному ClientAccessServer. Sticky-cookie тоже решит задачу, но source-hash проще и не ломает ActiveSync с legacy-устройств.

healthcheck.htm — официальный endpoint; возвращает «200 OK Healthy» без аутентификации.

На заметку
После CU-обновления Microsoft иногда меняет ответ с «Healthy» на «OK». Ловите 503? Сравните тело ответа в curl -k.

4.10. backend be_smtp

backend be_smtp
    mode tcp
    balance roundrobin
    option smtpchk EHLO firmachka.pro
    server ex01 192.168.0.100:25 check
    server ex02 192.168.0.101:25 check

option smtpchk посылает EHLO и ждёт кода 250. Это держит порт 25 в «green» даже при частичном падении Exchange.

4.11. listen stats

listen stats
    bind *:8404
    stats enable
    stats uri /stats
    stats realm "HAProxy Statistics"
    stats auth admin:ChangeMeNow!
    stats refresh 30s
  • Веб-панель по HTTP (не HTTPS). Ограничьте внешним файрволом!

  • stats socket из блока global позволяет вместо веб-GUI пользоваться socat /run/haproxy/admin.sock - и собирать метрики в Prometheus-exporter.


5. Безопасность и производительность

  1. TLS 1.3 + строгий список шифров, SessionTickets отключены.

  2. Один wildcard-сертификат упрощает сопровождение и закрывает все поддомены.

  3. stick-table фильтрует 100 req/10 s или 10 conn/10 s на SMTP.

  4. maxconn 20000 + Linux 5.10 (SO_REUSEPORT) дают 5–6 Гбит/с без CPU-узких мест. Опция SO_REUSEPORT позволяет HAProxy эффективнее использовать несколько ядер CPU, распределяя входящие соединения между несколькими рабочими процессами (worker processes) HAProxy, если они запущены.

  5. Health-checks раз в 3 s обеспечивают SLA ≥ 99.9 % без избыточного сетевого шума.


6. Тестирование

haproxy -c -f /etc/haproxy/haproxy.cfg          # проверка синтаксиса
curl -k https://lb.firmachka.pro/owa/healthcheck.htm  # Проверяем доступность и работоспособность Health Check страницы Exchange OWA
curl -k https://autodiscover.firmachka.pro/autodiscover/autodiscover.xml -H "Content-Type:text/xml"  # Имитируем запрос к службе Autodiscover Exchange через балансировщик
swaks --to recipient@example.com --from sender@firmachka.pro --server mail.firmachka.pro --port 25 -ehlo test  # Тестирование работы SMTP-сервера
openssl s_client -connect lb.firmachka.pro:443 -servername portal.firmachka.pro \    # Получаем информацию о выбранной версии TLS и наборе шифров
        -tlsextdebug -status | grep -E 'Selected|TLSv'

На заметку
Если openssl сообщает New, TLSv1.2, а не TLSv1.3, проверьте версию OpenSSL на клиенте.

7. Эксплуатация

  • Горячий reload: haproxy -f /etc/haproxy/haproxy.cfg -c && systemctl reload haproxy.

  • Runtime-API: echo "show table ft_https" | socat stdio /run/haproxy/admin.sock.

  • Мониторинг: github.com/prometheus/haproxy_exporter читает stats socket.


8. Частые проблемы

Симптом

Причина

Решение

503 на OWA после продления wildcard-серта

забыли reload

systemctl reload haproxy

401/403 при входе в /ecp

ACL admin_network не совпадает с новой VPN-подсетью

скорректировать CIDR

Серый список SMTP режет доставку

stick-table слишком строг

увеличить лимит conn_rate


9. Итог

Мы получили:

  • Wildcard-сертификат *.firmachka.pro, покрывающий все сервисы.

  • SNI-маршрутизацию четырёх DNS-имён (mailautodiscoverportallb) на одном IP.

  • SSL-offload + TLS 1.3 без компромиссов в безопасности.

  • Отказоустойчивые Exchange 2019 DAG и веб-кластер за одним фронтом.

  • Базовую DoS-защиту и удобный мониторинг без лишнего железа.

Официальная документация для углубления:

Теги:
Хабы:
+6
Комментарии4

Публикации

Информация

Сайт
www.cloud4y.ru
Дата регистрации
Дата основания
2009
Численность
51–100 человек
Местоположение
Россия