使用 certbot 代替 acme.sh 免费申请 wildcard 通配符证书和自动更新实践小结

因为 Google Chrome 和运营商劫持干扰访问者体验的努力推动了大型网站加速应用全站 HTTPS,而 Let’s Encrypt 这个项目通过自动化把配置和维护 HTTPS 变得更加简单,Let’s Encrypt 设计了一个 ACME 协议目前版本是 v2,并在 2018 年支持通配符证书 Wildcard Certificate Support is Live 。官网主推的客户端是 Certbot ,任何人都可以基于 ACME 协议实现一个客户端,比如大名鼎鼎的 acme.sh 。本文主要使用 certbot-dns-route53 插件为例,由于 certbot 官方 DNS Plugins 插件支持有限,如果你需要支持 aliyun/tencentyun/godaddy dns 可以参考 certbot-letencrypt-wildcardcertificates-alydns-au ,随着 Docker 容器化和 K8S(Kubernetes) 的进击,相信会促进 certbot 多样化玩法。

使用 certbot 代替 acme.sh 免费申请 wildcard 通配符证书和自动更新实践小结

更新历史

2020 年 02 月 19 日 - 初稿

阅读原文 - https://wsgzao.github.io/post/certbot/

基础知识

关于 HTTPS

引维基百科的说法

超文本传输安全协议(英语:Hypertext Transfer Protocol Secure,缩写:HTTPS)是一种网络安全传输协议。在计算机网络上,HTTPS 经由超文本传输协议进行通信,但利用 SSL/TLS 来对数据包进行加密。HTTPS 开发的主要目的,是提供对网络服务器的身份认证,保护交换数据的隐私与完整性

HTTPS 的主要思想是在不安全的网络上创建一安全信道,并可在使用适当的加密包和服务器证书可被验证且可被信任时,对窃听和中间人攻击提供合理防护。

