前提
- 有自己的域名和服务器,在国内的服务器,请确保进行了 ICP 备案,并通过了。
- 系统安装了 docker-compose。关于 docker 和 docker-compose 的安装和使用,请自行搜索解决。
本文以腾讯云 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 保存下来。
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_LABEL
和nginx:labels
配置的值是一样的。acme.sh 通过此配置了签发和部署证书。
上面配置中主要有两个目录,acmeout 和 nginx 目录:
- acmeout 目录主要存储 acme.sh 相关的文件(如账号配置信息等,这些文件由 acme.sh 自行维护,不需要我们处理)。
nginx 目录,主要存储 nginx 的配置信息。
- nginx/nginx.conf 文件,nginx 主配置文件。
- nginx/vhost 目录,用来保存不同站点的配置文件(也可以都写到 nginx.conf 文件中,但我们还是要模块化,以便于站点的维护),每一个文件就代表一个站点。
- nginx/ssl 目录,用来保存 ssl 证书(不是必须的,主要是为了解决 nginx 容器重启后,证书文件丢失问题,所以持久化到宿主机上,其实整个 nginx 目录和 acmeout 目录,都为了防止容器重启,重要数据丢失问题)。
- nginx/html 目录,主要用来存储静态站点,本文以静态站点进行举例(对应的动态站点,需要反向代理,请进行搜索如何配置)。
编写 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.com 和 http://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 总结
整体步骤如下 :
配置容器:
- 配置 acme.sh 环境变量,便于后期的部署证书(定期更新证书)。采用了
docker in docker
的模式(通过标签值来找到 nginx 容器实例),便于在证书部署完成后重启 nginx。 - 初步配置 nginx,不进行 ssl 相关的配置,防止因在初次启动时,因读取不到 ssl 证书而报错(这时候还没有生成证书文件)。
- 配置 acme.sh 环境变量,便于后期的部署证书(定期更新证书)。采用了
- 通过在 acme.sh 容器中执行命令来签发证书。
- 修改 nginx 配置,补充 ssl 相关的配置。
- 通过在 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
来查看日志,判断执行情况。
参考资料:
- 全员docker化!使用docker中的acme.sh为docker中的Nginx添加SSL证书 – Yu's Blog
- deploy to docker containers · acmesh-official/acme.sh Wiki · GitHub
- 【部署系列】Docker 部署 acme.sh - jesn - 博客园
- Docker使用acme.sh申请ssl证书 – 萌精灵
本文由 waynelone 创作,采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。