nginx upstream server主动健康监测模块添加https检测功能

2024-02-17 07:44

本文主要是介绍nginx upstream server主动健康监测模块添加https检测功能,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1 缘起

  前面的《nginx upstream server主动健康检测模块ngx_http_upstream_check_module 使用和源码分析》系列已经分析了ngx_http_upstream_check_module的实现原理,并且在借助这个模块的框架实现了一个udp健康检测的新功能。
  但是ngx_http_upstream_check_module还缺乏基于https监测上游服务器健康状况的能力,始终是一个缺憾,因此,本文基于《nginx upstream server主动健康检测模块ngx_http_upstream_check_module 使用和源码分析》和《nginx stream proxy 模块的ssl连接源码分析》两篇博文的分析成果,来实现一个基于https的上游服务器健康检测的能力。

1.1 功能定义

  本次支持的功能:

  • 支持向上游服务器发起https请求功能
  • 请求的报文复用原有的http检测的请求报文定义
  • 响应的状态码检测复用原有的http检测的响应码的定义
  • 支持ssl握手过程中添加sni扩展信息
  • 支持ssl握手协议类型的配置
  • 支持ssl握手协议加密套件的配置

  暂时不支持的功能:

  • 不支持ssl会话复用(会话复用可以降低上游服务器的ssl握手压力)
  • 不支持ssl证书双向验证
  • 不支持服务器端证书有效性验证

&ems;  由于本次主要是检验https的链接握手流程,对一些不是特别关键的ssl握手特性暂时不支持主要是为了简化代码逻辑,但是不影响业务流程,这样也便于在本文中将整个实现流程进行阐述。后续可以参照ngx_stream_proxy_module中的实现,继续将这些特性进行完善,以臻于完美。

2. 实现后的效果

  首先来看一下实现后的效果,有一些感性的认识。

2.1 配置文件


#user  nobody;
worker_processes  1;
daemon off;
master_process off;error_log  logs/error.log;
pid        logs/nginx.pid;load_module  objs/ngx_http_upstream_check_module.so;events {worker_connections  1024;
}http {include       mime.types;default_type  application/octet-stream;log_format  main  '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log  logs/access.log  main;sendfile        on;keepalive_timeout  65;upstream backend {check type=https interval=3000 rise=2 fall=5 timeout=1000 port=443;check_http_send "GET / HTTP/1.1\r\nHost: www.test.com\r\n\r\n";check_http_expect_alive http_2xx http_3xx;check_ssl_server_name www.test.com;server 192.168.0.1:443;}server {listen       9080;server_name  localhost;# 开启本模块的状态查询接口	location /status {check_status html;}location / {proxy_pass http://backend;}#error_page  404              /404.html;# redirect server error pages to the static page /50x.html#error_page   500 502 503 504  /50x.html;location = /50x.html {root   html;}}
}

   以上配置文件中的upstream块中定义了一个https的健康检测类型, check_http_send复用了http的定义,而check_ssl_server_name是新增的指令,用来配置ssl握手设置sni扩展主机host信息。

2.2 运行效果

查看nginx的error.log日志, 可以看到如下信息:

2024/02/16 09:31:33 [error] 23638#0: https check failed with return code: 403
2024/02/16 09:31:33 [error] 23638#0: check protocol https error with peer: 192.168.0.1:443
2024/02/16 09:31:45 [info] 23663#0: enable check peer: 192.168.0.1:443

error.log中的前面两条因为服务器响应403报了错误,对应在配置了“check_http_expect_alive http_2xx http_3xx;”的情况。如果配置改成“check_http_expect_alive http_2xx http_3xx http_4xx;” 则报后面那条上游服务器enable的信息。证明https的检测功能已经可用了。

3. 代码实现

3.1 配置指令

3.1.1 配置指令定义:

