docker部署certbot與nginx來獲取ssl證書添加https及自動更新

起因

  • 原先一直有一個騰訊雲的1核和1G的服務器在閒着,只是偶爾用下frp來映射開發演示,這次想再利用下,於是試下搭建bitwarden,轉而找到更加小巧的vaultwarden,但實際瀏覽器插件測試必須是要https纔行,所以必須要有域名及證書,順便就想試下certbot來自動續簽。

  • 域名隨便在阿里雲上弄了個top的,10年¥189,要想解析到服務器是要備案的,所以又在騰訊雲上備案,大約有7、8個工作日吧,備案才通過,此時可以正常訪問了,然後還要30日內進行公安備案。

  • 由於服務器上的nginxfrpvaultwarden都是docker來部署的,那certbot也順便就用docker,官方雖然提供了docker鏡像,但實際上並不是很推薦使用,因爲容器內比較難觸發外部的一些操作,會導致一些自動配置的功能很難使用,參考官方文檔:Running with 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容器的配置文件及控制啓停不太好實現,所以不採用這種方式。查資料時發現有人會將nginxcertbot通過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容器都會用到,所以我們需要用宿主機做爲中轉,在certbotnginx容器中都配置映射到宿主機的同一個目錄,從而實現certbot容器和nginx容器共用文件。我的服務器home/ubuntu/docker下的目錄可參照:

├── certbot
│   ├── ssl //存放證書
│   └── www //存放http驗證的臨時文件
├── nginx
│   ├── conf 
│   │   └── conf.d //配置文件
│   │       └── valtwarden.conf //配置文件
│   ├── log //日誌
│   └── web //存放網站
└── docker-compose.yml //docker-compse文件

準備

開始

  • 主要是把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映射串聯關係如下,通過宿主機來實現certbotnginx中相關的目錄實際上是同一個目錄,除了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 
    

    以上是計劃任務相關,目前我也是剛部署,還沒看到實際效果,不一定正確。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,首先這個容器不需要常開,只是用一次就關閉的,而容器的各種空間映射增加了複雜度,其次一些回調鉤子由於容器的原因無法調用,所以還是建議不要有強迫症,直接用snap在宿主機安裝就好了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章