如何让Docker容器共享主机已安装的SSL证书?
我希望确保安装在主机上的SSL证书可被该主机上运行的Docker容器使用。
- VM:运行Debian 11的Google Cloud Compute Engine实例,此前Web应用直接运行在此VM上,SSL证书也安装于此。
- SSL证书:通过LetsEncrypt为域名安装,证书文件存储在VM的
/root/etc/letsencrypt/live路径下。证书受权限保护,且无sudo密码(GCE预构建VM未设置),无法下载这些文件。 - Docker:正迁移至Docker容器架构,用docker-compose定义Web应用和后端服务。Web应用需访问SSL证书。每个服务的Dockerfile仅复制本地代码库,运行时执行
startup_script.sh(从GCP Secret Manager下载密钥并启动应用)。本地执行docker compose build可成功构建镜像。
docker-compose配置
version: '3.12' services: app: build: context: . dockerfile: docker/app/Dockerfile ports: - "8000"
应用Dockerfile
FROM python:3.12.0-bullseye WORKDIR /home COPY repo /home/repo WORKDIR /home/repo COPY /scripts/startup_script.sh /usr/local/bin RUN chmod +x /usr/local/bin/startup_script.sh CMD ["/usr/local/bin/startup_script.sh"]
启动脚本
#!/bin/bash #Download secrets mkdir -p secrets gcloud secrets versions access "latest" --secret=google_workspace_credentials --out-file=secrets/credentials.json gcloud secrets versions access "latest" --secret=credentials_webapp --out-file=secrets/credentials_webapp #Copy over the google workspace credentials cp "$HOME"/secrets/credentials.json auth/credentials.json cp "$HOME"/secrets/credentials_webapp auth/credentials_web.json #Acquire SSL Certificate ...don't know what to do here #Setup the runtime packages pip install -r requirements.txt #Run the programs python3 app.py
- 方案1:将证书复制到本地并用于构建Docker镜像
不可行,因为需要sudo密码才能从VM复制证书,且将密钥存储在镜像中存在安全风险。 - 方案2:在容器启动脚本中运行certbot获取新SSL证书
存在问题:无法确认获取是否成功;密钥直接加入镜像不安全;LetsEncrypt等CA服务有请求速率限制。但该方案无需处理主机密钥,可在任意主机启动容器。 - 方案3:运行容器时挂载证书文件
参考过相关方法,通过docker run path/to/certs挂载证书文件,但担心证书受权限保护无法生效,且不理解该方案原理。 - 方案4:开放容器端口接收主机传输的证书
不知道具体实现方法,但安全性较高。
作为新手开发者,我应该采用哪种方案?请给出具体实现步骤及示例代码!
正在使用Traefik,但需要检查docker-compose配置。当前配置包含Traefik服务和使用证书的Web应用。由于在本地构建镜像而非在有证书的VM上构建,所以尝试在运行时CMD中复制证书。
当前Traefik相关docker-compose配置:
services: traefik: image: traefik:v2.5 command: - sh - -c - | mkdir -p /certs cp /etc/letsencrypt/live/dreamdai.io /certs/ traefik --api.insecure=true --providers.docker=true --providers.docker.exposedbydefault=false --entrypoints.web.address=:80 --entrypoints.websecure.address=:443 ports: - "80:80" - "8080:8080" - "443:443" webapp: image: webapp build: context: . dockerfile: docker/webapp.dockerfile labels: - "traefik.http.routers.webapp.rule=Host(`dreamdai.io`)" - "traefik.http.routers.webapp.entrypoints=websecure" - "traefik.http.routers.webapp.tls=true" - "traefik.http.routers.webapp.tls.certresolver=myresolver"
执行docker compose build traefik后日志显示"[+] Building 0.0s (0/0)"并立即退出,无法构建Traefik服务镜像。
优先选择:挂载主机证书到容器(方案3优化)
核心原理
Docker的绑定挂载可以将主机上的文件/目录直接映射到容器内部,容器可直接读取这些文件,无需复制或重新生成证书,既安全又高效。证书的权限保护指的是私钥文件的访问权限(通常是600),只要挂载后容器内进程有读取权限即可,无需输入密码。
具体实现步骤
1. 调整docker-compose配置,挂载主机证书目录
修改你的docker-compose.yml,为app服务添加挂载配置,同时给Traefik挂载证书目录:
针对Web应用容器:
version: '3.12' services: app: build: context: . dockerfile: docker/app/Dockerfile ports: - "8000" # 添加挂载配置,只读模式避免容器修改证书 volumes: - /root/etc/letsencrypt/live/dreamdai.io:/home/repo/certs:ro
2. 修改启动脚本,使用挂载的证书
更新startup_script.sh中的证书获取部分:
#Acquire SSL Certificate # 直接使用挂载到容器内的证书文件,设置环境变量供app.py读取 export SSL_CERT_PATH="/home/repo/certs/fullchain.pem" export SSL_KEY_PATH="/home/repo/certs/privkey.pem"
之后在app.py中读取这两个环境变量,配置SSL服务即可。
3. 修复Traefik配置问题
你无法构建Traefik镜像的原因是:Traefik服务使用了官方镜像(image字段),docker compose build traefik会跳过构建(无build配置)。正确做法是直接运行Traefik,同时挂载证书目录:
services: traefik: image: traefik:v2.5 command: - --api.insecure=true - --providers.docker=true - --providers.docker.exposedbydefault=false - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 # 指定证书读取目录 - --certificatesresolvers.myresolver.file.directory=/certs ports: - "80:80" - "8080:8080" - "443:443" volumes: # 挂载主机证书目录到容器内 - /root/etc/letsencrypt/live/dreamdai.io:/certs:ro # 挂载Docker套接字,让Traefik自动发现容器 - /var/run/docker.sock:/var/run/docker.sock:ro webapp: image: webapp build: context: . dockerfile: docker/webapp.dockerfile labels: - "traefik.http.routers.webapp.rule=Host(`dreamdai.io`)" - "traefik.http.routers.webapp.entrypoints=websecure" - "traefik.http.routers.webapp.tls=true" - "traefik.http.routers.webapp.tls.certresolver=myresolver"
4. 验证权限
确保主机上的证书目录允许Docker进程读取:
# 查看证书目录权限 ls -ld /root/etc/letsencrypt/live/dreamdai.io # 调整权限,让docker用户组拥有读取权限 sudo chgrp docker /root/etc/letsencrypt/live/dreamdai.io sudo chmod g+r /root/etc/letsencrypt/live/dreamdai.io sudo chgrp docker /root/etc/letsencrypt/live/dreamdai.io/*.pem sudo chmod g+r /root/etc/letsencrypt/live/dreamdai.io/*.pem
注:若没有sudo密码,可尝试通过SSH密钥登录root(GCE预构建VM通常支持),或联系GCP管理员调整权限。
备选方案:让Traefik自动管理证书
如果挂载主机证书遇到权限问题,可配置Traefik自动通过LetsEncrypt获取证书,无需依赖主机已有的证书:
services: traefik: image: traefik:v2.5 command: - --api.insecure=true - --providers.docker=true - --providers.docker.exposedbydefault=false - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 # HTTP自动跳转HTTPS - --entrypoints.web.http.redirections.entrypoint.to=websecure - --entrypoints.web.http.redirections.entrypoint.scheme=https # 配置LetsEncrypt证书解析器 - --certificatesresolvers.myresolver.acme.httpchallenge=true - --certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web - --certificatesresolvers.myresolver.acme.email=your-email@example.com - --certificatesresolvers.myresolver.acme.storage=/acme.json ports: - "80:80" - "8080:8080" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro # 持久化证书文件 - traefik-acme:/acme.json webapp: image: webapp build: context: . dockerfile: docker/webapp.dockerfile labels: - "traefik.http.routers.webapp.rule=Host(`dreamdai.io`)" - "traefik.http.routers.webapp.entrypoints=websecure" - "traefik.http.routers.webapp.tls=true" - "traefik.http.routers.webapp.tls.certresolver=myresolver" volumes: traefik-acme:
该方案无需处理主机证书,Traefik会自动申请并维护证书,适合新手快速上手。
内容的提问来源于stack exchange,提问作者ylime