HTTPS 的信任继承基于预先安装在浏览器中的证书颁发机构(如 Symantec、Comodo、GoDaddy 和 GlobalSign 等)(意即“我信任证书颁发机构告诉我应该信任的”)。因此,一个到某网站的 HTTPS 连接可被信任,当且且当:

  • 用户相信他们的浏览器正确实现了 HTTPS 且安装了正确的证书颁发机构;
  • 用户相信证书颁发机构仅信任合法的网站;
  • 被访问的网站提供了一个有效的证书,意即,它是由一个被信任的证书颁发机构签发的(大部分浏览器会对无效的证书发出警告);
  • 该证书正确地验证了被访问的网站(如,访问 https://example.com 时收到了给 example.com 而不是其他组织的证书);
  • 或者互联网上相关节点是值得信任的,或者用户相信本协议的加密层(TLS 或 SSL)不能被窃听者破坏。

HTTP 和 HTTPS 区别

HTTP 协议传输的数据都是未加密的,也就是明文的,因此使用 HTTP 协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了 SSL(Secure Sockets Layer)协议用于对 HTTP 协议传输的数据进行加密,从而就诞生了 HTTPS。简单来说,HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,要比 HTTP 协议安全。

HTTPS 和 HTTP 的区别主要如下:

  • HTTPS 协议需要到 CA 申请证书,一般免费证书较少,因而需要一定费用。
  • HTTP 是超文本传输协议,信息是明文传输,HTTPS 则是具有安全性的 SSL 加密传输协议。
  • HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
  • HTTP 的连接很简单,是无状态的;HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全。

Types of SSL Certificates for a Secure Business Website

关于 TLS/SSL

传输层安全协议(英语:Transport Layer Security,缩写:TLS),及其前身安全套接层(Secure Sockets Layer,缩写:SSL)是一种安全协议,目的是为互联网通信,提供安全及数据完整性保障

为什么要部署 HTTPS

说到底,就是 HTTPS 更安全。甚至为了安全,一个专业可靠的网站, HTTPS 是必须的。 Firefox 和 Chrome 都计划将没有配置 SSL 加密的 HTTP 网站标记为不安全(貌似 Firefox 50 已经这么干了),目前它们也正在联合其他相关的基金会与公司推动整个互联网 HTTPS 化,现在大家访问的一些主要的网站。如 Google 多年前就已经全部启用 HTTPS ,国内的淘宝、搜狗、知乎、百度等等也全面 HTTPS 了。甚至 Google 的搜索结果也正在给予 HTTPS 的网站更高的排名和优先收录权。

怎么部署 HTTPS

你只需要有一张被信任的 CA ( Certificate Authority )也就是证书授权中心颁发的 SSL 安全证书,并且将它部署到你的网站服务器上。一旦部署成功后,当用户访问你的网站时,浏览器会在显示的网址前加一把小绿锁,表明这个网站是安全的,当然同时你也会看到网址前的前缀变成了 HTTPS ,不再是 HTTP 了。

怎么获得 SSL 安全证书

理论上,我们自己也可以签发 SSL 安全证书,但是我们自己签发的安全证书不会被主流的浏览器信任,所以我们需要被信任的证书授权中心( CA )签发的安全证书。而一般的 SSL 安全证书签发服务都比较贵,比如 Godaddy 、 GlobalSign 等机构签发的证书一般都需要 20 美金一年甚至更贵,不过为了加快推广 HTTPS 的普及, EEF 电子前哨基金会、 Mozilla 基金会和美国密歇根大学成立了一个公益组织叫 ISRG ( Internet Security Research Group ),这个组织从 2015 年开始推出了 Let’s Encrypt 免费证书。这个免费证书不仅免费,而且还相当好用,所以我们就可以利用 Let’s Encrypt 提供的免费证书部署 HTTPS 了

Let’s Encrypt 简介

Let’s Encrypt 是 一个叫 ISRG ( Internet Security Research Group ,互联网安全研究小组)的组织推出的免费安全证书计划。参与这个计划的组织和公司可以说是互联网顶顶重要的先驱,除了前文提到的三个牛气哄哄的发起单位外,后来又有思科(全球网络设备制造商执牛耳者)、 Akamai 加入,甚至连 Linux 基金会也加入了合作,这些大牌组织的加入保证了这个项目的可信度和可持续性。

部署 HTTPS 网站的时候需要证书,证书由 CA 机构签发,大部分传统 CA 机构签发证书是需要收费的,这不利于推动 HTTPS 协议的使用。

Let’s Encrypt 也是一个 CA 机构,但这个 CA 机构是免费的!!!也就是说签发证书不需要任何费用。

Let’s Encrypt 由于是非盈利性的组织,需要控制开支,他们搞了一个非常有创意的事情,设计了一个 ACME 协议,目前该协议的版本是 v1。

那为什么要创建 ACME 协议呢,传统的 CA 机构是人工受理证书申请、证书更新、证书撤销,完全是手动处理的。而 ACME 协议规范化了证书申请、更新、撤销等流程,只要一个客户端实现了该协议的功能,通过客户端就可以向 Let’s Encrypt 申请证书,也就是说 Let’s Encrypt CA 完全是自动化操作的。

任何人都可以基于 ACME 协议实现一个客户端,官方推荐的客户端是 Certbot 。

Let’s Encrypt 通配符证书

在没有出现通配符证书之前,Let’s Encrypt 支持两种证书。

1)单域名证书:证书仅仅包含一个主机。

2)SAN 证书:一张证书可以包括多个主机(Let’s Encrypt 限制是 20),也就是证书可以包含下列的主机: www.example.com、www.example.cn、blog.example.com 等等。

证书包含的主机可以不是同一个注册域,不要问我注册域是什么?注册域就是向域名注册商购买的域名。

