Haproxy для прокси на основе имени SSH

У меня есть хост-машина с несколькими контейнерами lxc. Я пытаюсь дать SSH доступ к контейнерам непосредственно на основе доменных имен. Для этого я попытался настроить HAProxy. Можно легко добиться этого с помощью ACL в режиме http. Когда я пытаюсь сделать то же самое с режимом tcp для ssh на основе acls, я не могу добиться предоставления ssh доступа к контейнерам напрямую. Ниже приведен фрагмент, который я использую в haproxy.cfg.

listen SSHD :2200
    mode tcp
    acl is_apple hdr_dom i apple
    acl is_orange hdr_dom -i orange
    use_backend apple if is_apple
    use_backend orange if is_orange

backend apple
    mode tcp
    server apple 10.0.3.221:22

backend orange
    mode tcp
    server orange 10.0.3.222:22

Где apple.myhost.com и orange.myhost.com являются доменными именами для достижения каждого из контейнеров. HTTP прокси работает нормально с этими acls, но я сталкиваюсь с проблемой с трафиком SSH.

Я получаю следующую ошибку.

ssh_exchange_identification: соединение закрыто удаленным хостом

4 ответа

Я использую экземпляр HAproxy, работающий на pfSense именно для той цели, которую вы искали.

Я написал подробное описание здесь: http://loredo.tumblr.com/post/116633549315/geeking-out-with-haproxy-on-pfsense-the-ultimate

Я собираюсь пойти еще дальше с этой настройкой: порт 443 используется совместно для трафика SSH, SSL/TLS и OpenVPN, а SSH защищен с помощью клиентского сертификата X.509:

  • обычный HTTPS-трафик (действует как обычный обратный прокси-сервер для защиты веб-трафика)
  • обычный HTTPS-трафик с аутентификацией по сертификату пользователя X509
  • OpenVPN коммутируемый трафик
  • TLS-туннельный трафик SSH, включая аутентификацию по сертификату пользователя X509 (шлюз SSLH)

Это также защищает от сканирования портов в точках входа SSH. Кроме того, он может помочь с переходом с IPv4 на IPv6 (и наоборот), гибкими решениями для совместной работы и домашнего офиса для администраторов и т. П.

Я знаю, что есть этот блестящий инструмент SSLH, но это решение гораздо более гибкое благодаря мощности HAproxy.

Это файл haproxy.cfg, созданный pfSense на основе моего блога для вашей справки:

global
    maxconn         2000
    stats socket /tmp/haproxy.socket level admin
    uid         80
    gid         80
    nbproc          1
    chroot          /tmp/haproxy_chroot
    daemon
    tune.ssl.default-dh-param   2048
    # Modern browser compatibility only as mentioned here:
    # https://wiki.mozilla.org/Security/Server_Side_TLS
    ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK

    # Time-to-first-Byte (TTFB) value needs to be optimized based on
    # the actual public certificate chain
    # see https://www.igvita.com/2013/10/24/optimizing-tls-record-size-and-buffering-latency/
    tune.ssl.maxrecord 1370

listen HAProxyLocalStats
    bind 127.0.0.1:2200 name localstats
    mode http
    stats enable
    stats admin if TRUE
    stats uri /haproxy_stats.php?haproxystats=1
    timeout client 5000
    timeout connect 5000
    timeout server 5000

frontend HTTP_redirect
    bind            0.0.0.0:80 name 0.0.0.0:80   
    mode            http
    log         global
    option          http-keep-alive
    timeout client      30000
    default_backend     _ssl-redirect_http_ipvANY

frontend LAN_HTTPS
    bind            10.108.2.1:443 name 10.108.2.1:443 ssl no-sslv3 no-tls-tickets no-tlsv10 no-tlsv11 crt /var/etc/haproxy/LAN_HTTPS.pem  
    mode            http
    log         global
    option          http-keep-alive
    option          forwardfor
    acl https ssl_fc
    reqadd X-Forwarded-Proto:\ http if !https
    reqadd X-Forwarded-Proto:\ https if https
    timeout client      30000
    # Remove headers that expose security-sensitive information.
    rspidel ^Server:.*$
    rspidel ^X-Powered-By:.*$
    rspidel ^X-AspNet-Version:.*$
    default_backend     gwsch01_http_ipvANY

