本文分享自天翼雲開發者社區《nginx中同一端口不同tls版本與加密套件》.作者:z****n

1.問題描述

需要在nginx中使用2個域名同時監聽443端口,域名a只允許使用tls1.2,域名b允許tls1.2,tls1.3。 實際運行發現原生nginx無法實現

nginx.conf配置如下

worker_processes  1;
error_log  logs/error.log  debug;
events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    server {
        listen 443 ssl;
        server_name www.a.com;
        ssl_protocols       TLSv1.2;
        ssl_certificate         ssl/a.crt;
        ssl_certificate_key     ssl/a.key;
        location / {
            return 200 ok;
        }
    }

    server {
        listen 443 ssl;
        server_name www.b.com;
        ssl_protocols       TLSv1.2  TLSv1.3;
        ssl_certificate         ssl/b.crt;
        ssl_certificate_key     ssl/b.key;
        location / {
            return 200 ok;
        }
    }
}

 

1.使用tls1.2連接www.a.com可以實現  openssl s_client -connect 127.0.0.1:443  -servername www.a.com -tls1_2

nginx中同一端口不同tls版本與加密套件_nginx

2.使用tls1.2連接www.b.com可以實現  openssl s_client -connect 127.0.0.1:443  -servername www.b.com -tls1_2

3.使用tls1.3連接www.b.com不可以實現  openssl s_client -connect 127.0.0.1:443  -servername www.b.com -tls1_3

nginx中同一端口不同tls版本與加密套件_中間件_02

2.原理

tls握手原理中,在客户端發起的client_hello到服務端時,會告知服務端客户端支持的tls版本,加密套件。接着服務器響應serve_hello告知客户端,服務器選擇的serve_hello。所以tls版本在握手的client_hello和serve_hello階段必須協商完成,

然而原生的nginx並沒有介入openssl的client_hello的階段,即openssl在握手過程中會使用nginx的默認default server (默認第一個server塊) 的ssl_protocols 指令配置。

所以要根據不同的sni制定不同的tls版本就需要在openssl提供的client_hello階段中根據不同的sni判斷是否使用不同的tls協議版本。

 

 

3.第三方模塊

使用github上模塊 https://github.com/zengjinji/ngx_http_ssl_client_hello_module

修改nginx.conf配置,新增ssl_client_hello on;指令

worker_processes  1;
error_log  logs/error.log  debug;
events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    #第三方模塊新增指令
    ssl_client_hello on;

    server {
        listen 443 ssl;
        server_name www.a.com;
        ssl_protocols       TLSv1.2;
        ssl_certificate         ssl/a.crt;
        ssl_certificate_key     ssl/a.key;
        location / {
            return 200 ok;
        }
    }

    server {
        listen 443 ssl;
        server_name www.b.com;
        ssl_protocols       TLSv1.2  TLSv1.3;
        ssl_certificate         ssl/b.crt;
        ssl_certificate_key     ssl/b.key;
        location / {
            return 200 ok;
        }
    }
}

 

再次嘗試用tls1.3連接www.b.com可以實現  openssl s_client -connect 127.0.0.1:443  -servername www.b.com -tls1_3

nginx中同一端口不同tls版本與加密套件_中間件_03

 

使用tls1.2連接www.b.com可以實現  openssl s_client -connect 127.0.0.1:443  -servername www.b.com -tls1_2

使用tls1.3連接www.a.com不可以實現  openssl s_client -connect 127.0.0.1:443  -servername www.a.com -tls1_3

使用tls1.2連接www.a.com可以實現  openssl s_client -connect 127.0.0.1:443  -servername www.a.com -tls1_2 

此外,也可以使用openresty的1.21.4.1版本,1.21.4.1版本增加了ssl_client_hello_by_lua_block ,原理一樣也是在openssl的client_hello階段中根據不同的sni區分使用不同的協議