使用NGINX Plus键值存储从HashiCorp Vault保护临时SSL密钥
383 次浏览
发表于 2020-05-08 14:24

在我们的系列的前两篇有关在传输和存储过程中保护SSL密钥和证书的文章中,我们讨论了使用诸如HashiCorp Vault和硬件安全模块(HSM)之类的工具为磁盘上的NGINX保护SSL密钥和证书数据:


在许多情况下,将SSL证书数据存储在磁盘上是可以容忍的风险,只要使用其他安全防护栏来管理对这些证书的访问即可。但是在某些用例中,还需要将所有与安全相关的组件保留在磁盘之外,而仅存储在内存中并可以从内存中访问。两种最常见的用例是:安全性得到增强的环境,其中任何静态存储都将受到威胁,以及系统是临时的(例如容器)或SSL证书和密钥本身都是临时的环境。


在本文中,我们重点讨论后一种用例:短期SSL证书密钥对。我们正在使用HashiCorp Vault颁发临时SSL证书,并将其存储在NGINX Plus键值存储(内存数据库)中。


NGINX Plus R18和更高版本支持用于安全SSL密钥管理的强大体系结构,因为SSL证书密钥对可以加载到内存中,并可以通过变量(例如NGINX Plus密钥值存储中的值)进行访问。通过结合NGINX Plus,通过密钥值存储和HashiCorp Vault进行SSL密钥存储和管理,您可以通过将敏感而临时的SSL数据存储在密钥值存储中而不用将其提交给SSL证书来创建安全的环境。磁盘。


HashiCorp Vault除了颁发SSL证书外,还具有许多用于管理安全数据的功能。在主要需要临时证书来保护传输数据的环境中,并且这些证书或使用它们的系统经常循环使用,我们可以利用Vault作为证书颁发机构(CA)。在本文中,我们将配置并使用Vault突出显示如何将实时Vault证书发布请求与NGINX Plus中的动态证书加载功能集成在一起。


先决条件

