Назад | Перейти на главную страницу

Как я могу использовать HAproxy с SSL и получать заголовки X-Forwarded-For И сообщить PHP, что SSL используется?

У меня следующая установка:

(internet) ---> [  pfSense Box  ]    /-> [ Apache / PHP server ]
                [running HAproxy] --+--> [ Apache / PHP server ]
                                    +--> [ Apache / PHP server ]
                                     \-> [ Apache / PHP server ]

Для HTTP-запросов это работает здоровозапросы распределяются на мои серверы Apache нормально. Для SSL-запросов у меня был HAproxy, распределяющий запросы с использованием балансировки нагрузки TCP, и он работал, однако, поскольку HAproxy не действовал как прокси, он не добавлял X-Forwarded-For HTTP-заголовок, и серверы Apache / PHP не знали реального IP-адреса клиента.

Итак, я добавил stunnel перед HAproxy, чтение этого stunnel может добавить X-Forwarded-For Заголовок HTTP. Однако пакет, который я мог установить в pfSense, не добавляет этот заголовок ... также это явно убивает мою способность использовать запросы KeepAlive, который мне бы очень хотелось сохранить. Но самая большая проблема, которая убила эту идею, заключалась в том, что stunnel преобразовывал запросы HTTPS в простые запросы HTTP, поэтому PHP не знал, что SSL включен, и пытался перенаправить на сайт SSL.

Как я могу использовать HAproxy для балансировки нагрузки на нескольких серверах SSL, чтобы оба эти сервера знали IP-адрес клиента и знаете, что SSL используется? И если возможно, как это сделать на моем сервере pfSense?

Или мне все это бросить и просто использовать nginx?

Для записи, поскольку этот поток часто упоминается в отношении HAProxy + SSL, HAProxy поддерживает собственный SSL с обеих сторон, начиная с версии 1.5-dev12. Таким образом, наличие X-Forwarded-For, HTTP keep-alive, а также заголовок, сообщающий серверу, что соединение было выполнено через SSL, очень просты:

listen front
    bind :80
    bind :443 ssl crt /etc/haproxy/haproxy.pem
    mode http
    option http-server-close
    option forwardfor
    http-request set-header X-Forwarded-Proto https if { ssl_fc }
    server srv1 1.1.1.1:80 check ...
    ...

Я уверен, что к тому времени вы придумаете что-то другое, но, по крайней мере, новые посетители теперь получат простое решение :-)

Вам не нужно отказываться от всего этого, вы можете просто использовать nginx перед haproxy для поддержки SSL, сохраняя всю свою конфигурацию балансировки нагрузки. Вам даже не нужно использовать nginx для HTTP, если вы не хотите. Nginx может передавать как X-Forwarded-For, так и настраиваемый заголовок, указывающий на использование SSL (и информацию о сертификате клиента, если хотите). Фрагмент конфигурации Nginx, который отправляет необходимую информацию:

proxy_set_header SCHEME $scheme;      # http/https
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header CLIENT_CERT $ssl_client_raw_cert;

