欢迎光临
我们一直在努力

Nginx https 协议配置 ssl_protocols 的相关问题

1. Nginx https相关配置

本文主要针对以下两个主要配置从代码层次进行分析:
协议配置:

  1. Syntax: ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2] [TLSv1.3];
  2. Default:
  3. ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  4. Context: http, server

以及加密套件配置

  1. Syntax: ssl_ciphers ciphers;
  2. Default:
  3. ssl_ciphers HIGH:!aNULL:!MD5;
  4. Context: http, server

Nginx的虚拟主机配置,使多个网站可以部署在同一个服务器(同一IP地址)对外提供服务。但是在实际测试中发现,虽然两个配置都在server 块内,ssl_protocols 却属于全局配置,而 ssl_ciphers 却针对特定的虚拟主机起作用。

  1. server {
  2. server_name www.a.com;
  3. ssl_ciphers ECDHERSAAES128GCMSHA256;
  4. ssl_protocols TLSv1.2;
  5.  
  6. # 其他配置略
  7. }
  1. server {
  2. server_name www.b.com;
  3. ssl_ciphers ECDHERSAAES256GCMSHA384;
  4. ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  5.  
  6. # 其他配置略
  7. }

这里存在一个问题:

当存在多个server块的时候,即使两个(如上)ssl_protocols 的配置不同,但是 浏览器访问(携带SNI信息)www.a.com的时候,仍然可以使用 TLSv1 TLSv1.1 协议,即使没有配置。

而ssl_ciphers得配置就可以根据域名来区分开来。

从Nginx的使用文档上来看,两者的配置生效区域相同,却发挥了不同的作用。于是从源码开始分析原因在哪里。

2 Nginx源码分析

Nginx在启动过程中,加载配置文件的时候,对于每个server块的解析,会调用ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data) 这个函数。

看参数,其中 protocols 就是配置文件中对应的配置项,他是一个 ngx_uint_t 类型,Nginx中关于协议的标志是:ngx_event_openssl.h中定义

  1. #define NGX_SSL_SSLv2 0x0002
  2. #define NGX_SSL_SSLv3 0x0004
  3. #define NGX_SSL_TLSv1 0x0008
  4. #define NGX_SSL_TLSv1_1 0x0010
  5. #define NGX_SSL_TLSv1_2 0x0020

可以看到,每种协议占一位,protocols 的取值就是各个协议做或运算得到的值。比如上文中第一个配置,只有TLSv1.2,那protocols =0x0020【十进制32】,如果是TLSv1 TLSv1.1 TLSv1.2,那就是 protocols = 0x0020 | 0x0010 | 0x0008 = 0x0038【十进制56】。
那么问题来了,造成server间protocols配置混乱的一定是其中某个地方出错了。
那么我们可以先看一下这个protocols的取值是不是对应两个server的取值,就可以看到是不是一开始解析配置文件的时候,就搞错了,于是Nginx源码debug 分析。
得到如下:

  1. (gdb) b ngx_event_openssl.c:ngx_ssl_create
  2. Breakpoint 1 at 0x473b91: file src/event/ngx_event_openssl.c, line 234.
  3. (gdb) r
  4. Breakpoint 1, ngx_ssl_create (ssl=0x9ad548, protocols=56, data=0x9ad540)
  5. at src/event/ngx_event_openssl.c:234
  6. (gdb) p protocols
  7. $1 = 56
  8. (gdb) c
  9. Continuing.
  10. Breakpoint 1, ngx_ssl_create (ssl=0x9bec60, protocols=32, data=0x9bec58)
  11. at src/event/ngx_event_openssl.c:234
  12. (gdb) p protocols
  13. $2 = 32

可以看到,没问题,一个32,一个65,没毛病!
那只能继续往下跟踪了。
接下来马上进入Openssl的源码了,看一下nginx在调用Openssl接口的地方代码:

  1. if (!(protocols & NGX_SSL_SSLv2)) {
  2. SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv2);
  3. }
  4. if (!(protocols & NGX_SSL_SSLv3)) {
  5. SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv3);
  6. }
  7. if (!(protocols & NGX_SSL_TLSv1)) {
  8. SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1);
  9. }
  10. #ifdef SSL_OP_NO_TLSv1_1
  11. SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TLSv1_1);
  12. if (!(protocols & NGX_SSL_TLSv1_1)) {
  13. SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_1);
  14. }
  15. #endif
  16. #ifdef SSL_OP_NO_TLSv1_2
  17. SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TLSv1_2);
  18. if (!(protocols & NGX_SSL_TLSv1_2)) {
  19. SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_2);
  20. }
  21. #endif
  1. # define SSL_OP_NO_SSLv2 0x01000000L
  2. # define SSL_OP_NO_SSLv3 0x02000000L
  3. # define SSL_OP_NO_TLSv1 0x04000000L
  4. # define SSL_OP_NO_TLSv1_2 0x08000000L
  5. # define SSL_OP_NO_TLSv1_1 0x10000000L

大概意思就是,如果没有定义SSLv2、SSLv3、TLSv1、TLSv1.1、TLSv1.2那么就调用接口SSL_CTX_set_options 进入Openssl来设置协议。

3 Openssl源码分析

版本:Openssl-1.0.2j

源码文件 ssl.h

  1. # define SSL_CTX_set_options(ctx,op) \
  2. SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL)
  3. # define SSL_CTX_clear_options(ctx,op) \
  4. SSL_CTX_ctrl((ctx),SSL_CTRL_CLEAR_OPTIONS,(op),NULL)
  5. # define SSL_CTX_get_options(ctx) \
  6. SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,0,NULL)
  7. # define SSL_set_options(ssl,op) \
  8. SSL_ctrl((ssl),SSL_CTRL_OPTIONS,(op),NULL)
  9. # define SSL_clear_options(ssl,op) \
  10. SSL_ctrl((ssl),SSL_CTRL_CLEAR_OPTIONS,(op),NULL)
  11. # define SSL_get_options(ssl) \
  12. SSL_ctrl((ssl),SSL_CTRL_OPTIONS,0,NULL)

所以要去看 SSL_CTX_ctrl 的源码

源码文件 ssl_lib.c
SSL_CTRL_OPTIONS 的值是32 在下面代码中有个switch(cmd)
关键代码

  1. long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg)
  2. {
  3. switch (cmd) {
  4. case SSL_CTRL_CLEAR_OPTIONS:
  5. return (ctx->options &= ~larg);
  6. }
  7. }

最后的操作是: ctx->options &= ~larg

再回到Nginx 源码:
对于每种协议,都要调用一次 SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_XXXXX);
也就是 ssl->ctx->options &= ~SSL_OP_NO_XXXXX

赞(4) 打赏
转载请注明来源:IT技术资讯 » Nginx https 协议配置 ssl_protocols 的相关问题

评论 抢沙发

评论前必须登录!

 

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