ChatGPT解决这个技术问题 Extra ChatGPT

将 AWS 凭证传递给 Docker 容器的最佳方式是什么?

我在 Amazon EC2 上运行 docker-container。目前我已将 AWS 凭证添加到 Dockerfile。你能告诉我最好的方法吗?

如果我在我的笔记本电脑上运行一个 Docker 容器,当我将它推到那里时,它应该也能在 ECS 中神奇地工作?我猜我使用了 --volume 标志……一定有人已经回答了……

P
Pang

自从提出这个问题以来,Docker 发生了很多变化,所以这里尝试更新答案。

首先,特别是对于已经在云中运行的容器上的 AWS 凭证,使用 IAM 角色作为 Vor suggests 是一个非常好的选择。如果你能做到这一点,那么在他的答案中再加一加一并跳过其余部分。

一旦你开始在云之外运行东西,或者拥有不同类型的机密,我建议不要在两个关键位置存储机密:

环境变量:当这些在容器上定义时,容器内的每个进程都可以访问它们,它们可以通过 /proc 看到,应用程序可以将它们的环境转储到标准输出并存储在日志中,最重要的是,它们出现在检查容器时的明文。在镜像本身中:镜像通常被推送到许多用户具有拉取访问权限的注册表,有时不需要任何凭据来拉取镜像。即使您从一层中删除秘密,也可以使用常见的 Linux 实用程序(如 tar)反汇编该图像,并且可以从首次将其添加到图像的步骤中找到该秘密。

那么 Docker 容器中的秘密还有哪些其他选择呢?

选项 A: 如果您仅在构建映像期间需要此密钥,在构建开始之前无法使用该密钥,并且还无法访问 BuildKit,那么 multi-stage build 是最好的不好的选择。您可以将秘密添加到构建的初始阶段,在那里使用它,然后将没有秘密的该阶段的输出复制到您的发布阶段,并且仅将该发布阶段推送到注册表服务器。这个秘密仍然在构建服务器上的图像缓存中,所以我倾向于仅将其用作最后的手段。

选项 B: 同样在构建期间,如果您可以使用在 18.09 中发布的 BuildKit,目前有 experimental features 允许将秘密注入作为单个 RUN 行的卷安装。该挂载不会写入映像层,因此您可以在构建期间访问该密钥,而不必担心它将被推送到公共注册表服务器。生成的 Dockerfile 如下所示:

# syntax = docker/dockerfile:experimental
FROM python:3
RUN pip install awscli
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials aws s3 cp s3://... ...

然后您使用 18.09 或更高版本中的命令构建它,例如:

DOCKER_BUILDKIT=1 docker build -t your_image --secret id=aws,src=$HOME/.aws/credentials .

选项 C:在单个节点上运行时,无需 Swarm 模式或其他编排,您可以将凭证挂载为只读卷。访问此凭据需要与您在 docker 之外对同一凭据文件具有相同的访问权限,因此与没有 docker 的情况相比,它没有更好或更差。最重要的是,当您检查容器、查看日志或将映像推送到注册表服务器时,该文件的内容不应该是可见的,因为在每种情况下该卷都在此之外。这确实需要您在 docker 主机上复制您的凭据,与容器的部署分开。 (请注意,任何能够在该主机上运行容器的人都可以查看您的凭据,因为对 docker API 的访问权限是主机上的 root 并且 root 可以查看任何用户的文件。如果您不信任主机上具有 root 的用户,那么不要给他们 docker API 访问权限。)

对于 docker run,如下所示:

docker run -v $HOME/.aws/credentials:/home/app/.aws/credentials:ro your_image

或者对于撰写文件,您将拥有:

version: '3'
services:
  app:
    image: your_image
    volumes:
    - $HOME/.aws/credentials:/home/app/.aws/credentials:ro

选项 D:使用 Swarm Mode 和 Kubernetes 等编排工具,我们现在拥有比卷更好的秘密支持。使用 Swarm 模式,文件在管理器文件系统上加密(尽管解密密钥通常也在那里,允许管理员在没有管理员输入解密密钥的情况下重新启动管理器)。更重要的是,秘密只发送给需要秘密的工作人员(运行具有该秘密的容器),它只存储在工作人员的内存中,而不是磁盘中,并且它作为一个文件注入到具有 tmpfs 的容器中山。 swarm 之外的主机上的用户不能直接将该秘密挂载到他们自己的容器中,但是,通过对 docker API 的开放访问,他们可以从节点上正在运行的容器中提取秘密,因此再次限制谁可以访问API。从 compose 来看,这个秘密注入看起来像:

version: '3.7'

secrets:
  aws_creds:
    external: true

services:
  app:
    image: your_image
    secrets:
    - source: aws_creds
      target: /home/user/.aws/credentials
      uid: '1000'
      gid: '1000'
      mode: 0700

您使用 docker swarm init 为单个节点打开集群模式,然后按照说明添加其他节点。您可以使用 docker secret create aws_creds $HOME/.aws/credentials 在外部创建密钥。然后使用 docker stack deploy -c docker-compose.yml stack_name 部署撰写文件。

