5分钟搞懂!Nginx流模块中ssl_reject_handshake指令的正确用法解析
你是否还在为Nginx配置SSL时遇到的握手拒绝问题而烦恼?本文将详细解析Nginx流模块中ssl_reject_handshake指令的正确用法,帮助你轻松解决SSL握手相关的安全问题。读完本文后,你将能够:
- 理解
ssl_reject_handshake指令的工作原理 - 掌握该指令在不同场景下的配置方法
- 学会排查与该指令相关的常见问题
- 了解该指令的实现原理及源码位置
什么是ssl_reject_handshake指令?
ssl_reject_handshake是Nginx流模块(Stream Module)中的一个配置指令,用于控制是否拒绝SSL握手。当该指令设置为on时,Nginx会主动拒绝客户端的SSL握手请求,并返回"unrecognized name"(无法识别的名称)告警。
该指令的语法格式如下:
ssl_reject_handshake on | off;
默认值为off,表示不拒绝SSL握手。该指令可以在stream块或server块中配置。
为什么需要使用ssl_reject_handshake?
在实际应用中,我们经常需要在同一IP地址和端口上托管多个SSL服务,这就需要用到SNI(Server Name Indication,服务器名称指示)扩展。SNI允许客户端在SSL握手过程中指定要连接的服务器名称,从而让服务器能够为不同的域名提供不同的SSL证书。
然而,如果客户端不支持SNI或者未正确提供服务器名称,服务器就无法确定应该使用哪个证书,这可能导致安全问题。此时,ssl_reject_handshake指令就派上用场了——它可以帮助我们拒绝那些不支持SNI或提供无效服务器名称的连接请求。
ssl_reject_handshake的工作原理
要理解ssl_reject_handshake的工作原理,我们需要先了解Nginx处理SSL握手的流程。当客户端发起SSL握手时,Nginx会首先检查是否启用了SNI。如果启用了SNI,Nginx会根据客户端提供的服务器名称选择相应的虚拟主机配置。
在这个过程中,ssl_reject_handshake指令的作用是:如果找不到与客户端提供的服务器名称匹配的虚拟主机,Nginx会拒绝此次握手。
从源码实现来看,ssl_reject_handshake的逻辑主要在ngx_stream_ssl_servername函数中实现,该函数位于src/stream/ngx_stream_ssl_module.c文件中。关键代码如下:
done:
sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
if (sscf->reject_handshake) {
c->ssl->handshake_rejected = 1;
*ad = SSL_AD_UNRECOGNIZED_NAME;
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
c->ssl->sni_accepted = 1;
return SSL_TLSEXT_ERR_OK;
从这段代码可以看出,如果reject_handshake被设置为on(即sscf->reject_handshake为真),Nginx会将handshake_rejected标记为1,并返回"unrecognized name"告警,从而拒绝此次握手。
ssl_reject_handshake的配置示例
基本配置示例
下面是一个基本的ssl_reject_handshake配置示例:
stream {
server {
listen 443 ssl;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
ssl_reject_handshake on;
# 其他配置...
}
}
在这个示例中,我们在server块中启用了ssl_reject_handshake指令。这意味着,如果客户端请求的服务器名称与该server块不匹配,Nginx将拒绝SSL握手。
结合SNI的多虚拟主机配置
更常见的场景是结合SNI使用多个虚拟主机,如下所示:
stream {
# 默认服务器,拒绝所有不匹配的SSL握手
server {
listen 443 ssl default_server;
ssl_reject_handshake on;
}
# 第一个虚拟主机
server {
listen 443 ssl;
ssl_certificate /path/to/cert1.pem;
ssl_certificate_key /path/to/key1.pem;
server_name example.com;
# 其他配置...
}
# 第二个虚拟主机
server {
listen 443 ssl;
ssl_certificate /path/to/cert2.pem;
ssl_certificate_key /path/to/key2.pem;
server_name example.org;
# 其他配置...
}
}
在这个配置中,我们定义了一个默认服务器,并将ssl_reject_handshake设置为on。同时,我们还定义了两个虚拟主机,分别对应不同的域名。当客户端请求的域名与这两个虚拟主机都不匹配时,默认服务器会拒绝SSL握手。
与ssl_preread模块结合使用
ssl_reject_handshake还可以与ssl_preread模块结合使用,实现更复杂的SSL握手控制。例如,我们可以根据客户端请求的服务器名称,动态决定是否拒绝握手:
stream {
map $ssl_preread_server_name $reject_handshake {
default on;
example.com off;
example.org off;
}
server {
listen 443;
ssl_preread on;
ssl_reject_handshake $reject_handshake;
# 其他配置...
}
}
在这个示例中,我们使用map指令根据$ssl_preread_server_name变量(由ssl_preread模块提供)的值来动态设置$reject_handshake变量。当客户端请求的服务器名称是example.com或example.org时,$reject_handshake变量的值为off,即不拒绝握手;对于其他所有服务器名称,$reject_handshake变量的值为on,即拒绝握手。
ssl_reject_handshake的常见问题及解决方案
问题1:配置后所有SSL握手都被拒绝
如果你在配置了ssl_reject_handshake on后发现所有SSL握手都被拒绝,可能是因为你没有正确配置虚拟主机。请确保你至少有一个虚拟主机的server_name与客户端请求的服务器名称匹配,或者没有设置default_server。
问题2:配置不生效
如果你发现ssl_reject_handshake配置不生效,可能是因为你没有正确加载Nginx流模块。请检查你的Nginx是否编译了流模块,你可以通过以下命令来验证:
nginx -V 2>&1 | grep -o stream
如果输出中包含stream,说明Nginx编译了流模块。否则,你需要重新编译Nginx,添加--with-stream和--with-stream_ssl_module选项。
问题3:与HTTP模块的ssl_reject_handshake混淆
需要注意的是,HTTP模块中也有一个同名指令ssl_reject_handshake,但两者的作用和实现方式有所不同。HTTP模块中的ssl_reject_handshake是在src/http/ngx_http_ssl_module.c文件中实现的,而流模块中的ssl_reject_handshake是在src/stream/ngx_stream_ssl_module.c文件中实现的。
ssl_reject_handshake的源码解析
ssl_reject_handshake指令的实现主要在ngx_stream_ssl_module模块中。该模块的源码位于src/stream/ngx_stream_ssl_module.c文件中。
首先,我们来看一下该指令的定义:
{ ngx_string("ssl_reject_handshake"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_STREAM_SRV_CONF_OFFSET,
offsetof(ngx_stream_ssl_srv_conf_t, reject_handshake),
NULL },
这段代码定义了ssl_reject_handshake指令,指定了它可以在stream块和server块中配置,接受on或off作为参数,并将其值存储在ngx_stream_ssl_srv_conf_t结构体的reject_handshake字段中。
接下来,我们来看一下reject_handshake字段的使用场景,主要在ngx_stream_ssl_servername函数中:
sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
if (sscf->reject_handshake) {
c->ssl->handshake_rejected = 1;
*ad = SSL_AD_UNRECOGNIZED_NAME;
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
当sscf->reject_handshake为真时,Nginx会设置c->ssl->handshake_rejected = 1,并返回SSL_TLSEXT_ERR_ALERT_FATAL,从而拒绝SSL握手。
总结
ssl_reject_handshake是Nginx流模块中一个非常有用的SSL安全配置指令,它可以帮助我们有效控制SSL握手过程,提高服务器的安全性。通过本文的介绍,我们了解了该指令的基本概念、使用场景、配置方法以及实现原理。
在实际应用中,我们应该根据具体需求合理配置ssl_reject_handshake指令,以达到最佳的安全效果。同时,我们还应该关注Nginx的官方文档和源码,以便及时了解该指令的最新变化和最佳实践。
如果你想深入了解Nginx流模块的其他SSL相关指令,可以参考Nginx官方文档或查看src/stream/ngx_stream_ssl_module.c文件中的源码实现。
希望本文能够帮助你更好地理解和使用ssl_reject_handshake指令。如果你有任何问题或建议,欢迎在评论区留言讨论。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



