felixfontein.acme_certificate

acme_certificate 1.1.1

此角色已被弃用!请使用 felixfontein.acme 集合版本https://github.com/felixfontein/ansible-acme/)!

允许从 Let's Encrypt 获取证书,几乎无需与 Web 服务器交互。大部分代码在控制器上执行,账户密钥从不发送到节点。

可以通过 Ansible Galaxy 安装此角色:

ansible-galaxy install felixfontein.acme_certificate

有关此角色的更改,请参见 变更日志

描述

这是一个 Ansible 角色,可以使用任何支持 ACME 协议的 CA,如 Let's EncryptBuypass,为您的服务器颁发 TLS/SSL 证书。此角色需要 Ansible 2.8.3 或更高版本,并基于 Ansible 附带的 acme_certificate 模块

这种方法的主要优势是 几乎没有代码在您的 Web 服务器上执行:只有在使用 HTTP 挑战时,需要将文件复制到 Web 服务器上,之后再删除。其他所有操作都在本地计算机上执行!

(这不包括安装证书,您需要在其他角色中自己完成。)

需求

要求在控制器上安装 Python cryptography 库,并可用于执行 playbook 的 Python 版本。如果没有安装 cryptography,Ansible openssl_privatekeyopenssl_csr 模块当前支持的一个较新版本的 PyOpenSSL 可以作为回退。

openssl 二进制文件必须在控制器的可执行路径中。它在 cryptography 未安装的情况下由 acme_certificate 模块所需,且用于证书链验证。

如果使用了 DNS 挑战,可能会根据 DNS 提供商的不同而有其他要求。例如,对于 Amazon 的 Route 53,Ansible route53 模块需要 Python boto 包。

账户密钥设置

您可以使用 openssl 二进制文件创建账户密钥,如下所示:

# RSA 4096 位密钥
openssl genrsa 4096 -out keys/acme-account.key
# ECC 256 位密钥(P-256)
openssl ecparam -name prime256v1 -genkey -out keys/acme-account.key
# ECC 384 位密钥(P-384)
openssl ecparam -name secp384r1 -genkey -out keys/acme-account.key

使用 Ansible,您可以这样使用 openssl_privatekey 模块:

- name: 生成 RSA 4096 密钥
  openssl_privatekey:
    path: keys/acme-account.key
    type: RSA
    size: 4096