支持的配置指令如下:

  • check_ssl_ciphers:
        配置加密套件,格式参考proxy_ssl_ciphers
  • check_ssl_protocols:
        和服务器交互采用的ssl协议版本,如TLSv1.1 TLSv1.2等,格式参考proxy_ssl_protocols。
  • check_ssl_server_name:
        和服务器进行ssl握手时候采用的sni扩展host名字,如果不设置并且upstream块中的server是用域名设置的,那么默认就采用设置的服务器名字。
  • check_ssl_verify:[on/off]
        是否校验服务器的证书有效性。(待后续实现)
  • check_ssl_session_reuse: [on/off]
        和上游服务器进行ssl握手的时候是否复用ssl会话信息。

3.1.2 配置指令结构体:


struct ngx_http_upstream_check_srv_conf_s {ngx_uint_t                               port;ngx_uint_t                               fall_count;ngx_uint_t                               rise_count;ngx_msec_t                               check_interval;ngx_msec_t                               check_timeout;ngx_uint_t                               check_keepalive_requests;ngx_check_conf_t                        *check_type_conf;ngx_str_t                                send;union {ngx_uint_t                           return_code;ngx_uint_t                           status_alive;} code;ngx_array_t                             *fastcgi_params;ngx_uint_t                               default_down;ngx_uint_t                               unique;ngx_uint_t                               udp : 1;                       /* 是否udp socket */ngx_int_t                                match_part : 1;                /* 是否只要部分匹配就可以了 */ngx_int_t                                match_offset;                  /* udp响应期望的内容从哪个字节开始匹配 */ngx_str_t                                expect;                        /* udp响应的期望内容 */#if (NGX_HTTP_SSL)ngx_ssl_t                               *ssl;                            /* ssl 配置上下文 */ngx_str_t                                ssl_ciphers;                    /* ssl 加密套件 */ngx_uint_t                               ssl_protocols;                  /* 采用的ssl协议 */ngx_str_t                                ssl_server_name;                /* ssl握手的sni扩展hostname */
#endif
};

  以上添加的ssl_protocols参数在ngx_http_upstream_check_create_srv_conf函数中需要将其设置为NGX_CONF_UNSET_UINT,避免nginx在解析配置文件的时候出现参数重复的报错。

3.1.3 配置指令源码定义:

#if (NGX_HTTP_SSL){ ngx_string("check_ssl_ciphers"),NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,ngx_conf_set_str_slot,NGX_HTTP_SRV_CONF_OFFSET,offsetof(ngx_http_upstream_check_srv_conf_t, ssl_ciphers),NULL },{ ngx_string("check_ssl_protocols"),NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,ngx_conf_set_bitmask_slot,NGX_HTTP_SRV_CONF_OFFSET,offsetof(ngx_http_upstream_check_srv_conf_t, ssl_protocols),&ngx_upstream_check_ssl_protocols },{ ngx_string("check_ssl_server_name"),NGX_HTTP_UPS_CONF|NGX_CONF_FLAG,ngx_conf_set_str_slot,NGX_HTTP_SRV_CONF_OFFSET,offsetof(ngx_http_upstream_check_srv_conf_t, ssl_server_name),NULL },
#endif

  通过以上配置指令,可以将解析到的参数设置到ngx_http_upstream_check_srv_conf_s结构体对应的字段中。其中ngx_upstream_check_ssl_protocols是一个可选协议的列表,定义如下:

#if (NGX_HTTP_SSL)static ngx_conf_bitmask_t  ngx_upstream_check_ssl_protocols[] = {{ ngx_string("SSLv2"), NGX_SSL_SSLv2 },{ ngx_string("SSLv3"), NGX_SSL_SSLv3 },{ ngx_string("TLSv1"), NGX_SSL_TLSv1 },{ ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },{ ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },{ ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 },{ ngx_null_string, 0 }
};#endif

3.2 模块的初始化

