在Amazon EKS中使用AWS Secrets Manager代理实现安全密钥管理

本文详细介绍了如何在Amazon EKS环境中部署AWS Secrets Manager代理作为边车容器,通过本地HTTP接口安全检索密钥,减少SDK依赖并增强应用安全性,包含完整部署步骤和代码示例。

在Amazon EKS中使用AWS Secrets Manager代理

AWS Secrets Manager是一项可用于管理、检索和轮换数据库凭据、应用程序凭据、API密钥和其他密钥在整个生命周期中的服务。您还可以使用Secrets Manager将应用程序源代码中的硬编码凭据替换为运行时调用,以便在需要时动态检索凭据。

在Amazon Elastic Kubernetes Service(Amazon EKS)环境中管理密钥会带来三个主要挑战:对特定语言AWS SDK的依赖、直接API调用带来的网络依赖以及跨多个Pod的复杂密钥轮换。

AWS Secrets Manager代理通过提供在计算环境中本地运行的语言无关HTTP接口来解决这些挑战。在本文中,我们将向您展示如何在Amazon EKS中将Secrets Manager代理部署为边车容器,以通过HTTP调用检索密钥。

新方法:Secrets Manager代理

Secrets Manager代理是一个客户端代理,您可以使用它在AWS计算环境中标准化从Secrets Manager使用密钥的方式。该代理在计算环境中拉取和缓存密钥,并允许您的应用程序通过本地HTTP端点(localhost:2773)直接从内存缓存中使用密钥。

您可以从本地代理获取密钥值,而不是向Secrets Manager进行网络调用,从而提高应用程序可用性,同时减少API调用。由于Secrets Manager代理是语言无关的,您可以在不同的编程语言中使用它,而无需依赖AWS SDK。

后量子密码学保护

Secrets Manager代理实现了ML-KEM(基于机器学习的密钥封装机制)密钥交换,为密钥检索操作提供了额外的密码学保护。此功能默认启用,无需额外配置。

身份验证和访问控制

此解决方案使用Amazon EKS Pod Identity进行到AWS服务的安全身份验证。Pod Identity提供了一种简化方法,将AWS Identity and Access Management(IAM)角色与Kubernetes服务账户关联,避免了OpenID Connect(OIDC)提供程序配置的需要。IAM主体需要GetSecretValue和DescribeSecret权限才能通过代理检索密钥。

Secrets Manager代理提供针对服务器端请求伪造(SSRF)的保护。安装代理时,它会生成一个随机SSRF令牌并将其存储在/var/run/awssmatoken中。代理会主动阻止未在X-Aws-Parameters-Secrets-Token标头中包含此令牌的请求。

解决方案概述

在此解决方案中,您将Secrets Manager代理作为边车容器部署在Amazon EKS Pod中,与NGINX应用程序一起运行。边车模式有助于确保每个Pod都有自己的代理实例,提供隔离和细粒度的安全边界。

本文演示了Secrets Manager代理边车方法,补充了[宣布ASCP与Pod Identity集成]中涵盖的AWS Secrets and Configuration Provider(ASCP)指南。

Amazon EKS支持多种使用Secrets Manager密钥的模式。当您希望将密钥作为文件挂载并偏好Kubernetes原生密钥管理时,用于Kubernetes Secrets Store CSI驱动程序的ASCP效果很好。当您需要基于HTTP的密钥访问、希望在密钥轮换期间避免Pod重启或需要通过refreshNow参数进行细粒度刷新控制时,请使用Secrets Manager代理。

在Secrets Manager代理和CSI驱动程序之间选择:

方法 访问方法 最适合
Secrets Manager代理 到localhost:2773的HTTP API调用 需要运行时密钥访问、动态刷新和语言无关HTTP接口的应用程序
ASCP和CSI驱动程序 作为文件挂载的密钥 Kubernetes原生密钥管理和基于文件的密钥使用

每种密钥管理方法对于不同的使用场景都有特定的优势。Secrets Manager代理适用于需要基于HTTP的访问和动态密钥更新的应用程序,而带有CSI驱动程序的ASCP适用于需要基于文件的密钥挂载的应用程序。在选择这些方法时,请考虑应用程序的具体要求、操作模式和安全性需求。

要部署此解决方案,您需要构建代理二进制文件,将其容器化,并使用Kubernetes清单和Amazon EKS Pod Identity将其部署到Amazon EKS,以实现对Secrets Manager的安全访问。

图1:解决方案工作流程

解决方案的工作流程如图1所示,包括以下步骤:

  1. 应用程序容器发送GET /secretsmanager/get(localhost:2773)以检索密钥
  2. Secrets Manager代理检查本地缓存以确定密钥是否已存储在内存中
    • 如果未缓存,使用Pod Identity进行身份验证以建立到AWS Secrets Manager的安全访问
    • 承担IAM角色以从AWS Secrets Manager检索密钥
    • 将密钥返回到边车容器进行缓存
  3. 将密钥返回到应用程序容器以满足原始请求