对于个人用户来说,由于主机并不是太多,所以使用 SAN 证书完全没有问题,但是对于大公司来说有一些问题:

  • 子域名非常多,而且过一段时间可能就要使用一个新的主机。
  • 注册域也非常多。

读者可以思考下,对于大企业来说,SAN 证书可能并不能满足需求,类似于 sina 这样的网站,所有的主机全部包含在一张证书中,而使用 Let’s Encrypt 证书是无法满足的。

通配符证书就是证书中可以包含一个通配符,比如 .example.com、.example.cn,读者很快明白,大型企业也可以使用通配符证书了,一张证书可以防止更多的主机了。

这个功能可以说非常重要,从功能上看 Let’s Encrypt 和传统 CA 机构没有什么区别了,会不会触动传统 CA 机构的利益呢?

如何申请 Let’s Encrypt 通配符证书

为了实现通配符证书,Let’s Encrypt 对 ACME 协议的实现进行了升级,只有 v2 协议才能支持通配符证书。

也就是说任何客户端只要支持 ACME v2 版本,就可以申请通配符证书了,是不是很激动。

在了解该协议之前有几个注意点:

客户在申请 Let’s Encrypt 证书的时候,需要校验域名的所有权,证明操作者有权利为该域名申请证书,目前支持三种验证方式:

  • dns-01:给域名添加一个 DNS TXT 记录。
  • http-01:在域名对应的 Web 服务器下放置一个 HTTP well-known URL 资源文件。
  • tls-sni-01:在域名对应的 Web 服务器下放置一个 HTTPS well-known URL 资源文件。

而申请通配符证书,只能使用 dns-01 的方式

Certbot 简介

What’s Certbot?

Certbot is a free, open source software tool for automatically using Let’s Encrypt certificates on manually-administrated websites to enable HTTPS.

Certbot is made by the Electronic Frontier Foundation (EFF) , a 501(c)3 nonprofit based in San Francisco, CA, that defends digital privacy, free speech, and innovation.

Certbot 的官方网站是 https://certbot.eff.org/ ,打开这个链接选择自己使用的 web server 和操作系统,EFF 官方会给出详细的使用方法,如果 DNS 在官方的支持插件列表中可以按官方文档操作,但如果是国内的 DNS 可以参考 certbot-letencrypt-wildcardcertificates-alydns-au

DNS plugin’s name on the Documentation list

certbot docs

certbot 插件介绍

Certbot 现在需要运行在安装了 Python (2.7 or 3.4)的类 unix 系统上,内存大于 512MB(如果小于的话, 官方解决方案 ),默认是需要 root 权限的,比如写证书操作需要 root 权限。

Certbot 客户机支持获取和安装证书的两种插件: authinstall ,当使用 certonly 参数的时候,只会获取证书,并不会安装证,获取的证书位于 / etc/letsencrypt 目录下

主要插件的介绍:

Getting certificates (and choosing plugins)

Plugin Auth Install Notes Challenge types (and port)
apache Y Y 自动化获取并安装证书 tls-sni-01 (443)
webroot Y N 已经有运行的服务,通过验证 webroot 目录来获取证书 http-01 (80)
nginx Y Y 使用 nginx 自动获取和安装证书 tls-sni-01 (443)
standalone Y N 建立一个 standalone WEB 服务,需要 80 或者 443 端口可用,如果你没有类似 nginx 和 apache 等服务,这很有用 http-01 (80) or tls-sni-01 (443)
DNS plugins Y N 通过修改 dns 服务器的 text 记录,来获取证书,野卡证书只能通过此方式获取 dns-01 (53)
manual Y N 通过自己给指令获取证书,支持添加定制脚本来完成任务 http-01 (80), dns-01 (53) or tls-sni-01 (443)

解析:

  • 如果你使用 standalone 插件,那么需要使用 80 和 443 端口,因为要建一个监听这些端口的服务,如果你有别的服务使用了该端口,那么就会出问题了。
  • webroot 方式,如果你使用了 nginx,那么你需要更改一些 nginx 配置,确保能验证你对该域名的所有权限