- name: 生成 ECC 256 位密钥(P-256openssl_privatekey:
    path: keys/acme-account.key
    type: ECC
    curve: secp256r1
- name: 生成 ECC 384 位密钥(P-384openssl_privatekey:
    path: keys/acme-account.key
    type: ECC
    curve: secp384r1

确保安全存储账户密钥。与证书私钥相比,不需要频繁重新生成它,使用它颁发的证书的撤销也变得非常简单。

角色变量

请注意,从 2020 年 5 月起,所有变量必须以 acme_certificate_ 为前缀。在一段时间内,如果未定义较长的变量名,模块仍将使用旧的(短的)变量名。请尽快升级您的角色用法。

以下是主要变量:

  • acme_certificate_acme_account:私有 ACME 账户密钥的路径。必须始终指定。
  • acme_certificate_acme_email:您希望与 ACME 账户关联的电子邮件地址。必须始终指定。
  • acme_certificate_algorithm:用于创建私钥的算法。默认是 "rsa";其他选择是 "p-256""p-384""p-521",分别用于 NIST 椭圆曲线 prime256v1secp384r1secp521r1
  • acme_certificate_key_length:RSA 私钥的位长。默认是 4096。
  • acme_certificate_key_name:存储密钥和证书的基本名称。默认是第一个指定的域名,将 * 替换为 _
  • acme_certificate_keys_path:密钥和证书存储的位置。默认值是 "keys/"
  • acme_certificate_keys_old_path:旧密钥和证书应复制到的位置;如果 acme_certificate_keys_old_store 为真,则使用此。默认值是 "keys/old/"
  • acme_certificate_keys_old_store:如果设置为 true,将制作旧密钥和证书的副本。副本将存储在 acme_certificate_keys_old_store 指定的目录中。默认值是 false
  • acme_certificate_keys_old_prepend_timestamp:旧密钥和证书的副本是否应加上当前日期和时间。默认值是 false
  • acme_certificate_ocsp_must_staple:是否请求具有 OCSP Must Staple 扩展的证书。默认值是 false
  • acme_certificate_agreement:用户同意的服务条款文档。默认值是 https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf
  • acme_certificate_acme_directory:要使用的 ACME 目录。默认是 https://acme-v02.api.letsencrypt.org/directory,这是 Let’s Encrypt 当前的生产 ACME v2 端点。
  • acme_certificate_acme_version:ACME 目录的版本。默认是 2。对于 ACME v1,使用 1。
  • acme_certificate_challenge:要使用的挑战类型。应为 http-01,表示使用 HTTP 挑战(需要访问 Web 服务器),或 dns-01,表示使用 DNS 挑战(需要访问 DNS 提供商)。
  • acme_certificate_root_certificate:ACME 目录的根证书。默认值是 https://letsencrypt.org/certs/isrgrootx1.pem,这是 Let’s Encrypt 的根证书。
  • acme_certificate_deactivate_authzs:在之后是否应停用 authz(授权)。默认值是 true。设置为 false 以便重新使用 authz
  • acme_certificate_modify_account:是否应创建 ACME 账户(如果不存在),并更新联系数据(电子邮件地址)。默认值是 true。如果希望使用 acme_account 模块管理您的 ACME 账户(此角色不处理),请设置为 false
  • acme_certificate_privatekey_mode:私钥文件使用的文件模式。默认值是 "0600",这意味着仅所有者可读和写,但其他人不可访问(除非可能为 root)。
  • acme_certificate_select_chain:(仅可与 Ansible 2.10+ 一起使用)必须采用在 此处 描述的格式。允许选择要使用的证书链;必须与 acme_certificate_root_certificate 一起使用。例如,可以与 Let's Encrypt 一起使用,以选择要使用的根证书。以下配置确保使用了 IdenTrust 交叉签名的中间证书,这在某些较旧的 Android 版本上比新 IRSG 根证书更兼容:
    acme_certificate_root_certificate: https://letsencrypt.org/certs/trustid-x3-root.pem.txt
    acme_certificate_select_chain:
      - test_certificates: last
        issuer:
          CN: DST Root CA X3
          O: Digital Signature Trust Co.
    
    以下配置选择新的 IRSG X1 根:
    acme_certificate_root_certificate: https://letsencrypt.org/certs/isrgrootx1.pem
    acme_certificate_select_chain:
      - test_certificates: last
        issuer:
          CN: ISRG Root X1
          O: Internet Security Research Group
    

HTTP 挑战

对于 HTTP 挑战,以下变量定义了如何将挑战放置到(远程)Web 服务器上:

  • acme_certificate_server_location.well-known/acme-challenge/ 服务的位置。默认是 /var/www/challenges
  • acme_certificate_http_becomefilecopy 任务的 become: 参数。默认值是 false
  • acme_certificate_http_challenge_user:挑战文件的拥有者用户。默认值是 root
  • acme_certificate_http_challenge_group:挑战文件的拥有者组。默认值是 http
  • acme_certificate_http_challenge_folder_mode:用于挑战文件夹的模式。默认值是 0750(八进制)。
  • acme_certificate_http_challenge_file_mode:用于挑战文件的模式。默认值是 0640(八进制)。

以下小节展示了如何为 HTTP 挑战配置 nginx。其他 Web 服务器的配置可以类似进行。

Nginx 配置

假设对于您的一些 TLS/SSL 保护的域名,您使用 HTTP 到 HTTPS 的重定向。假设它看起来像这样:

server {
    listen       example.com:80;
    server_name  example.com *.example.com;
    return 301   https://www.example.com$request_uri;
}

要允许 acme_certificate 角色在 http://*.example.com/.well-known/acme-challenge/ 上放置东西,您可以将其更改为:

server {
    listen       example.com:80;
    server_name  example.com *.example.com;
    location /.well-known/acme-challenge/ {
        alias /var/www/challenges/;
        try_files $uri =404;
    }
    location / {
        return 301   https://www.example.com$request_uri;
    }
}

使用此 nginx 配置,所有其他的 URL 在 *.example.comexample.com 上仍将重定向,而 *.example.com/.well-known/acme-challenge/ 中的所有内容则从 /var/www/challenges 提供。当调整 /var/www/challenges 的位置时,还必须更改 acme_certificate_server_location

您甚至可以通过将所有无法解析为 /var/www/challenges 中有效文件的 *.example.com/.well-known/acme-challenge/ 中的 URL 重定向到 HTTPS 服务器来进一步优化。这样做的一个方法是:

server {
    listen       example.com:80;
    server_name  example.com *.example.com;
    location /.well-known/acme-challenge/ {
        alias /var/www/challenges/;
        try_files $uri @forward_https;
    }
    location @forward_https {
        return 301   https://www.example.com$request_uri;
    }
    location / {
        return 301   https://www.example.com$request_uri;
    }
}

使用此配置,如果 /var/www/challenges/ 为空,您的 HTTP 服务器将表现得好像未指定 /.well-known/acme-challenge/ 位置。

DNS 挑战

如果使用 DNS 挑战,以下变量定义了如何实现挑战:

  • acme_certificate_dns_provider:必须是 route53hosttechns1 之一。每个都需要更多信息:

    • 对于 route53(Amazon Route 53),必须传递凭据作为 acme_certificate_aws_access_keyacme_certificate_aws_secret_key
    • 对于 hosttech(hosttech GmbH,需要外部 hosttech_dns_record 模块)。
    • 对于 ns1ns1.com),您的 API 账户的密钥必须作为 acme_certificate_ns1_secret_key 传递。同时依赖外部模块 ns1_record。假设默认目录结构和设置,您可能需要下载两个文件到执行 playbook 的机器:
    curl --create-dirs -L -o ~/.ansible/plugins/module_utils/ns1.py https://github.com/ns1/ns1-ansible-modules/raw/master/module_utils/ns1.py
    curl --create-dirs -L -o ~/.ansible/plugins/modules/ns1_record.py https://github.com/ns1/ns1-ansible-modules/raw/master/library/ns1_record.py
    

请注意,DNS 挑战代码并不完美。对 Route 53、Hosttech 和 NS1 的功能已进行了测试。需要注意的一点是,该代码试图通过取用点分隔的最后两个部分来提取域名的 DNS 区域。例如,对于 .co.uk 域或其他嵌套区域,这将失败。

可以通过添加 tasks/dns-NAME-create.ymltasks/dns-NAME-cleanup.yml 文件,内容类似于现有文件,来支持更多 DNS 提供商。

账户密钥转换

请注意,此 Ansible 角色期望 Let's Encrypt 账户密钥为 PEM 格式,而非由 官方 Let's Encrypt 客户端 certbot 使用的 JWK 格式。如果您使用官方客户端创建了账户密钥,并希望使用此密钥与此 Ansible 角色配合,则需要进行转换。可以使用的一个工具是 pem-jwk

生成的文件

假设您为 www.example.com 创建了 TLS 密钥。您必须将相关文件复制到您的 Web 服务器。Ansible 角色创建了以下文件:

  • keys/www.example.com.key:这是证书的私钥。确保没有人可以访问它。
  • keys/www.example.com.pem:这是证书本身。
  • keys/www.example.com-chain.pem:这是所需的中间证书,用于建立信任路径。
  • keys/www.example.com.cnf:这是用于创建证书签名请求的 OpenSSL 配置文件。可以安全地删除。
  • keys/www.example.com.csr:这是用于获取证书的证书签名请求。可以安全地删除。
  • keys/www.example.com-fullchain.pem:这是与中间证书结合的证书。
  • keys/www.example.com-rootchain.pem:这是与根证书结合的中间证书。您可能需要此文件以便进行 OCSP stapling。
  • keys/www.example.com-root.pem:这是 Let's Encrypt 的根证书。

要配置您的 Web 服务器,您需要私钥 (keys/www.example.com.key),以及合并在一个文件中的证书和中间证书 (keys/www.example.com-fullchain.pem),或者将证书和中间证书作为两个单独的文件 (keys/www.example.com.pemkeys/www.example.com-chain.pem)。如果您想使用 OCSP stapling,您还需要 keys/www.example.com-rootchain.pem

要将这些文件放到您的 Web 服务器上,您可以添加如下任务:

- name: 复制私钥
  copy:
    src: keys/{{ item }}
    dest: /etc/ssl/private/
    owner: root
    group: root
    mode: "0400"
  with_items:
  - www.example.com.key
  notify: reload webserver

- name: 复制证书
  copy:
    src: keys/{{ item }}
    dest: /etc/ssl/server-certs/
    owner: root
    group: root
    mode: "0444"
  with_items:
  - www.example.com-rootchain.pem
  - www.example.com-fullchain.pem
  - www.example.com.pem
  notify: reload webserver

Web 服务器配置可以如下所示(对于 nginx):

server {
    listen www.example.com:443 ssl;  # IPv4: 监听 www.example.com 指向的 IP
    listen [::]:443 ssl;             # IPv6: 监听本地主机
    server_name www.example.com;
    
    # 仅允许 TLS 1.0 和 1.2,并有很少的加密套件选择。
    # 根据 SSL Lab 的 SSL 服务器测试,这将阻止:
    #   - Android 2.3.7
    #   - Windows XP 下的 IE 6 和 8
    #   - Java 6、7 和 8
    # 如果这对您不可接受,请选择其他加密套件列表。可以参考
    # 例如:https://wiki.mozilla.org/Security/Server_Side_TLS
    ssl_protocols TLSv1.2 TLSv1;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "-ALL !ADH !aNULL !EXP !EXPORT40 !EXPORT56 !RC4 !3DES !eNULL !NULL !DES !MD5 !LOW ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES256-SHA384 ECDHE-RSA-AES256-SHA384 DHE-RSA-AES256-SHA256 ECDHE-ECDSA-AES256-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES256-SHA";
    
    # 发送给浏览器的证书链,以及私钥。
    # 确保您的私钥仅在配置加载期间可由 Web 服务器访问
    # (默认情况下以 root 用户身份加载)。
    ssl_certificate /etc/ssl/server-certs/www.example.com-fullchain.pem;
    ssl_certificate_key /etc/ssl/private/www.example.com.key;
    
    # 对于 OCSP stapling,我们需要一个 DNS 解析器。这里指定的仅是公共的 Quad9 和
    # Google DNS 服务器;建议在前面加上您主机提供商的 DNS 服务器。您通常可以在
    # Web 服务器的 /etc/resolv.conf 中找到它们的 IP。
    resolver 9.9.9.9 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 10s;
    
    # 启用 OCSP stapling,Nginx 将自动处理 OCSP 数据的检索。
    # 有关 OCSP stapling 的详细信息,请参见 https://wiki.mozilla.org/Security/Server_Side_TLS#OCSP_Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/ssl/server-certs/www.example.com-rootchain.pem;
    
    # 启用 SSL 会话缓存。根据您网站的使用情况调整数字。
    ssl_session_cache shared:SSL:50m;
    ssl_session_timeout 30m;
    ssl_session_tickets off;
    
    # 您应该仅在使用适当证书的情况下使用 HSTS;Let's Encrypt 的证书是可以的,自签名的则不可以。
    # 有关更多详细信息,请参阅 MozillaWiki:
    # https://wiki.mozilla.org/Security/Server_Side_TLS#HSTS:_HTTP_Strict_Transport_Security
    add_header Strict-Transport-Security "max-age=3155760000;";
    
    charset utf-8;
    
    access_log  /var/log/nginx/www.example.com.log combined;
    error_log  /var/log/nginx/www.example.com.log error;
    
    location / {
        root   /var/www/www.example.com;
        index  index.html;
    }
}

依赖关系

此角色不依赖其他角色。

示例 Playbook

此角色可以如下使用。请注意,它为多个证书获取,并为所有证书全局定义了使用的变量:

---
- name: 获取 Web 服务器的证书
  hosts: webserver
  vars:
    acme_certificate_acme_account: 'keys/acme-account.key'
    acme_certificate_acme_email: '[email protected]'
    # 对于 HTTP 挑战:
    acme_certificate_server_location: '/var/www/challenges/'
    acme_certificate_http_challenge_user: root
    acme_certificate_http_challenge_group: http
    acme_certificate_http_challenge_folder_mode: "0750"
    acme_certificate_http_challenge_file_mode: "0640"
    # 对于使用 route53 的 DNS 挑战:
    acme_certificate_dns_provider: route53
    acme_certificate_aws_access_key: REPLACE_WITH_YOUR_ACCESS_KEY
    acme_certificate_aws_secret_key: REPLACE_WITH_YOUR_SECRET_KEY
    # 对于使用 ns1 的 DNS 挑战:
    # acme_certificate_dns_provider: ns1
    # acme_certificate_ns1_secret_key: REPLACE_WITH_YOUR_SECRET_KEY
  roles:
    - role: acme_certificate
      acme_certificate_domains: ['example.com', 'www.example.com']
      # 使用 DNS 挑战:
      acme_certificate_challenge: dns-01
      # 证书文件将存储在:
      #    keys/example.com.key  (私钥)
      #    keys/example.com.csr  (证书签名请求)
      #    keys/example.com.pem  (证书)
      #    keys/example.com.cnf  (用于 CSR 创建的 OpenSSL 配置 -- 可以安全删除)
      #    keys/example.com-chain.pem  (中间证书)
      #    keys/example.com-fullchain.pem  (与中间证书结合的证书)
      #    keys/example.com-root.pem  (根证书)
      #    keys/example.com-rootchain.pem  (与根证书结合的中间证书)
    - role: acme_certificate
      acme_certificate_domains: ['another.example.com']
      acme_certificate_key_name: 'another.example.com-rsa'
      acme_certificate_key_length: 4096
      # 使用 HTTP 挑战:
      acme_certificate_challenge: http-01
      # 证书文件将存储在:
      #    keys/another.example.com-rsa.key  (私钥)
      #    keys/another.example.com-rsa.csr  (证书签名请求)
      #    keys/another.example.com-rsa.pem  (证书)
      #    keys/another.example.com-rsa.cnf  (用于 CSR 创建的 OpenSSL 配置 -- 可以安全删除)
      #    keys/another.example.com-rsa-chain.pem  (中间证书)
      #    keys/another.example.com-rsa-fullchain.pem  (与中间证书结合的证书)
      #    keys/another.example.com-rsa-root.pem  (根证书)
      #    keys/another.example.com-rsa-rootchain.pem  (与根证书结合的中间证书)
    - role: acme_certificate
      acme_certificate_domains: ['another.example.com']
      acme_certificate_key_name: 'another.example.com-ecc'
      acme_certificate_algorithm: 'p-256'
      # 使用 HTTP 挑战(挑战的默认值为 http-01)。
      # 证书文件将存储在:
      #    keys/another.example.com-ecc.key  (私钥)
      #    keys/another.example.com-ecc.csr  (证书签名请求)
      #    keys/another.example.com-ecc.pem  (证书)
      #    keys/another.example.com-ecc.cnf  (用于 CSR 创建的 OpenSSL 配置 -- 可以安全删除)
      #    keys/another.example.com-ecc-chain.pem  (中间证书)
      #    keys/another.example.com-ecc-fullchain.pem  (与中间证书结合的证书)
      #    keys/another.example.com-ecc-root.pem  (根证书)
      #    keys/another.example.com-ecc-rootchain.pem  (与根证书结合的中间证书)

许可证

MIT 许可证(MIT)

版权所有 (c) 2018-2020 Felix Fontein

特此免费授权任何获取本软件及其相关文档文件(以下简称“软件”)的人员,在不受限制的情况下使用、复制、修改、合并、出版、分发、再授权和/或销售该软件的副本,并许可他人遵守以下条件:

上述版权声明和本许可声明应包含在软件的所有副本或重要部分中。

本软件是按“原样”提供的,没有任何种类的担保,无论是明示的还是暗示的,包括但不限于对适销性、适合特定目的和不侵权的担保。在任何情况下,作者或版权持有人均不对因使用本软件或其他交易而引起的任何索赔、损害或其他责任承担责任,无论是在合同、侵权或其他方面。

作者信息

该角色的主页为 https://github.com/felixfontein/acme-certificate/。请使用问题追踪器报告问题。

关于项目

Wrapper of Ansible's included acme_certificate module, whose aim is that almost no code is executed on the webserver. Requires the Python cryptography library as well as the OpenSSL binary installed locally and available on executable path.

安装
ansible-galaxy install felixfontein.acme_certificate
许可证
mit
下载
381
拥有者