如何验证 Docker 注册表使用的 SSL 证书

DockerDockerBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

介绍

保护你的 Docker 环境至关重要,而验证你的 Docker 注册表使用的 SSL 证书是维护这种安全性的一个重要步骤。本教程将指导你完成 Docker 注册表 SSL 证书的验证过程,帮助你确保安全通信并解决任何与 SSL 证书相关的的问题。

通过完成这个实验(Lab),你将了解 Docker 如何使用 SSL 证书,能够检查和验证 Docker 注册表使用的证书,并知道如何处理常见的证书问题。

理解 Docker 注册表 SSL 证书

Docker 注册表是存储和分发 Docker 镜像的仓库。这些注册表使用 SSL/TLS 证书来保护你的 Docker 客户端和注册表服务器之间的通信。让我们来了解一下这些证书是什么以及它们为什么重要。

什么是 SSL/TLS 证书?

SSL/TLS 证书是一个数字文档,它:

  • 在客户端和服务器之间建立安全连接
  • 加密它们之间传输的数据
  • 验证服务器的身份

当你连接到 Docker 注册表时,你的 Docker 客户端会检查注册表的 SSL 证书,以确保连接是安全的,并且你连接到的是合法的注册表。

使用 OpenSSL 检查证书信息

让我们从探索如何使用 OpenSSL(一个用于处理 SSL 证书的强大工具)来检查证书信息开始。

首先,让我们为我们的实验工作创建一个目录:

mkdir -p ~/project/ssl-lab
cd ~/project/ssl-lab

现在,让我们检查 Docker Hub 的 SSL 证书,Docker Hub 是一个常见的 Docker 注册表:

openssl s_client -connect hub.docker.com:443 -showcerts < /dev/null

这个命令连接到 Docker Hub 并显示有关其 SSL 证书的信息。你应该在输出中看到很多信息,包括:

  • 证书链
  • 颁发者信息
  • 证书有效期
  • 公钥

让我们以更易读的格式提取证书信息:

echo | openssl s_client -connect hub.docker.com:443 2> /dev/null | openssl x509 -text -noout | head -20

这个命令显示证书详细信息的前 20 行,其中包括重要信息,例如:

  • 版本
  • 序列号
  • 签名算法
  • 颁发者(证书颁发机构)
  • 有效期
  • 主题(证书所属者)

理解这些信息是验证证书真实性的第一步。

使用 Docker CLI 验证注册表 SSL 证书

现在我们了解了 SSL 证书的基础知识,让我们学习如何使用 Docker CLI 专门验证 Docker 注册表的证书。

使用 docker info 检查注册表配置

Docker CLI 提供了检查注册表配置的工具,包括它们的证书设置。

让我们检查 Docker 知道的当前注册表配置:

docker info --format '{{json .RegistryConfig.IndexConfigs}}' | python3 -m json.tool

此命令输出 Docker 守护程序知道的所有注册表的配置详细信息,并以可读的 JSON 结构格式化。你会注意到 Docker Hub(在 index.docker.io)上是默认配置的。

测试与注册表的连接

让我们尝试连接到 Docker Hub 来验证其证书:

docker login

当提示时,你可以按 Ctrl+C 取消登录,因为我们只是在测试连接,而不是实际登录。

Docker 客户端会在登录过程中自动验证注册表的 SSL 证书。如果证书有效,你将看到登录提示。如果无效,Docker 将显示一条错误消息。

创建一个文件来检查特定的注册表

让我们创建一个脚本来更彻底地检查特定注册表的证书:

cat > check_registry_cert.sh << 'EOF'
#!/bin/bash

REGISTRY=${1:-"hub.docker.com"}
PORT=${2:-"443"}

echo "Checking certificate for $REGISTRY:$PORT..."
echo | openssl s_client -connect $REGISTRY:$PORT 2>/dev/null | openssl x509 -noout -dates -issuer -subject

echo -e "\nVerifying certificate chain..."
openssl s_client -connect $REGISTRY:$PORT -showcerts </dev/null 2>/dev/null | grep -A 1 "Certificate chain"
EOF

现在使脚本可执行:

chmod +x check_registry_cert.sh

让我们运行它来检查 Docker Hub 的证书:

./check_registry_cert.sh

你应该看到输出显示:

  • 证书的颁发时间和过期时间
  • 谁颁发的证书
  • 证书属于谁
  • 关于证书链的信息

我们还可以检查另一个注册表,即 Microsoft 的容器注册表的证书:

./check_registry_cert.sh mcr.microsoft.com

比较输出,看看证书在注册表之间如何不同。

解决 SSL 证书问题

即使有了正确的验证过程,在处理 Docker 注册表时,你仍然可能会遇到 SSL 证书问题。让我们学习如何识别和解决最常见的问题。

常见的 SSL 证书问题

最常见的 SSL 证书问题包括:

  1. 自签名证书
  2. 过期证书
  3. 证书主机名不匹配
  4. 不受信任的证书颁发机构

让我们创建一个目录来模拟和解决这些问题:

mkdir -p ~/project/ssl-lab/troubleshooting
cd ~/project/ssl-lab/troubleshooting

