Middleware / 运维笔记

Nginx 动态SSL / TLS证书加载

Einic Yeo · 6月17日 · 2019年 · ·

对于以前版本的NGINX,管理安全站点和应用程序的SSL / TLS证书的典型方法是server为每个主机名创建一个单独的块,将证书和关联的私钥静态指定为磁盘上的文件。(为了便于阅读,我们将从现在开始使用证书来引用配对的证书和密钥。)然后在NGINX 启动时加载证书。使用NGINX 1.16.0 ( 但需要OpenSSL 1.0.2或更高版本 ),可以动态加载证书, 支持使用变量 ,并可选择将其存储在内存中的NGINX键值存储中,而不是存储在磁盘上。

动态证书加载有两个主要用例:

  • 磁盘证书的“延迟加载”
  • 将证书存储在内存中

在这两种情况下,NGINX 都可以根据服务器名称指示(SNI)提供的主机名执行动态证书加载,作为TLS握手的一部分。这使NGINX 能够在单个服务器配置下托管多个安全网站,并根据需要为每个传入请求选择适当的证书。

从磁盘延迟加载SSL / TLS证书

使用“延迟加载”时,SSL / TLS证书仅在请求到达时加载到内存中并指定相应的主机名。这既简版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!化了配置(通过消除每个主机名证书的列表),又降低了主机上的资源利用率。使用大量(数千)证书,可能需要几秒钟才能从磁盘读取所有证书并将其加载到内存中。此外,重新加载NGINX配置时会使用大量内存,因为新的一组工作进程会将新证书副本加载到内存中,以及上一组工作程序加载的证书。先前的证书将保留在内存中,直到在旧配置下建立的最终连接完成且先前的工作程序终止为止。

从磁盘延迟加载证书对于具有大量证书的部署和/或频繁进行配置重新加载非常理想。例如,SaaS公司通常为每个客户分配一个单独的子域。入门新客户很困难,因为您必须为每个客户创建一个新的虚拟服务器,然后将新配置和客户证书复制到每个NGINX 实例。延迟加载消除了对配置更改的需要 – 只需在每个实例上部署证书即可完成。

为了支持延迟加载,ssl_certificatessl_certificate_key指令现在接受变量参数。该变量必须在SNI处理期间可用,这在读取请求行和标题之前发生。最常用的变量是$ssl_server_name,它保存在SNI处理期间由NGINX 提取的主机名。在每个客户端会话开始时的TLS握手期间从磁盘读取证书和密钥,并将其缓存在文件系统缓存的内存中,从而进一步降低内存利用率。

安全站点配置变得如此简单,以websocket 示例配置:

server {
   listen 80;
   listen 443 ssl;
   server_name ws.infvie.com ws.infvie.org;
   access_log /data/wwwlogs/ws_nginx.log combined;
 
   ssl_certificate /etc/letsencrypt/live/$ssl_server_name.pem;
   ssl_certificate_key /etc/letsencrypt/live/$ssl_server_name.key;
   ssl_prefer_server_ciphers on;
   ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
   ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
   ssl_session_cache shared:SSL:20m;
   ssl_session_timeout 5m;   

      location / {
        proxy_pass http://websocket_path:8888/;
        proxy_http_version 1.1;
        proxy_read_timeout 3600s;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-real-ip $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
      }
 
   error_log /data/wwwlogs/ws_err.log debug;
}

这种相同的server配置可用于无限数量的安全站点。这有两个好处:

  1. 它消除了server每个主机名的单独块,使配置更小,从而更易于阅读和管理。
  2. 每次添加新主机名时,都无需重新加载配置。当您必须重新加载配置时,它会更快,因为NGINX 不会加载所有证书。

请注意,延迟加载会使TLS握手时间延长20-30%,具体取决于环境,因为从磁盘检索证书需要进行文件系统调用。但是,附加延迟仅影响握手 – 一版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!旦建立TLS会话,请求处理将花费通常的时间。

内存中SSL / TLS证书存储

您现在可以将SSL / TLS证书数据存储在内存中,NGINX 键值存储版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!中以及磁盘上的文件中。当ssl_certificateor或参数的参数以前缀ssl_certificate_key开头时data:,NGINX 将参数解释为原始PEM数据(以变量的形式提供,该变量标识数据实际所在的键值存储中的条目)。

存储在键值存储而不是磁盘上的另一个好处是,部署映像和备份不再包含私钥的副本,攻击者可以使用该副本来解密发送到服务器和从服务器发送的所有流量。具有高度自动化部署流水线的公司可以灵活地使用NGINX API以编程方式将证书插入键值存储中。此外,将应用程序迁移到公共云环境的公司,其中没有用于私钥保护的真实硬件安全模块(HSM),这得益于不在磁盘上存储私钥的附加安全性。

以下是从键值存储中加载证书的示例配置:

keyval_zone zone=ssl_crt:10m; # Key-value store for certificate data
keyval_zone zone=ssl_key:10m; # Key-value store for private key data

keyval $ssl_server_name $crt_pem zone=ssl_crt; # Use SNI as key to obtain cert
keyval $ssl_server_name $key_pem zone=ssl_key;

server {
    listen 443 ssl;
    server_name mem.infvie.com 
    ssl_certificate      data:$crt_pem; # Certificate from key-value store      
    ssl_certificate_key  data:$key_pem; # Private key from key-value store                                       
    ssl_protocols        TLSv1.3 TLSv1.2 TLSv1.1;
    ssl_prefer_server_ciphers on;
    ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
}

使用NGINX API将证书和私钥上传到键值存储区的一种方法是运行以下curl命令(仅显示密钥数据的最开头)。如果版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!使用该curl命令,请记住复制PEM数据并替换每个换行符\n;否则会从JSON有效负载中删除换行符。

$ curl -d '{"mem.infvie.com":"-----BEGIN RSA PRIVATE KEY-----\n..."}' http://localhost:8080/api/4/http/keyvals/ssl_key

使用证书的键值存储是NGINX 群集部署的理想选择,因为您只需上载一次证书即可在群集中自动传播。要保护证书数据本身,请使用该zone_sync_ssl指令对集群成员之间的连接进行TLS加密。使用键值存储也非常适用于短期证书或自动与证书发行者(如Let’s EncryptHashicorp Vault)的集成。

与从磁盘延迟加载一样,在每次TLS握手期间都会从键值存储中加载证书,从而导致性能下降。对于最快的TLS握手,使用ssl_certificatessl_certificate_key指令用硬编码参数磁盘上的文件。此外,ECC证书比RSA证书更快。

请注意,虽然键值存储使攻击者获取私钥文件比从磁盘存储更难,但具有shell访问NGINX 主机的攻击者仍可能能够访问内存中加版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!载的密钥。键值存储不像硬件安全模块(HSM)那样保护私钥;要从HSM获取NGINX 获取密钥,请使用该参数指令。engine:engine-name:key-id ssl_certificate_key

参考文献

https://nginx.org/en/docs/stream/ngx_stream_ssl_module.html#ssl_certificate_key

https://www.nginx.com/blog/

0 条回应