先决条件

要构建本文中的解决方案,您必须满足以下条件:

  • AWS账户
  • 位于us-west-2 AWS区域的Amazon EKS集群(1.32)
    • 请注意,AWS Secrets Manager代理支持自其初始发布以来可用的Amazon EKS和Kubernetes版本,为跨集群版本的密钥管理提供通用兼容性。
  • 在集群上安装的Amazon EKS Pod Identity代理
  • 基于Linux的虚拟机或Amazon Elastic Compute Cloud(Amazon EC2)实例以构建代理
  • AWS命令行界面(AWS CLI)版本2
  • 已安装和配置的kubectl
  • 已安装的Docker

安装Secrets Manager代理

在本节中,您将安装Secrets Manager代理。安装代理后,您将创建Pod Identity关联、Secrets Manager二进制映像、将二进制映像推送到Amazon Elastic Container Registry(Amazon ECR),并在Secrets Manager中创建密钥。

验证Pod Identity代理安装:

1
kubectl get daemonset eks-pod-identity-agent -n kube-system

使用以下命令创建Pod Identity关联:

1
2
3
4
5
aws eks create-pod-identity-association \   
	--cluster-name <cluster> \   
	--namespace default \   
	--service-account secrets-manager-sa \   
	--role-arn arn:aws:iam::<ACCOUNT_ID>:role/eks-secrets-manager-role

创建一个名为install的文件并添加以下内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash -e 
PATH=/bin:/usr/bin:/sbin:/usr/sbin # Use a safe path 
AGENTTARGETDIR=/opt/aws/secretsmanageragent 
AGENTSOURCEDIR=/etc/aws_secretsmanager_agent/configuration 
AGENTBIN=aws_secretsmanager_agent 
TOKENGROUP=awssmatokenreader 
AGENTUSER=awssmauser 
TOKENSCRIPT=/etc/aws_secretsmanager_agent/configuration/awssmaseedtoken 
AGENTSCRIPT=awssmastartup 
if [ `id -u` -ne 0 ]; then     
	echo "This script must be run as root" >&2     
	exit 1 
fi 
if [ ! -r ${TOKENSCRIPT} ]; then     
	echo "Can not read ${TOKENSCRIPT}" >&2     
	exit 1 
fi 
if [ ! -r ${AGENTSOURCEDIR}/${AGENTBIN} ]; then     
	echo "Can not read ${AGENTBIN}" >&2     
	exit 1 
fi 
groupadd -f ${TOKENGROUP} 
useradd -r -m -g ${TOKENGROUP} -d ${AGENTTARGETDIR} ${AGENTUSER} || true 
chmod 755 ${AGENTTARGETDIR} 
install -D -T -m 755 
${AGENTSOURCEDIR}/${AGENTBIN} ${AGENTTARGETDIR}/bin/${AGENTBIN} 
chown -R ${AGENTUSER} ${AGENTTARGETDIR} 
exit 0

在基于Linux的实例上使用以下命令构建代理二进制文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/bin/bash -e
# Here we are building the Secrets Manager Agent Binary for Linux x86_64 architecture
sudo yum -y groupinstall "Development Tools"
sudo yum install -y git
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source $HOME/.cargo/env
git clone https://github.com/aws/aws-secretsmanager-agent
cd aws-secretsmanager-agent
mv ../install aws_secretsmanager_agent/configuration
cargo build --release --target x86_64-unknown-linux-gnu

创建一个名为startup.sh的文件作为入口点并添加以下内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/bin/bash 
set -e 

echo "Starting AWS Secrets Manager Agent initialization..." 

# Step 1: Run the install script (equivalent to install-agent init container) 
echo "Running agent installation..." 
/etc/aws_secretsmanager_agent/configuration/install 

# Step 2: Initialize the token (equivalent to token-init init container) 
echo "Starting token initialization..." 
chmod +x 
/etc/aws_secretsmanager_agent/configuration/awssmaseedtoken /etc/aws_secretsmanager_agent/configuration/awssmaseedtoken start 

# Step 3: Start the main secrets manager agent 
echo "Starting secrets manager agent..." 
exec 
/etc/aws_secretsmanager_agent/configuration/aws_secretsmanager_agent

创建一个名为Docker-eks的文件并添加以下内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
FROM public.ecr.aws/amazonlinux/amazonlinux:2023 

# Install required dependencies 
RUN yum install -y ca-certificates bash shadow-utils && yum clean all 
RUN mkdir -p /opt/aws/secretsmanageragent /var/run 

