V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sherryxueli
V2EX  ›  推广

阿里云 CDN HTTPS 最佳实践系列——动态证书(一)

  •  1
     
  •   sherryxueli · 2017-11-13 15:32:01 +08:00 · 1632 次点击
    这是一个创建于 2568 天前的主题,其中的信息可能已经有所发展或是发生改变。

    背景

    了解阿里云 CDN 架构的朋友应该知道,阿里云 CDN 7 层的接入组件是 Tengine,我们知道 Tengine 原生是支持 SSL 的,只需要在配置文件中配置证书和私钥即可。在 CDN HTTPS 产品化以前,要开通 HTTPS 的域名需要把证书私钥给我们,我们在 Tengine 静态配置中配置,然后再同步到所有 CDN 边缘节点,显然这种方式在越来越多的域名开通 HTTPS 后,Tengine 静态配置文件会越来越大,难以管理,同时也会导致 Tengine reload 变得很慢,这样配置生效的时间就很糟糕,还有私钥安全等等一系列问题。所以 CDN HTTPS 产品化时就必须采用动态证书的方式,目前阿里云 CDN HTTPS 证书配置之后 1 分钟内生效,极大的提高了证书管理效率和用户体验。这两种方式的简单对比如下:

    image

    架构

    动态配置的同步有两种方式:

    1. 主动同步
          当用户在用户控制台上配置证书和私钥之后主动向下同步到 CDN 所有节点的 Tengine 中,这样当配置同步成功后用户域名的 https 访问就正常了,但是这种方式有很多缺点,因为 Tengine 机器有几千台,甚至上万台,把证书私钥同步到这么多机器用时较大,生效时间较慢,另外其他一些域名并不是访问到所有的 CDN 机器,如果把证书私钥同步到这台机器并没有什么用,白白浪费内存,以及影响证书的搜索性能。这种方式适合在少量机器群集中使用。
    2. Lazy pull
          当 https 请求到达 Tengine 后,Tengine 再去拉取配置,这样就只拉取该机器有访问的域名配置,其他域名配置不需要拉取,解决了 Tengine 静态配置过多的问题,也避免了 Tengine reload,从而生效时间更快。

    综合以上两种方式的优点,阿里云 CDN 在节点机房内部采用了 Lazy pull 方式拉取配置,在节点机房和中心机房之前采用主动同步的方式。以下是简化的 HTTPS 动态配置架构图。 https_1

    实现

    主动同步有很多种方式可以选择,比如 redis、zoomkeeper 等等,这里只讲 Lazy pull 的实现,其关键技术是基于 Tengine 的指令 ssl_certificate_by_lua_file,这个指令是 lua-nginx 模块提供的,其用途就是在 openssl 中查找证书时的一个 lua 回调,然后在 lua 中动态设置证书和私钥。总之,如果 Tengine 的 server 块中配置了这个指令的话,当 openssl 在处理 SSL 握手消息 ClientHello 时会调用到这个指令配置的 lua 代码,在 lua 中我们可以做我们想做的事情,比如发 http 请求去远程拉取证书和私钥,做配置缓存,私钥加解密处理,动态设置证书和私钥等等一系列业务逻辑。

    以下是 Tengine 的配置:

    server {
        listen  0.0.0.0:443 default_server ssl http2;
        ssl_protocols                     TLSv1 TLSv1.1 TLSv1.2;
        #ssl_ciphers                      [xx];
        ssl_prefer_server_ciphers         on;
        ssl_certificate                   default.crt;
        ssl_certificate_key               default.key;
        ssl_certificate_by_lua_file       dycert.lua;
        include                           dyconf.cfg;
    }
    

    如上配置,dycert.lua 是阿里云 CDN 实现的动态证书模块,在 SSL 完整握手时会调用到这个模块,在 Session 复用的握手情况下不会调用到这个模块,这是因为 Session 复用时不需要证书和私钥,这是 openssl 回调接口的官方实现,但是阿里云 CDN 的实现中,还有很多 HTTPS 的动态配置需要在 dycert 模块中来设置,所以我们修改了 openssl,让其在 Session 复用时也调用到 dycert 模块,这为我们实现很多 HTTPS 动态配置(比如:HTTP/2 开关,客户端认证,TLS record size 配置)提供了方便。

    如下图所示,dycert 模块是所有 HTTP(S) 业务模块的第一个重要模块,当 https 请求到达 CDN Tengine 时,dycert 模块会去远程拉取 HTTPS 动态配置(证书、私钥、HTTP/2 开关、客户端证书、TLS record size 配置等等),然后解密私钥,将证书和私钥设置到 openssl 中,让其恢复 SSL 握手流程。握手成功后会将该请求交给 Tengine 的后续业务模块处理。

    https_2

    下面是 dycert 模块的实现原理图: https_3

    众所周知,Tengine 是多 worker 的,我们需要使用共享内存来缓存拉取到的动态配置,这样可以防止多个 worker 进程同时去远程拉取同一份配置,并且在 worker 进程内存中也同时做了缓存以提高效率。

    当一个 https 请求调用到 Tengine dycert 模块时,先在本 worker 缓存中查询是否存在配置,如果没有就去共享内存中查询,如果还是没有就得去远程拉取了(远程拉取采用 http 的 resty api 方式),拉取到动态配置之后在本 worker 中缓存,同时也在共享内存中缓存,为了防止共享内存暴增,加了一个定时器来删除该域名的配置,下次从共享内存中查询不到配置再重新从远程拉取。

    另外一个重点就是配置的更新后如何实时生效了:配置管理系统 agent 是可以实时发现域名的配置变化的,并且提供了配置变更注册机制,dycert 会定时的去注册 HTTPS 配置变更,并提供一个 purge resty api,当配置管理系统 agent 发现配置更新之后会回调到该接口,然后该接口会从共享内存中删除该域名的配置,下次该域名的 https 请求到达 dycert 模块后,发现共享内存中没有配置会再去配置管理系统 agent 中拉取,从而解决了配置更新后实时生效的问题。

    以上为本文内容。目前,阿里云 CDN HTTPS 已经全面降价,后付费 HTTPS 0.05 元 /万次请求。下一篇我们将介绍 HTTPS 最佳实践—— HTTP/2,敬请期待。

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2977 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 13:07 · PVG 21:07 · LAX 05:07 · JFK 08:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.