Nginx: ssl_preread_protocol을 ssl_preread_server_name(ssh-multiplexing 및 ssl-sni-passthrough)과 결합하는 방법은 무엇입니까?

Nginx: ssl_preread_protocol을 ssl_preread_server_name(ssh-multiplexing 및 ssl-sni-passthrough)과 결합하는 방법은 무엇입니까?

아래에 nginx.conf 파일이 있습니다.

포트 443/SSL에서 ssh와 웹 서버를 모두 실행하고 싶습니다.
SSL 포트 멀티플렉싱이라고도 합니다.
동시에 SNI와 함께 SSL-passthrough를 사용하고 싶습니다.

SSH 멀티플렉싱의 경우 $ssl_preread_protocol을 사용합니다.
SSL-SNI-passthrough의 경우 $ssl_preread_server_name을 사용합니다.

을 설정하면 proxy_pass $upstream;ssh는 제대로 작동하지만 웹페이지는 작동하지 않습니다.
을 설정하면 proxy_pass $name;SSL-SNI-passthrough는 작동하지만 SSH에 액세스할 수 없습니다.

두 지도 지침을 어떻게 결합할 수 있나요? 예를 들어

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

문제는 프로토콜 선택과 server_name 선택을 결합하는 방법이 필요하다는 것입니다.

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

내 구성 파일은 다음과 같습니다.

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

답변1

이미 해결책을 찾았나요?

저도 이런 문제가 있어서 시도해 봤습니다. 괜찮은 것 같습니다.

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

답변2

네크로맨싱.
다른 사람들의 이익을 위해 내 질문에 답합니다.
nginx(AFAIK)에서는 불가능합니다.
그러나 HAproxy를 사용하면 목표를 달성할 수 있습니다.
구성은 그다지 쉽지 않으므로 아래에서 나에게 적합한 해킹을 참조하세요.

메모장에서 검색 및 바꾸기(오류가 있을 수 있음)를 사용하여 모든 값을 변경했습니다.
이 구성은 다음을 가정합니다.

프록시 프로토콜(프록시 v2가 최신임)을 사용하려면 다음 # send-proxy-v2 줄의 주석 처리를 제거하세요.

server web0 127.0.0.1:8005 # send-proxy-v2

된다

server web0 127.0.0.1:8005 send-proxy-v2

sni-passthrough는 프록시 순서를 반대로 합니다.
nginx에서 순서는 다음과
-> request -> decrypt -> proxy headering decrypted request -> re-encrypt request -> forward
같습니다. haproxy SNI-passthrough에서 순서는 다음과 같습니다.
-> request -> proxy headering encrypted request -> forward


따라서 nginx를 사용하는 http 서버(포트 8000+x)의 미들웨어 처리 순서는 -> SSL-decrypt -> unheader -> process
HAproxy를 사용하는 동안입니다.-> unheader -> SSL-decrypt -> process

이는 HAproxy에서 sni-passthrough를 사용하고 nginx에서 SSL 키를 사용하기 때문입니다(통과 없음). 이 소소한 사실 때문에 머리가 많이 긁혔습니다.

또한 테스트 목적으로 로컬 네트워크에서 10.0.0.2(HAproxy가 있는 시스템의 내부 네트워크 IP 주소)로 확인되는 호스트 파일에 example.int, foo.int 및 bar.int를 설정했습니다. 이 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
        

이 구성은 다음에 대한 모든 요청을 전달합니다.

ssh [email protected] -p 443 

에게127.0.0.1:22

그리고 모든 요청은
http://firstname-lastname.com127.0.0.1:800X, 여기서 X = 2n(짝수)
https://firstname-lastname.com127.0.0.1:800X(여기서 X = 2n+1(홀수))
(더 나은 아이디어는 http에 800X를 사용하고 https에 900X를 사용하는 것이었습니다)

관련 정보