# Copy in the agent binary and configuration scripts 
COPY aws_secretsmanager_agent/configuration/ 
/etc/aws_secretsmanager_agent/configuration 
COPY target/x86_64-unknown-linux-
gnu/release/aws_secretsmanager_agent 
/etc/aws_secretsmanager_agent/configuration 

# Make binaries and scripts executable 
RUN chmod -R +x /etc/aws_secretsmanager_agent/configuration 

# Copy and setup startup script 
COPY startup.sh /startup.sh 
RUN chmod +x /startup.sh 

WORKDIR / 
# Use the startup script as entrypoint 
ENTRYPOINT ["/startup.sh"]

使用以下命令构建和发布映像:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/bin/bash -e 

#Create the ECR Repo ( us-west-2 region) 
aws ecr create-repository --repository-name secrets-manager-agent --image-tag-mutability MUTABLE 

#Build the image 
docker build -f Dockerfile-eks -t secrets-manager-agent:eks . 

#Tag the image 
docker tag secrets-manager-agent:eks <ACCOUNT_ID>.dkr.ecr.us-west-2.amazonaws.com/secrets-manager-agent:eks 

# Login into ECR 
aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin <ACCOUNT_ID>.dkr.ecr.us-west-2.amazonaws.com 

#Push the image  
docker push  <ACCOUNT_ID>.dkr.ecr.us-west-2.amazonaws.com/secrets-manager-agent:eks

成功后,您的私有Amazon ECR仓库将显示已发布的映像。

创建密钥

映像成功发布后,您就可以创建密钥了。

使用AWS CLI在终端中输入以下命令,在Secrets Manager中创建一个密钥。

1
2
aws secretsmanager create-secret --name MySecret --description "My Secret" \       
	--secret-string "{\"user\": \"my_user\", \"password\": \"my-password\"}"

您应该看到类似以下的输出:

1
2
3
4
5
{     
    "ARN": "arn:aws:secretsmanager:us-west-
   2:XXXXXXXXXXXX:secret:MySecret-LrBlpm",     
   	"Name": "MySecret",     "VersionId": "b5e73e9b-6ec5-4144-a176-3648304b2d60"     
}

记录密钥Amazon Resource Name(ARN)以便在下一节中使用。

创建IAM角色

Amazon EKS应用程序需要一个IAM角色,该角色授予检索您刚创建的密钥的权限。

要创建IAM角色:

  1. 使用编辑器创建一个名为eks_iam_policy.json的文件,内容如下:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{     
     "Version": "2012-10-17",     
     "Statement": [         
         {             
             "Effect": "Allow",             
             "Principal": {                 
                 "Service": "pods.eks.amazonaws.com"             
             },             
             "Action": [                 
                 "sts:AssumeRole",                 
                 "sts:TagSession"             
             ]         
         }     
     ] 
}
  1. 在终端中输入以下命令以创建IAM角色:
1
aws iam create-role --role-name eks-secrets-manager-role --assume-role-policy-document file://eks_iam_policy.json
  1. 创建一个名为iam_permission.json的文件,内容如下,将<SECRET_ARN>替换为您之前记录的密钥ARN:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{     
    "Version": "2012-10-17",     
    "Statement": [         
        {             
            "Effect": "Allow",             
            "Action": [                 
                "secretsmanager:GetSecretValue",                 
                "secretsmanager:DescribeSecret"             
            ],             
            "Resource": "<SECRET_ARN>"         
        }     
    ] 
}
  1. 输入以下命令创建策略:
1
aws iam create-policy --policy-name get-secret-policy --policy-document file://iam_permission.json
  1. 记录策略ARN以便在下一步中使用。
  2. 输入以下命令将此策略添加到IAM角色,将<POLICY_ARN>替换为您刚才记录的值:
1
aws iam attach-role-policy --role-name eks-secrets-manager-role --policy-arn <POLICY_ARN>

配置应用程序并将Secrets Manager代理部署到Amazon EKS

以下是用于将Secrets Manager代理作为边车容器与应用程序容器一起安装的示例Kubernetes部署YAML。将<ACCOUNT_ID>替换为您的AWS账户号,并运行代码以将NGINX应用程序部署到Amazon EKS集群。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# nginx-with-secrets-agent.yaml 
apiVersion: apps/v1 
kind: Deployment 
metadata:   
	name: nginx-with-secrets-simplified   
	labels:     
		app: nginx-with-secrets-simplified 
spec:   
	replicas: 1   
	selector:     
		matchLabels:       
			app: nginx-with-secrets-simplified   
	template:     
		metadata:       
			labels:         
				app: nginx-with-secrets-simplified     
		spec:       
			serviceAccountName: secrets-manager-sa       
			containers:         
				- 	name: nginx           
					image: nginx:latest           	
					ports:             
						- containerPort: 80           
					volumeMounts:                  
						- 	name: token-volume               
							mountPath: /var/run         
				- 	name: secrets-manager-agent           
					image: <ACCOUNT_ID>.dkr.ecr.us-west-
