community.crypto.acme_certificate 模块 – 使用 ACME 协议创建 SSL/TLS 证书
注意
此模块是 community.crypto 集合(版本 2.22.3)的一部分。
如果您正在使用 ansible
包,则可能已经安装了此集合。它不包含在 ansible-core
中。要检查是否已安装,请运行 ansible-galaxy collection list
。
要安装它,请使用:ansible-galaxy collection install community.crypto
。您需要其他要求才能使用此模块,有关详细信息,请参阅 要求。
要在剧本中使用它,请指定:community.crypto.acme_certificate
。
概要
使用支持 ACME 协议的 CA 创建和续订 SSL/TLS 证书,例如 Let’s Encrypt 或 Buypass。当前实现支持
http-01
、dns-01
和tls-alpn-01
挑战。要使用此模块,必须执行两次。可以在同一次运行中作为两个不同的任务执行,也可以在两次运行期间执行。请注意,第一次运行的输出需要记录下来,并作为模块参数
data
传递给第二次运行。在这两个任务之间,您必须通过任何必要的方式完成所选挑战所需的步骤。对于
http-01
,这意味着在目标 Web 服务器上创建必要的挑战文件。对于dns-01
,必须创建必要的 DNS 记录。对于tls-alpn-01
,必须创建并提供必要的证书。执行这些步骤不是此模块的责任。有关如何完成这些挑战的详细信息,您可能需要阅读 主要的 ACME 规范 和 TLS-ALPN-01 规范。此外,请考虑为此模块提供的示例。
该模块包括对根据 RFC 8738 的 IP 标识符的实验性支持。
要求
执行此模块的主机上需要以下要求。
openssl 或 cryptography >= 1.5
ipaddress
参数
参数 |
注释 |
---|---|
与此帐户关联的电子邮件地址。 它将用于证书过期警告。 请注意,当 |
|
ACME 帐户 RSA 或椭圆曲线密钥的内容。 与 如果未使用 警告:内容将写入临时文件,该文件将在模块完成时被 Ansible 删除。由于这是一个重要的私钥 - 它可用于更改帐户密钥,或在不知道其私钥的情况下吊销您的证书 - 这可能是不可接受的。 如果使用 |
|
用于解码帐户密钥的密码。 注意: |
|
包含 ACME 帐户 RSA 或椭圆曲线密钥的文件的路径。 可以使用 community.crypto.openssl_privatekey 或 community.crypto.openssl_privatekey_pipe 模块创建私钥。如果所需的 (cryptography) 不可用,也可以直接使用 与 如果未使用 |
|
如果指定,则假定帐户 URI 与给定值相同。如果帐户密钥与此帐户不匹配,或者不存在具有此 URI 的帐户,则模块将失败。 |
|
要使用的 ACME 目录。这是访问 ACME CA 服务器 API 的入口点 URL。 出于安全原因,默认设置为 Let's Encrypt 暂存服务器(用于 ACME v1 协议)。这将创建技术上正确但不受信任的证书。 对于 Let's Encrypt,所有暂存端点都可以在这里找到:https://letsencrypt.openssl.ac.cn/docs/staging-environment/。对于 Buypass,所有端点都可以在这里找到:https://community.buypass.com/t/63d4ay/buypass-go-ssl-endpoints 对于 Let's Encrypt,ACME v2 的生产目录 URL 为 https://acme-v02.api.letsencrypt.org/directory。 对于 Buypass,ACME v2 和 v1 的生产目录 URL 为 https://api.buypass.com/acme/directory。 对于 ZeroSSL,ACME v2 的生产目录 URL 为 https://acme.zerossl.com/v2/DV90。 对于 Sectigo,ACME v2 的生产目录 URL 为 https://acme-qa.secure.trust-provider.com/v2/DV。 此模块的注释包含此模块已测试过的 ACME 服务列表。 |
|
端点的 ACME 版本。 对于经典的 Let's Encrypt 和 Buypass ACME 端点,必须为 值 选项
|
|
在使用 默认值是从 仅当 |
|
如果指定,则中间证书将写入此文件。 |
|
要执行的质询。 如果设置为 选项
|
|
包含新证书 CSR 的文件。 可以使用 community.crypto.openssl_csr 创建。 CSR 可能包含多个使用者备用名称,但每个名称都会导致一个单独的质询,必须满足该质询才能签署 CSR。 注意:用于创建 CSR 的私钥不能是帐户密钥。从安全角度来看,这是一个坏主意,并且 CA 不应接受 CSR。在这种情况下,ACME 服务器应返回错误。 必须指定 |
|
新证书的 CSR 的内容。 可以使用 community.crypto.openssl_csr_pipe 创建。 CSR 可能包含多个使用者备用名称,但每个名称都会导致一个单独的质询,必须满足该质询才能签署 CSR。 注意:用于创建 CSR 的私钥不能是帐户密钥。从安全角度来看,这是一个坏主意,并且 CA 不应接受 CSR。在这种情况下,ACME 服务器应返回错误。 必须指定 |
|
在颁发证书后或在颁发证书失败时,停用身份验证对象 (authz)。 身份验证对象绑定到帐户密钥,并在一定时间内保持有效,并且可以用于颁发证书,而无需重新验证域。这可能是一个安全问题。 选项
|
|
证书的目标文件。 如果未指定 |
|
完整链(即,证书后跟中间证书链)的目标文件。 如果未指定 |
|
确定是否根据 ACME ARI 草案 3 请求续订现有证书。 仅当
通常,如果您知道 ACME 服务支持 ARI 扩展的兼容草案(或最终版本,一旦发布),则应使用 对于已存在订单的证书,ACME 服务器可能会拒绝使用 选项
|
|
一个布尔值,指示模块是否应在必要时创建帐户并更新其联系人数据。 如果您想使用 community.crypto.acme_account 模块来管理您的帐户,并避免在使用 community.crypto.acme_account 更改帐户密钥后意外使用旧密钥创建新帐户,请将其设置为 如果设置为 选项
|
|
证书必须保持有效的天数。如果 要确保在任何情况下都续订证书,可以使用 默认值: |
|
Ansible 应等待来自 ACME API 的响应的时间。 此超时应用于所有 HTTP(S) 请求(HEAD、GET、POST)。 默认值: |
|
设置为 选项
|
|
允许指定选择(备用)信任链的条件。 将逐个处理条件列表,直到找到与条件匹配的链。如果找到这样的链,则模块将使用它而不是默认链。 如果一个条件匹配多个链,则将返回第一个匹配的链。顺序由 ACME 服务器返回的 每个条件可以包含多个不同的条件,例如 此选项只能与 |
|
检查 AuthorityKeyIdentifier 扩展。这是基于中间证书颁发者的私钥的标识符。 标识符必须采用 |
|
允许指定链中证书的颁发者必须具有的部分才能被选择。 如果 一个示例值可以是 |
|
允许指定链中证书的主体必须具有的部分才能被选择。 如果 一个示例值可以是 |
|
检查 SubjectKeyIdentifier 扩展。这是基于中间证书私钥的标识符。 标识符必须采用 |
|
确定将测试链中的哪些证书。
选项
|
|
确定要使用的加密后端。 默认选择是 如果设置为 如果设置为 选项
|
|
对 ACME 目录的调用是否将验证 TLS 证书。 警告: 仅应出于测试目的将其设置为 选项
|
属性
属性 |
支持 |
描述 |
---|---|---|
操作组: community.crypto.acme, acme |
在 |
|
支持:完全 |
可以在 |
|
支持: 无 |
当处于 diff 模式时,将返回已更改(或可能需要在 |
|
支持:完全 |
使用 Ansible 严格的文件操作函数,以确保正确的权限并避免数据损坏。 |
注意
注意
必须至少指定
dest
和fullchain_dest
中的一个。此模块包含基本的帐户管理功能。 如果您希望对 ACME 帐户进行更多控制,请使用 community.crypto.acme_account 模块,并使用
modify_account
选项禁用此模块的帐户管理。在 Ansible 2.6 之前,此模块名为
letsencrypt
。 用法没有改变。尽管默认值经过选择,以便该模块可以与 Let’s Encrypt CA 一起使用,但原则上,该模块可以与任何提供 ACME 端点的 CA 一起使用,例如 Buypass Go SSL。
到目前为止,ACME 模块仅由开发人员针对 Let’s Encrypt(测试和生产)、Buypass(测试和生产)、ZeroSSL(生产)和 Pebble 测试服务器进行了测试。 我们收到了社区反馈,表明它们也适用于 Sectigo ACME Service for InCommon。 如果您在使用其他 ACME 服务器时遇到问题,请创建一个问题,以帮助我们支持它。 如果您发现未提及的 ACME 服务器有效,也欢迎提供反馈。
如果可以使用足够新版本的
cryptography
库(有关详细信息,请参阅“要求”),则将使用它而不是openssl
二进制文件。 可以使用select_crypto_backend
选项显式禁用或启用此选项。 请注意,使用openssl
二进制文件会更慢且安全性更低,因为私钥内容始终必须存储在磁盘上(请参阅account_key_content
)。
另请参阅
另请参阅
- Let’s Encrypt 文档
Let’s Encrypt 证书颁发机构的文档。 提供例如速率限制等有用信息。
- Buypass Go SSL
Buypass 证书颁发机构的文档。 提供例如速率限制等有用信息。
- 自动证书管理环境 (ACME)
ACME 协议规范 (RFC 8555)。
- ACME TLS ALPN 质询扩展
tls-alpn-01
质询的规范 (RFC 8737)。- community.crypto.acme_challenge_cert_helper
帮助准备
tls-alpn-01
质询。- community.crypto.openssl_privatekey
可用于创建私钥(用于证书和帐户)。
- community.crypto.openssl_privatekey_pipe
可用于创建私钥,而无需将其写入磁盘(用于证书和帐户)。
- community.crypto.openssl_csr
可用于创建证书签名请求 (CSR)。
- community.crypto.openssl_csr_pipe
可用于创建证书签名请求 (CSR),而无需将其写入磁盘。
- community.crypto.certificate_complete_chain
允许查找返回的完整链的根证书。
- community.crypto.acme_certificate_revoke
允许吊销证书。
- community.crypto.acme_account
允许创建、修改或删除 ACME 帐户。
- community.crypto.acme_inspect
允许调试问题。
- community.crypto.acme_certificate_deactivate_authz
允许停用(使无效)ACME v2 订单。
示例
### Example with HTTP challenge ###
- name: Create a challenge for sample.com using a account key from a variable.
community.crypto.acme_certificate:
account_key_content: "{{ account_private_key }}"
csr: /etc/pki/cert/csr/sample.com.csr
dest: /etc/httpd/ssl/sample.com.crt
register: sample_com_challenge
# Alternative first step:
- name: Create a challenge for sample.com using a account key from Hashi Vault.
community.crypto.acme_certificate:
account_key_content: >-
{{ lookup('community.hashi_vault.hashi_vault', 'secret=secret/account_private_key:value') }}
csr: /etc/pki/cert/csr/sample.com.csr
fullchain_dest: /etc/httpd/ssl/sample.com-fullchain.crt
register: sample_com_challenge
# Alternative first step:
- name: Create a challenge for sample.com using a account key file.
community.crypto.acme_certificate:
account_key_src: /etc/pki/cert/private/account.key
csr_content: "{{ lookup('file', '/etc/pki/cert/csr/sample.com.csr') }}"
dest: /etc/httpd/ssl/sample.com.crt
fullchain_dest: /etc/httpd/ssl/sample.com-fullchain.crt
register: sample_com_challenge
# perform the necessary steps to fulfill the challenge
# for example:
#
# - name: Copy http-01 challenge for sample.com
# ansible.builtin.copy:
# dest: /var/www/html/{{ sample_com_challenge['challenge_data']['sample.com']['http-01']['resource'] }}
# content: "{{ sample_com_challenge['challenge_data']['sample.com']['http-01']['resource_value'] }}"
# when: sample_com_challenge is changed and 'sample.com' in sample_com_challenge['challenge_data']
#
# Alternative way:
#
# - name: Copy http-01 challenges
# ansible.builtin.copy:
# dest: /var/www/{{ item.key }}/{{ item.value['http-01']['resource'] }}
# content: "{{ item.value['http-01']['resource_value'] }}"
# loop: "{{ sample_com_challenge.challenge_data | dict2items }}"
# when: sample_com_challenge is changed
- name: Let the challenge be validated and retrieve the cert and intermediate certificate
community.crypto.acme_certificate:
account_key_src: /etc/pki/cert/private/account.key
csr: /etc/pki/cert/csr/sample.com.csr
dest: /etc/httpd/ssl/sample.com.crt
fullchain_dest: /etc/httpd/ssl/sample.com-fullchain.crt
chain_dest: /etc/httpd/ssl/sample.com-intermediate.crt
data: "{{ sample_com_challenge }}"
### Example with DNS challenge against production ACME server ###
- name: Create a challenge for sample.com using a account key file.
community.crypto.acme_certificate:
account_key_src: /etc/pki/cert/private/account.key
account_email: [email protected]
src: /etc/pki/cert/csr/sample.com.csr
cert: /etc/httpd/ssl/sample.com.crt
challenge: dns-01
acme_directory: https://acme-v01.api.letsencrypt.org/directory
# Renew if the certificate is at least 30 days old
remaining_days: 60
register: sample_com_challenge
# perform the necessary steps to fulfill the challenge
# for example:
#
# - name: Create DNS record for sample.com dns-01 challenge
# community.aws.route53:
# zone: sample.com
# record: "{{ sample_com_challenge.challenge_data['sample.com']['dns-01'].record }}"
# type: TXT
# ttl: 60
# state: present
# wait: true
# # Note: route53 requires TXT entries to be enclosed in quotes
# value: "{{ sample_com_challenge.challenge_data['sample.com']['dns-01'].resource_value | community.dns.quote_txt(always_quote=true) }}"
# when: sample_com_challenge is changed and 'sample.com' in sample_com_challenge.challenge_data
#
# Alternative way:
#
# - name: Create DNS records for dns-01 challenges
# community.aws.route53:
# zone: sample.com
# record: "{{ item.key }}"
# type: TXT
# ttl: 60
# state: present
# wait: true
# # Note: item.value is a list of TXT entries, and route53
# # requires every entry to be enclosed in quotes
# value: "{{ item.value | map('community.dns.quote_txt', always_quote=true) | list }}"
# loop: "{{ sample_com_challenge.challenge_data_dns | dict2items }}"
# when: sample_com_challenge is changed
- name: Let the challenge be validated and retrieve the cert and intermediate certificate
community.crypto.acme_certificate:
account_key_src: /etc/pki/cert/private/account.key
account_email: [email protected]
src: /etc/pki/cert/csr/sample.com.csr
cert: /etc/httpd/ssl/sample.com.crt
fullchain: /etc/httpd/ssl/sample.com-fullchain.crt
chain: /etc/httpd/ssl/sample.com-intermediate.crt
challenge: dns-01
acme_directory: https://acme-v01.api.letsencrypt.org/directory
remaining_days: 60
data: "{{ sample_com_challenge }}"
when: sample_com_challenge is changed
# Alternative second step:
- name: Let the challenge be validated and retrieve the cert and intermediate certificate
community.crypto.acme_certificate:
account_key_src: /etc/pki/cert/private/account.key
account_email: [email protected]
src: /etc/pki/cert/csr/sample.com.csr
cert: /etc/httpd/ssl/sample.com.crt
fullchain: /etc/httpd/ssl/sample.com-fullchain.crt
chain: /etc/httpd/ssl/sample.com-intermediate.crt
challenge: tls-alpn-01
remaining_days: 60
data: "{{ sample_com_challenge }}"
# We use Let's Encrypt's ACME v2 endpoint
acme_directory: https://acme-v02.api.letsencrypt.org/directory
acme_version: 2
# The following makes sure that if a chain with /CN=DST Root CA X3 in its issuer is provided
# as an alternative, it will be selected. These are the roots cross-signed by IdenTrust.
# As long as Let's Encrypt provides alternate chains with the cross-signed root(s) when
# switching to their own ISRG Root X1 root, this will use the chain ending with a cross-signed
# root. This chain is more compatible with older TLS clients.
select_chain:
- test_certificates: last
issuer:
CN: DST Root CA X3
O: Digital Signature Trust Co.
when: sample_com_challenge is changed
返回值
通用返回值记录在此处,以下是此模块独有的字段
Key |
描述 |
---|---|
ACME 帐户 URI。 返回: changed |
|
当 有关详细信息,请参阅 RFC8555 的第 7.4.2 节。 返回: 当检索到证书并且 |
|
叶子证书本身,采用 PEM 格式。 返回: always |
|
证书链,不包括根证书,作为串联的 PEM 证书。 返回: always |
|
证书链,不包括根证书,但包括叶子证书,作为串联的 PEM 证书。 返回: always |
|
ACME 授权数据。 将标识符映射到 ACME 授权对象。 请参阅 https://tools.ietf.org/html/rfc8555#section-7.1.4。 返回: changed 示例: |
|
证书保持有效的剩余天数。 返回: success |
|
每个标识符/质询类型的质询数据。 自 Ansible 2.8.5 起,仅返回尚未有效的质询。 返回: changed |
|
对于每个标识符,提供一个质询类型字典,该字典映射到质询数据。 此字典中的键是标识符。 请注意,这些键不是有效的 Jinja2 标识符。 返回: changed |
|
每种质询类型的数据。 此字典中的键是质询类型。 请注意,这些键不是有效的 Jinja2 标识符。 返回: changed |
|
质询的完整 DNS 记录名称。 返回: changed 并且质询为 示例: |
|
必须为验证创建的质询资源。 返回: changed 示例: |
|
资源必须产生的值以进行验证。 对于 对于 返回: changed 示例: |
|
如果质询为 自 Ansible 2.8.5 起,仅返回尚未有效的质询。 返回: changed |
|
ACME 最终 URI。 返回: changed |
|
ACME 订单 URI。 返回: changed |