static ngx_http_module_t  ngx_http_upstream_check_module_ctx = {NULL,                                    /* preconfiguration */ngx_http_upstream_check_init,            /* postconfiguration */ngx_http_upstream_check_create_main_conf,/* create main configuration */ngx_http_upstream_check_init_main_conf,  /* init main configuration */ngx_http_upstream_check_create_srv_conf, /* create server configuration */NULL,                                    /* merge server configuration */ngx_http_upstream_check_create_loc_conf, /* create location configuration */ngx_http_upstream_check_merge_loc_conf   /* merge location configuration */
};

  在以上代码中ngx_http_upstream_check_module_ctx结构体的初始化定义中,ngx_http_upstream_check_init_main_conf函数将在配置文件的http块解析完成后进行初始化,因此,我们通过修改这个函数来实现ssl上下文的初始化,即ngx_http_upstream_check_srv_conf_s的ssl指针的初始化。ssl是一个指向ngx_ssl_t的指针,用来对ssl协议的相关参数进行设定并初始化ssl上下文。
  以下是ngx_http_upstream_check_init_main_conf函数的代码:

static char *
ngx_http_upstream_check_init_main_conf(ngx_conf_t *cf, void *conf)
{ngx_buf_t                      *b;ngx_uint_t                      i;ngx_http_upstream_srv_conf_t  **uscfp;ngx_http_upstream_main_conf_t  *umcf;umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);b = ngx_http_upstream_check_create_fastcgi_request(cf->pool,fastcgi_default_params,sizeof(fastcgi_default_params) / sizeof(ngx_str_t) / 2);if (b == NULL) {return NGX_CONF_ERROR;}fastcgi_default_request.data = b->pos;fastcgi_default_request.len = b->last - b->pos;uscfp = umcf->upstreams.elts;for (i = 0; i < umcf->upstreams.nelts; i++) {if (ngx_http_upstream_check_init_srv_conf(cf, uscfp[i]) != NGX_OK) {return NGX_CONF_ERROR;}}return ngx_http_upstream_check_init_shm(cf, conf);
}

  在以上代码中,

    for (i = 0; i < umcf->upstreams.nelts; i++) {if (ngx_http_upstream_check_init_srv_conf(cf, uscfp[i]) != NGX_OK) {return NGX_CONF_ERROR;}}

  这段代码是对所有配置的http upstream进行遍历,然后对它进行初始化,因为我们的ssl上下文信息每个upstream对应一个,因此,正好在ngx_http_upstream_check_init_srv_conf函数里面可以进行初始化。
  在函数ngx_http_upstream_check_init_srv_conf的最后返回前,我们添加ssl的初始化代码,如下:

static char *
ngx_http_upstream_check_init_srv_conf(ngx_conf_t *cf, void *conf)
{
......
#if (NGX_HTTP_SSL)/* 如果当前的监测类型是https才需要进行ssl的初始化设置 */if (check->type == NGX_HTTP_CHECK_HTTPS) {/* 如果ssl_protocols没有设置,那么默认开启NGX_SSL_TLSv1,NGX_SSL_TLSv1_1和NGX_SSL_TLSv1_2 */if (ucscf->ssl_protocols == NGX_CONF_UNSET_UINT) {ucscf->ssl_protocols = NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1|NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2;}/* 如果没有设置ssl_ciphers,那么设置默认值为DEFAULT */if (ucscf->ssl_ciphers.len == 0) {ngx_str_set(&ucscf->ssl_ciphers,"DEFAULT");}/* 为ssl分配内存 */ucscf->ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));if (ucscf->ssl == NULL) {return NGX_CONF_ERROR;}ucscf->ssl->log = cf->log;/* 创建ssl上下文 */if (ngx_ssl_create(ucscf->ssl, ucscf->ssl_protocols, NULL) != NGX_OK) {return NGX_CONF_ERROR;}/* 设置ssl上下文回收回调函数 */cln = ngx_pool_cleanup_add(cf->pool, 0);if (cln == NULL) {return NGX_CONF_ERROR;}cln->handler = ngx_ssl_cleanup_ctx;cln->data = ucscf->ssl;/* 设置可以支持的加密套件 */if (ngx_ssl_ciphers(cf, ucscf->ssl, &ucscf->ssl_ciphers, 0) != NGX_OK) {return NGX_CONF_ERROR;}}#endifreturn NGX_CONF_OK;
}