创建一个测试自签名证书

首先,让我们创建一个自签名证书来了解如何处理它们:

openssl req -newkey rsa:2048 -nodes -keyout registry.key -x509 -days 365 -out registry.crt -subj "/CN=registry.example.com"

此命令创建:

  • 一个私钥(registry.key
  • 一个自签名证书(registry.crt),有效期为 365 天

让我们检查我们的自签名证书:

openssl x509 -in registry.crt -text -noout | grep -E "Issuer|Subject|Not"

请注意,在自签名证书中,颁发者(Issuer)和主题(Subject)是相同的,因为证书是自己签名的。

配置 Docker 以信任自签名证书

要使 Docker 信任自签名证书,你通常会将其添加到 Docker 证书目录中。让我们创建必要的目录结构:

sudo mkdir -p /etc/docker/certs.d/registry.example.com:5000
sudo cp registry.crt /etc/docker/certs.d/registry.example.com:5000/ca.crt

添加证书后,你通常会重启 Docker:

## 在真实的实验环境中,你将运行:sudo systemctl restart docker
echo "在真实的实验环境中,你将运行:sudo systemctl restart docker"

处理过期证书

让我们通过创建一个具有过去到期日期的证书来模拟检查过期证书:

openssl req -newkey rsa:2048 -nodes -keyout expired.key -x509 -days -30 -out expired.crt -subj "/CN=expired.example.com"

现在让我们检查过期证书:

openssl x509 -in expired.crt -text -noout | grep -E "Issuer|Subject|Not"

你将看到“Not After”日期在过去,这意味着证书已过期。

配置不安全的注册表

在某些情况下,你可能需要使用存在证书问题的注册表。Docker 允许你将特定的注册表标记为“不安全”:

cat > daemon.json << 'EOF'
{
  "insecure-registries": [
    "registry.example.com:5000",
    "expired.example.com:5000"
  ]
}
EOF

echo "在真实的实验环境中,你将把这个文件放在 /etc/docker/daemon.json"
cat daemon.json

此配置告诉 Docker 跳过对这些注册表的证书验证,这对于测试环境可能很有用,但在生产环境中应避免使用。

检查证书过期的脚本

让我们创建一个有用的脚本来检查证书是否即将过期:

cat > check_expiration.sh << 'EOF'
#!/bin/bash

CERT_FILE=$1
DAYS_WARNING=${2:-30}

if [ ! -f "$CERT_FILE" ]; then
    echo "Certificate file not found: $CERT_FILE"
    exit 1
fi

## Get expiration date in seconds since epoch
EXPIRY=$(openssl x509 -in "$CERT_FILE" -noout -enddate | cut -d= -f2)
EXPIRY_SECONDS=$(date -d "$EXPIRY" +%s)
NOW_SECONDS=$(date +%s)
SECONDS_LEFT=$((EXPIRY_SECONDS - NOW_SECONDS))
DAYS_LEFT=$((SECONDS_LEFT / 86400))

echo "Certificate: $CERT_FILE"
echo "Expires on: $EXPIRY"
echo "Days remaining: $DAYS_LEFT"

if [ $DAYS_LEFT -lt 0 ]; then
    echo "CRITICAL: Certificate has EXPIRED!"
    exit 2
elif [ $DAYS_LEFT -lt $DAYS_WARNING ]; then
    echo "WARNING: Certificate will expire in less than $DAYS_WARNING days!"
    exit 1
else
    echo "OK: Certificate is valid for more than $DAYS_WARNING days."
    exit 0
fi
EOF

chmod +x check_expiration.sh

让我们使用这两个证书测试我们的脚本:

./check_expiration.sh registry.crt
./check_expiration.sh expired.crt

你将看到脚本正确地识别了有效证书和过期证书。

Docker 注册表 SSL 证书的最佳实践

现在我们了解了如何验证和解决 SSL 证书问题,让我们探讨使用 Docker 注册表管理证书的最佳实践。

自动化证书验证

定期验证你的证书以防止意外故障至关重要。让我们创建一个可以定期运行的脚本:

cd ~/project/ssl-lab
cat > monitor_registry_certs.sh << 'EOF'
#!/bin/bash

## 要检查的注册表列表
REGISTRIES=(
  "hub.docker.com"
  "mcr.microsoft.com"
  "registry.k8s.io"
  "quay.io"
)

echo "========================================"
echo "Docker 注册表证书监视器"
echo "========================================"
echo "日期: $(date)"
echo ""

for registry in "${REGISTRIES[@]}"; do
  echo "正在检查 $registry..."
  CERT_INFO=$(echo | openssl s_client -connect $registry:443 2>/dev/null | openssl x509 -noout -dates -issuer -subject 2>/dev/null)
  
  if [ -z "$CERT_INFO" ]; then
    echo "错误: 无法检索 $registry 的证书"
  else
    echo "$CERT_INFO"
    
    ## 提取到期日期
    EXPIRY=$(echo "$CERT_INFO" | grep "notAfter" | cut -d= -f2)
    EXPIRY_SECONDS=$(date -d "$EXPIRY" +%s)
    NOW_SECONDS=$(date +%s)
    DAYS_LEFT=$(( (EXPIRY_SECONDS - NOW_SECONDS) / 86400 ))
    
    echo "剩余到期天数: $DAYS_LEFT"
    
    if [ $DAYS_LEFT -lt 30 ]; then
      echo "警告: 证书将在 30 天内过期!"
    fi
  fi
  echo "----------------------------------------"
done
EOF

chmod +x monitor_registry_certs.sh

让我们运行脚本,看看它是如何工作的:

./monitor_registry_certs.sh

此脚本检查多个注册表,并在任何证书即将过期时发出警告,这对于防止意外停机至关重要。

证书管理最佳实践

让我们创建一个文档,概述 Docker 注册表证书管理的最佳实践:

cat > certificate_best_practices.md << 'EOF'
## Docker 注册表证书管理最佳实践

### 证书采购
- 对于生产环境,使用来自受信任的证书颁发机构(Certificate Authorities)的证书
- 根据需要使用适当的证书类型(DV、OV 或 EV)
- 确保证书与用于访问注册表的精确域名匹配
- 考虑使用通配符证书(wildcard certificates)用于多个子域名
- 使用适当的密钥长度(RSA 至少 2048 位)

### 证书部署
- 安全地存储证书
- 使用正确的文件权限(仅 Docker 守护程序可读)
- 安全地备份证书和私钥
- 实施正确的证书轮换程序
- 在集群中的所有节点上保持证书路径一致

### 监控和维护
- 设置证书即将到期(至少提前 30 天)的警报
- 维护正在使用的所有证书的清单
- 记录续订程序
- 在登台环境(staging environment)中测试证书续订,然后再投入生产
- 尽可能自动化证书续订(使用 certbot 等工具)

### 安全注意事项
- 永远不要在生产环境中使用不安全的注册表
- 避免在生产中使用自签名证书
- 实施正确的证书吊销程序
- 定期审计证书使用情况和权限
- 在所有系统上保持 CA 捆绑包的更新
EOF

cat certificate_best_practices.md

为安全注册表创建配置模板

最后,让我们为安全 Docker 注册表配置创建一个模板:

cat > secure_registry_config.yml << 'EOF'
version: '3'

services:
  registry:
    image: registry:2
    ports:
      - 5000:5000
    environment:
      REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
      REGISTRY_HTTP_TLS_KEY: /certs/domain.key
      ## 附加安全设置
      REGISTRY_STORAGE_DELETE_ENABLED: "true"
      REGISTRY_HTTP_HEADERS_X_CONTENT_TYPE_OPTIONS: nosniff
      REGISTRY_HTTP_HEADERS_X_FRAME_OPTIONS: DENY
    volumes:
      - ./certs:/certs
      - ./data:/var/lib/registry
    restart: always
    
  ## 可选:为你的注册表添加一个 UI
  registry-ui:
    image: joxit/docker-registry-ui:latest
    ports:
      - 8080:80
    environment:
      - REGISTRY_URL=https://registry:5000
      - REGISTRY_TITLE=Secure Docker Registry
      - SINGLE_REGISTRY=true
    depends_on:
      - registry
EOF

cat secure_registry_config.yml

此配置提供了一个模板,用于运行具有正确 SSL 证书配置的安全 Docker 注册表。

SSL 证书管理步骤摘要

让我们为 Docker 的 SSL 证书管理创建一个快速参考:

cat > ssl_management_summary.txt << 'EOF'
## Docker 注册表 SSL 证书管理摘要

1. 验证注册表的证书:
   openssl s_client -connect registry.example.com:443 -showcerts </dev/null

2. 添加自定义证书到注册表:
   sudo mkdir -p /etc/docker/certs.d/registry.example.com:5000
   sudo cp registry.crt /etc/docker/certs.d/registry.example.com:5000/ca.crt
   sudo systemctl restart docker

3. 配置不安全的注册表(仅用于开发):
   添加到 /etc/docker/daemon.json:
   { "insecure-registries": ["registry.example.com:5000"] }
   sudo systemctl restart docker

4. 定期检查到期日期:
   openssl x509 -in certificate.crt -noout -dates

5. 自动化证书监控:
   创建并定期运行脚本以检查证书
EOF

cat ssl_management_summary.txt

此摘要用作与 Docker 注册表 SSL 证书相关的最常见操作的快速参考。

总结

在这个实验中,你学习了如何验证和管理 Docker 注册表的 SSL 证书。你现在拥有以下知识和工具:

  • 了解什么是 SSL 证书以及它们为什么对 Docker 注册表的安全性至关重要
  • 使用 OpenSSL 和 Docker CLI 命令验证 SSL 证书
  • 解决常见的证书问题,例如过期证书和自签名证书
  • 实施证书管理的最佳实践
  • 配置 Docker 以处理各种证书场景
  • 自动化证书监控以防止意外故障

这些技能对于维护安全的 Docker 环境至关重要,尤其是在安全至上的生产环境中。通过定期验证和正确管理你的 Docker 注册表 SSL 证书,你可以防止安全漏洞并确保容器化应用程序的顺利运行。