Nginx: Wie kombiniere ich ssl_preread_protocol mit ssl_preread_server_name (SSH-Multiplexing und SSL-SNI-Passthrough)?

Nginx: Wie kombiniere ich ssl_preread_protocol mit ssl_preread_server_name (SSH-Multiplexing und SSL-SNI-Passthrough)?

Ich habe die unten gezeigte Datei nginx.conf.

Ich möchte sowohl SSH als auch einen Webserver auf Port 443/SSL ausführen.
Auch bekannt als SSL-Port-Multiplexing.
Gleichzeitig möchte ich SSL-Passthrough mit SNI verwenden.

Für SSH-Multiplexing verwende ich $ssl_preread_protocol.
Für SSL-SNI-Passthrough verwende ich $ssl_preread_server_name

Wenn ich einstelle proxy_pass $upstream;, funktioniert SSH einwandfrei, aber die Webseite(n) funktionieren nicht.
Wenn ich einstelle proxy_pass $name;, funktioniert SSL-SNI-Passthrough, aber auf SSH kann nicht zugegriffen werden.

Wie kann ich die beiden Kartenanweisungen kombinieren? Zum Beispiel so etwas wie

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

Das Problem besteht darin, dass ich eine Möglichkeit brauche, die Protokollauswahl mit der Servernamenauswahl zu kombinieren.

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

Hier ist meine Konfigurationsdatei:

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;
    }
    
}

Antwort1

Schon die Lösung gefunden?

Ich habe auch dieses Problem und versuche Folgendes. Es scheint in Ordnung zu sein.

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;
}
}

Antwort2

Nekromantie.
Ich beantworte meine eigene Frage zum Nutzen anderer.
Das ist mit nginx NICHT möglich (soweit ich weiß).
Sie können das Ziel jedoch mit HAproxy erreichen.
Die Konfiguration ist nicht ganz so einfach, sehen Sie sich also unten den Hack an, der bei mir funktioniert.

Beachten Sie, dass ich alle Werte mit Suchen und Ersetzen im Editor geändert habe (es können Fehler auftreten).
Diese Konfiguration setzt Folgendes voraus:

Wenn Sie das Proxy-Protokoll verwenden möchten (Proxy v2 ist das neueste), entfernen Sie das Kommentarzeichen # send-proxy-v2 z. B. aus der Zeile

server web0 127.0.0.1:8005 # send-proxy-v2

wird

server web0 127.0.0.1:8005 send-proxy-v2

Beachten Sie, dass sni-passthrough die Proxy-Reihenfolge umkehrt.
In nginx ist die Reihenfolge
-> request -> decrypt -> proxy headering decrypted request -> re-encrypt request -> forward
In haproxy SNI-passthrough wird die Reihenfolge
-> request -> proxy headering encrypted request -> forward

Somit ist die Middleware-Verarbeitungsreihenfolge in Ihren HTTP-Servern (auf Port 8000+x)
bei Verwendung von nginx -> SSL-decrypt -> unheader -> process
bei Verwendung von HAproxy-> unheader -> SSL-decrypt -> process

Dies liegt daran, dass ich auf HAproxy SNI-Passthrough verwende und in Nginx SSL-Schlüssel verwende (kein Passthrough). Diese kleine, unangenehme Tatsache hat mir viel Kopfzerbrechen bereitet.

Beachten Sie auch, dass ich zu Testzwecken example.int, foo.int und bar.int in der Hosts-Datei so eingerichtet habe, dass sie im lokalen Netzwerk auf 10.0.0.2 (interne Netzwerk-IP-Adresse der Maschine mit HAproxy) aufgelöst werden. Sie sehen diese Einträge immer noch in dieser haproxy.cfg-Datei

# /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
        

Diese Konfiguration leitet alle Anfragen weiter für

ssh [email protected] -p 443 

Zu127.0.0.1:22

und alle Anfragen für
http://vorname-nachname.combis 127.0.0.1:800X, wobei X = 2n (gerade)
https://vorname-nachname.combis 127.0.0.1:800X, wobei X = 2n+1 (ungerade)
(die bessere Idee wäre gewesen, 800X für http und 900X für https zu verwenden)

verwandte Informationen