Nginx: Como combinar ssl_preread_protocol com ssl_preread_server_name (multiplexação ssh e ssl-sni-passthrough)?

Nginx: Como combinar ssl_preread_protocol com ssl_preread_server_name (multiplexação ssh e ssl-sni-passthrough)?

Eu tenho o arquivo nginx.conf mostrado abaixo.

Quero executar o ssh e um servidor web na porta 443/SSL.
Também conhecido como multiplexação de porta SSL.
Ao mesmo tempo, quero usar o SSL-passthrough com o SNI.

Para multiplexação ssh, eu uso $ ssl_preread_protocol.
Para passagem SSL-SNI, eu uso $ssl_preread_server_name

Se eu definir proxy_pass $upstream;, o ssh funcionará bem, mas as páginas da web não funcionarão.
Se eu definir proxy_pass $name;, a passagem SSL-SNI funcionará, mas o ssh não poderá ser acessado.

Como posso combinar as duas instruções do mapa? por exemplo, algo como

if $upstream = ssh 
then proxy_pass $upstream
else proxy_pass $name;
endif

O problema é que preciso combinar a seleção de protocolo com a seleção de server_name.

if(ssh) => forward to port 22
else => forward to port xy depending on server_name

Aqui está meu arquivo de configuração:

stream{

    upstream ssh 
    {
        server 127.0.0.1:22;
    }
    
    upstream https_default_backend 
    {
        server 127.0.0.1:443;
    }
    
    upstream daniel_backend 
    {
        server 127.0.0.1:5005;
    }
    
    
    map $ssl_preread_protocol $upstream 
    {
        default ssh;
        "TLSv1.3" https_default_backend;
        "TLSv1.2" https_default_backend;
        "TLSv1.1" https_default_backend;
        "TLSv1" https_default_backend;
    }
    
    
    map $ssl_preread_server_name $name 
    {
        localhost daniel_backend;
        prodesk daniel_backend;
        daniel-steiger.ch daniel_backend;
        www.daniel-steiger.ch daniel_backend;
        default https_default_backend;
    }
    
    
    # SSH and SSL on the same port
    server {
        listen 443;
        
        ssl_preread on;
        #proxy_protocol on;
        
        # proxy_pass $upstream;
        proxy_pass $name;
    }
    
}

Responder1

Já encontrou a solução?

Eu também tenho esse problema e tento isso. Parece ok.

stream {

upstream ssh {
    server 127.0.0.1:22;
}

upstream https_default_backend {
    server 127.0.0.1:443;
}

upstream daniel_backend {
    server 127.0.0.1:5005;
}

map $ssl_preread_protocol $upstream {
    "" ssh;
    default $name;
    "TLSv1.3" $name;
    "TLSv1.2" $name;
    "TLSv1.1" $name;
    "TLSv1" $name;
}
    
map $ssl_preread_server_name $name {
    localhost daniel_backend;
    prodesk daniel_backend;
    daniel-steiger.ch daniel_backend;
    www.daniel-steiger.ch daniel_backend;
    default https_default_backend;
}

server {
    listen 443;
    ssl_preread on;
    proxy_pass $upstream;
}
}

Responder2

Necromante.
Respondendo à minha própria pergunta para o benefício de outros.
Isso NÃO é possível com nginx (AFAIK).
Você pode, no entanto, atingir o objetivo com o HAproxy.
A configuração não é tão fácil, então veja abaixo o hack que funciona para mim.

Observe que alterei todos os valores com pesquisar e substituir (pode haver erros) no bloco de notas.
Esta configuração pressupõe o seguinte:

Se você quiser usar o protocolo proxy (o proxy v2 é o mais novo), remova o comentário, # send-proxy-v2 por exemplo, a linha

server web0 127.0.0.1:8005 # send-proxy-v2

torna-se

server web0 127.0.0.1:8005 send-proxy-v2

Observe que o sni-passthrough inverte a ordem do proxy.
No nginx, o pedido é
-> request -> decrypt -> proxy headering decrypted request -> re-encrypt request -> forward
In haproxy SNI-passthough, o pedido se torna
-> request -> proxy headering encrypted request -> forward