插件的具体使用可以参考 letsencrypt 证书 - 管理工具 certbot

我个人推荐选择 DNS plugins 或者 manual 方式来管理

certbot-dns-route53 实践

因为域名在 Amazon Route 53,所以选择使用 certbot-dns-route53 插件会比较方便,敏感信息都用 xxx 打码了但不影响阅读理解

https://github.com/certbot/certbot/tree/master/certbot-dns-route53

# Create a virtual environment
pip install virtualenv
cd /root
virtualenv certbot
source certbot/bin/activate

# Update its pip and setuptools (VENV/bin/pip install -U setuptools pip) to avoid problems with cryptography's dependency on setuptools>=11.3.

certbot/bin/pip install -U setuptools pip

pip list

Package    Version
---------- -------
pip        20.0.2
setuptools 44.0.0
wheel      0.34.2

# Make sure you have libssl-dev and libffi (or your regional equivalents) installed. You might have to set compiler flags to pick things up (I have to use CPPFLAGS=-I/usr/local/opt/openssl/include LDFLAGS=-L/usr/local/opt/openssl/lib on my macOS to pick up brew's openssl, for example).

pip install certbot-dns-route53

# create aws credentials
mkdir ~/.aws/
vim ~/.aws/credentials
[default]
aws_access_key_id=xxx
aws_secret_access_key=xxx

# generate certificate
certbot certonly \
  -n --agree-tos --email xxx \
  --dns-route53 \
  -d "*.xxx" 

(certbot) [root@xxx ~]# certbot certonly \
>   -n --agree-tos --email xxx  \
>   --dns-route53 \
>   -d "*.xxx"
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Found credentials in shared credentials file: ~/.aws/credentials
Plugins selected: Authenticator dns-route53, Installer None
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for xxx
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/xxx/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/xxx/privkey.pem
   Your cert will expire on 2020-05-19. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

certbot 证书默认存放路径结构

Where are my certificates

All generated keys and issued certificates can be found in /etc/letsencrypt/live/$domain . In the case of creating a SAN certificate with multiple alternative names, $domain is the first domain passed in via -d parameter. Rather than copying, please point your (web) server configuration directly to those files (or create symlinks). During the, /etc/letsencrypt/live is updated with the latest necessary files.

# check letsencrypt directory
[root@xxx ~]# tree /etc/letsencrypt/
/etc/letsencrypt/
├── accounts
│   └── acme-v02.api.letsencrypt.org
│       └── directory
│           └── xxx
│               ├── meta.json
│               ├── private_key.json
│               └── regr.json
├── archive
│   └── xxx
│       ├── cert1.pem
│       ├── chain1.pem
│       ├── fullchain1.pem
│       └── privkey1.pem
├── csr
│   ├── 0000_csr-certbot.pem
│   └── 0001_csr-certbot.pem
├── keys
│   ├── 0000_key-certbot.pem
│   └── 0001_key-certbot.pem
├── live
│   ├── xxx
│   │   ├── cert.pem -> ../../archive/xxx/cert1.pem
│   │   ├── chain.pem -> ../../archive/xxx/chain1.pem
│   │   ├── fullchain.pem -> ../../archive/xxx/fullchain1.pem
│   │   ├── privkey.pem -> ../../archive/xxx/privkey1.pem
│   │   └── README
│   └── README
├── renewal
│   └── xxx.conf
└── renewal-hooks
    ├── deploy
    ├── post
    └── pre

15 directories, 18 files
  • live 目录下存放的将会链接到最新的证书和私钥
  • csr keys 用来存放当前代理的授权密钥对
  • account 用来存放证书的管理信息, 这里涉及 ACME
  • renewal 存放当前代理所管理的域的信息

如果是配置 Nginx SSL 证书,通常只需要按照下面这样修改即可