我经常使用以下脚本对我的秘密进行版本控制:https://github.com/sudo-bmitch/docker-config-update

选项 E: 存在其他管理机密的工具,我最喜欢的是 Vault,因为它可以创建自动过期的限时机密。然后,每个应用程序都会获得自己的一组令牌来请求机密,这些令牌使他们能够在可以到达保险库服务器的情况下请求这些时间有限的机密。如果秘密从您的网络中取出,这会降低风险,因为它要么不起作用,要么很快就会过期。特定于 AWS for Vault 的功能记录在 https://www.vaultproject.io/docs/secrets/aws/index.html


当我不使用 docker compose 时该怎么办?
@Jun711 上一行的 docker run 没有涵盖这一点?
@Jun711 如果您使用的是 linux,则主机上文件的 uid 需要与容器用户的 uid 匹配。否则,我建议发布一个带有 minimal reproducible example 的新问题,以获得有关您的问题的帮助。
我在 Mac 上,我将容器路径更改为 root 而不是 /home/app/ 并且它有效。 docker run -v $HOME/.aws/credentials:/root/.aws/credentials:ro -it -p 8080:8080 imageName:tagName 你知道我如何访问那个根目录吗?我使用了 docker exec imageId ls -la,但在那里找不到我的 aws 凭据文件
是的,为我工作:docker run -d -e ASPNETCORE_ENVIRONMENT=development -v "%UserProfile%\.aws\credentials":/root/.aws/credentials:ro --name containerName imageName
P
Philipp Kyeck

