如何配置 Docker 注册表使用自签名 SSL 证书

DockerBeginner
立即练习

介绍

Docker 已经成为开发人员和 DevOps 团队的必备工具,它能够无缝地部署和管理容器化应用。在使用私有 Docker 注册表(registry)时,保护 Docker 客户端和注册表服务器之间的通信对于保护你的容器镜像至关重要。

在这个实验(Lab)中,你将学习如何设置一个本地 Docker 注册表,并使用自签名 SSL 证书来保护它。这种方法非常适合开发环境、测试和学习场景,在这些场景中,你需要一个安全的注册表,而无需从受信任的机构购买证书。

通过本教程,你将拥有一个使用 HTTPS 进行安全通信的 Docker 注册表,这使你能够在开发环境中安全地推送和拉取 Docker 镜像。

设置一个基本的 Docker 注册表(Registry)

在用 SSL 保护我们的 Docker 注册表之前,让我们首先了解什么是 Docker 注册表,并设置一个基本注册表来使用。

什么是 Docker 注册表?

Docker 注册表是 Docker 容器镜像的存储和分发系统。它允许你:

  • 在中心位置存储你的 Docker 镜像
  • 与你的团队或组织共享镜像
  • 控制对你的镜像的访问
  • 从你的镜像中跨不同环境部署容器

Docker Hub 是最广为人知的公共注册表,但对于许多组织来说,拥有一个私有注册表对于安全性、性能和控制至关重要。

设置一个基本注册表

让我们从运行一个简单的、不安全的 Docker 注册表开始:

  1. 创建一个目录来存储我们的注册表数据:

    mkdir -p ~/project/registry-data
    
  2. 使用官方镜像运行一个基本的 Docker 注册表:

    docker run -d -p 5000:5000 --restart=always --name registry -v ~/project/registry-data:/var/lib/registry registry:2
    

    此命令:

    • 在分离模式(-d)下运行注册表容器
    • 将你的主机上的端口 5000 映射到容器中的端口 5000
    • 设置容器在停止时自动重启
    • 将容器命名为 "registry"
    • 挂载我们创建的目录来存储注册表数据
  3. 验证注册表是否正在运行:

    docker ps
    

    你应该看到类似如下的输出:

    CONTAINER ID   IMAGE        COMMAND                  CREATED         STATUS         PORTS                    NAMES
    a7d8098de3a2   registry:2   "/entrypoint.sh /etc…"   5 seconds ago   Up 4 seconds   0.0.0.0:5000->5000/tcp   registry
    
  4. 通过推送一个示例镜像来测试注册表:

    首先,拉取一个小的镜像:

    docker pull hello-world
    

    为你的本地注册表打标签:

    docker tag hello-world localhost:5000/hello-world
    

    将其推送到你的注册表:

    docker push localhost:5000/hello-world
    

    你应该看到显示镜像被推送到你的注册表的输出。

  5. 在我们接下来的步骤中保护它之前,停止注册表容器:

    docker stop registry
    docker rm registry
    

这个基本注册表可以工作,但有一个重要的限制:它使用 HTTP,这是不安全的。默认情况下,Docker 客户端拒绝从不安全的注册表推送或拉取。在接下来的步骤中,我们将使用 SSL 保护我们的注册表。

生成自签名 SSL 证书

现在我们了解了 Docker 注册表的基础知识,让我们通过生成自签名 SSL 证书来保护它。此证书将启用与我们注册表的 HTTPS 通信。

什么是自签名证书?

自签名证书不是由受信任的证书颁发机构(CA)签名的 SSL 证书。虽然不适用于暴露给公共互联网的生产环境,但自签名证书非常适合开发、测试和内部应用程序。

生成证书和密钥

