使用golang/x/crypto/acme/autocert的DirCache为何出现权限拒绝错误?
解决Docker卷中autocert权限拒绝问题
问题场景
- 需求:通过Docker卷
secret-dir共享证书,避免容器重启时重复执行ACME挑战 - 配置细节:使用非root用户
10001:10001,启动容器时挂载卷-v secret-dir:/secret-dir,已手动设置卷目录权限为10001:10001(先后试过750、777权限) - 代码情况:main.go中
autocert.Manager配置了DirCache("secret-dir"),Dockerfile采用多阶段构建最终使用scratch镜像 - 报错:TLS握手失败,提示无法读取/创建
secret-dir下的证书文件,权限拒绝
解决方案
1. 修正卷目录的权限初始化逻辑
Docker挂载卷时会覆盖容器内原有目录的权限设置,scratch镜像本身没有chown/chmod命令,因此构建阶段设置的权限无效。需在容器启动时修正卷权限:
方式A:通过entrypoint先修正权限再启动程序
修改Dockerfile最后阶段,添加权限初始化步骤(需在stage1安装su-exec工具):
FROM golang:1.19-alpine3.16 AS stage0 # ... 原有构建逻辑 ... FROM alpine:3.16 AS stage1 WORKDIR / RUN apk add --no-cache ca-certificates su-exec # 新增安装su-exec RUN adduser \ -h "/dev/null" \ -g "" \ -s "/sbin/nologin" \ -D \ -H \ -u 10001 \ playerone FROM scratch WORKDIR / COPY --from=stage0 /main /main COPY --from=stage1 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=stage1 /etc/passwd /etc/passwd COPY --from=stage1 /sbin/su-exec /sbin/su-exec # 启动时先以root修正卷权限,再切换到10001用户启动程序 ENTRYPOINT ["sh", "-c", "chown 10001:10001 /secret-dir || true && su-exec 10001:10001 /main"] EXPOSE 80/tcp 443/tcp
|| true用于避免目录权限已正确时,报错中断容器启动。
方式B:提前用初始化容器设置卷权限
在启动应用容器前,先运行一个临时容器设置卷的权限:
docker run --rm -v secret-dir:/secret-dir alpine:3.16 chown 10001:10001 /secret-dir && chmod 750 /secret-dir
后续启动应用容器时,卷的权限已正确配置,10001用户可正常读写。
2. 将autocert的缓存路径改为绝对路径
main.go中使用相对路径可能导致程序在不同工作目录下找不到目标目录,改为绝对路径更可靠:
m := &autocert.Manager{ Cache: autocert.DirCache("/secret-dir"), // 改为绝对路径 Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist(*host), }
3. 验证卷的实际权限
启动容器后,可通过临时容器检查卷的权限是否正确:
docker run --rm -v secret-dir:/secret-dir alpine:3.16 ls -ld /secret-dir
确认输出中目录所有者为10001,权限为drwxr-x---(750)即可。
问题根源说明
- Docker挂载卷时,会用宿主机卷的权限覆盖容器内原有目录的权限设置,因此构建阶段在stage1中设置的权限会被卷本身的权限取代
- scratch镜像不包含用户管理工具,无法在构建阶段修改已挂载卷的权限,必须在运行时处理
内容的提问来源于stack exchange,提问作者Jason Rich Darmawan
相关产品推荐
相关产品推荐