最好的方法是使用 IAM 角色并且根本不处理凭证。 (见http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html

可以从 http://169.254.169.254..... 检索凭证,因为这是一个私有 IP 地址,它只能从 EC2 实例访问。

所有现代 AWS 客户端库都“知道”如何从那里获取、刷新和使用凭证。所以在大多数情况下,你甚至不需要知道它。只需使用正确的 IAM 角色运行 ec2 即可。

作为一个选项,您可以在运行时将它们作为环境变量传递(即 docker run -e AWS_ACCESS_KEY_ID=xyz -e AWS_SECRET_ACCESS_KEY=aaa myimage

您可以通过在终端运行 printenv 来访问这些环境变量。


在本地开发/测试期间是否有一种不会影响生产安全性的好方法?我很想确保图像在不完全部署的情况下正常工作。
我发布的带有环境变量的替代方法在开发/本地环境中运行良好。
我想知道这是否是一个错字,但我需要输入 AWS_SECRET_ACCESS_KEY,而不是 AWS_SECRET_KEY,反正你的回答很有帮助。谢谢你。
简单地说(对于那些和我一样得到这个答案的人);在 EC2 上运行的 docker 容器将继承与主机实例相同的角色。 (当我的容器中的 AWS CLI 命令神秘地工作时,我需要一个像这样的“ELI5”,尽管没有传递给它们的凭证!)
从本地配置文件获取键值以分配给环境变量以用于开发目的的简单方法(如 cameroneckelberry.co/words/… 中所建议):“aws --profile default configure get aws_access_key_id”
P
Pang

另一种方法是在 docker-compose.yaml 中创建临时只读卷。 AWS CLI 和 SDK(如 boto3 或 AWS SDK for Java 等)正在 ~/.aws/credentials 文件中寻找 default 配置文件。

如果您想使用其他配置文件,您还需要在运行 docker-compose 命令之前导出 AWS_PROFILE 变量。

export AWS_PROFILE=some_other_profile_name

version: '3'

services:
  service-name:
    image: docker-image-name:latest
    environment:
      - AWS_PROFILE=${AWS_PROFILE}
    volumes:
      - ~/.aws/:/root/.aws:ro

在这个例子中,我在 docker 上使用了 root 用户。如果您正在使用其他用户,只需将 /root/.aws 更改为用户主目录。

:ro - 代表只读 docker 卷

当您在 ~/.aws/credentials 文件中有多个配置文件并且您也在使用 MFA 时,这非常有用。当您想在将 docker-container 部署到您拥有 IAM 角色但在本地没有的 ECS 上之前对其进行本地测试时也很有帮助。


在 Windows 上,.aws 目录位于 "%UserProfile%\.aws"。所以我假设你必须改变: - ~/.aws/:/root/.aws:ro- %UserProfile%\.aws:/root/.aws:ro
这仅适用于单个构建过程,而不适用于多阶段。
@wlarcheveque 需要详细说明吗?
使用 - host:container 语法时要非常小心,如果文件/文件夹在它创建的主机上不存在(以 root 身份),并且 awscli 不会感谢您为其提供零字节文件。您应该使用“长格式”来指定类型是绑定,主机路径和容器路径在单独的行上,如果文件不存在,这将失败,这是您在 docker-compose.dev 中想要的。 yml 但不在您的 docker-compose.yml (产品/AWS 部署)中。
P
Pang

另一种方法是将密钥从主机传递到 docker 容器。您可以将以下行添加到 docker-compose 文件中。

services:
  web:
    build: .
    environment:
      - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
      - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
      - AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION}

正确的区域环境变量是 AWS_REGION。请参阅stackoverflow.com/questions/44151982/…
请查看提到 AWS_DEFAULT_REGION docs.aws.amazon.com/cli/latest/userguide/… 的官方文档
当我使用 AWS_DEFAULT_REGION 时,我得到了一个找不到默认区域的异常。我的搜索导致 docs.aws.amazon.com/sdk-for-java/v1/developer-guide/… 指定了 AWS_REGION 环境变量,这对我有用。
如果您使用的是临时凭证,那么您可能还需要 AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN}
您是否需要使用 `export AWS_ACCESS_KEY_ID="myaccesskeyid" 导出 AWS_ACCESS_KEY_ID 等? AWS_ACCESS_KEY_ID 环境变量对我来说是未定义的。
0
0x90

即使我的凭据是由 aws-oktasaml2aws 设置的,以下单行代码也适用于我:

$ docker run -v$HOME/.aws:/root/.aws:ro \
            -e AWS_ACCESS_KEY_ID \
            -e AWS_CA_BUNDLE \
            -e AWS_CLI_FILE_ENCODING \
            -e AWS_CONFIG_FILE \
            -e AWS_DEFAULT_OUTPUT \
            -e AWS_DEFAULT_REGION \
            -e AWS_PAGER \
            -e AWS_PROFILE \
            -e AWS_ROLE_SESSION_NAME \
            -e AWS_SECRET_ACCESS_KEY \
            -e AWS_SESSION_TOKEN \
            -e AWS_SHARED_CREDENTIALS_FILE \
            -e AWS_STS_REGIONAL_ENDPOINTS \
            amazon/aws-cli s3 ls 

请注意,对于高级用例,您可能需要允许 rw(读写)权限,因此在 -v$HOME/.aws:/root/.aws:ro 中安装 .aws 卷时忽略 ro(只读)限制


V
ViaTech

此线程中记录了卷安装,但从 docker-compose v3.2 + 开始,您可以绑定安装。

例如,如果您的项目根目录中有一个名为 .aws_creds 的文件:

在您的撰写文件服务中,为卷执行此操作:

volumes:
     # normal volume mount, already shown in thread
  - ./.aws_creds:/root/.aws/credentials
    # way 2, note this requires docker-compose v 3.2+
  - type: bind                         
    source: .aws_creds              # from local
    target: /root/.aws/credentials  # to the container location

使用这个想法,您可以在 docker-hub 上公开存储您的 docker 图像,因为您的 aws credentials 不会物理地在图像中...要使它们关联,您必须在容器启动的本地具有正确的目录结构(即从 Git 中提取)


P
Pang

您可以创建包含以下内容的 ~/aws_env_creds

touch ~/aws_env_creds
chmod 777 ~/aws_env_creds
vi ~/aws_env_creds

添加这些值(替换您的密钥):

AWS_ACCESS_KEY_ID=AK_FAKE_KEY_88RD3PNY
AWS_SECRET_ACCESS_KEY=BividQsWW_FAKE_KEY_MuB5VAAsQNJtSxQQyDY2C

按“esc”保存文件。

运行并测试容器:

 my_service:
      build: .
      image: my_image
      env_file:
        - ~/aws_env_creds

这是一个可行的解决方案,我会避免使用 777 权限设置文件,因为任何其他有权访问主机的用户都可以读取凭据文件......不是很好,因为使用 env 变量的要点是使凭证远离任何人/任何不是需要它们的 aws 服务的东西!也许744 is more appropriate
A
Abhay Maniyar

如果有人在按照接受的答案中提到的说明进行操作后仍然面临同样的问题,请确保您没有从两个不同的来源传递环境变量。在我的情况下,我通过文件将环境变量传递给 docker run,并作为导致作为参数传递的变量显示无效的参数。

所以以下命令对我不起作用:

docker run --env-file ./env.list -e AWS_ACCESS_KEY_ID=ABCD -e AWS_SECRET_ACCESS_KEY=PQRST IMAGE_NAME:v1.0.1

将 aws 凭证移动到提到的 env.list 文件中会有所帮助。


嗨 Abhay - 您在 AWS_SECRET_ACCES_KEY 上缺少一个“S”
感谢@lukass77 指出这一点。更新。
t
tjurkan

根据之前的一些答案,我构建了自己的如下。我的项目结构:

├── Dockerfile
├── code
│   └── main.py
├── credentials
├── docker-compose.yml
└── requirements.txt

我的 docker-compose.yml 文件:

version: "3"

services:
  app:
    build:
      context: .
    volumes:
      - ./credentials:/root/.aws/credentials
      - ./code:/home/app

我的 Docker 文件:

FROM python:3.8-alpine

RUN pip3 --no-cache-dir install --upgrade awscli

RUN mkdir /app
WORKDIR /home/app

CMD python main.py