docker-compose + acme.sh + nginx 签署 ssl 证书
in OS with 0 comment
docker-compose + acme.sh + nginx 签署 ssl 证书
in OS with 0 comment

前提

本文以腾讯云 DNSPod 举例说明,即域名在 DNSPod 注册并解析的。

在实际应用中,请替换 vkarz.com 域名为自己的域名。

项目结构

在用户家目录中,创建了 docker 目录,基于此目录进行文中的配置。最终目录结构如下:

.
|-- acmeout
|   |-- account.conf
|   |-- ca
|   |   `-- acme.zerossl.com
|   |-- http.header
|   `-- vkarz.com_ecc
|       |-- ca.cer
|       |-- fullchain.cer
|       |-- vkarz.com.cer
|       |-- vkarz.com.conf
|       |-- vkarz.com.csr
|       |-- vkarz.com.csr.conf
|       `-- vkarz.com.key
|-- nginx
|   |-- html
|   |   |-- vkarz.com
|   |   `-- blog.vkarz.com
|   |-- nginx.conf
|   |-- ssl
|   |   `-- vkarz.com
|   |       |-- ca.pem
|   |       |-- cert.pem
|   |       |-- full.pem
|   |       `-- key.pem
|   `-- vhost
|       |-- vkarz.com.conf
|       |-- vkarz.com.conf.bak
|       |-- blog.vkarz.com.conf
|       `-- blog.vkarz.com.conf.bak
目录将在下面章节中阐述。

0x01 申请 DNSPod Token

访问 https://console.dnspod.cn/account/token/token 地址,并把 id 和 token 保存下来。

dns_pod token

0x02 编写配置文件

在 docker 目录下创建 docker-compose.yml 文件,填写以下内容:

version: "3.9"

services:

  acme.sh:
    image: neilpang/acme.sh
    container_name: acme.sh
    command: daemon
    volumes:
      - ~/docker/acmeout:/acme.sh
      - /var/run/docker.sock:/var/run/docker.sock 
    environment:
      - DEPLOY_DOCKER_CONTAINER_LABEL=sh.acme.autoload.domain=vkarz.com
      - DEPLOY_DOCKER_CONTAINER_KEY_FILE=/etc/nginx/ssl/vkarz.com/key.pem
      - DEPLOY_DOCKER_CONTAINER_CERT_FILE="/etc/nginx/ssl/vkarz.com/cert.pem"
      - DEPLOY_DOCKER_CONTAINER_CA_FILE="/etc/nginx/ssl/vkarz.com/ca.pem"
      - DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/etc/nginx/ssl/vkarz.com/full.pem"
      - DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload"

  nginx:
    image: nginx:alpine
    restart: always
    ports:
      - "80:80"
      - "443:443"
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - ~/docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ~/docker/nginx/vhost:/etc/nginx/conf.d
      - ~/docker/nginx/ssl:/etc/nginx/ssl
      - ~/docker/nginx/html:/usr/share/nginx/html
    labels:
      - sh.acme.autoload.domain=vkarz.com
注意 acme.sh:environment:DEPLOY_DOCKER_CONTAINER_LABELnginx:labels 配置的值是一样的。acme.sh 通过此配置了签发和部署证书。

上面配置中主要有两个目录,acmeout 和 nginx 目录:

编写 nginx 配置