Assim, a ordem de processamento do middleware em seus servidores http (na porta 8000+x)
usando nginx é -> SSL-decrypt -> unheader -> process
enquanto usa HAproxy, é-> unheader -> SSL-decrypt -> process

Isso se deve ao uso de sni-passthrough no HAproxy e ao uso de chaves SSL no nginx (sem passagem). Esse pequeno fato desagradável me causou muita preocupação.

Observe também que configurei example.int, foo.int e bar.int no arquivo hosts resolvendo para 10.0.0.2 (endereço IP da rede interna da máquina com HAproxy) na rede local para fins de teste. Você ainda vê essas entradas neste arquivo haproxy.cfg

# /etc/haproxy/haproxy.cfg

# Validate: 
# haproxy -c -V -f /etc/haproxy/haproxy.cfg
# Another way is to 
# sudo service haproxy configtest

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

    # Default SSL material locations
    ca-base /etc/ssl/certs
    crt-base /etc/ssl/private

    # See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
        ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_comACHA20_POLY1305_SHA256
        ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults
    log global
    mode    http
    option  httplog
    option  dontlognull
        timeout connect 5000
        timeout client  50000
        timeout server  50000
    errorfile 400 /etc/haproxy/errors/400.http
    errorfile 403 /etc/haproxy/errors/403.http
    errorfile 408 /etc/haproxy/errors/408.http
    errorfile 500 /etc/haproxy/errors/500.http
    errorfile 502 /etc/haproxy/errors/502.http
    errorfile 503 /etc/haproxy/errors/503.http
    errorfile 504 /etc/haproxy/errors/504.http

    

frontend http
    bind *:80
    mode http
    option forwardfor
    # option httpchk /check.cfm
    # use-server  server1 if { hdr(host) -i server1.domain.net }
    # use-server  server2 if { hdr(host) -i server2.domain.net }
    # server server1 localhost:22201 check
    # server server2 localhost:22202 check
    # default_backend nodes
    # redirect scheme https code 301 if !{ ssl_fc }
    
    
    
    # http://10.0.0.2/.well-known/acme-challenge/token.txt
    # http://44.33.22.11/.well-known/acme-challenge/token.txt
    # http://firstname-lastname.com/.well-known/acme-challenge/token.txt
    # http://forename-familyname.com/.well-known/acme-challenge/token.txt
    
    # https://www.haproxy.com/documentation/aloha/12-5/traffic-management/lb-layer7/acls/  
    # For ACLs sharing the same name, the following rules apply:
    # It is possible to use the same <aclname> for many ACLs, even if they do not have the same matching criterion
    # A logical OR applies between all of them
    
    # acl firstname_lastname_com dst 10.0.0.2
    # acl firstname_lastname_com dst 44.33.22.11
    
    acl firstname_lastname_com  hdr(host)     -i 44.33.22.11
    acl firstname_lastname_com  hdr(host)     -i 10.0.0.2
    
    acl firstname_lastname_com  hdr(host)     -i firstname-lastname.com
    acl firstname_lastname_com  hdr(host)     -m end .firstname-lastname.com
    
    acl forename_familyname_com  hdr(host)     -i forename-familyname.com
    acl forename_familyname_com  hdr(host)     -m end .forename-familyname.com
    
    
    #use_backend http_firstname_lastname_com if { hdr(host) -i firstname-lastname.com }
    #use_backend http_firstname_lastname_com if { hdr(host) -m end .firstname-lastname.com }
    
    use_backend http_firstname_lastname_com if firstname_lastname_com 
    use_backend http_forename_familyname_com if forename_familyname_com 
    
    
    
    

    
backend http_firstname_lastname_com
    mode http
    balance roundrobin
    server web0 127.0.0.1:8006
    
    
    
backend http_forename_familyname_com
    mode http
    balance roundrobin
    server web0 127.0.0.1:8008
    
    
    
#backend nodes
#    mode http
#    balance roundrobin
#    option forwardfor
#    reqirep ^Host: Host:\ node1.myapp.mycompany.com
#    server web01 node1.myapp.mycompany.com:80
    
# sudo systemctl stop nginx
# sudo systemctl disable nginx