我们将使用 OpenSSL,一个广泛使用的密码学工具包,来创建我们的证书:

  1. 创建一个目录来存储我们的证书:

    mkdir -p ~/project/registry-certs
    cd ~/project/registry-certs
    
  2. 生成一个私钥:

    openssl genrsa -out registry.key 2048
    

    此命令生成一个 2048 位 RSA 私钥。如果成功,你应该看不到任何输出。

  3. 使用私钥创建证书签名请求(CSR):

    openssl req -new -key registry.key -out registry.csr
    

    你将被提示输入将包含在你的证书中的信息:

    Country Name (2 letter code) [AU]:US
    State or Province Name (full name) [Some-State]:California
    Locality Name (eg, city) []:San Francisco
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example Company
    Organizational Unit Name (eg, section) []:IT
    Common Name (e.g. server FQDN or YOUR name) []:localhost
    Email Address []:admin@example.com
    

    注意:对于 Common Name,输入 localhost,因为我们将在本地机器上连接到注册表。

    你还将收到以下提示:

    Please enter the following 'extra' attributes
    to be sent with your certificate request
    A challenge password []:
    An optional company name []:
    

    你可以通过按 Enter 键将这些留空。

  4. 使用 CSR 生成自签名证书:

    openssl x509 -req -days 365 -in registry.csr -signkey registry.key -out registry.crt
    

    此命令创建一个有效期为 365 天的自签名证书。

    你应该看到类似如下的输出:

    Signature ok
    subject=C = US, ST = California, L = San Francisco, O = Example Company, OU = IT, CN = localhost, emailAddress = admin@example.com
    Getting Private key
    
  5. 验证是否已创建证书和密钥文件:

    ls -l
    

    你应该看到三个文件:

    total 12
    -rw-r--r-- 1 labex labex 1220 [date] registry.crt
    -rw-r--r-- 1 labex labex 1054 [date] registry.csr
    -rw-r--r-- 1 labex labex 1679 [date] registry.key
    

现在我们有了保护 Docker 注册表所需的必要文件。在下一步中,我们将配置我们的注册表以使用此证书进行 HTTPS 通信。

使用 SSL 证书配置 Docker 注册表

现在我们有了自签名证书,我们可以配置我们的 Docker 注册表以使用 SSL 进行安全通信。

设置安全注册表

  1. 首先,让我们为我们的注册表创建一个简单的配置文件。此文件将指定 HTTPS 设置:

    mkdir -p ~/project/registry-config
    cd ~/project/registry-config
    nano config.yml
    
  2. 将以下配置添加到文件中:

    version: 0.1
    storage:
      filesystem:
        rootdirectory: /var/lib/registry
    http:
      addr: 0.0.0.0:5000
      tls:
        certificate: /certs/registry.crt
        key: /certs/registry.key
    

    此配置告诉注册表:

    • 使用文件系统进行存储
    • 在端口 5000 上监听所有接口
    • 使用 TLS(HTTPS)与我们的证书和密钥
  3. 通过按 Ctrl+X,然后 Y,然后 Enter 保存并退出。

  4. 现在让我们使用 SSL 证书运行我们的注册表:

    docker run -d -p 5000:5000 --restart=always --name registry \
      -v ~/project/registry-data:/var/lib/registry \
      -v ~/project/registry-certs:/certs \
      -v ~/project/registry-config/config.yml:/etc/docker/registry/config.yml \
      registry:2
    

    此命令:

    • 挂载我们的证书和密钥目录
    • 挂载我们的配置文件
    • 使用我们之前创建的相同数据目录
  5. 验证注册表是否正在运行:

    docker ps
    

    你应该看到显示注册表容器正在运行的输出。

配置 Docker 客户端以信任证书

默认情况下,Docker 客户端不信任自签名证书。我们需要告诉 Docker 信任我们的证书:

  1. 创建一个目录供 Docker 存储受信任的证书:

    sudo mkdir -p /etc/docker/certs.d/localhost:5000
    
  2. 将我们的证书复制到此目录:

    sudo cp ~/project/registry-certs/registry.crt /etc/docker/certs.d/localhost:5000/ca.crt
    
  3. 重新启动 Docker 服务以获取更改:

    sudo systemctl restart docker
    

    这可能需要几秒钟才能完成。

  4. 由于重新启动 Docker 将停止我们的注册表容器,让我们再次启动它:

    docker start registry
    
  5. 再次验证注册表是否正在运行:

    docker ps
    

现在我们的 Docker 注册表已配置为使用 HTTPS 和我们的自签名证书,并且我们的 Docker 客户端已配置为在连接到 localhost:5000 时信任此证书。

