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在宿主机安装就好了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章