【未完待续】

这篇关于nginx upstream server主动健康监测模块添加https检测功能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/717120

相关文章

Linux 安装、配置Tomcat 的HTTPS

Linux 安装 、配置Tomcat的HTTPS 安装Tomcat 这里选择的是 tomcat 10.X ,需要Java 11及更高版本 Binary Distributions ->Core->选择 tar.gz包 下载、上传到内网服务器 /opt 目录tar -xzf 解压将解压的根目录改名为 tomat-10 并移动到 /opt 下, 形成个人习惯的路径 /opt/tomcat-10

iOS HTTPS证书不受信任解决办法

之前开发App的时候服务端使用的是自签名的证书,导致iOS开发过程中调用HTTPS接口时,证书不被信任 - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAu

android 免费短信验证功能

没有太复杂的使用的话,功能实现比较简单粗暴。 在www.mob.com网站中可以申请使用免费短信验证功能。 步骤: 1.注册登录。 2.选择“短信验证码SDK” 3.下载对应的sdk包,我这是选studio的。 4.从头像那进入后台并创建短信验证应用,获取到key跟secret 5.根据技术文档操作(initSDK方法写在setContentView上面) 6.关键:在有用到的Mo

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现

Android我的二维码扫描功能发展史(完整)

最近在研究下二维码扫描功能,跟据从网上查阅的资料到自己勉强已实现扫描功能来一一介绍我的二维码扫描功能实现的发展历程: 首页通过网络搜索发现做android二维码扫描功能看去都是基于google的ZXing项目开发。 2、搜索怎么使用ZXing实现自己的二维码扫描:从网上下载ZXing-2.2.zip以及core-2.2-source.jar文件,分别解压两个文件。然后把.jar解压出来的整个c

iptables(7)扩展模块state

简介         前面文章我们已经介绍了一些扩展模块,如iprange、string、time、connlimit、limit,还有扩展匹配条件如--tcp-flags、icmp。这篇文章我们介绍state扩展模块  state          在 iptables 的上下文中,--state 选项并不是直接关联于一个扩展模块,而是与 iptables 的 state 匹配机制相关,特

SQL Server中,查询数据库中有多少个表,以及数据库其余类型数据统计查询

sqlserver查询数据库中有多少个表 sql server 数表:select count(1) from sysobjects where xtype='U'数视图:select count(1) from sysobjects where xtype='V'数存储过程select count(1) from sysobjects where xtype='P' SE

SQL Server中,always on服务器的相关操作

在SQL Server中,建立了always on服务,可用于数据库的同步备份,当数据库出现问题后,always on服务会自动切换主从服务器。 例如192.168.1.10为主服务器,12为从服务器,当主服务器出现问题后,always on自动将主服务器切换为12,保证数据库正常访问。 对于always on服务器有如下操作: 1、切换主从服务器:假如需要手动切换主从服务器时(如果两个服务

SQL Server中,isnull()函数以及null的用法

SQL Serve中的isnull()函数:          isnull(value1,value2)         1、value1与value2的数据类型必须一致。         2、如果value1的值不为null,结果返回value1。         3、如果value1为null,结果返回vaule2的值。vaule2是你设定的值。        如

SQL Server中,添加数据库到AlwaysOn高可用性组条件

1、将数据添加到AlwaysOn高可用性组,需要满足以下条件: 2、更多具体AlwaysOn设置,参考:https://msdn.microsoft.com/zh-cn/library/windows/apps/ff878487(v=sql.120).aspx 注:上述资源来自MSDN。