nginx stream proxy 模块的ssl连接源码分析

1. 源起

  我一直来对ssl建立连接的过程一知半解,以前分析nginx代码的时候一旦碰到ssl连接部分的代码都是直接跳过,前面在分析ngx_http_upstream_dynamic_module的时候正好想到了是不是可以给它添加一个能够支持https健康检查的功能,所以今天决定沉下心来仔细分析一下nginx本身的与上游服务器建立连接的实现逻辑。

  为了简单起见,我决定选用ngx_stream_proxy_module模块作为分析学习的目标,因为相对于ngx_http_stream_proxy_module来说,前者逻辑上更加纯粹,少了七层的业务逻辑,让我能够更加专注地去分析关于ssl连接处理逻辑部分。

  希望这次通过分析,能够对ssl连接的建立以及其后续的读写交互的实现逻辑有个整体的把握,在此基础上,将来为ngx_http_upstream_dynamic_module实现一个能够支持https主动健康检测的功能。

  这次,我准备采用官方原生的最新稳定版nginx 1.24.0作为分析对象。为什么不用tengine呢?因为现在的tengine还夹杂了国密openssl的支持逻辑,采用官方原生版本更加纯粹,让分析的目标更加聚焦。

2. 分析验证环境的配置

  配置一个分析验证环境来进行测试分析还是非常简单的,过程如下:

  1. 从官网下载好nginx 1.24.0版本后进行解压,然后用以下命令进行配置:

	./configure --prefix=`pwd` --with-stream --with-stream_ssl_module

  这样nginx从源码层面就开启了支持ssl的TCP代理能力。

  2. 对nginx.conf文件进行配置,配置内容如下:

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


stream {
	server {
		listen 9000;
		proxy_ssl on;               /* 连接上游服务器采用ssl协议 */
		proxy_pass 104.193.88.77:443;
	}
}

  3. 启动nginx,进行curl测试:

 curl "http://127.0.0.1:9000/test/" -v
 
*   Trying 127.0.0.1:9000...
* Connected to 127.0.0.1 (127.0.0.1) port 9000 (#0)
> GET /test/ HTTP/1.1
> Host: 127.0.0.1:9000
> User-Agent: curl/7.81.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 500 Internal Server Error
< Content-Length: 0
< Content-Type: text/plain; charset=utf-8
< Date: Wed, 07 Feb 2024 02:57:01 GMT
< Server: bfe
< 
* Connection #0 to host 127.0.0.1 left intact

  从上面的信息中已经可以看到上游服务器已经响应了,只不过它响应是500错误,这个无所谓,至少表明https透明代理的功能已经正常工作了。

3. 源码分析

  本文主要聚焦在ssl连接逻辑的分析,所以中间会跳过和ssl逻辑不太相关的代码,虽然可能这部分对nginx本身的功能逻辑非常重要。另外,本文也假设只是TCP代理,不对UDP代理进行分析。

  下面直接进入主题,从代理模块的请求入口点开始分析。

3.1 代理模块的请求入口点分析

  代理模块的请求入口点是ngx_stream_proxy_handler函数,一旦客户端和nginx建立了TCP连接后,nginx就会调用代理模块的这个函数,开始与上游服务器建立连接。

  该函数源码主要就是以下列出的三个步骤:

static void
ngx_stream_proxy_handler(ngx_stream_session_t *s)
{
   
	/* 创建ngx_stream_upstream_t上下文,对它进行必要的初始化,
	   并关联到ngx_stream_session_t中 */
	/* 如果上游服务器的地址已经解析好就调用ngx_stream_proxy_connect开始连接上游服务器 */
	/* 如果上游服务器的地址需要域名解析则开启异步解析流程 */   
}

  因此,我们需要重点关注的是ngx_stream_proxy_connect,它负责与上游服务器建立TCP连接。

3.2 发起与上游服务器的连接

static void
ngx_stream_proxy_connect(ngx_stream_session_t *s)
{
   
    ngx_int_t                     rc;
    ngx_connection_t             *c, *pc;
    ngx_stream_upstream_t        *u;
    ngx_stream_proxy_srv_conf_t  *pscf;

    c = s->connection;

    c->log->action = "connecting to upstream";

    pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);

    u = s->upstream;

    u->connected = 0;
    u->proxy_protocol = pscf->proxy_protocol;

    if (u->state) {
   
        u->state->response_time = ngx_current_msec - u->start_time;
    }

    u->state = ngx_array_push(s->upstream_states);
    if (u->state == NULL) {
   
        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
        return;
    }

    ngx_memzero(u->state, sizeof(ngx_stream_upstream_state_t));

    u->start_time = ngx_current_msec;

    u->state->connect_time = (ngx_msec_t) -1;
    u->state->first_byte_time = (ngx_msec_t) -1;
    u->state->response_time = (ngx_msec_t) -1;
	
	/* 创建SOCKET,并发起异步连接请求*/
    rc = ngx_event_connect_peer(&u->peer);

    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "proxy connect: %i", rc);

    if (rc == NGX_ERROR) {
   
        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
        return;
    }

    u->state->peer = u->peer.name;

    if (rc == NGX_BUSY) {
   
        ngx_log_error(NGX_LOG_ERR, c->log, 0, "no live upstreams")
  • 40
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农心语

您的鼓励是我写作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值