frontend WAN_443-merged
    bind            178.26.150.88:443 name 178.26.150.88:443   
    mode            tcp
    log         global
    timeout client      7200000
    tcp-request inspect-delay 5s

    # block SSLv3 as early as possible
    acl sslv3 req.ssl_ver 3
    tcp-request content reject if sslv3
    tcp-request content accept if { req.ssl_hello_type 1 } or !{ req.ssl_hello_type 1 }
    acl         aclusr_custom_req.ssl_hello_type_201    req.ssl_hello_type 1
    acl         aclusr_custom_req.ssl_sni_20-m_20end_20-i_20.ssh.example.com    req.ssl_sni -m end -i .ssh.example.com
    acl         aclusr_custom_req.ssl_sni_20-m_20end_20-i_20.vpn.example.com    req.ssl_sni -m end -i .vpn.example.com
    acl         aclusr_custom_req.len_200   req.len 0
    use_backend     _WAN_HTTPS_tcp_ipvANY if aclusr_custom_req.ssl_hello_type_201 !aclusr_custom_req.ssl_sni_20-m_20end_20-i_20.ssh.example.com !aclusr_custom_req.ssl_sni_20-m_20end_20-i_20.vpn.example.com 
    use_backend     _WAN_HTTPS_auth_tcp_ipvANY if aclusr_custom_req.ssl_hello_type_201 aclusr_custom_req.ssl_sni_20-m_20end_20-i_20.vpn.example.com 
    use_backend     _openvpn_tcp_ipvANY if aclusr_custom_req.len_200 aclusr_custom_req.ssl_hello_type_201 
    use_backend     _WAN_SSLH_tcp_ipvANY if aclusr_custom_req.ssl_hello_type_201 aclusr_custom_req.ssl_sni_20-m_20end_20-i_20.ssh.example.com 
    default_backend     _none_tcp_ipvANY

frontend WAN_HTTPS
    bind            127.0.0.1:2043 name 127.0.0.1:2043 ssl no-sslv3 no-tls-tickets no-tlsv10 no-tlsv11 crt /var/etc/haproxy/WAN_HTTPS.pem  accept-proxy npn http/1.1
    mode            http
    log         global
    option          http-keep-alive
    option          forwardfor
    acl https ssl_fc
    reqadd X-Forwarded-Proto:\ http if !https
    reqadd X-Forwarded-Proto:\ https if https
    timeout client      7200000
    # Remove headers that expose security-sensitive information.
    rspidel ^Server:.*$
    rspidel ^X-Powered-By:.*$
    rspidel ^X-AspNet-Version:.*$
    default_backend     _none_http_ipvANY

frontend WAN_HTTPS_auth-merged
    bind            127.0.0.1:2044 name 127.0.0.1:2044 ssl no-sslv3 no-tls-tickets no-tlsv10 no-tlsv11 crt /var/etc/haproxy/WAN_HTTPS_auth.pem ca-file /var/etc/haproxy/clientca_WAN_HTTPS_auth.pem verify required  accept-proxy npn http/1.1
    mode            http
    log         global
    option          http-keep-alive
    option          forwardfor
    acl https ssl_fc
    reqadd X-Forwarded-Proto:\ http if !https
    reqadd X-Forwarded-Proto:\ https if https
    timeout client      7200000
    # Remove headers that expose security-sensitive information.
    rspidel ^Server:.*$
    rspidel ^X-Powered-By:.*$
    rspidel ^X-AspNet-Version:.*$
    acl         aclusr_host_matches_gwsch01.vpn.example.com hdr(host) -i gwsch01.vpn.example.com
    use_backend     gwsch01_http_ipvANY if aclusr_host_matches_gwsch01.vpn.example.com 
    default_backend     _none_http_ipvANY

frontend WAN_SSLH-merged
    bind            127.0.0.1:2022 name 127.0.0.1:2022 ssl no-sslv3 no-tls-tickets no-tlsv10 no-tlsv11 crt /var/etc/haproxy/WAN_SSLH.pem ca-file /var/etc/haproxy/clientca_WAN_SSLH.pem verify required  accept-proxy npn ssh/2.0
    mode            tcp
    log         global
    timeout client      7200000
    acl         aclusr_custom_ssl_fc_sni_reg_20gwsch01.ssh.example.com  ssl_fc_sni_reg gwsch01.ssh.example.com
    acl         aclusr_custom_ssl_fc_npn_20-i_20ssh_2f2.0   ssl_fc_npn -i ssh/2.0
    use_backend     SSH_gwsch01_https_ipvANY if aclusr_custom_ssl_fc_sni_reg_20gwsch01.ssh.example.com aclusr_custom_ssl_fc_npn_20-i_20ssh_2f2.0 
    default_backend     _none_https_ipvANY

backend _ssl-redirect_http_ipvANY
    mode            http
    timeout connect     30000
    timeout server      30000
    retries         3
    option          httpchk
    redirect scheme https code 301

backend gwsch01_http_ipvANY
    mode            http
    rspadd Strict-Transport-Security:\ max-age=31536000;
    rspirep ^(Set-Cookie:((?!;\ secure).)*)$ \1;\ secure if { ssl_fc }
    timeout connect     3000
    timeout server      7200000
    retries         2
    option          httpchk
    server          gwsch01 127.0.0.1:8443 ssl  verify none 

