细粒度控制Nginx的HTTP2开与关

Posted OpenResty爱好者

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了细粒度控制Nginx的HTTP2开与关相关的知识,希望对你有一定的参考价值。

    引言

    nginx官方在早11.6号通告1.9.5-1.15.5的版本都存在两安全漏洞CVE-2018-16843、CVE-2018-16844,可能会导致内存、CPU耗尽,具体与启用了http2功能相关。


    修复这两漏洞,无外乎两大途径:一是升级版本至1.15.6/1.14.1;二是关闭http2功能。


    诉求

    在Nginx里,只需在listen指令后面去除http2配置项即可关闭http2功能;而这一动作是将所有server的http2功能彻底关闭。这有时候并不是想要的效果,最好能针对server级别进行开关控制,更细粒度的把控功能在每个server的使用情况。


    大致思路

    http2配置项想能在指定server中开启与关闭,大致实现思路如下:

  1. 注册配置项http2,允许在server范围使用,设定为flag类型(即选项是on或off)

  2. 初始化默认值为on

  3. 在协议协商之前把对应的开关项更新,且不影响其它server的开关配置


    实现落地

    直接参考配置项underscores_in_headers的逻辑实现代码即可,如下:


1.定义配置项http2


    { ngx_string("http2"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_SRV_CONF_OFFSET,

      offsetof(ngx_http_core_srv_conf_t, http2),

      NULL },



2.初始化


static void *

ngx_http_core_create_srv_conf(ngx_conf_t *cf)

{

    ngx_http_core_srv_conf_t  *cscf;

    ......

    cscf->http2 = NGX_CONF_UNSET;

    return cscf;

}



3.合并http与server块配置时,设定默认值--1(on 开启)


static char *

ngx_http_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)

{

    ......

    ngx_conf_merge_value(conf->http2,

                              prev->http2, 1);

    ......

}



4.开关项更新


有了配置项后,究竟在哪个地方可嵌入使用呢?

关键一步:查日志,看http/2协议协商选择发生在哪里


2018/11/14 07:58:55 [                 ngx_http_ssl_servername,   870]  [debug] 9981#0: *1 SSL server name: "a.com"

2018/11/14 07:58:55 [                ngx_http_ssl_alpn_select,   368]  [debug] 9981#0: *1 SSL ALPN supported by client: h2

2018/11/14 07:58:55 [                ngx_http_ssl_alpn_select,   368]  [debug] 9981#0: *1 SSL ALPN supported by client: http/1.1

2018/11/14 07:58:55 [                ngx_http_ssl_alpn_select,   395]  [debug] 9981#0: *1 SSL ALPN selected: h2

2018/11/14 07:58:55 [                       ngx_ssl_handshake,  1234]  [debug] 9981#0: *1 SSL_do_handshake: -1



从以上可看出,先是ngx_http_ssl_servername获取到ssl协议带上的主机名,再通过ngx_http_ssl_alpn_select协商选择通讯协议h2或http/1.1。


所以在ngx_http_ssl_servername函数里实现http2开关控制即可影响后续的协议选择,具体如下:


int

ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)

{

    ......

    hc->ssl_servername = ngx_palloc(c->pool, sizeof(ngx_str_t));

    if (hc->ssl_servername == NULL) {

        return SSL_TLSEXT_ERR_NOACK;

    }

    *hc->ssl_servername = host;


    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 hc:%d cscf:%d", hc->addr_conf->http2, cscf->http2);

    if (hc->addr_conf->http2 && !cscf->http2) {

        adcf = ngx_palloc(c->pool, sizeof(ngx_http_addr_conf_t));

        if (adcf == NULL) {

            return SSL_TLSEXT_ERR_NOACK;

        }

        ngx_memcpy(adcf, hc->addr_conf, sizeof(ngx_http_addr_conf_t));

        adcf->http2 = 0;

        hc->addr_conf = adcf;

    }

    hc->conf_ctx = cscf->ctx;

}




效果查验

重新编译Nginx,对比配置http2开与关的效果:


server {

        listen 443 http2;

        server_name a.com;

        http2 off;

}


2018/11/14 07:55:43 [                 ngx_http_ssl_servername,   898]  [debug] 9962#0: *1 http2 hc:1 cscf:0

2018/11/14 07:55:43 [                ngx_http_ssl_alpn_select,   368]  [debug] 9962#0: *1 SSL ALPN supported by client: h2

2018/11/14 07:55:43 [                ngx_http_ssl_alpn_select,   368]  [debug] 9962#0: *1 SSL ALPN supported by client: http/1.1

2018/11/14 07:55:43 [                ngx_http_ssl_alpn_select,   395]  [debug] 9962#0: *1 SSL ALPN selected: http/1.1

2018/11/14 07:55:43 [                       ngx_ssl_handshake,  1234]  [debug] 9962#0: *1 SSL_do_handshake: -1



从debug日志可看到:http2开关在连接上先是开启的,而在server块上是关闭的,经过alpn协商选择最终下降选择了http/1.1协议,即使客户端支持h2协议。


以上即可达到细粒度地控制http2配置的开与关。


往期回顾




以上是关于细粒度控制Nginx的HTTP2开与关的主要内容,如果未能解决你的问题,请参考以下文章

粗粒度与细粒度权限控制

十二 K8S细粒度权限控制

Spring细粒度控制扫描Bean

自定义标签 + shiro 实现权限细粒度控制

如何将 DynamoDB 细粒度访问控制与 Cognito 用户池结合使用?

对 DynamoDb 使用细粒度访问控制时出现 AccessDeniedException