背景
没啥背景,就是需要搞证书,花钱的证书买不起,就只能白嫖了,白嫖肯定就只有let's encrypt,没啥说的直接开干。
我准备了三个域名,www.xxx.com,api.xxx.com和admin.xxx.com,
zerossl
差了let's encrype的官方文档。有很多的acme的客户端。因为我基本都会优先考虑容器,发现docker哪里有个zerossl,然后也去研究了一下,zerossl的官网(https://zerossl.com/)也提供了很多关于证书的相关接口,对接他的接口我觉得应该也可以。但是我没有,因为我觉得都是ACME的客户端实现,我为啥还要再去实现一遍。没必要。然后就去研究了一下zerossl的docker容器。搜索引擎关于zerossl的容器资料特别少,它的官方主页(https://hub.docker.com/r/zerossl/client/)也写的有点复杂,反正我没时间出来,(总是会提示method not allowed),所以最终还是放弃了,回到最原始的certbot
以上是一堆废话,下面开始正文
certbot
certbot其实也有对应的docker容器(https://hub.docker.com/r/certbot/certbot)版本,这也是我后面才发现的,因为他官方文档写的docker客户端是zerossl。
certbot使用docker的文档地址:https://certbot.eff.org/docs/install.html#running-with-docker
不说废话了,直接拉取镜像,然后使用命令查看帮助信息
docker run -it --rm certbot/certbot --help
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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获取证书有好几种方式,主要就是验证域名是不是你的。
一种就是在你的站点下放一个/.well-known/acme-challenge,在里面创建一个文件然后let's encrypt会去请求整个文件,这种方式就是官网的Webroot的方式
第二种:就是通过DNS的查找方式取验证,具体我就不知道了,我看到有个大佬用这种方式成功了(https://soulteary.com/2018/08/30/use-docker-certbot-to-obtain-ssl-certificates.html)括号里就是大佬的链接,有兴趣可以试试,我说说我用这种方式遇到的坑。
开始我对这种方式也很感兴趣,因为看文档说这事唯一方式去获取通配符域名的证书,官方文档提供了很多DNS的插件(https://certbot.eff.org/docs/using.html#dns-plugins),我按博客里的选择了cloudflare(文档地址:https://certbot-dns-cloudflare.readthedocs.io/en/stable/),他会让你去他的网址注册账号,然后验证你的域名,这个过程说实话我觉得还是比较漫长的,最后还不知道要加什么,我是没成功,算了放弃了。所以最后我又回到了第一种方式,至于官网上的其他什么Nginx、Standalone等等我就没实践了。反正第一种方式(也就是webroot)我走通了,本来我主要就是实践,解决问题,并不计划进行科研
webroot方式因为要访问你的站点目录,所以certbot的容器呀开放站点目录的读写权限,当然首先容器肯定要映射站点目录,执行docker的命令如下
docker run -it --rm -v /home/docker-nginx/certs/www.xxx.com:/etc/letsencrypt -v /home/docker-nginx/certs/www.xxx.com:/var/lib/letsencrypt
-v /home/docker-nginx/certs/www.xxx.com:/var/log/letsencrypt
-v /home/docker-app/webapp/app:/data/letsencrypt
certbot/certbot certonly --webroot --agree-tos --webroot-path=/data/letsencrypt -m xxxx@qq.com -d www.xxx.com
解释一下
--rm,容器执行完以后删除
-v 映射容器目录
前三个主要映射证书的存储路径,因为官方文档说明了证书存储在容器里的/etc/letsencrypt目录下,为了保存证书,我所以我映射到了服务器的物理路径下。
最后一个目录就是映射我的站点目录,因为certbot验证是需要往你的站点目录写东西供他访问,所以我要做这一步。
certonly,帮助上说的很清楚了,只获取争取不安装
--webroot:验证方式使用webroot
--agree-tos:统一他的协议
--webroot-path:站点目录,这里写的是容器内的目录,要与上面映射的目录一致
-m:邮箱,说是要过期了会通知到该邮箱
-d 域名,如果有多个域名,好像说是可以接多个-d,我没试过。
第一次执行会咨询你是否确定什么的,具体忘记,反正输入一个Y就行了
说一下我在这里遇到的坑,前面说我准备了三个域名,www.xxx.com,api.xxx.com,admin.xxx.com,其中www和admin两个域名我是放静态文件的,api是我的接口站点使用asp.netcore写的webapi,www和admin我使用的就是Nginx做web服务器。对外使用一个Nginx就分流。前两个www和admin获取证书都没有问题,另一个api验证失败,错误信息大概就是请求我api站点目录下的/.well-known/acme-challenge下的文件404。所以我开始怀疑是不是没有权限写,但是我检查了我整个wwwroot目录,甚至我把权限改成了777都没有效果,找不到原因。
没办法,只能想办法绕过去。后面想到,既然nginx的证书没问题,干脆我用分流的Nginx来验证我的api,然后我就在我的分流Nginx目录下放建了一个目录。修改Nginx关于api的配置
server { listen 80; #监听端口 listen 443 ssl; server_name api.xxxx.top;
location ~/.well-known/acme-challenge/ { root /home/app; } #默认请求设置 location / { proxy_pass http://webapi; #转向.netcore处理 proxy_set_header Host $proxy_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Via "nginx"; } }
主要没加什么,添加了一个location,把所有有/.well-known/acme-challenge/的请求都转到/root/app上去,主语两个location的顺序,然后在Nginx容器的目录映射到物理主机上然后再执行命令
docker run -it --rm -v /home/docker-nginx/certs/api.xxx.com :/etc/letsencrypt -v /home/docker-nginx/certs/api.xxx.com :/var/lib/letsencrypt -v /home/docker-nginx/certs/api.xxx.com :/var/log/letsencrypt -v /home/docker-nginx/rootd:/data/letsencrypt certbot/certbot certonly --webroot --agree-tos --webroot-path=/data/letsencrypt -m xxxx@qq.com -d api.xxx.com
看到成功的那一刻,内心真的无比激动。
证书已经获取到对应的目录下了,然后就是配置对应的站点证书了。
贴一个server就行了,其他配置都一模一样的。
server { listen 80; listen 443 ssl; server_name www.wenwangjiegua.top; ssl_certificate /home/certs/www.xxx.com/live/www.xxx.com/fullchain.pem; #证书里面,必须是包含两套完整的-----BEGIN CERTIFICATE-----和-----END CERTIFICATE-----
ssl_certificate_key /home/certswww.xxx.com/live/www.xxx.com/privkey.pem; #证书密钥文件
location / { proxy_set_header Host $proxy_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Via "nginx"; proxy_pass http://webapp; #转向.netcore处理 } }
这个配置是同时存在http和https请求,Nginx的302跳转百度一下就行了。
说一下我在这步犯下的一个错误,记录一下,这里我犯了一个错误,我上面的配置都完成了,但是一直用https请求都不成功,找了半天,发现原来的我的Nginx的docker没有对外开放443端口,打开就好了。这个问题真的是查了半天。傻逼了,傻逼了。
https://hub.docker.com/r/certbot/certbot
自动续期
前面实现了使用certbot获取https证书,之所以搞这么复杂其实还是为了做自动续期,要不然直接使用zerossl,验证服务器然后可以直接在zerossl的后台下载证书,快到期了会给你发邮件,然后自己登陆后台续期即可,别人也给你提供了api也可以尝试自己写代码调用他们的接口。
思路很简单
使用certbot renew进行续期,然后把重启镜像,把结果以邮件的方式发送到邮箱,先直接贴脚本代码
#!/bin/bash
export LANG=en_US.utf8
#如果存在结果文件,先删除
#重新获取证书
#docker run -it --rm -v /home/ProjectPublish/Divination/docker-nginx/certs/www.xxx.com:/etc/letsencrypt -v /home/ProjectPublish/Divination/docker-nginx/certs/www.xxx.com/lib:/var/lib/letsencrypt -v /home/ProjectPublish/Divination/docker-nginx/certs/www.xxx.com/log:/var/log/letsencrypt -v /home/ProjectPublish/Divination/docker-app/webapp/app:/data/letsencrypt certbot/certbot certonly --webroot --agree-tos --webroot-path=/data/letsencrypt -m 805513839@qq.com -d www.wenwangjiegua.top
#执行续期证书命令(执行docker容器)
docker run -it --rm -v /home/ProjectPublish/Divination/docker-nginx/certs/www.xxx.com:/etc/letsencrypt -v /home/ProjectPublish/Divination/docker-nginx/certs/www.xxx.com/lib:/var/lib/letsencrypt -v /home/ProjectPublish/Divination/docker-nginx/certs/www.xxx.com/log:/var/log/letsencrypt -v /home/ProjectPublish/Divination/docker-app/webapp/app:/data/letsencrypt certbot/certbot renew
#重启容器
docker-compose -f /home/ProjectPublish/Divination/docker-compose.yml restart dv-front
#将结果获取下来,并发送文件
mail -s "www.xxx.com 证书更新 " 805513839@qq.com < /home/ProjectPublish/Divination/docker-nginx/certs/www.wenwangjiegua.top/log/letsencrypt.log
#0 3 * * 1 /home/divination/cert_sh/www.sh
前面先判断结果文件是否存在,存在了,先把上次执行的结果清理掉。为啥定义两个文件一会儿解释
然后docker run就是执行certbot容器在容器里执行cerbot renew命令进行续期,方式跟获取证书的方式一样,也是在web中创建文件,然后let's encrypte来请求改文件来验证服务器的归属
tee 命令就是将结果保存在File文件中就是参数的www.txt
cat -v $FILE | tr '^M' ' ' | tr '^[[0m' ' ' | tr '^[[1m' ' ' | tr '^[[31m' ' ' >$FILE2
这条命令就是我纠结了很久的原因,开始的时候我也是直接将www.txt发送邮箱,结果发现我邮箱收到的内容在附件里面而且还不能预览,必须要下载下来然后用txt打开才能看,这明显不方便,查找了很多资料,大多说一些是编码的问题
然后使用cat -v www.txt查看,的确有不少特殊字符,虽然我也不明白是啥字符。但是可以肯定的是就是这些字符造成我的邮件内容变成了附件,如果不图好看,也可以直接不替换。直接cat -v www.txt > w.txt命令把隐藏字符现实出来然后输出到另一个文本然后把新文本内容发送邮件就行了。这样的缺点就是邮件有些^M等等乱七八糟的字符
所以我选择了替换一下,吧这些字符去掉,虽然可能跟原来的内容有点出入,但是大致还是能看明白这也算是一个处理方式吧
直接发送日志文件,不再保存输出结果。
最后使用mail命理发送邮件
定时执行的话使用crontab 就行了