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

Предисловие

Я просто не мог пройти мимо этой статьи на habrahabr.ru

Содержание

Довольно часто требуется балансировать нагрузку между несколькими веб-серверами. При этом, как правило, необходимо, чтобы веб-приложения получали реальные 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].