backend _none_tcp_ipvANY
    mode            tcp
    timeout connect     30000
    timeout server      30000
    retries         3
    option          httpchk OPTIONS / 
    server          none 127.0.0.1:61235 check inter 1000 disabled 

backend _WAN_HTTPS_tcp_ipvANY
    mode            tcp
    timeout connect     30000
    timeout server      7200000
    retries         3
    option          httpchk
    server          WAN_HTTPS 127.0.0.1:2043 check-ssl  verify none send-proxy 

backend _WAN_HTTPS_auth_tcp_ipvANY
    mode            tcp
    timeout connect     30000
    timeout server      7200000
    retries         3
    option          httpchk
    server          WAN_HTTPS_auth 127.0.0.1:2044 check-ssl  verify none send-proxy 

backend _openvpn_tcp_ipvANY
    mode            tcp
    timeout connect     3000
    timeout server      7200000
    retries         2
    option          httpchk
    server          openvpn1 127.0.0.1:1194  

backend _WAN_SSLH_tcp_ipvANY
    mode            tcp
    timeout connect     30000
    timeout server      7200000
    retries         3
    option          httpchk
    server          WAN_SSLH 127.0.0.1:2022 check-ssl  verify none send-proxy 

backend _none_http_ipvANY
    mode            http
    timeout connect     30000
    timeout server      30000
    retries         3
    option          httpchk OPTIONS / 
    server          none 127.0.0.1:61235 check inter 1000 disabled 

backend _none_https_ipvANY
    mode            tcp
    timeout connect     30000
    timeout server      30000
    retries         3
    option          httpchk OPTIONS / 
    server          none 127.0.0.1:61235 check inter 1000 disabled 

backend SSH_gwsch01_https_ipvANY
    mode            tcp
    timeout connect     3000
    timeout server      7200000
    retries         2
    option          httpchk
    server          ssh_gwsch01 127.0.0.1:22

Это невозможно. Протокол HTTP отличается, поскольку существует концепция "виртуального хоста", и HAProxy может различать разные хосты с помощью заголовка "Host:". SSH не имеет ничего подобного, поэтому lxc-host не может знать контейнер, вы пытаетесь подключиться.

Но вы можете использовать другую функцию SSH, которая называется "SSH gateway". внутри ~/.ssh/authorized_keys E сть command= вариант. Сначала настраивает ssh на основе ключей с вашего lxc-хоста на apple и orange. Затем поместите эти строки в LXC authorized_keys файл:

command="ssh -q -t user@apple $SSH_ORIGINAL_COMMAND" ssh-rsa AAAAsomeB3N...== user@client
command="ssh -q -t user@orange $SSH_ORIGINAL_COMMAND" ssh-rsa AAAAanotherB3N...== user@client

Теперь хост lxs может автоматически подключаться к apple а также orangeна основе клиентского ключа.

Узнать больше:

Боюсь, это невозможно. Протокол SSH не поддерживает имена хостов. Он просто подключается к IP (после разрешения конечно) и устанавливает зашифрованное соединение. Не существует понятия "виртуальные хосты".

Блог HAProxy предлагает интересный подход: Маршрутизация SSH-соединений с помощью HAProxy | 21 декабря 2020 г.

Чтобы направить SSH-соединения на разные серверы, вы должны знать, к какому серверу хочет получить доступ пользователь. Как было сказано ранее, HAProxy не анализирует протокол SSH и, в любом случае, этот протокол не дает никакой подсказки о пункте назначения. Итак, нам нужно обернуть соединения внутри другого протокола, который поможет в этом вопросе.

Мы будем использовать протокол TLS и его расширение SNI вместе с функцией SSHProxyCommand. Или, другими словами, мы оборачиваем наши соединения TLS, но делаем это просто для того, чтобы использовать SNI, чтобы клиент мог сообщить нам, к какому серверу он хочет подключиться. Мы не будем использовать криптографические функции TLS.

[...]

Из своих клиентов вы можете получить доступ к своим SSH-серверам с помощью следующих команд:

       $ ssh -o ProxyCommand="openssl s_client -quiet -connect 172.16.0.10:2222 -servername server1" dummyName1 
$ ssh -o ProxyCommand="openssl s_client -quiet -connect 172.16.0.10:2222 -servername server2" dummyName2 
$ ssh -o ProxyCommand="openssl s_client -quiet -connect 172.16.0.10:2222 -servername server3" dummyName3 

Обратите внимание: команда ssh требует, чтобы вы отправили имя сервера, к которому вы хотите подключиться. Мы установили для него значение dummyName, потому что вместо этого мы указываем имя сервера, используя поле ProxyCommand. Переключатель имени сервера позволяет вам установить содержимое поля SNI. Чтобы сделать эту команду короче, рассмотрите возможность создания псевдонима bash или сценария.

Другие вопросы по тегам