2.amazonaws.com/secrets-manager-agent:eks           
					env:             
						- 	name: AWS_TOKEN               
							value: "file:///var/run/awssmatoken"           
					volumeMounts:             
						- 	name: token-volume               
							mountPath: /var/run                    

			volumes:         
				- 	name: token-volume           
					emptyDir: {}          
--- 
apiVersion: v1 
kind: Service 
metadata:   
	name: nginx-service 
spec:   
	selector:     
		app: nginx-with-secrets-simplified   
	ports:     
		- 	port: 80       
			targetPort: 80   
	type: ClusterIP 

--- 
apiVersion: v1 
kind: ServiceAccount 
metadata:   
	name: secrets-manager-sa
1
kubectl apply -f nginx-with-secrets-agent.yaml

如果成功,Pod将运行两个活动容器。

检索密钥

现在您可以运行以下命令来使用本地Web服务器检索代理。kubectl exec进入应用程序容器,通过REST API调用从Web服务器检索密钥。

1
2
kubectl exec -it nginx-with-secrets-c7945f8dc-7hrzr -c nginx -- sh
curl -v -H "X-Aws-Parameters-Secrets-Token: $(cat/var/run/awssmatoken)" 'http://localhost:2773/secretsmanager/get?secretId=<SecretID>'

如果IAM权限配置正确,您应该看到Success 200消息和密钥值。

清理

运行以下清理脚本以删除为解决方案创建的资源:

1
2
chmod +x cleanup.sh
./cleanup.sh

完成后,您可以检查repo中名为cleanup.sh的文件以验证清理是否成功:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/bin/bash 
set -e 

echo "Cleaning up EKS resources..." 
kubectl delete deployment nginx-with-secrets-simplified --ignore-not-found=true 
kubectl delete service nginx-service --ignore-not-found=true 
kubectl delete serviceaccount secrets-manager-sa --ignore-not-found=true 

echo "Cleaning up Pod Identity association..." 
# Replace with your actual cluster name 
read -p "Enter your CLUSTER_NAME: " CLUSTER_NAME 

if [ -n "$CLUSTER_NAME" ]; then     
	ASSOCIATION_ID=$(aws eks list-pod-identity-associations \       
                     --cluster-name $CLUSTER_NAME \       
                     --query 'associations[?serviceAccount==`secrets-manager-sa`].associationId' \       
                     --output text)          

if [ -n "$ASSOCIATION_ID" ] && [ "$ASSOCIATION_ID" != 
"None" ]; then         
		aws eks delete-pod-identity-association \           
			--cluster-name $CLUSTER_NAME \           
			--association-id $ASSOCIATION_ID || echo "Pod Identity 
association already deleted"         
		echo "Pod Identity association deleted"     
	else         
		echo "No Pod Identity association found"     
	fi 
fi 

echo "Cleaning up IAM resources..." 
# Replace with your actual policy ARN from the create-policy 
output 
read -p "Enter your POLICY_ARN: " POLICY_ARN 

if [ -n "$POLICY_ARN" ]; then     
		aws iam detach-role-policy \       
			--role-name eks-secrets-manager-role \       
			--policy-arn $POLICY_ARN || echo "Policy already detached"

		aws iam delete-policy --policy-arn $POLICY_ARN || echo 
"Policy already deleted" 
fi 
aws iam delete-role --role-name eks-secrets-manager-role || echo 
"Role already deleted" 

echo "Cleaning up secret..." 
aws secretsmanager delete-secret --secret-id MySecret || echo 
"Secret already deleted" 

echo "Cleaning up container image..." 
aws ecr delete-repository \   
	--repository-name secrets-manager-agent \   
	--force || echo "Repository already deleted" 

echo "Cleanup complete!"

结论

在本文中,我们向您展示了如何在Amazon EKS中将AWS Secrets Manager代理部署为边车容器。这种方法提供了一种语言无关的方式,通过HTTP调用检索密钥,减少了SDK依赖,同时通过SSRF保护和基于IAM的访问控制保持安全性。

Secrets Manager代理可以作为边车容器或DaemonSet部署。使用边车部署用于隔离密钥和细粒度安全边界,使用DaemonSet部署用于跨多个应用程序的共享密钥和优化的资源利用。

这种方法补充了现有的密钥管理模式,并为团队提供了基于HTTP的密钥访问、即时刷新控制以及跨AWS计算环境的一致接口。

要了解更多信息,请访问AWS Secrets Manager文档。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计