Для всех, кто нашел этот вопрос, я последовал совету Охото и использовал nginx. Вот конкретные шаги, которые я использовал, чтобы заставить эту работу на моем роутере pfSense:

  1. Используя веб-интерфейс pfsense, я установил pfsense Пакет PfJailctl и пакет "jail_template" в Система> Пакеты так что я мог создать тюрьму FreeBSD для компиляции и установки nginx в системе pfsense.

  2. Я настроил джейл для своего сервера nginx под Услуги> Тюрьмы, давая новой тюрьме то же имя хоста и IP-адрес виртуального IP-псевдонима, на котором у меня был запущен HAproxy. Привязал джейл к WAN интерфейсу. Я использовал шаблон тюрьмы по умолчанию и включил unionfs, а не nullfs.

  3. Как только тюрьма была запущена, я SSH вошел в ящик pfsense и запустил jls чтобы узнать номер тюрьмы. Я тогда побежал jexec 1 sh чтобы получить оболочку внутри тюрьмы. Оттуда я настроил порты BSD и установил nginx, используя:

    portsnap extract
    portsnap fetch update
    cd /usr/ports/www/nginx
    make install clean
    
  4. Затем я настроил nginx для прослушивания порта 443 и передачи всех запросов HAproxy на порт 80, включая реальный IP-адрес и статус SSL внутри заголовков HTTP. Мой usr/local/etc/nginx/nginx.conf выглядит как:

    worker_processes  1;
    
    events {
        worker_connections  2048;
    }
    
    http {
        upstream haproxy {
            server 209.59.186.35:80;
        }
    
        server {
            listen       443;
            server_name  my.host.name default_server;
            ssl                  on;
            ssl_certificate      my.crt;
            ssl_certificate_key  my.key;
            ssl_session_timeout  5m;
    
            ssl_protocols  SSLv3 TLSv1;
            ssl_ciphers  HIGH:!aNULL:!MD5;
            ssl_prefer_server_ciphers   on;
    
            location / {
                proxy_pass http://haproxy;
    
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
                proxy_set_header X-Forwarded-Proto https;
            }
        }
    
    }
    
  5. Затем я изменил свое приложение PHP, чтобы обнаружить X-Forwarded-Proto Заголовок HTTP:

    function usingSSL()
    {
        return (
           (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' )
            || (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
                   && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https' ));
    }
    

Итак, окончательная настройка:

(internet) ---> [ -> nginx -> haproxy -]--> (pool of apache servers)
                [    (pfSense server)  ]

Моя конфигурация для версии haproxy 1.5-dev-17:

global
        log 127.0.0.1   local0
        log 127.0.0.1   local1 notice
        #log loghost    local0 info
        maxconn 4096
        #chroot /usr/share/haproxy
        user haproxy
        group haproxy
        daemon
        #debug
        #quiet

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        option  http-server-close
        retries 3
        option redispatch
        fullconn 1000        
        maxconn 1000
        timeout queue 600s
        timeout connect 5s
        timeout client 600s
        timeout server 600s

frontend http-in
        bind *:80
        bind *:443 ssl crt /usr/local/etc/ssl/certs
        reqadd X-Forwarded-Proto:\ https if { ssl_fc }
        default_backend varnish-ha
        option forwardfor
backend varnish-ha
  server hafront1 10.1.69.1:6081  minconn 100 maxqueue 10000

Он использует ssl_fc ACL. Обратите внимание, что option http-server-close часть очень важна.

HAProxy не может поразить серверную часть SSL без использования режима raw TCP, теряя X-Forwarded-For, но вы потенциально можете повторно зашифровать трафик с помощью stunnel для внутреннего транзита. Хотя некрасиво.

Мне больше нравится подход Ochoto, но с одной оговоркой: nginx - отлично работающий балансировщик нагрузки; если вы его используете, я бы сказал, используйте его для всего. Прокси-сервер вашего входящего HTTPS для загрузки сбалансированных HTTPS-серверов - и таким образом нет необходимости в настраиваемых заголовках для информации SSL (если вам не нужен сертификат клиента).

В прошлом году я реализовал решение для интегрировать HAProxy с pfSense таким образом, что он использует все функции HAProxy и поддерживает хорошую изоляцию с помощью pfSense. Так что это жизнеспособный вариант для производственная среда. SSL отключен на HAProxy. Я установил HAProxy внутри тюрьмы в pfSense с использованием ezjail и коллекции портов. Таким образом, очень легко поддерживать оба компонента независимо друг от друга. И вы можете установить любую версию, которую хотите. Я начал с 1.5-dev13. И с тех пор у меня он отлично работает. Я все это задокументировал.

Установка HAProxy на pfSense

Кстати, Вилли, большое спасибо за такой отличный продукт.