掌握 Docker 中的环境变量:配置与机密信息

通过掌握环境变量,解锁安全且灵活的 Docker 部署。本综合指南阐明了将环境变量用于常规应用配置与安全管理 API 密钥和密码等敏感数据之间的关键区别。了解传递非敏感设置的实用方法,理解通过环境变量泄露机密信息的严重风险,并发现如何利用 Docker Secrets 和 Compose 进行稳健的加密机密管理。提升您的 Docker 知识并保护您的应用程序安全。

37 浏览量

Docker 中环境(环境变量)的最佳实践:配置 vs. 密钥

Docker 革命性地改变了应用程序的构建、发布和运行方式,为开发过程的各个阶段提供了统一的环境。管理容器化应用程序的一个基本方面是配置它们,而环境变量是实现这一目标的主要机制。然而,并非所有数据都是平等的;有些配置是无害的,而另一些信息,如 API 密钥或数据库凭证,则高度敏感。混淆这两种类别可能导致重大的安全漏洞。

本文深入探讨了在将环境变量用于常规配置与管理敏感数据(通常称为“密钥”)的适当、安全方法之间的关键区别。我们将探讨将配置传递给 Docker 容器的各种方式,重点介绍将密钥作为普通环境变量处理的固有风险,并介绍 Docker 用于安全密钥管理的专用解决方案。届时,您将清楚了解何时以及如何使用每种方法,确保您的应用程序既灵活又安全。

理解用于配置的环境变量

环境变量是一种直接且被广泛采用的方法,用于将运行时配置传递给应用程序,包括在 Docker 容器中运行的应用程序。它们允许您在不重新构建 Docker 镜像的情况下修改应用程序的行为,从而使您的容器更具灵活性和可移植性。这非常适合非敏感的动态设置,如应用程序端口号、调试标志或第三方服务 URL。

传递配置变量的方法

Docker 提供了多种方法来定义和注入环境变量到您的容器中:

1. Dockerfile 中的 ENV 指令

ENV 指令会设置一个默认环境变量,该变量将在容器运行时可用。这适用于不太可能更改或为您的应用程序提供合理默认值的变量。

FROM alpine:latest

ENV APP_PORT=8080
ENV DEBUG_MODE=false

COPY ./app /app
WORKDIR /app
CMD ["/app/start.sh"]

提示:虽然 ENV 设置了默认值,但这些值可以在运行时被覆盖。

2. docker run 命令的 -e--env 标志

在启动单个容器时,您可以使用 -e--env 标志直接传递环境变量。这在临时测试或提供与 Dockerfile 默认值不同的特定设置时很常见。

docker run -d -p 80:8080 --name my_app_instance \n  -e APP_PORT=80 \n  -e DEBUG_MODE=true \n  my_app_image:latest

3. Docker Compose 中的 env_file

为了管理多个环境变量,尤其是在 docker-compose.yml 文件中定义的多个服务之间,env_file 选项非常方便。它允许您从一个或多个 .env 文件加载变量,从而使您的 docker-compose.yml 更整洁。

docker-compose.yml:

version: '3.8'
services:
  webapp:
    image: my_app_image:latest
    ports:
      - "80:8080"
    env_file:
      - ./config/app.env

./config/app.env:

APP_PORT=8080
DEBUG_MODE=false
API_ENDPOINT=https://api.example.com/v1

4. Docker Compose 中的 environment

或者,您可以在 docker-compose.yml 中服务的 environment 部分直接定义环境变量。对于少量变量或仅对单个服务特定的变量,通常首选这种方法。

version: '3.8'
services:
  webapp:
    image: my_app_image:latest
    ports:
      - "80:8080"
    environment:
      APP_PORT: 8080
      DEBUG_MODE: false

将环境变量用于密钥的陷阱

虽然环境变量非常适合配置,但它们从根本上不安全,不适合管理敏感数据(密钥),如数据库密码、API 密钥或私有 SSH 密钥。这是一个关键的安全漏洞,尤其是在开发环境中,它经常被忽视。

