将 AWS 凭证传递到 Docker 容器的最佳方式是什么?
What is the best way to pass AWS credentials to a Docker container?
我是 运行 docker-Amazon EC2 上的容器。目前我已经将 AWS 凭证添加到 Dockerfile。你能告诉我最好的方法吗?
最好的方法是使用 IAM 角色并且根本不处理凭据。 (见 http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html )
可以从 http://169.254.169.254.....
检索凭据 因为这是一个私有 IP 地址,所以只能从 EC2 实例访问它。
所有现代 AWS 客户端库 "know" 如何从那里获取、刷新和使用凭证。所以在大多数情况下你甚至不需要知道它。只需 运行 具有正确 IAM 角色的 ec2,您就可以开始了。
作为一个选项,您可以在 运行 时将它们作为环境变量(即 docker run -e AWS_ACCESS_KEY_ID=xyz -e AWS_SECRET_ACCESS_KEY=aaa myimage
)传递
您可以在终端通过 运行ning printenv 访问这些环境变量。
另一种方法是将密钥从主机传递到 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}
另一种方法是在 docker-compose.yaml 中创建临时 read-only 卷。 AWS CLI 和 SDK(如 Java 的 boto3 或 AWS SDK 等)正在 ~/.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
- 代表 read-only docker 音量
当您在 ~/.aws/credentials
文件中有多个配置文件并且您也在使用 MFA 时,这非常有用。当您想在本地测试 docker-container 之前将其部署到您拥有 IAM 角色的 ECS 上时也很有用,但在本地您没有。
自从提出这个问题后,Docker 发生了很多变化,因此这里尝试更新答案。
首先,特别是在云中已经 运行 的容器上使用 AWS 凭据,使用 IAM 角色作为 是一个非常好的选择。如果你能做到这一点,那么在他的答案中再加一加一并跳过其余部分。
一旦您开始 运行云外的事物,或者拥有不同类型的秘密,我 建议不要 存储秘密的两个关键位置:
环境变量:当这些在容器上定义时,容器内的每个进程都可以访问它们,它们通过 /proc 可见,应用程序可以将它们的环境转储到标准输出,它存储在日志,最重要的是,当您检查容器时,它们以明文形式显示。
在图像本身中:图像通常被推送到许多用户具有拉取访问权限的注册表,有时不需要任何凭据来拉取图像。即使您从一层删除秘密,图像也可以使用 tar
等常用 Linux 实用程序进行反汇编,并且可以从首次将秘密添加到图像的步骤中找到秘密。
那么 Docker 容器中的秘密还有哪些其他选项?
选项 A: 如果您仅在构建映像期间需要此密钥,无法在构建开始前使用该密钥,并且还没有访问 BuildKit 的权限,则multi-stage build 是坏选项中最好的一个。您可以将秘密添加到构建的初始阶段,在那里使用它,然后将没有秘密的那个阶段的输出复制到您的发布阶段,并且只将该发布阶段推送到注册服务器。这个秘密仍然在构建服务器的图像缓存中,所以我倾向于将其用作最后的手段。
选项 B: 同样在构建期间,如果您可以使用 18.09 中发布的 BuildKit,目前有 experimental features 允许注入秘密作为单个 运行 行的卷安装。该挂载不会写入镜像层,因此您可以在构建期间访问秘密,而不必担心它会被推送到 public 注册表服务器。生成的 Docker 文件如下所示:
# 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,他们可以从 运行ning 容器中提取秘密节点再次限制谁可以访问 API。从撰写,这个秘密注入看起来像:
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 because it gives the ability to create time limited secrets that automatically expire. Every application then gets its own set of tokens to request secrets, and those tokens give them the ability to request those time limited secrets for as long as they can reach the vault server. That reduces the risk if a secret is ever taken out of your network since it will either not work or be quick to expire. The functionality specific to AWS for Vault is documented at https://www.vaultproject.io/docs/secrets/aws/index.html
您可以创建 ~/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
即使我的凭据是由 aws-okta or saml2aws 设置的,以下一行对我也有效:
$ 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
(读写)权限,因此在挂载 .aws
音量 -v$HOME/.aws:/root/.aws:ro
如果有人按照接受的答案中提到的说明进行操作后仍然遇到相同的问题,请确保您没有从两个不同的来源传递环境变量。在我的例子中,我通过文件和参数将环境变量传递给 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
文件中有帮助。
此线程中记录了卷安装,但从 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 图像公开存储在 docker-hub 上,因为您的 aws credentials
不会实际出现在图像中...将它们关联起来,您必须在本地启动容器的地方具有正确的目录结构(即从 Git 拉取)
根据之前的一些回答,我自己搭建了如下。
我的项目结构:
├── 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
我是 运行 docker-Amazon EC2 上的容器。目前我已经将 AWS 凭证添加到 Dockerfile。你能告诉我最好的方法吗?
最好的方法是使用 IAM 角色并且根本不处理凭据。 (见 http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html )
可以从 http://169.254.169.254.....
检索凭据 因为这是一个私有 IP 地址,所以只能从 EC2 实例访问它。
所有现代 AWS 客户端库 "know" 如何从那里获取、刷新和使用凭证。所以在大多数情况下你甚至不需要知道它。只需 运行 具有正确 IAM 角色的 ec2,您就可以开始了。
作为一个选项,您可以在 运行 时将它们作为环境变量(即 docker run -e AWS_ACCESS_KEY_ID=xyz -e AWS_SECRET_ACCESS_KEY=aaa myimage
)传递
您可以在终端通过 运行ning printenv 访问这些环境变量。
另一种方法是将密钥从主机传递到 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}
另一种方法是在 docker-compose.yaml 中创建临时 read-only 卷。 AWS CLI 和 SDK(如 Java 的 boto3 或 AWS SDK 等)正在 ~/.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
- 代表 read-only docker 音量
当您在 ~/.aws/credentials
文件中有多个配置文件并且您也在使用 MFA 时,这非常有用。当您想在本地测试 docker-container 之前将其部署到您拥有 IAM 角色的 ECS 上时也很有用,但在本地您没有。
自从提出这个问题后,Docker 发生了很多变化,因此这里尝试更新答案。
首先,特别是在云中已经 运行 的容器上使用 AWS 凭据,使用 IAM 角色作为
一旦您开始 运行云外的事物,或者拥有不同类型的秘密,我 建议不要 存储秘密的两个关键位置:
环境变量:当这些在容器上定义时,容器内的每个进程都可以访问它们,它们通过 /proc 可见,应用程序可以将它们的环境转储到标准输出,它存储在日志,最重要的是,当您检查容器时,它们以明文形式显示。
在图像本身中:图像通常被推送到许多用户具有拉取访问权限的注册表,有时不需要任何凭据来拉取图像。即使您从一层删除秘密,图像也可以使用
tar
等常用 Linux 实用程序进行反汇编,并且可以从首次将秘密添加到图像的步骤中找到秘密。
那么 Docker 容器中的秘密还有哪些其他选项?
选项 A: 如果您仅在构建映像期间需要此密钥,无法在构建开始前使用该密钥,并且还没有访问 BuildKit 的权限,则multi-stage build 是坏选项中最好的一个。您可以将秘密添加到构建的初始阶段,在那里使用它,然后将没有秘密的那个阶段的输出复制到您的发布阶段,并且只将该发布阶段推送到注册服务器。这个秘密仍然在构建服务器的图像缓存中,所以我倾向于将其用作最后的手段。
选项 B: 同样在构建期间,如果您可以使用 18.09 中发布的 BuildKit,目前有 experimental features 允许注入秘密作为单个 运行 行的卷安装。该挂载不会写入镜像层,因此您可以在构建期间访问秘密,而不必担心它会被推送到 public 注册表服务器。生成的 Docker 文件如下所示:
# 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,他们可以从 运行ning 容器中提取秘密节点再次限制谁可以访问 API。从撰写,这个秘密注入看起来像:
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 because it gives the ability to create time limited secrets that automatically expire. Every application then gets its own set of tokens to request secrets, and those tokens give them the ability to request those time limited secrets for as long as they can reach the vault server. That reduces the risk if a secret is ever taken out of your network since it will either not work or be quick to expire. The functionality specific to AWS for Vault is documented at https://www.vaultproject.io/docs/secrets/aws/index.html
您可以创建 ~/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
即使我的凭据是由 aws-okta or saml2aws 设置的,以下一行对我也有效:
$ 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
(读写)权限,因此在挂载 .aws
音量 -v$HOME/.aws:/root/.aws:ro
如果有人按照接受的答案中提到的说明进行操作后仍然遇到相同的问题,请确保您没有从两个不同的来源传递环境变量。在我的例子中,我通过文件和参数将环境变量传递给 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
文件中有帮助。
此线程中记录了卷安装,但从 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 图像公开存储在 docker-hub 上,因为您的 aws credentials
不会实际出现在图像中...将它们关联起来,您必须在本地启动容器的地方具有正确的目录结构(即从 Git 拉取)
根据之前的一些回答,我自己搭建了如下。 我的项目结构:
├── 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