habrahabr

Получаем IP-адреса HTTPS-клиентов с HAProxy (frontend) на Nginx (backend) в режимах HTTP и TCP-балан

  • понедельник, 5 января 2015 г. в 02:10:52
http://habrahabr.ru/post/247297/

Довольно часто требуется балансировать нагрузку между несколькими веб-серверами. При этом, как правило, необходимо, чтобы веб-приложения получали реальные IP-адреса клиентов, а не IP балансировщика.

В случае балансировки и терминации HTTP(S)-трафика на HAProxy (Layer 7 [1]) данная задача легко решается добавлением заголовка “X-Real-IP” и его обработкой на Nginx при помощи модуля ngx_http_realip_module [2]. При балансировке TCP-трафика от HTTPS-клиентов и передаче его на веб-сервера напрямую без модификации или терминации (Layer 4 [3]) добавить данный заголовок невозможно, поэтому требуется воспользоваться возможностями, предоставляемыми Proxy Protocol [4, 5, 6].

Рассмотрим оба варианта (балансировка L7 и L4) на примере выдержек из конфигурационных файлов haproxy 1.5.9 и nginx 1.6.2

Балансировка на прикладном уровне (Layer 7): терминация HTTPS-трафика на HAProxy и передача по HTTPS на Nginx


В данном примере HTTPS-трафик от клиента терминируется на HAProxy, модифицируется и передается на Nginx так же по HTTPS.

haproxy.cfg


global
        maxconn 4096
        chroot /usr/share/haproxy
        uid 99
        gid 99
        daemon
        tune.ssl.default-dh-param  2048

defaults
        log     global
        option  redispatch
        option  tcp-smart-accept
        option  tcp-smart-connect
        retries 3
        maxconn 2000
        timeout connect 5000
        timeout check   3000
        timeout client  50000
        timeout server  50000

frontend http_frontend *:80
        mode http
        redirect scheme https code 301 if !{ ssl_fc }

frontend https_frontend_ssl_terminate
        mode http
        bind *:443 ssl crt /etc/haproxy/ssl/public.example.com.pem
        option forwardfor header X-Real-IP
        default_backend web_server_http

backend web_server_http
        mode http
        balance roundrobin
        # Отправляем трафик на backend по HTTPS
        server s1_https 192.168.1.10:443 ssl verify none
        server s2_https 192.168.1.20:443 ssl verify none


nginx.conf


server {
    server_name localhost;

    listen 443 ssl default_server;

    ssl_certificate      /etc/nginx/ssl/internal.example.com.pem;
    ssl_certificate_key  /etc/nginx/ssl/internal.example.com.key;

    # Адрес HAProxy
    set_real_ip_from 192.168.1.254;
    real_ip_header X-Real-IP;

    root /usr/share/nginx/html;
    index index.html index.htm;

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }

    location ~ /\.ht {
        deny all;
    }
}


Балансировка на транспортном уровне (Layer 4): передача TCP-трафика с HAProxy на Nginx


В данном примере HTTPS-трафик клиентов не модифицируется (HAProxy вмешивается в транспортный уровень) и его терминация происходит непосредственно на Nginx.

haproxy.cfg


global
        maxconn 4096
        chroot /usr/share/haproxy
        uid 99
        gid 99
        daemon

defaults
        log     global
        option  redispatch
        option  tcp-smart-accept
        option  tcp-smart-connect
        retries 3
        maxconn 2000
        timeout connect 5000
        timeout check   3000
        timeout client  50000
        timeout server  50000

frontend http_frontend *:80
        mode http
        redirect scheme https code 301 if !{ ssl_fc }

frontend https_frontend_ssl_pass
        mode tcp
        bind *:443
        default_backend web_server_tcp

backend web_server_tcp
        mode tcp
        balance roundrobin
        # ВНИМАНИЕ! Работа с send-proxy возможна только,
        # когда принимающая сторона понимает, что это такое.
        # Для Nginx необходимо включить в директиву listen
        # опцию proxy_protocol.
        server s1_tcp 192.168.1.10:443 send-proxy
        server s2_tcp 192.168.1.20:443 send-proxy


nginx.conf


server {
    server_name localhost;

    # ВНИМАНИЕ! Работа с директивой proxy_protocol возможна только в связке с haproxy.
    # Для прямого доступа данную директиву необходимо отключить.
    listen 443 ssl default_server proxy_protocol;

    ssl_certificate      /etc/nginx/ssl/public.example.com.pem;
    ssl_certificate_key  /etc/nginx/ssl/public.example.com.key;

    # Адрес HAProxy
    set_real_ip_from 192.168.1.254;
    real_ip_header proxy_protocol;

    root /usr/share/nginx/html;
    index index.html index.htm;

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }

    location ~ /\.ht {
        deny all;
    }
}


Заключение


Используя описанные выше настройки мы смогли передать веб-серверу Nginx, расположенному за HAProxy, реальные IP-адреса клиентов при работе по HTTPS. Подобным подходом так же можно воспользоваться при работе со сторонними балансировщиками нагрузки, например CloudFlare [7, 8] и AWS ELB [9, 10].

Литература


  1. Протоколы прикладного уровня сетевой модели OSI — ru.wikipedia.org
  2. Модуль ngx_http_realip_module — nginx.org
  3. Транспортный уровень сетевой модели OSI — ru.wikipedia.org
  4. The PROXY protocol — haproxy.org
  5. HAProxy Configuration Manual: send-proxy — cbonte.github.io
  6. Модуль ngx_http_core_module: директива listen — nginx.org
  7. Getting Real IP Addresses Using CloudFlare, Nginx, and Varnish — danielmiessler.com
  8. Getting Real IP Addresses Using Nginx and CloudFlare — babaei.net
  9. Using Proxy Protocol With Nginx — chrislea.com
  10. AWS Elastic Load Balancing — aws.amazon.com