ssl_certificate /etc/letsencrypt/live/xxx/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xxx/privkey.pem;

Certbot command-line options

Certbot supports a lot of command line options. Here’s the full list, from certbot --help all :

(certbot) [root@xxx ~]# certbot -h

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  certbot [SUBCOMMAND] [options] [-d DOMAIN] [-d DOMAIN] ...

Certbot can obtain and install HTTPS/TLS/SSL certificates.  By default,
it will attempt to use a webserver both for obtaining and installing the
certificate. The most common SUBCOMMANDS and flags are:

obtain, install, and renew certificates:
    (default) run   Obtain & install a certificate in your current webserver
    certonly        Obtain or renew a certificate, but do not install it
    renew           Renew all previously obtained certificates that are near
expiry
    enhance         Add security enhancements to your existing configuration
   -d DOMAINS       Comma-separated list of domains to obtain a certificate for

  (the certbot apache plugin is not installed)
  --standalone      Run a standalone webserver for authentication
  (the certbot nginx plugin is not installed)
  --webroot         Place files in a server's webroot folder for authentication
  --manual          Obtain certificates interactively, or using shell script
hooks

   -n               Run non-interactively
  --test-cert       Obtain a test certificate from a staging server
  --dry-run         Test"renew"or"certonly"without saving any certificates
to disk

manage certificates:
    certificates    Display information about certificates you have from Certbot
    revoke          Revoke a certificate (supply --cert-name or --cert-path)
    delete          Delete a certificate (supply --cert-name)

manage your account:
    register        Create an ACME account
    unregister      Deactivate an ACME account
    update_account  Update an ACME account
  --agree-tos       Agree to the ACME server's Subscriber Agreement
   -m EMAIL         Email address for important account notifications

More detailed help:

  -h, --help [TOPIC]    print this message, or detailed help on a topic;
                        the available TOPICS are:

   all, automation, commands, paths, security, testing, or any of the
   subcommands or plugins (certonly, renew, install, register, nginx,
   apache, standalone, webroot, etc.)
  -h all                print a detailed help page including all topics
  --version             print the version number
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

certbot 自动更新证书

简单介绍 2 种常见的需求,其他情况如使用容器 renew 的朋友相信应该都不用参考本文了

# 最简单的手动 renew 命令,看到 success 表示成功
certbot renew --force-renewal

(certbot) [root@xxx ~]# certbot renew --force-renewal
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/xxx.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found credentials in shared credentials file: ~/.aws/credentials
Plugins selected: Authenticator dns-route53, Installer None
Renewing an existing certificate

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/xxx/fullchain.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/xxx/fullchain.pem (success)

使用 crontab 定期执行

# 编辑 certbot_renew.sh,强制更新并重新 reload nginx 加载新证书
vim /root/certbot_renew.sh

#!/bin/bash
certbot renew --force-renew
nginx -s reload

# 如果是 virtualenv 虚拟环境可以这样写

#!/bin/bash
export PATH=$PATH:/usr/local/bin
source /root/source certbot/bin/activate
certbot renew --force-renew
nginx -s reload


# 添加执行权限
chmod a+x /root/certbot_renew.sh

# 设置定时任务自动更新证书,“At 01:01 on day-of-month 1.”
vim /etc/crontab
1 1 1 * * /root/certbot_renew.sh >/root/crontab.log 2>&1

# 如果需要记录日志可以这样写
1 1 1 * * echo `date -R` >> /var/log/certbot.crontab.log; certbot renew --force-renewal >> /var/log/certbot.crontab.log 2>&1; nginx -s reload

# certbot 官方使用 python 产生了一个分钟的随机数,让更新时间随机一些
echo "0 0,12 * * * root python -c'import random; import time; time.sleep(random.random() * 3600)'&& certbot renew" | sudo tee -a /etc/crontab > /dev/null
我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章