起因
-
原先一直有一個騰訊雲的1核和1G的服務器在閑着,只是偶爾用下
frp
來映射開發演示,這次想再利用下,於是試下搭建bitwarden
,轉而找到更加小巧的vaultwarden
,但實際瀏覽器插件測試必須是要https
才行,所以必須要有域名及證書,順便就想試下certbot
來自動續簽。 -
域名隨便在阿里雲上弄了個
top
的,10年¥189,要想解析到服務器是要備案的,所以又在騰訊雲上備案,大約有7、8個工作日吧,備案才通過,此時可以正常訪問了,然后還要30日內進行公安備案。 -
由於服務器上的
nginx
、frp
、vaultwarden
都是docker
來部署的,那certbot
也順便就用docker
,官方雖然提供了docker
鏡像,但實際上並不是很推薦使用,因為容器內比較難觸發外部的一些操作,會導致一些自動配置的功能很難使用,參考官方文檔:Running with Docker。沒實際安裝前個人也覺得用docker
更方便,但實際操作完成后會發現還是官網說的對,沒特殊情況不要用docker
來做這些。
certbot
發放證書前需要驗證域名的合法性,確保你申請證書的域名是你的,驗證方式主要分兩種:
-
通過http請求驗證,會向
http://你的域名/.well-known/acme-challenge/
發送一條請求。 -
通過dns解析,需要在域名解析中增加一條
_acme-challenge.你的域名
的TXT記錄用於驗證。
certbot
的命令有很多種不同的參數,這些參數也主要是為了自動生成http驗證文件或自動添加dns解析等功能,主要的大體說下:
-
--manual
,手動配置,你可以自己選擇http驗證還是dns驗證都行,需要自己純手工去配置http驗證文件或是dns解析,個人沒有做測試。 -
--nginx
,走http驗證,如果不用docker
且用nginx感覺是最方便的,會自動修改nginx的配置文件,增加http請求的訪問配置及后續的ssl配置,但由於我們certbot
是在docker
容器內,所以要修改另一個nginx
容器的配置文件及控制啟停不太好實現,所以不采用這種方式。查資料時發現有人會將nginx
和certbot
通過dockerfile
打包到一個鏡像內,這樣此命令就比較方便的執行了,感覺是個好辦法,但個人沒有測試了解。 -
--dns-xxx
,走dns解析驗證,這個也很方便,配置好后會自動在域名解析中增加一條記錄進行域名驗證,但官方目前只支持國外的一些dns服務商,國內的阿里雲、dnspod等貌似也有第三方的插件,可以自己去github
上找下。如果想申請通配符泛域名的,則只能通過此方式,目前個人沒有測試。 -
--standalone
,走http驗證,這種方式你就算是沒有nginx
等服務,仍然可以驗證域名並獲取證書,它會虛擬一個服務來進行驗證,但需要保證你的80
端口別被占用,由於我這邊有nginx
了,所以便沒有用此類方法測試。 -
--webroot
,走http驗證,已有類似nginx等服務的情況下可以用此方法,會自動在你配置的目錄下生成http驗證文件,與standalone
相比好處是不需要預留80端口,也就是原有的網站不需要停止就可以申請證書,而缺點就是你還需要在nginx
等服務中手動配置/.well-known/acme-challenge/
使其對應到自動生成的驗證文件上。后續我們用的就是這種。
通過webroot
的方式運行后,會在兩個地方產生文件:
-
一個是在命令行配置的
--webroot-path
指定目錄下產生臨時文件,用於http驗證。 -
一個是驗證成功會產生存放證書的文件,默認是在
/etc/letsencrypt/live/你的域名
目錄下,需要注意的是這些證書是個軟鏈接,對應着../archive
下,所以我們在做volume
映射時不要只映射到live
這個目錄,而是要映射/etc/letsencrypt
這個目錄,否則無法找到相關的證書文件。
以上兩個文件雖然是certbot
容器生成的,但我們nginx
容器都會用到,所以我們需要用宿主機做為中轉,在certbot
和nginx
容器中都配置映射到宿主機的同一個目錄,從而實現certbot
容器和nginx
容器共用文件。我的服務器home/ubuntu/docker
下的目錄可參照:
├── certbot
│ ├── ssl //存放證書
│ └── www //存放http驗證的臨時文件
├── nginx
│ ├── conf
│ │ └── conf.d //配置文件
│ │ └── valtwarden.conf //配置文件
│ ├── log //日志
│ └── web //存放網站
└── docker-compose.yml //docker-compse文件
准備
-
有個服務器
-
有個已經備案且解析到服務器上的域名
-
安裝
docker
和docker-compose
,照着官網來就行,很詳細:Docker Documentation,docker
如果下載鏡像慢,可以注冊個阿里雲賬號,然后在【容器鏡像服務】中找到【鏡像工具】下的【鏡像加速器】中按照說明來配置。 -
docker
鏡像會用到:
開始
-
主要是把
volume
映射弄好,順便配置好nginx
的相關http驗證:-
docker-compose.yml:
version: "3.9" services: nginx: container_name: nginx image: nginx restart: unless-stopped ports: - "80:80" - "443:443" environment: TZ : 'Asia/Shanghai' volumes: - /home/ubuntu/docker/nginx/conf:/etc/nginx # 配置文件 - /home/ubuntu/docker/nginx/web:/usr/share/nginx # 網站 - /home/ubuntu/docker/nginx/log:/var/log/nginx #日志 - /home/ubuntu/docker/certbot/www:/usr/share/certbot/www:ro #http驗證目錄,可設置ro為只讀,因為文件最終時通過certbot容器映射來的 - /home/ubuntu/docker/certbot/ssl:/usr/share/certbot/ssl:ro #證書位置,同上 command: nginx -g 'daemon off;' certbot: container_name: certbot image: certbot/certbot volumes: - /home/ubuntu/docker/certbot/www:/usr/share/certbot/www:rw #http驗證目錄,可設置rw可寫,與nginx容器對應的宿主機目錄時一致的 - /home/ubuntu/docker/certbot/ssl:/etc/letsencrypt:rw #證書位置,同上,注意不要只映射到live,而是它的上一級
-
nginx配置valtwarden.conf:
server { listen 80; listen [::]:80; server_name bitwarden.xxx.top;#域名 server_tokens off; #配置http驗證可訪問 location /.well-known/acme-challenge/ { #此目錄都是nginx容器內的目錄,對應宿主機volumes中的http驗證目錄,而宿主機的又與certbot容器中命令--webroot-path指定目錄一致,從而就整個串起來了,解決了http驗證問題 root /usr/share/certbot/www; } #http跳轉到https location / { return 301 https://bitwarden.xxx.top$request_uri; } }
-
-
上方的volumes映射串聯關系如下,通過宿主機來實現
certbot
和nginx
中相關的目錄實際上是同一個目錄,除了certbot
中的/etc/letsencrypt
是固定的外,其余的目錄都是可以隨便找目錄放,只要對應起來就行。目錄 certbot 宿主機 nginx http驗證目錄 /usr/share/certbot/www /home/ubuntu/docker/certbot/www /usr/share/certbot/www 證書目錄 /etc/letsencrypt /home/ubuntu/docker/certbot/ssl /usr/share/certbot/ssl -
docker-compose up -d nginx
先把nginx開啟,然后測試證書發放是否可以:# --dry-run是只測試不實際生成; --webroot-path對應着certbot內的http驗證目錄;-d后面是域名;--rm是運行后接着刪除,certbot容器不需要一直開啟,只是啟動下生成證書即可 docker-compose run --rm certbot certonly --webroot --webroot-path /usr/share/certbot/www/ --dry-run -d bitwarden.xxx.top
-
輸入郵箱等信息,如果提示
The dry run was successful
則說明成功,可以去掉--dry-run
參數來進行實際的獲取證書docker-compose run --rm certbot certonly --webroot --webroot-path /usr/share/certbot/www/ -d bitwarden.xxx.top
-
如果上方正常,則會在
certbot
容器下的/etc/letsencrypt
下也就是nginx
容器下的/usr/share/certbot/ssl
下產生證書,此時,只需要配置nginx配置ssl即可,還是在文件valtwarden.conf中:server { listen 80; listen [::]:80; server_name bitwarden.xxx.top;#域名 server_tokens off; #配置http驗證可訪問 location /.well-known/acme-challenge/ { #此目錄都是nginx容器內的目錄,對應宿主機volumes中的http驗證目錄,而宿主機的又與certbot容器中命令--webroot-path指定目錄一致,從而就整個串起來了,解決了http驗證問題 root /usr/share/certbot/www; } #http跳轉到https location / { return 301 https://bitwarden.xxx.top$request_uri; } } #ssl配置 server { listen 443 ssl; server_name bitwarden.xxx.top; #日志 access_log /var/log/nginx/bitwarden.access.log main; error_log /var/log/nginx/bitwarden.error.log; #證書信息,這里的路徑是nginx容器內的 ssl_certificate /usr/share/certbot/ssl/live/bitwarden.xxx.top/fullchain.pem; ssl_certificate_key /usr/share/certbot/ssl/live/bitwarden.xxx.top/privkey.pem; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; ssl_prefer_server_ciphers on; location / { proxy_pass http://vaultwarden;#這里是代理到valutwarden容器上了 proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
-
以上
docker-compose exec nginx nginx -t
檢測配置nginx文件是否有問題,docker-compose exec nginx nginx -s reload
或者是直接docker-compose restart nginx
來重啟nginx -
正常啟動的話應該就沒問題了,我們接着需要做定期更新證書,主要就是為了這個,否則直接騰訊雲、阿里雲申請一年的免費證書好了。增加計划任務,
sudo crontab -e
打開定時任務編輯,每月1號和15號運行一次#1、切換到docker-compose.yml所在目錄;2、然后更新證書,只有距離過期時間30天內才會真正成功;3、然后重載nginx使新證書生效 0 0 1,15 * * cd /home/ubuntu/docker && /usr/local/bin/docker-compose run --rm certbot renew && /usr/local/bin/docker-compose exec nginx nginx -s reload #上方命令測試可以獲取新證書,但是reload后實際測試新證書沒有生效,可以換成下方的直接重啟nginx試試,網上有人說reload可以,有人說必須restart,我這邊的情況是第一次沒自動生效,手動執行了reload后刷新瀏覽器可以了,但第二次同樣沒自動生效,手動reload也不行,restart后可以了,所以就很怪。而且都沒自動生效,懷疑nginx的操作並沒有等待renew執行完成后,可能在renew時間長在nginx重啟之后才執行完成了。總之目前還沒結論,所以這里可能還是不應該用docker,而應該用原生的鈎子來觸發比較好? 0 0 1,15 * * cd /home/ubuntu/docker && /usr/local/bin/docker-compose run --rm certbot renew && /usr/local/bin/docker-compose restart nginx
以上是計划任務相關。
certbot
容器中renew
雖然有--deploy-hook
等實際更新后觸發鈎子的功能,但由於和nginx
不在同一個容器,因此無法觸發nginx
重啟,所以上述命令不管是否證書真正更新成功nginx
是一定會重啟的,最好放到深夜執行。搜索了下從一個容器中控制另一個容器的操作,一種是本容器內裝docker
,然后映射宿主機內的docker.sock
,這樣容器內操作docker
就和宿主機內操作一致了;還有一種就是ssh
來連接另一個容器來操作了,這兩種都挺麻煩的,這里就沒必要也不做嘗試了。
其它
certbot
申請證書是有次數限制的,重復申請5次以上,再次請求會報too many certificates already issued...
等錯誤,會停用一周后才能再繼續,此時只能再換個域名或者是等一周了,參考速率限制。- 除官網外的參考文檔:HTTPS using Nginx and Let's encrypt in Docker,寫的很詳細。
- 最后的最后,感覺還是官網說的對,壓根沒必要用
docker
來運行certbot
,首先這個容器不需要常開,只是用一次就關閉的,而容器的各種空間映射增加了復雜度,其次一些回調鈎子由於容器的原因無法調用,所以還是建議不要有強迫症,直接在宿主機安裝就好了。而且更建議安裝dns插件來用dns方式驗證,這樣可以申請通配符ssl,非常適用於多個二級域名的情況,但這些dns插件用docker的話更不容易找到相關的鏡像,自己實現起來也很麻煩。 - 個人的網站可以嘗試下
caddy
,自帶https
,不需要這么麻煩的配置。