# sudo systemctl enable haproxy
# service haproxy start
# sudo haproxy -c -V -f /etc/haproxy/haproxy.cfg
# service haproxy restart

    
frontend https
    bind *:443
    mode tcp
    option tcplog
    tcp-request inspect-delay 5s
    tcp-request content accept if { req.ssl_hello_type 1 }
    #tcp-request content accept if { req_ssl_hello_type 1 }

    # https://datamakes.com/2018/02/17/high-intensity-port-sharing-with-haproxy/
    # systemctl restart sshd
    # systemctl disable sshd
    # systemctl enable sshd
    # sudo apt-get install openssh-server
    # sudo systemctl status ssh
    # sudo ufw allow ssh
    # sudo ufw enable
    # sudo ufw status
    # ufw allow 443/tcp
    # ufw allow 8443/tcp
    # /etc/ssh/sshd_config  ==> PermitRootLogin yes  + PasswordAuthentication no + ChallengeResponseAuthentication no  ~/.ssh/id_rsa.pub ==> ~/.ssh/authorized_keys
    acl ssh_payload payload(0,7) -m bin 5353482d322e30
    

    
    
    
    
    # /mnt/sshfs/var/www/.dotnet/corefx/cryptography/crls/
    # sudo apt-get install exfat-utils exfat-fuse

    
    # https://10.0.0.2/.well-known/acme-challenge/token.txt
    # https://44.33.22.11/.well-known/acme-challenge/token.txt
    # http://firstname-lastname.com/.well-known/acme-challenge/token.txt
    # http://forename-familyname.com/.well-known/acme-challenge/token.txt
  
    # https://www.haproxy.com/documentation/aloha/12-5/traffic-management/lb-layer7/acls/  
    # For ACLs sharing the same name, the following rules apply:
    # It is possible to use the same <aclname> for many ACLs, even if they do not have the same matching criterion
    # A logical OR applies between all of them
  
  
  # sequence matters ! 
    use_backend openssh if ssh_payload
    use_backend openssh if !{ req.ssl_hello_type 1 } { req.len 0 }
  
  
    # having these two lines here blocks ssh if use_backend openssh comes afterwards ...
    # also, this fucks up SNI ...
    # acl firstname_lastname_com dst 10.0.0.2
    # acl firstname_lastname_com dst 44.33.22.11
    
    acl firstname_lastname_com req_ssl_sni -i firstname-lastname.com
    acl firstname_lastname_com req.ssl_sni -m end .firstname-lastname.com
    
    acl forename_familyname_com req_ssl_sni -i forename-familyname.com
    acl forename_familyname_com req.ssl_sni -m end .forename-familyname.com
    
    
    # wildcard
    use_backend https_firstname_lastname_com if firstname_lastname_com
    use_backend https_forename_familyname_com if forename_familyname_com
    
    
    # use_backend example_int if { req_ssl_sni -i example.int }
    # use_backend example_int if { req_ssl_sni -m end .example.int }

    # use_backend example_int if { req_ssl_sni -i example.int }
    # use_backend foo_int if { req_ssl_sni   -i foo.int }
    # use_backend bar_int if { req_ssl_sni -i bar.int }

    
# sudo haproxy -c -V -f /etc/haproxy/haproxy.cfg
    
backend https_firstname_lastname_com
    mode tcp
    balance roundrobin
    server web0 127.0.0.1:8005 # send-proxy-v2
    
backend https_forename_familyname_com
    mode tcp
    balance roundrobin
    server web0 127.0.0.1:8007 # send-proxy-v2

backend foo_int
    balance roundrobin
    server web1 127.0.0.1:8005 send-proxy

backend bar_int 
    balance roundrobin
    server web2 127.0.0.1:8005 ##send-proxy


backend openssh
        mode tcp
        # option tcplog
        # option tcp-check
        # tcp-check expect string SSH-2.0-
        timeout server 3h
        # server openssh 127.0.0.1:22 check 
        server openssh 127.0.0.1:22
        

Esta configuração encaminha todas as solicitações de

ssh [email protected] -p 443 

para127.0.0.1:22

e todos os pedidos de
http://nome-sobrenome.compara 127.0.0.1:800X onde X = 2n (par)
https://nome-sobrenome.compara 127.0.0.1:800X onde X = 2n+1 (ímpar)
(a melhor ideia seria usar 800X para http e 900X para https)

informação relacionada