测试安全 Docker 注册表

现在我们已经让 Docker 注册表使用 SSL 运行,让我们通过推送和拉取镜像来测试它。这将确认一切工作正常。

使用示例镜像进行测试

  1. 首先,让我们拉取一个示例镜像用于测试:

    docker pull alpine:latest
    

    你应该看到显示 Docker 正在下载 Alpine Linux 镜像的输出。

  2. 为我们的安全注册表标记镜像:

    docker tag alpine:latest localhost:5000/alpine:latest
    

    此命令创建一个指向我们本地注册表的新标签。

  3. 将镜像推送到我们的安全注册表:

    docker push localhost:5000/alpine:latest
    

    你应该看到显示镜像层被推送到你的注册表的输出:

    The push refers to repository [localhost:5000/alpine]
    213ec9aee27d: Pushed
    latest: digest: sha256:69e70a79f2d41ab5d637de98c1e0b055206ba40a8145e7bddb55ccc04e13cf8f size: 528
    
  4. 删除本地镜像以确保我们从注册表中拉取:

    docker rmi localhost:5000/alpine:latest
    docker rmi alpine:latest
    
  5. 从我们的安全注册表中拉取镜像:

    docker pull localhost:5000/alpine:latest
    

    你应该看到显示 Docker 从你的注册表中下载镜像的输出:

    latest: Pulling from alpine
    213ec9aee27d: Pull complete
    Digest: sha256:69e70a79f2d41ab5d637de98c1e0b055206ba40a8145e7bddb55ccc04e13cf8f
    Status: Downloaded newer image for localhost:5000/alpine:latest
    localhost:5000/alpine:latest
    

验证注册表内容

让我们使用 Docker 注册表 API 检查我们注册表的内容:

  1. 列出注册表中的所有存储库:

    curl -X GET https://localhost:5000/v2/_catalog --cacert ~/project/registry-certs/registry.crt
    

    你应该看到类似这样的输出:

    { "repositories": ["alpine", "hello-world"] }
    

    这显示了我们已推送到注册表的所有镜像。

  2. 列出 alpine 存储库的所有标签:

    curl -X GET https://localhost:5000/v2/alpine/tags/list --cacert ~/project/registry-certs/registry.crt
    

    你应该看到类似这样的输出:

    { "name": "alpine", "tags": ["latest"] }
    

了解我们已经完成的工作

让我们回顾一下我们所做的事情:

  1. 使用自签名 SSL 证书设置了带有 HTTPS 的 Docker 注册表
  2. 配置了我们的 Docker 客户端以信任此证书
  3. 成功地将镜像推送到我们的安全注册表并从中拉取镜像

此设置提供:

  • 加密通信:Docker 客户端和注册表之间传输的所有数据都已加密。
  • 身份验证基础:SSL 是实现身份验证的第一步。
  • Docker 客户端兼容性:默认情况下,Docker 客户端需要 HTTPS 用于非 localhost 注册表。

你现在可以将此安全注册表用于你的开发和测试需求。对于生产环境,你通常会使用来自受信任证书颁发机构的证书,而不是自签名证书。

总结

恭喜你!你已经成功地使用自签名 SSL 证书设置了一个安全的 Docker 注册表。以下是你已经完成的工作:

  1. 设置了一个基本的 Docker 注册表,并了解了它的目的和功能
  2. 生成了一个自签名 SSL 证书,用于保护你的注册表
  3. 配置了 Docker 注册表以使用 HTTPS 和你的证书
  4. 配置了你的 Docker 客户端以信任自签名证书
  5. 通过推送和拉取镜像成功地测试了安全注册表

这些技能使你能够为你的开发和测试环境创建安全的私有注册表。私有注册表让你控制 Docker 镜像的存储位置以及谁可以访问它们,而 SSL 加密可确保你的数据在传输过程中保持安全。

对于生产环境,你通常会使用来自受信任证书颁发机构的证书,但配置过程将类似于你在本实验中学到的内容。

现在,你可以自信地在自己的项目和开发工作流程中实现安全的 Docker 注册表,从而增强你的容器化应用程序的安全性。