该博客做出以下假设:

  • 您对保管箱有所了解
  • 保险柜已安装,配置,作为服务运行且未密封(请参阅保险柜文档
  • 您具有Vault实例的管理员级别访问权限
  • 在指定的实例上配置了以下环境变量:

    • 保管箱实例:

      • VAULT_ADDR=https://127.0.0.1:8200
      • VAULT_EXT=https://externally_accessible_Vault_IP_address:8200
      • VAULT_TOKEN=initial_root_token
    • SSL管理实例:

      • VAULT_EXT=https://externally_accessible_Vault_IP_address:8200
      • VAULT_APP_TOKEN=NGINX_role_token


建筑设计

在最基本的示例部署中,某些类型的SSL请求/帖子管理工具用于从Vault请求临时证书,并将其加载到NGINX Plus键值存储中。在此示例中,我们使用简单的curl命令来模拟SSL请求/发布工具。为了进行测试,可以将Vault和NGINX Plus安装在同一系统,不同系统,容器中,等等。唯一的要求是您用来请求证书(curl在我们的情况下)并将其加载到NGINX Plus(也curl)的工具能够通过HTTPS与Vault和NGINX Plus进行通信。

下图描述了体系结构,其中:

  1. 请求/发布工具通过API调用从保险柜请求新的临时PEM
  2. 该工具通过NGINX Plus API将短暂的PEM数据写入内存中的键值存储,在此过程中永远不会写入磁盘
  3. HTTPS客户端从NGINX Plus 请求使用临时证书的https://www.example.com


将HashiCorp Vault配置为临时证书CA

将保险柜配置为临时证书的CA的主要过程有两个:


将保管库配置为CA

为了进行测试,我们使用Vault的CA功能来生成并签名我们的临时证书。这使我们可以将Vault用作这些临时证书的一站式端点,但是如果您的体系结构需要不同的工具,则可以根据需要进行调整。

要将保管库用作发行CA,我们首先配置其Vault的公钥基础结构(PKI)存储,以生成并发行新的临时证书和密钥。从本地主机实例配置保管库时,以下命令是合适的。

为NGINX Plus证书请求启用PKI

首先,我们通过为NGINX Plus请求定义自定义端点并验证配置来启用PKI支持。

# vault secrets enable -path pki/nginx-plus-ephem-certs pki
# vault secrets list

创建CA颁发者

我们在保险柜中创建一个新的CA,仅用于我们的临时证书申请。也可以将现有的CA证书导入到Vault中,而不用创建新的CA证书。有关导入CA详细信息的说明以及有关构建PKI端点的其他选项的信息,请参阅Vault文档

我们生成的CA有效期为一周(168小时),并且为了安全起见,将JSON格式的CA证书密钥对写入文件中:

# vault write -format=json pki/nginx-plus-ephem-certs/root/generate/internal common_name="Example\ Company" ttl=168h > NGINX-Plus-Ephem-CA.json

请注意,将输出保存到本地JSON文件不是强制性的。在需要重新生成CA而不是将CA的任何详细信息保留在可访问磁盘上的开发环境或高度安全的环境中,您既不能存储生成的JSON,也不能将CA的详细信息存储回Vault的安全密钥存储中。请参阅我们博客上有关使用密钥存储区的保险柜文档,或使用NashiNX使用HashiCorp保险柜保护SSL私钥

在CA上配置端点和角色

将Vault配置为充当本地发行CA之后,我们可以开始从中发行临时证书。首先,我们将Vault配置为通过NGINX Plus实例的证书吊销列表(CRL)端点支持请求。

# vault write pki/nginx-plus-ephem-certs/config/urls issuing_certificates="$VAULT_EXT/v1/pki/nginx-plus-ephem-certs/ca" crl_distribution_points="$VAULT_EXT/v1/pki/nginx-plus-ephem-certs/crl"

现在,我们为NGINX Plus实例(仅它们)创建一个分配给Vault的角色,该角色允许这些实例从CA请求新证书。配置新角色时,可以定义每个角色的证书要求。例如,以下是NGINX证书请求角色的两个选项:一个强制证书请求包括显式域,另一个允许您为针对主CA签名的任何公用名(CN)生成证书。在我们的用例中,两个示例角色都支持证书中的使用者备用名称(SAN)字符串。

注意:以下命令仅是示例,每个命令都具有安全隐患,您需要在生产环境中使用这些命令之前进行检查。

将证书请求限制为特定的子域,并允许SAN条目:

# vault write pki/nginx-plus-ephem-certs/roles/nginx-cert-requests allowed_domains=example.com allow_subdomains=true allow_ip_sans=true allow_alt_names=true key_bits=2048 max_ttl=72h no_store=true

或者,允许任何CN和SAN条目:

# vault write pki/nginx-plus-ephem-certs/roles/nginx-cert-requests allow_any_name=true allow_ip_sans=true allow_alt_names=true key_bits=2048 max_ttl=72h no_store=true


为NGINX Plus实例创建只读策略

最后,我们向Vault添加了HashiCorp配置语言(HCL)策略,该策略仅允许特定类型的证书请求。我们建议不要共享Vault根访问令牌或将其用于日常操作,因此我们专门为与此策略绑定的NGINX Plus实例生成访问令牌。

首先,我们通过创建具有以下内容的文件nginx-cert-requests.hcl并将其保存在/ etc / vault目录中,为新的PKI终结点定义HCL策略:

path "pki/nginx-plus-ephem-certs/issue/*" {
    capabilities = ["create","update"]
}

还有更多选项可用来进一步限制对PKI端点的访问,但是在此示例中,我们只是授予请求新证书的权限。

接下来,我们将策略加载到保险柜中:

# vault policy write nginx-cert-requests /etc/vault/nginx-cert-requests.hcl

现在,我们为这个新的只读策略创建一个唯一的访问令牌,该令牌用于为我们的NGINX Plus实例请求临时证书:

# vault token create -policy=nginx-cert-requests

我们token将此命令返回的值保存在我们VAULT_APP_TOKEN要从中请求证书的系统上调用的环境变量中。这是用于通过SSL管理工具从保险柜中请求新PEM数据并将SSL数据加载到键值存储中的令牌。

在高度安全的环境中,您可能希望对只读令牌设置较短的生存时间(TTL),并定期重新生成和重新分发它。有关创建令牌的其他详细信息和配置选项,请参见Vault文档

现在,我们准备从安全的Vault CA请求新的临时证书。为了验证我们能够请求新证书,我们在系统上运行以下命令,在该系统上发出request命令并将生成的证书加载到键值存储中。该命令将必要的证书请求信息传递给Vault(例如CN,SAN和持续时间),然后Vault生成JSON格式的证书和密钥有效负载。这是我们将在下面的API调用中以更易读的形式提交给Vault的JSON:

{
  "common_name":"www.example.com",
  "ip_sans":"10.10.1.1,192.168.76.76",
  "alt_names":"dev.example.com,eng.example.com",
  "ttl":"1h",
  "format":"pem_bundle"
}
# curl -ks --header "X-Vault-Token: $VAULT_APP_TOKEN" -X POST -d '{"common_name":"www.example.com","ip_sans":"10.10.1.1,192.168.76.76","alt_names":"dev.example.com,eng.example.com","ttl":"1h","format":"pem_bundle"}' $VAULT_EXT/v1/pki/nginx-plus-ephem-certs/issue/nginx-cert-requests

生成的JSON包含SSL证书,密钥和有关颁发CA的数据。由于我们配置保险柜的方式,该证书数据仅存在于此JSON响应中-保险柜不存储它。出于我们的测试目的,这是理想的响应,但是如果您需要保存SSL数据以进行手动测试,则可以将输出通过管道传递到,例如jq


为SSL存储配置NGINX Plus键值存储

NGINX Plus可以通过其内存中的密钥值存储来存储和读取SSL证书和密钥,从而使其可以直接从内存而不是从磁盘加载证书。此外,NGINX Plus可以基于实时会话数据使用不同的证书,例如服务器名称指示(SNI)主机或其他条件。

要将NGINX Plus配置为直接从内存中加载SSL数据,请在ssl_certificatessl_certificate_key指令前添加参数data:。该前缀指示NGINX Plus将字符串解释为原始证书密钥PEM内容,可以直接从内存中键值存储中将其作为变量提供。

这种设计消除了从磁盘存储库存储和分发平面文件证书和密钥的需要,这意味着它们不再可以在部署映像中存储和访问,也不能在备份中存储,等等。

在以下示例中,我们将构建具有以下功能的NGINX Plus配置:

  • 一个名为vault_ssl_pem的键值存储,用于保存我们组合的证书密钥PEM字符串。
  • 证书在连接时根据Host传入请求的SNI 标头值进行匹配(例如,仅当主机名等于已配置的密钥(例如www.example.com)时,才从密钥值存储区加载证书。
  • SSL PEM数据被加载到server绑定到端口443 的块中。
  • 调试日志记录已启用以进行测试,因此我们可以在访问日志中捕获用于每个连接的完整PEM字符串。(我们不建议在生产中使用此配置,因为它会生成大量数据并可能带来安全风险。)
  • NGINX Plus API端点已配置为仅接受到/ api位置的HTTPS通信。

/etc/nginx/conf.d中创建一个名为ssl_keyval.conf的配置文件,其内容如下:

log_format vault_ssl_keyval '$remote_addr [$time_local] - '
                            'ssl_server_name:"$ssl_server_name" '
                            'host:"$host" ';

keyval_zone zone=vault_ssl_pem:1m;
keyval $ssl_server_name $certificate_pem zone=vault_ssl_pem;

server {
    listen 443 ssl;
    access_log /var/log/nginx/vault-ssl-keystore-access.log vault_ssl_keyval;
    error_log  /var/log/nginx/vault-ssl-keystore-error.log debug;

    # Load PEMs from variable. Note the 'data:' prefix.
    ssl_certificate     data:$certificate_pem;
    ssl_certificate_key data:$certificate_pem;

    location / {
        root /usr/share/nginx/html;
        index index.html;
    }
}

server {
    listen 8443 ssl;
    access_log /var/log/nginx/status-api-access.log api;
    error_log  /var/log/nginx/status-api-error.log notice;

    ssl_certificate     /etc/nginx/ssl/nginx-ssl.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx-ssl.key;

    location /api {
        api write=on;
        if ($request_method !~ ^(GET|POST|HEAD|OPTIONS|PUT|PATCH|DELETE)$) {
            return 405;
        }
    }
}

现在,我们可以通过NGINX Plus API 将PEM数据直接加载到vault_ssl_pem键值存储中。

请注意,我们已将NGINX Plus API启用为read-write。强烈建议在处理加载敏感数据(例如SSL密钥)时,为NGINX Plus API启用SSL和身份验证。因此,以下示例通过端口8443上的HTTPS 访问NGINX Plus API。

我们可以通过运行以下命令以将www.example.com的 PEM数据(或在这种情况下,仅作为示例的SSL PEM密钥的第一行)加载到密钥值存储中,来测试NGINX Plus是否已正确配置。NGINX_Plus_instance是我们配置了NGINX Plus API的NGINX Plus实例的主机名。

# curl -s -X POST -d '{"www.example.com":"-----BEGIN RSA PRIVATE KEY-----\n..."}' https://NGINX_Plus_instance:8443/api/6/http/keyvals/vault_ssl_pem

然后,我们通过NGINX Plus API查询vault_ssl_pem密钥值存储,以验证PEM证书和密钥数据是否为与www.example.com密钥关联的值:

# curl https://NGINX_Plus_instance:8443/api/6/http/keyvals/vault_ssl_pem
{"www.example.com":"-----BEGIN RSA PRIVATE KEY-----\n..."}

至此,我们已经配置了NGINX Plus,以便从内存中键值存储中动态检索PEM证书和键数据,以实现所有与端口443上名为www.example.com的服务的HTTPS连接。

要删除与www.example.com密钥关联的SSL PEM数据,我们可以使用空值PATCH调用NGINX Plus API:

# curl -s -X PATCH -d '{"www.example.com":null}' https://NGINX_Plus_instance:8443/api/6/http/keyvals/vault_ssl_pem


将Vault CA证书请求与NGINX Plus键值存储集成

请求新的临时证书/密钥PEM并将其加载到NGINX Plus密钥值存储中

最后一步是从Vault请求临时SSL密钥证书PEM捆绑包,并将其加载到NGINX Plus密钥值存储中。在处理临时证书或增强的安全环境时,理想的是请求新的PEM数据并将其一步一步地加载到内部键值存储中,这样就不会在磁盘上存储任何数据。为此,我们只需将上述示例中的PEM请求和Vault的键值POST请求以及对NGINX Plus API的键值请求结合在一个命令中即可。我们可以从SSL管理工具或可以访问Vault API和NGINX Plus API的另一个中央位置运行它。

# echo "{\"www.example.com\":$( curl -ks --header "X-Vault-Token: $VAULT_APP_TOKEN" -X POST -d '{"common_name":"www.example.com","ip_sans":"10.10.1.1,192.168.76.76","alt_names":"dev.example.com,eng.example.com","ttl":"1h","format":"pem_bundle"}' $VAULT_EXT/v1/pki/nginx-plus-ephem-certs/issue/nginx-cert-requests | jq '.data | "\(.certificate)"' )}" | curl -ks -X POST -d @- https://NGINX_Plus_instance:8443/api/6/http/keyvals/vault_ssl_pem

哪里:

  • echo命令将字符串传递给NGINX Plus API
  • 第一条curl命令从新创建的Vault CA请求JSON格式的密钥证书PEM
  • jq命令仅从Vault CA生成的响应中提取PEM数据
  • 最后一个curl命令POST调用NGINX Plus API,将PEM数据插入vault_ssl_pem键值存储中

与上一节一样,我们查询vault_ssl_pem密钥值存储以验证证书密钥PEM数据是否为与密钥值存储中的www.example.com密钥相关联的值:

# curl https://NGINX_Plus_instance:8443/api/6/http/keyvals/vault_ssl_pem
{"www.example.com":"-----BEGIN RSA PRIVATE KEY-----…"}

将新的PEM数据字符串加载到www.example.com域的键值存储中后,所有与适用NGINX Plus服务的新HTTPS连接都将使用此证书和密钥来保护连接。我们运行以下命令来测试它是否正常工作:

# curl https://www.example.com

假设您的键值存储具有www.example.com密钥的有效PEM数据,则HTTPS请求成功。您可以通过在NGINX Plus实例的命令中使用www.example.com以外的其他工具(例如,与该名称对应的IP地址)来测试与键值存储中的PEM数据匹配的SNI是否正常工作curl。这会导致SSL握手失败,您可以在错误日志(/var/log/nginx/vault-ssl-keystore-error.log)中进行确认。

# curl https://IP-address-for-www.example.com


更新和吊销证书

如果您需要更改,撤销或删除与主机关联的PEM数据,则可以按照上述类似方法,利用API PATCH方法更新键值存储中www.example.com的值。对于定期在固定时间范围(例如每天)或需要吊销证书的情况下循环SSL证书,这可能是一个很好的用例。

来请求来自保管库的新证书更新与相关联的值www.example.com在键值存储,运行一个类似于上一节中的指令,但更改从HTTP方法POST,以PATCH在该第二curl命令,为了推通过API将更新的PEM捆绑包添加到键值存储区:

# echo "{\"www.example.com\":$( curl -ks --header "X-Vault-Token: $VAULT_APP_TOKEN" -X POST -d '{"common_name":"www.example.com","ip_sans":"10.10.1.1,192.168.76.76","alt_names":"dev.example.com,eng.example.com","ttl":"1h","format":"pem_bundle"}' $VAULT_EXT/v1/pki/nginx-plus-ephem-certs/issue/nginx-cert-requests | jq '.data | "\(.certificate)"' )}" | curl -ks -X PATCH -d @- https://NGINX_Plus_instance:8443/api/6/http/keyvals/vault_ssl_pem

要完全删除或撤消与www.example.com密钥关联的值,请使用PATCHnull值:

# curl -s -X PATCH -d '{"www.example.com":null}' https://NGINX_Plus_instance:8443/api/6/http/key-vals/vault_ssl_pem


结论

验证一切正常后,您可以将这些命令编写为Vault和NGINX Plus API的脚本,以根据需要轮换证书。您也可以将此工作流添加到任何已用于SSL证书和密钥管理,NGINX Plus部署或NGINX Plus密钥值存储管理的CI / CD管道中。可以将其他证书请求详细信息(例如唯一的CN和SAN指令)提供给Vault,以生成仅适用于特定服务名称和其他指定条件的证书。这种体系结构允许使用非常特殊的系统来生成定制的临时证书并将其存储在内存中,从而使任何内容都不会写入磁盘。

对于可以在磁盘上存储SSL密钥的部署,请参阅本系列的其他两个博客:

亲自尝试NGINX Plus –立即开始30天免费试用,或与我们联系以讨论您的用例

如果您觉得不错,就打赏支持一下吧〜
已有 0 人进行打赏
发表评论
  • 阿尔巴

    文章内容有点长,但很实用,欢迎各位细细品读

    2020-05-09 15:58
    1
    回复
发表者

阿尔巴

如果你联盟

  • 12

    文章

  • 19

    关注

  • 9

    粉丝

活动推荐
版权所有©F5 Networks,Inc.保留所有权利。京ICP备16013763号-5