nginx.conf 文件内容如下:

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    #tcp_nopush     on;
    keepalive_timeout  65;
    #gzip  on;
    server_names_hash_bucket_size 128;
    client_header_buffer_size 32k;
    large_client_header_buffers 4 32k;
    client_max_body_size 1024m;
    client_body_buffer_size 10m;

    include /etc/nginx/conf.d/*.conf;
}

在 nginx/vhost 目录下,创建 vkarz.com.conf 文件,内容如下:

server {
    listen 80;
    server_name vkarz.com;
    index index.html index.htm;
    root  /usr/share/nginx/html/vkarz.com;

    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
        expires 30d;
    }

    location ~ .*\.(js|css)?$ {
        expires 12h;
    }

    location ~ /.well-known {
        allow all;
    }

    location ~ /\. {
        deny all;
    }
}

再在该目录下创建 blog.vkarz.com.conf 文件,内容如下:

server {
    listen 80;
    server_name blog.vkarz.com;
    index index.html index.htm;
    root  /usr/share/nginx/html/blog.vkarz.com;
    # 剩下的上面的配置一样 在此省略
}

最后在 nginx/html 目录创建 vkarz.com 目录和 blog.vkarz.com 目录,并编写测试文件,在两个目录下分别创建 index.html 文件,在 index.html 文件中编写一些可以识别的内容。

0x03 使用 acme.sh 签发证书

运行站点服务

在正式签发证书前,我们先把服务启动起来,在 docker 目录下执行以下命令:

docker-compose up -d

访问 http://vkarz.comhttp://blog.vkarz.com 站点,如站点不能正常访问,运行 docker-compose ps 查看是否启动了,如果没有启动运行 docker-compose logs xxx (xxx 替换为 acme.sh 或 nginx,即在 yaml 文件中配置的名称)查看容器日志,根据错误信息进行搜索,并解决问题。

签发证书

使用邮箱进行注册(具体注册什么,我也没细究,应该是 lets encrypt 或 zerossl 为了防止恶意申请证书做的吧),运行以下命令:

docker-compose exec acme.sh --register-account -m demo@example.com
邮箱替换为实际的邮箱。这一步可以合并到下面的命令中一起执行。

接下来进行证书签发,运行以下命令:

docker-compose exec \
    -e DP_Id=123456 \
    -e DP_Key=abcdefg \
    acme.sh --issue \
    --dns dns_dp \
    -d vkarz.com \
    -d '*.vkarz.com'

DP_Id 和 DP_Key 分别替换为一开始在 DNSPod 中申请的 token id 和 token。

如果出现错误,请在上述命令的基础上添加 --debug 参数,再次运行,查看具体错误原因,并寻找解决方案。

如果正常运行,可以在 docker/acmeout 目录下看到相应的文件。其中 account 文件中存储了 dnspod token 相关信息。

0x04 使用 acme.sh 部署证书

再次配置 nginx

在部署证书前,我们先调整 nginx 配置文件以启用 ssl 证书,调整 nginx/vhost/vkarz.com.conf 文件内容如下:

server {
    listen  80;
    server_name vkarz.com;

    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name vkarz.com;
    index index.html index.htm;
    root /usr/share/nginx/html/vkarz.com;

    ssl_certificate /etc/nginx/ssl/vkarz.com/full.pem;
    ssl_certificate_key /etc/nginx/ssl/vkarz.com/key.pem;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    # openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    add_header Strict-Transport-Security "max-age=31536000; preload";
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;

    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
        expires 1h;
    }

    location ~ .*\.(js|css)?$ {
        expires 12h;
    }

    location ~ /\. {
        deny all;
    }
}

blog.vkarz.com.conf 文件调整内容和其类似,就不再粘出(主要是 root server_name 不一样)。

另外,关于 ssl 配置部分,随着 nginx 版本的升级(ssl 配置也会有变化),上面的配置可能会有问题,请访问 https://ssl-config.mozilla.org/ 站点,根据里面的内容来调整自己的配置。

部署证书文件

执行下面的命令,部署证书:

docker-compose exec acme.sh --deploy -d vkarz.com --deploy-hook docker

观察输出结果,如果出现错误,请根据具体的错误内容,搜索并解决问题(在上述命令中添加 --debug 观察处理细节)。

如一切正常,再次访问站点,就可以看到那个安全的小锁了。

0x05 总结

整体步骤如下 :

  1. 配置容器:

    • 配置 acme.sh 环境变量,便于后期的部署证书(定期更新证书)。采用了 docker in docker 的模式(通过标签值来找到 nginx 容器实例),便于在证书部署完成后重启 nginx。
    • 初步配置 nginx,不进行 ssl 相关的配置,防止因在初次启动时,因读取不到 ssl 证书而报错(这时候还没有生成证书文件)。
  2. 通过在 acme.sh 容器中执行命令来签发证书。
  3. 修改 nginx 配置,补充 ssl 相关的配置。
  4. 通过在 acme.sh 容器中执行命令来部署证书,部署成功后,会自动重启 nginx 容器实例。
这里不单独把 dnspod token 给列出,上面的方法同样适用于其它能提供 dns api 的云服务供应商。

关于参考资料中一些步骤的思考:

很多资料中,都在宿主机上额外添加了 cron 任务(目的可能是为了保证万无一失吧)。首先,acme.sh 的 docker 容器实例中,运行 crontab -e(可以通过 docker-compose exec acme.sh sh 进入容器内)命令可以看到是加了定时任务的(每天 00:02 执行):

2 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" --config-home "/acme.sh" > /proc/1/fd/1 2>/proc/1/fd/2
如果不放心,第二天可以和我一样,通过执行 docker-compose logs acme.sh 来查看日志,判断执行情况。

参考资料:

The article has been posted for too long and comments have been automatically closed.