systemli.letsencrypt
Ansible角色以获取Let's Encrypt SSL证书
该角色旨在使用HTTP或DNS挑战来请求Let's Encrypt的SSL证书,采用其ACME API。
功能:
- 安装并配置certbot及DNS挑战辅助脚本
- 支持HTTP和DNS挑战
- 对于HTTP挑战,支持身份验证插件
apache
、nginx
、standalone
和webroot
- 对于HTTP挑战,支持身份验证插件
- DNS挑战仅使用专用区域用于ACME挑战令牌,从而降低动态更新的安全风险。相关概念可在这里找到。
- 证书续订时使用后钩子或自定义后钩子命令重启服务
- 使用专用系统组对证书进行权限控制
支持的发行版:
- Debian 11 (Bullseye)
- Debian 12 (Bookworm)
测试环境:
- Debian 11 (Bullseye)
- Debian 12 (Bookworm)
- CentOS7
- Ubuntu 2204 (Jammy Jellyfish)
它执行以下操作:
当
letsencrypt_setup
为True(默认值)时,此角色将:- 安装certbot
- 在Let's Encrypt注册一个账户
- 安装DNS挑战所需的文件/密钥
- 创建系统组'letsencrypt'
当以填充的变量
letsencrypt_cert
调用时:- 通过Let's Encrypt的ACME API请求SSL证书,可以使用HTTP挑战或DNS挑战
- 可选地设置证书续订的后钩子(以在续订后重启所需服务)
- 可选地将系统用户添加到'letsencrypt'系统组,以授予他们对SSL证书及其私钥的读取访问权限
工作原理(示例)
- 安装certbot
ansible-playbook site.yml -l localhost -t letsencrypt
- 通过HTTP挑战和
webroot
身份验证器创建证书(续订时重启服务'apache2'):ansible-playbook site.yml -l localhost -t letsencrypt -e '{"letsencrypt_cert":{"name":"sub.example.org","domains":["sub.example.org"],"challenge":"http","http_auth":"webroot","webroot_path":"/var/www/sub.example.org","services":["apache2"]}}'
- 通过DNS挑战创建证书(向用户'Debian-exim'授予证书读取访问权限,续订时重启服务'exim4'和'dovecot'):
ansible-playbook site.yml -l localhost -t letsencrypt -e '{"letsencrypt_cert":{"name":"sub2","domains":["sub2.example.org","sub2.another.example.org"],"challenge":"dns","services":["dovecot","exim4"],"users":["Debian-exim"]}}'
- 通过HTTP挑战和
standalone
身份验证器创建证书(在续订时重用相同的私钥并运行自定义后钩子脚本):ansible-playbook site.yml -l localhost -t letsencrypt -e '{"letsencrypt_cert":{"name":"sub3","domains":["sub3.example.org"],"challenge":"http","http_auth":"standalone","reuse_key":True,"post_hook":"/usr/local/bin/cert-post-hook.sh"}}'
变量letsencrypt_cert
的预期结构
变量letsencrypt_cert
应为字典:
letsencrypt_opts_extra: "--register-unsafely-without-email"
letsencrypt_cert:
name: sub.example.org
domains:
- sub.example.org
challenge: http
http_auth: webroot
webroot_path: /var/www/sub.example.org
services:
- apache2
或者:
letsencrypt_cert:
name: sub2
domains:
- sub2.example.org
- sub2.another.example.org
challenge: dns
services:
- dovecot
- exim4
users:
- Debian-exim
或者:
letsencrypt_cert:
name: sub3
domains:
- sub3.example.org
challenge: http
http_auth: standalone
reuse_key: True
post_hook: "/usr/local/bin/cert-post-hook.sh"
letsencrypt_cert:
name: sub3
domains:
- sub3.example.org
challenge: http
http_auth: standalone
reuse_key: True
deploy_hook: "/usr/local/bin/cert-post-hook.sh"
字典支持以下键:
name
:证书名称 [可选]domains
:证书的域名列表 [必需]challenge
:'http'或'dns' [必需]- 对于挑战'http':
http_auth
:'webroot'、'apache'或'nginx' [可选,默认'webroot']- 对于http_auth 'webroot':
webroot_path
[可选,默认'/var/www']
- 对于http_auth 'webroot':
- 对于挑战'http':
services
:后钩子中需要重启的服务列表 [可选]reuse_key
:在证书续订时重用相同的私钥。'True'或'False'(默认'False')post_hook
:获取/续订证书后要执行的自定义后钩子 [可选]deploy_hook
:获取/续订证书成功后要执行的自定义部署钩子 [可选]renew_hook
:在每次续订证书后执行一次的自定义续订钩子 [可选]users
:要添加到'sletsencrypt'系统组的用户列表 [可选]
一般前提条件
该角色负责安装certbot并使用HTTP或DNS挑战请求SSL证书。它不会安装或配置所需的基础设施(即Apache网络服务器或DNS服务器)。
该角色仅与Ansible 2.2进行测试。不保证在Ansible早期版本上正常运行。
HTTP挑战
要求:
- 请求证书的域名必须指向系统
- 对于http_auth 'apache',系统上必须安装(并配置)Apache2
- 对于http_auth 'nginx',系统上必须安装(并配置)NGINX
DNS挑战
要求:
- 一台带有专用区域的DNS服务器,仅用于ACME DNS挑战。该区域必须允许对TXT记录进行动态DNS更新(NSUPDATE)。
- 所有请求证书的域名的CNAME记录必须指向
sub.example.org._le.example.org
(位于ACME DNS挑战的专用区域内)。 - DNS更新密钥及私有DNS更新密钥的内容需要在Ansible变量
letsencrypt_ddns_key
和letsencrypt_ddns_privkey
中提供(最好放在一个保险箱里)。
该角色安装了一个用于DNS挑战的辅助脚本到/usr/local/bin/certbot-dns-hook.sh
。该脚本将在DNS挑战期间将验证令牌添加到sub.example.org._le.example.org
的TXT记录,并在之后将其删除。
DNS挑战的通配符支持
通过DNS挑战获得通配符证书应该可以开箱即用。
配置bind9以进行DNS挑战
(另一个选项是使用acme-dns服务器来实现)
生成一个动态更新密钥:
cd /etc/bind/keys
dnssec-keygen -a HMAC-SHA512 -b 512 -n USER _le.example.org_ddns_update
chown -R bind:bind /etc/bind/keys
将密钥添加到bind配置中(例如在/etc/bind/named.conf.options
中):
key "_le.example.org_ddns_update" {
algorithm hmac-sha512;
secret "...";
};
创建用于动态更新的区域:
$ORIGIN .
$TTL 86400 ; 1天
_le.example.org IN SOA ns1.example.org. postmaster.example.org. (
2017061501 ; serial
86400 ; refresh (1天)
3600 ; retry (1小时)
2419200 ; expire (4周)
86400 ; minimum (1天)
)
NS ns1.example.org.
NS ns2.example.org.
TXT "v=spf1 -all"
并在bind配置中进行配置(例如在/etc/bind/named.conf.local
):
zone "_le.example.org" {
type master;
file "/etc/bind/zones/db._le.example.org";
update-policy { grant _le.example.org_ddns_update wildcard *._le.example.org. TXT; };
};
/etc/letsencrypt/keys/ddns_update.key
的格式(来自bind)
key "<key>" {
algorithm HMAC-SHA512;
secret "<key>";
};
/etc/letsencrypt/keys/ddns_update.private
的格式
Private-key-format: v1.3
Algorithm: 165 (HMAC_SHA512)
Key: <key>
Bits: AAA=
Created: 20181017144534
Publish: 20181017144534
Activate: 20181017144534
Ansible变量默认值
# 执行设置步骤;设置为false以禁用
letsencrypt_setup: True
# 提供现有账户数据以进行复制
letsencrypt_account: ""
# letsencrypt_account:
# hash: 1234567890abcdef1234567890abcdef
# id: 123456789
# creation_host: localhost
# creation_dt: 2020-12-13T13:12:00Z
# private_key:
# n: 1234
# e: 5678
# d: 90ab
# p: cdef
# q: 1234
# dp: 5678
# dq: 90ab
# qi: cdef
# kty: RSA
# 设置与Let's Encrypt账户关联的电子邮件地址
letsencrypt_account_email: ""
# 默认HTTP挑战的身份验证器('webroot'或'apache')
letsencrypt_http_auth: webroot
# 身份验证器'webroot'的默认webroot路径
letsencrypt_webroot_path: /var/www
# 安装DNS挑战辅助脚本和DNS更新密钥
letsencrypt_dns_challenge: yes
# 动态DNS区域更新的设置
# letsencrypt_ddns_server: ""
# letsencrypt_ddns_zone: ""
# letsencrypt_ddns_key: ""
# letsencrypt_ddns_privkey: ""
# 创建系统组'letsencrypt'以访问证书
letsencrypt_group: yes
# 在证书续订时重用私钥?
letsencrypt_reuse_key: False
# 允许部分名称?
letsencrypt_subset_names: True
# 为certbot设置全局额外命令行选项
letsencrypt_opts_extra: ""
# 设置letsencrypt目录的路径(不带尾随"/" !!)
letsencrypt_directory: /etc/letsencrypt
测试
出于测试目的,可以设置变量letsencrypt_test
。如果设置为True,该角色将使用Let's Encrypt测试服务器进行账户创建和证书获取。
在开发和测试角色时,我们使用Molecule和Vagrant/Github Actions。在本地环境中,可以轻松地使用以下命令测试角色:
molecule test
许可
该Ansible角色根据GNU GPLv3许可。
作者
版权 2017-2019 systemli.org (https://www.systemli.org/)