背景
沒啥背景,就是需要搞證書,花錢的證書買不起,就只能白嫖了,白嫖肯定就只有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 就行了