为什么环境变量对密钥不安全:

  1. 通过 docker inspect 可见:任何有权访问 Docker 主机的人都可以使用 docker inspect <container_id> 轻松查看正在运行容器的环境变量。这意味着您的密钥会明文显示。

    ```bash

    暴露密钥的示例(请勿在生产环境中使用此方法)

    docker run -d -e DB_PASSWORD=mysecretpassword --name insecure_app nginx:latest

    任何人都可以看到密码

    docker inspect insecure_app | grep DB_PASSWORD
    ```

  2. 进程窥探:在容器内部,其他进程或用户(如果存在多个用户)可能能够读取环境变量,特别是当应用程序以 root 用户身份运行或拥有提升的权限时。

  3. 日志和历史记录:环境变量可能会无意中出现在日志、CI/CD 流水线历史记录或 shell 历史记录中,导致意外暴露。

  4. 镜像层:如果您在 Dockerfile 中对密钥使用了 ENV 指令,该密钥将被烘焙到镜像层中,并且即使您在后续层中尝试 unset 它,它仍然存在。这使得密钥可以从镜像本身中检索。

  5. 意外共享:包含密钥的 .env 文件或 docker-compose.yml 文件经常会被提交到版本控制系统或被不当共享,导致广泛暴露。

警告:将敏感信息当作普通环境变量处理是一个常见的安全误区。请始终假设环境变量在主机和容器内部都是公开可见的。

在 Docker 中安全地管理密钥

为了解决环境变量在处理敏感数据(密钥)方面的安全缺陷,Docker 提供了专用的密钥管理功能,主要通过Docker Secrets(用于 Docker Swarm)和外部工具(如支持 secrets 功能的 Docker Compose)来实现(后者可以利用 Docker Swarm 密钥或简单地挂载文件)。

Docker Secrets(Docker Swarm 模式)

Docker Secrets 是一个与 Docker Swarm 模式集成的功能,它提供了一种安全的方式来传输和存储服务的敏感数据。Secrets 的特点是:

  • 在 Swarm 管理器的 Raft 日志中加密存储。
  • 安全地传输到授权的服务任务。
  • 在容器的文件系统中挂载为内存文件,通常位于 /run/secrets/<secret_name>,而不是暴露为环境变量。
  • 允许明确授予访问权限的服务访问。

如何使用 Docker Secrets(Swarm 模式)

  1. 初始化 Swarm(如果尚未初始化)
    bash docker swarm init

  2. 创建 Secret:Secrets 可以从文件或标准输入创建。
    bash echo "my_secure_db_password" | docker secret create db_password_secret - echo "SG.your_api_key_here" | docker secret create sendgrid_api_key -

  3. 部署带有 Secret 的服务:服务通过名称引用 Secrets。Docker 将 Secret 挂载到容器中。
    bash docker service create --name my-webapp \n --secret db_password_secret \n --secret sendgrid_api_key \n my_app_image:latest

  4. 在容器中访问 Secrets:应用程序从挂载的文件路径读取 Secret。
    ```python

在您的 Python 应用程序代码中(或其他语言类似)

with open('/run/secrets/db_password_secret', 'r') as f:
db_password = f.read().strip()

with open('/run/secrets/sendgrid_api_key', 'r') as f:
sendgrid_key = f.read().strip()
```

Docker Compose 和 Secrets(用于单主机或 Swarm)

Docker Compose 3.1+ 版本引入了 secrets 部分,允许您在 docker-compose.yml 中定义和引用 Secrets。在 Swarm 模式下运行时,Compose 会利用 Docker Swarm 的原生 Secrets。当在没有 Swarm 模式的单主机上运行时,Compose 仍然通过将主机上的文件安全地挂载到容器中来支持 Secrets,尽管没有 Swarm 提供的加密存储功能。

docker-compose.yml 中使用 secrets

  1. 定义 Secrets:您可以定义 Secrets,方法是引用外部文件或将其声明为外部 Secret(预先创建的 Swarm Secret)。

    ```yaml

    docker-compose.yml

    version: '3.8'

    services:
    webapp:
    image: my_app_image:latest
    ports:
    - "80:8080"
    secrets:
    - db_password
    - sendgrid_api_key

    secrets:
    db_password:
    file: ./secrets/db_password.txt # 主机上包含密码的文件的路径
    sendgrid_api_key:
    external: true # 引用预先存在的名为 'sendgrid_api_key' 的 Docker Swarm Secret
    ```

  2. 创建本地 Secret 文件(如果使用 file
    bash mkdir secrets echo "my_local_db_password" > ./secrets/db_password.txt

  3. 使用 Compose 部署docker compose up -d 将部署您的服务,使 Secrets 在容器内的 /run/secrets/<secret_name> 处可用。

    ```bash

    在容器内,./secrets/db_password.txt 的内容将位于:

    /run/secrets/db_password

    ```

选择正确的工具:配置 vs. Secrets

决定是使用环境变量进行配置还是使用专用的密钥管理解决方案,归结为一个主要问题:

数据是否敏感?

  • 如果是(敏感数据):使用Docker Secrets(与 Swarm 一起使用)或类似的密钥管理系统(例如 Kubernetes Secrets、HashiCorp Vault)。对于单主机 Compose 设置,请使用 secrets 部分安全地挂载文件。
  • 如果否(非敏感配置):使用环境变量(通过 Dockerfile 中的 ENV-e 标志、env_file 或 Compose 中的 environment)。
特性 环境变量(用于配置) Docker Secrets(用于敏感数据)
目的 非敏感应用程序配置 敏感数据(密码、API 密钥)
可见性 可通过 docker inspectps -e 查看 挂载为文件;不在 docker inspect 中显示
安全性 对敏感数据不安全 加密、安全传输和存储
应用程序访问 os.environ(或类似方式)读取 /run/secrets/<secret_name> 文件读取
由谁管理 Docker 运行时、Docker Compose Docker Swarm、Docker Compose
用例 端口号、调试标志、非敏感 URL 数据库密码、API 令牌、私钥

两者的最佳实践

对于配置(环境变量):

  • 在 Dockerfile 中使用 ENV 提供合理的默认值。这使得您的镜像开箱即用,并清晰地记录了预期的变量。
  • 尽可能外部化配置。对于大型部署,使用带有 docker compose.env 文件或外部配置服务。
  • 记录所有配置选项及其预期值,可能在 README.md 或应用程序文档中。
  • 避免硬编码在不同环境(开发、暂存、生产)之间可能更改的值。

对于 Secrets(Docker Secrets 及其他):

  • 切勿将 Secrets(例如,包含 Secrets 的 .env 文件、db_password.txt提交到版本控制系统(如 Git)。
  • 定期轮换 Secrets。这可以最大限度地减少 Secret 被泄露时的暴露窗口。
  • 授予最小权限。仅授予服务访问其绝对需要的 Secrets。
  • 避免记录 Secret 值。确保您的应用程序和基础设施日志不打印 Secret 内容。
  • 对于大规模、企业级的部署,请考虑专用的密钥管理解决方案,如HashiCorp VaultAWS Secrets ManagerAzure Key Vault,它们提供了更高级的功能,如审计、动态密钥生成以及与身份和访问管理(IAM)的集成。

结论

掌握 Docker 中的环境变量不仅仅是了解如何传递它们;它还意味着理解通用配置与敏感密钥之间的根本区别。虽然环境变量为应用程序配置提供了无与伦比的灵活性,但它们在处理敏感数据方面本质上是不安全的。

通过在 Swarm 环境中利用 Docker Secrets 来处理敏感信息,或者通过在单主机部署中谨慎使用 Docker Compose 的 secrets 功能,您可以显著增强容器化应用程序的安全性。始终通过使用适合的工具、遵循最佳实践并确保您的敏感数据免遭意外暴露来优先考虑安全性。这种严谨的方法将带来更健壮、可维护且更安全的 Docker 部署。