aws/secrets-store-csi-driver-provider-aws
GitHub: aws/secrets-store-csi-driver-provider-aws
该工具是 Kubernetes Secrets Store CSI Driver 的 AWS 专属 Provider,用于将 AWS Secrets Manager 和 SSM Parameter Store 中的密钥以文件形式安全挂载到 EKS Pod 中。
Stars: 582 | Forks: 163
# 适用于 Secret Store CSI Driver 的 AWS Secrets Manager 和 Config Provider

[](https://codecov.io/gh/aws/secrets-store-csi-driver-provider-aws)
AWS 提供了两项服务,可以在您的代码中便捷地管理密钥和参数。AWS [Secrets Manager](https://aws.amazon.com/secrets-manager/) 允许您在整个生命周期内轻松轮换、管理和检索数据库凭证、API 密钥、证书和其他密钥。AWS [Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) 为配置数据提供分层存储。[Secrets Store CSI Driver](https://github.com/kubernetes-sigs/secrets-store-csi-driver) 的 AWS provider 允许您将存储在 Secrets Manager 中的密钥和存储在 Parameter Store 中的参数,以挂载文件的形式呈现在 Kubernetes pod 中。
## 安装
### 要求
* 运行 EC2 节点组的 Amazon Elastic Kubernetes Service (EKS) 1.17+(**不支持** Fargate 节点组 **[^1]**)。如果使用 EKS Pod Identity 功能,则需要 EKS 1.24+。
* [已安装 Secrets Store CSI driver](https://secrets-store-csi-driver.sigs.k8s.io/getting-started/installation.html):
helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm install -n kube-system csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver
**请注意**,旧版本的驱动在安装步骤中可能需要 ```--set grpcSupportedProviders="aws"``` 标志。
**注意**:如果通过 Helm 安装,可以跳过此步骤。ASCP 的 Helm chart 默认会自动安装兼容版本的 Secrets Store CSI driver 作为 helm 依赖。可以通过设置 `secrets-store-csi-driver.install=false` 来禁用此功能。
* 服务账户的 IAM 角色([IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html))或 [EKS Pod Identity](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html),如下文使用部分所述。
[^1]: CSI Secret Store driver 以 DaemonSet 形式运行,并且如 [AWS 文档](https://docs.aws.amazon.com/eks/latest/userguide/fargate.html#fargate-considerations)所述,Fargate 上不支持 DaemonSet。
### 安装 AWS Provider 和 Config Provider (ASCP)
#### 选项 1:使用 helm
```
helm repo add aws-secrets-manager https://aws.github.io/secrets-store-csi-driver-provider-aws
helm install -n kube-system secrets-provider-aws aws-secrets-manager/secrets-store-csi-driver-provider-aws
```
#### 选项 2:使用 kubectl
```
kubectl apply -f https://raw.githubusercontent.com/aws/secrets-store-csi-driver-provider-aws/main/deployment/aws-provider-installer.yaml
```
### 单独安装 CSI Driver
如果您单独安装(不通过此 Helm chart)secrets-store-csi-driver,则必须在 CSI driver 中配置 `tokenRequests`,以便 AWS provider 能够通过 AWS 服务进行身份验证:
```
helm upgrade csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver \
--set tokenRequests[0].audience="sts.amazonaws.com" \
--set tokenRequests[1].audience="pods.eks.amazonaws.com"
```
或者,如果您使用 kubectl,请将以下内容添加到您的 CSIDriver manifest 中:
```
apiVersion: storage.k8s.io/v1
kind: CSIDriver
metadata:
name: secrets-store.csi.k8s.io
spec:
tokenRequests:
- audience: "sts.amazonaws.com"
- audience: "pods.eks.amazonaws.com"
```
## 使用方法
在后续的 bash 命令中,设置要使用的区域名称和集群名称:
```
REGION=
CLUSTERNAME=
```
其中 **<REGION>** 是您的 Kubernetes 集群所在的区域,**<CLUSTERNAME>** 是您的集群名称。
创建一个测试密钥:
```
aws --region "$REGION" secretsmanager create-secret --name MySecret --secret-string '{"username":"memeuser", "password":"hunter2"}'
```
为 pod 创建一个仅限于其应拥有密钥权限的访问策略,并将策略 ARN 保存在 shell 变量中:
```
POLICY_ARN=$(aws --region "$REGION" --query Policy.Arn --output text iam create-policy --policy-name nginx-deployment-policy --policy-document '{
"Version": "2012-10-17",
"Statement": [ {
"Effect": "Allow",
"Action": ["secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret"],
"Resource": ["arn:*:secretsmanager:*:*:secret:MySecret-??????"]
} ]
}')
```
**注意**:当使用 SSM 参数时,策略中需要 "ssm:GetParameters" 权限。为了简化此示例,我们上面使用了通配符匹配,但您可以使用上面 create-secret 输出中的完整 ARN 进一步锁定权限。
#### 选项 1:使用服务账户的 IAM 角色 (IRSA)
1. 如果尚未创建,请为集群创建 IAM OIDC provider:
```
eksctl utils associate-iam-oidc-provider --region="$REGION" --cluster="$CLUSTERNAME" --approve # Only run this once
```
2. 接下来,创建要由 pod 使用的服务账户,并将上述 IAM policy 与该服务账户关联。在此示例中,我们使用 *nginx-irsa-deployment-sa* 作为服务账户名称:
```
eksctl create iamserviceaccount --name nginx-irsa-deployment-sa --region="$REGION" --cluster "$CLUSTERNAME" --attach-policy-arn "$POLICY_ARN" --approve --override-existing-serviceaccounts
```
对于私有集群,请确保集群所在的 VPC 具有 AWS STS 端点。有关更多信息,请参阅 AWS IAM 用户指南中的 [Interface VPC endpoints](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_interface_vpc_endpoints.html)。
3. 创建 SecretProviderClass,它告诉 AWS provider 要将哪些密钥挂载到 pod 中。[examples](./examples) 目录中的 ExampleSecretProviderClass-IRSA.yaml 将挂载上面创建的 "MySecret":
```
kubectl apply -f https://raw.githubusercontent.com/aws/secrets-store-csi-driver-provider-aws/main/examples/ExampleSecretProviderClass-IRSA.yaml
```
4. 最后,我们可以部署我们的 pod。examples 目录中的 ExampleDeployment-IRSA.yaml 包含了一个 nginx 示例部署,它将密钥挂载到 pod 的 /mnt/secrets-store 下:
```
kubectl apply -f https://raw.githubusercontent.com/aws/secrets-store-csi-driver-provider-aws/main/examples/ExampleDeployment-IRSA.yaml
```
要验证密钥是否已正确挂载,请参见以下示例:
```
kubectl exec -it $(kubectl get pods | awk '/nginx-irsa-deployment/{print $1}' | head -1) -- cat /mnt/secrets-store/MySecret; echo
```
#### 选项 2:使用 EKS Pod Identity
*注意:EKS Pod Identity 选项仅支持云端的 EKS。不支持 [Amazon EKS Anywhere](https://aws.amazon.com/eks/eks-anywhere/)、[Red Hat Openshift Service on AWS (ROSA)](https://aws.amazon.com/rosa/) 以及在 Amazon Elastic Compute Cloud (Amazon EC2) 实例上自管的 Kubernetes 集群。*
1. 在集群上安装 Amazon EKS Pod Identity Agent 附加组件。
```
eksctl create addon --name eks-pod-identity-agent --cluster "$CLUSTERNAME" --region "$REGION"
```
2. 创建一个可由 Amazon EKS 服务主体为 Pod Identity 承担的 IAM 角色,并附加上述 IAM policy 以授予访问测试密钥的权限。
```
ROLE_ARN=$(aws --region "$REGION" --query Role.Arn --output text iam create-role --role-name nginx-deployment-role --assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "pods.eks.amazonaws.com"
},
"Action": [
"sts:AssumeRole",
"sts:TagSession"
]
}
]
}')
```
```
aws iam attach-role-policy \
--role-name nginx-deployment-role \
--policy-arn $POLICY_ARN
```
3. 接下来,创建要由 pod 使用的服务账户,并将该服务账户与上面创建的 IAM 角色关联。在此示例中,我们使用 *nginx-pod-identity-deployment-sa* 作为服务账户名称:
```
eksctl create podidentityassociation \
--cluster "$CLUSTERNAME" \
--namespace default \
--region "$REGION" \
--service-account-name nginx-pod-identity-deployment-sa \
--role-arn $ROLE_ARN \
--create-service-account true
```
4. 现在创建 SecretProviderClass,它告诉 AWS provider 要将哪些密钥挂载到 pod 中。[examples](./examples) 目录中的 ExampleSecretProviderClass-PodIdentity.yaml 将挂载上面创建的 "MySecret":
```
kubectl apply -f https://raw.githubusercontent.com/aws/secrets-store-csi-driver-provider-aws/main/examples/ExampleSecretProviderClass-PodIdentity.yaml
```
5. 最后,我们可以部署我们的 pod。examples 目录中的 ExampleDeployment-PodIdentity.yaml 包含了一个 nginx 示例部署,它将密钥挂载到 pod 的 /mnt/secrets-store 下:
```
kubectl apply -f https://raw.githubusercontent.com/aws/secrets-store-csi-driver-provider-aws/main/examples/ExampleDeployment-PodIdentity.yaml
```
要验证密钥是否已正确挂载,请参见以下示例:
```
kubectl exec -it $(kubectl get pods | awk '/nginx-pod-identity-deployment/{print $1}' | head -1) -- cat /mnt/secrets-store/MySecret; echo
```
### 故障排除
大多数错误可以通过描述 pod 部署来查看。对于部署,使用 get pods 查找 pod 名称(如果您未使用 default 命名空间,请使用 -n **<NAMESPACE>**):
```
kubectl get pods
```
然后描述该 pod(将上面获取的 pod ID 替换 **<PODID>**,与之前一样,如果未使用 default 命名空间,请使用 -n):
```
kubectl describe pod/
```
provider 日志中可能提供更多信息:
```
kubectl -n kube-system get pods
kubectl -n kube-system logs pod/
```
在这种情况下,**<PODID>** 是 *csi-secrets-store-provider-aws* pod 的 ID。
### SecretProviderClass 选项
SecretProviderClass 具有以下格式:
```
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name:
spec:
provider: aws
parameters:
```
parameters 部分包含挂载请求的详细信息,并包含以下三个字段之一:
* objects:这是一个字符串,包含要挂载的密钥的 YAML 声明(如下所述)。使用 YAML 多行字符串或管道符编写此内容最为简单。例如:
parameters:
objects: |
- objectName: "MySecret"
objectType: "secretsmanager"
* region:一个可选字段,用于指定从 Secrets Manager 或 Parameter Store 检索密钥时使用的 AWS 区域。如果缺少此字段,provider 将从节点上的 `topology.kubernetes.io/region` 标签中查找区域。这种查找会增加挂载请求的开销,因此使用大量 pod 的集群将受益于此处提供的区域设置。
* failoverRegion:一个可选字段,用于指定在检索密钥时使用的辅助 AWS 区域。有关更多信息,请参阅本自述文件中的自动故障转移区域部分。
* pathTranslation:一个可选字段,用于指定当文件名中包含路径分隔符(Linux 上为斜杠)时要使用的替换字符。如果 Secret 或参数名称包含路径分隔符,当 provider 尝试使用该名称创建挂载文件时将发生失败。未指定时将使用下划线字符,因此 My/Path/Secret 将挂载为 My_Path_Secret。此 pathTranslation 值可以是字符串 "False" 或单个字符的字符串。当设置为 "False" 时,不执行字符替换。
* usePodIdentity:一个决定身份验证方法的可选字段。未指定时,默认使用服务账户的 IAM 角色 (IRSA)。
- 要使用 EKS Pod Identity,请使用以下任何值:"true"、"True"、"TRUE"、"t"、"T"。
- 要显式使用 IRSA,请将其设置为以下任何值:"false"、"False"、"FALSE"、"f" 或 "F"。
* preferredAddressType:一个可选字段,用于指定 Pod Identity Agent 端点通信的首选 IP 地址类型。此字段仅在使用 EKS Pod Identity 功能时适用,在使用服务账户的 IAM 角色时将被忽略。值不区分大小写。有效值为:
- "ipv4"、"IPv4" 或 "IPV4" - 强制使用 Pod Identity Agent IPv4 端点
- "ipv6"、"IPv6" 或 "IPV6" - 强制使用 Pod Identity Agent IPv6 端点
- 未指定 - 使用自动端点选择,优先尝试 IPv4 端点,如果 IPv4 失败则回退到 IPv6 端点
SecretProviderClass 的主要 objects 字段可以包含以下子字段:
* objectName:此字段为必填项。它指定要获取的密钥或参数的名称。对于 Secrets Manager,这是 [SecretId](https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html#API_GetSecretValue_RequestParameters) 参数,可以是友好名称或密钥的完整 ARN。对于 SSM Parameter Store,这是参数的 [Name](https://docs.aws.amazon.com/systems-manager/latest/APIReference/API_GetParameter.html#API_GetParameter_RequestParameters),可以是名称或参数的完整 ARN。
* objectType:当使用 Secrets Manager ARN 作为 objectName 时,此字段为可选,否则为必填。此字段可以是 "secretsmanager" 或 "ssmparameter"。
* objectAlias:此可选字段指定挂载密钥时使用的文件名。未指定时,文件名默认为 objectName。
* filePermission:此可选字段需要一个 4 位字符串,用于指定将挂载的密钥的文件权限。未指定时,默认权限为 "0644"。请确保该 4 位字符串是有效的八进制文件权限。
* objectVersion:此字段为可选,通常不建议使用,因为更新密钥需要更新此字段。对于 Secrets Manager,这是 [VersionId](https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html#API_GetSecretValue_RequestParameters)。对于 SSM Parameter Store,这是可选的 [version number](https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-paramstore-versions.html#reference-parameter-version)。
* objectVersionLabel:此可选字段指定用于版本的别名。大多数应用程序不应使用此字段,因为默认使用最新版本的密钥。对于 Secrets Manager,这是 [VersionStage](https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html#API_GetSecretValue_RequestParameters)。对于 SSM Parameter Store,这是可选的 [Parameter Label](https://docs.amazonaws.cn/en_us/systems-manager/latest/userguide/sysman-paramstore-labels.html)。
* failoverObject:使用 failoverRegion 功能时的可选字段。有关更多信息,请参阅本自述文件中的自动故障转移区域部分。failover 对象可以包含以下子字段:
* objectName:如果存在 failoverObject,则此字段为必填。指定要从故障转移区域获取的密钥或参数的名称。有关更多信息,请参见主要的 objectName 字段。
* objectVersion:此字段为可选,定义故障转移区域的 objectVersion。如果指定,它必须与主要区域的 objectVersion 匹配。有关更多信息,请参见主要的 objectVersion 字段。
* objectVersionLabel:此可选字段指定用于 failoverObject 版本的别名。有关更多信息,请参见主要的 objectVersionLabel 字段。
* jmesPath:此可选字段指定要从 JSON 格式的密钥中提取的特定键值对。您可以使用此字段将格式正确的密钥值中的键值对作为单独的密钥进行挂载。例如:考虑一个 JSON 内容如下的密钥 "MySecret":
{
"username": "testuser"
"password": "testpassword"
}
要将此密钥的 username 和 password 键值对作为单独的密钥挂载,请按如下方式使用 jmesPath 字段:
objects: |
- objectName: "MySecret"
objectType: "secretsmanager"
jmesPath:
- path: "username"
objectAlias: "MySecretUsername"
- path: "password"
objectAlias: "MySecretPassword"
如果 'path' 或 'objectAlias' 字段包含连字符,则必须用单引号对其进行转义:
- path: '"hyphenated-path"'
objectAlias: '"hyphenated-alias"'
如果您使用 jmesPath 字段,则必须提供以下两个子字段:
* path:此必填字段是用于检索的 [JMES path](https://jmespath.org/specification.html)
* objectAlias:此必填字段指定将挂载键值对密钥时使用的文件名。
您可以传递一个附加子字段来指定文件权限:
* filePermission:此可选字段需要一个 4 位字符串,用于指定将挂载的密钥的文件权限。未指定时,默认为父对象的文件权限。
## 附加注意事项
### 轮换
当使用 Secrets Store CSI driver 的可选 alpha [rotation reconciler](https://secrets-store-csi-driver.sigs.k8s.io/topics/secret-auto-rotation.html) 功能时driver 将定期重新挂载 SecretProviderClass 中的密钥。这将导致额外的 API 调用,从而产生额外费用。应用程序应使用适合其轮换策略的合理轮询间隔。建议将一小时的轮询间隔作为默认值,以减少过高的 API 成本。
想要测试轮换协调器功能的用户可以使用 helm 启用它:
```
helm upgrade -n kube-system csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver --set enableSecretRotation=true --set rotationPollInterval=3600s
```
### 自动故障转移区域
为了在连接中断期间提供可用性或用于灾难恢复配置,此 provider 支持自动故障转移功能,以从次要区域获取密钥或参数。要定义自动故障转移区域,请在 SecretProviderClass.yaml 文件中定义 failoverRegion:
```
spec:
provider: aws
parameters:
region: us-east-1
failoverRegion: us-east-2
```
当定义了 failoverRegion 时,driver 将尝试从两个区域获取密钥值。
* 如果两个区域都成功检索到密钥值,则挂载将包含主要区域中密钥的密钥值。
* 如果一个区域返回非客户端错误(代码 5XX),而另一个区域成功,则挂载将包含未发生故障区域的密钥值。
* 如果任一区域返回客户端错误(代码 4XX),则挂载将失败,并且必须解决错误原因才能成功挂载。
在主要区域和故障转移区域之间可以使用不同的密钥或参数。此示例将根据其拉取的区域使用不同的 ARN:
```
- objectName: "arn:aws:secretsmanager:us-east-1:123456789012:secret:PrimarySecret-12345"
failoverObject:
objectName: "arn:aws:secretsmanager:us-east-2:123456789012:secret:FailoverSecret-12345"
objectAlias: testArn
```
如果定义了 'failoverObject',则 objectAlias 是必需的。
### 使用 EKS Pod Identity 访问跨账户 AWS 资源
EKS Pod Identity [CreatePodIdentityAssociation](https://docs.aws.amazon.com/eks/latest/APIReference/API_CreatePodIdentityAssociation.html) 要求 IAM 角色与 EKS 集群位于同一个 AWS 账户中。
要挂载与您的 EKS 集群不在同一个 AWS 账户中的 AWS Secrets Manager 密钥,请按照 [跨账户访问](https://docs.aws.amazon.com/secretsmanager/latest/userguide/auth-and-access_examples_cross.html) 设置密钥的资源策略、KMS 密钥的密钥策略,以及 Pod Identity 关联中使用的 IAM 角色。
从 SSM Parameter Store 获取跨账户参数仅支持高级参数层中的参数。有关详细信息,请参阅 [Working with Shared Parameters](https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-shared-parameters.html)。
### 私有构建
您可以使用以下步骤拉取此 git 存储库,并将此插件构建并安装到您账户的 [AWS ECR](https://aws.amazon.com/ecr/) 注册表中。首先克隆存储库:
```
git clone https://github.com/aws/secrets-store-csi-driver-provider-aws
cd secrets-store-csi-driver-provider-aws
```
接下来,在 bash shell 变量中设置您的区域和仓库名称,以备后用:
```
export REGION=
export PRIVREPO=.dkr.ecr.$REGION.amazonaws.com/secrets-store-csi-driver-provider-aws
```
其中 **<REGION>** 是您的 Kubernetes 集群所在的 AWS 区域,**<ACCOUNT>** 是您的 AWS 账户 ID。如果您尚未创建,接下来请创建您的 ECR 存储库:
```
aws --region $REGION ecr create-repository --repository-name secrets-store-csi-driver-provider-aws # Only do this once
```
现在运行 make 来构建插件并将其推送到您账户的存储库中:
```
make
```
一旦镜像进入您的存储库,您可以从您的存储库而非公共存储库将其安装到您的集群中:
```
envsubst < deployment/private-installer.yaml | kubectl apply -f -
```
### 配置底层 Secrets Manager 客户端使用 FIPS 端点
如果您使用 Helm chart 安装 provider,请在安装步骤中附加 `--set useFipsEndpoint=true` 标志。您的安装命令将类似于
```
helm repo add aws-secrets-manager https://aws.github.io/secrets-store-csi-driver-provider-aws
helm install -n kube-system secrets-provider-aws aws-secrets-manager/secrets-store-csi-driver-provider-aws --set useFipsEndpoint=true
```
### 针对 Kubernetes API server 的客户端侧限流
为了在每个 pod 上挂载每个密钥,AWS CSI provider 通过调用 Kubernetes API 来查找 pod 的区域以及与服务账户关联的角色 ARN。如果您发现 provider 受到 API server 客户端速率限制的阻碍,您可以增加 qps 和 burst 的值。
如果您使用 Helm chart 安装 provider,请在安装步骤中附加 `--set-json 'k8sThrottlingParams={"qps": "", "burst": ""}'` 标志。
### Pod Identity 的 HTTP 超时
为了配置 Pod Identity 身份验证的 HTTP 超时,请在安装步骤中传递 `pod-identity-http-timeout` 标志:
```
helm install ... --pod-identity-http-timeout=250ms
```
超时值必须是有效的 Go duration 字符串(例如 `2s`、`500ms`)。默认情况下,超时值使用 [AWS SDK 默认值](https://github.com/aws/aws-sdk-go-v2/blob/main/aws/transport/http/client.go#L33)。
### Driver 写入密钥
默认情况下,AWS provider 负责将密钥文件写入 pod。为了改为让 Secrets Store CSI Driver 执行文件写入,请在安装步骤中将 `driver-writes-secrets` 标志设置为 `true`。
Helm 安装示例:
```
helm install -n kube-system secrets-provider-aws aws-secrets-manager/secrets-store-csi-driver-provider-aws --set driverWritesSecrets=true
```
**注意:**当启用 `driverWritesSecrets` 时,Secrets Store CSI Driver 使用 [atomic writer](https://github.com/kubernetes/kubernetes/blob/master/pkg/volume/util/atomic_writer.go) 来写入密钥文件。Atomic writer 依赖符号链接来更新文件内容。这意味着当密钥自动轮换时,读取文件元数据(例如上次更新的时间戳)需要跟随符号链接(例如,使用 `stat -L` 而不是 `stat`)。检查文件时间戳但不跟随符号链接的应用程序将看到过期的值。有关更多信息,请参阅 [相关的 Secrets Store CSI Driver 文档部分](https://secrets-store-csi-driver.sigs.k8s.io/known-limitations)。
### 安全注意事项
AWS Secrets Manager 和 Config Provider 为将密钥作为挂载文件在 pod 中访问的传统应用程序提供了兼容性。注重安全的应用程序应使用原生 AWS API 获取密钥,并可选择将其缓存在内存中,而不是存储在文件系统中。
## 安全
有关更多信息,请参阅 [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications)。
## 许可证
本项目基于 Apache-2.0 许可证授权。
标签:AWS, CSI Driver, DPI, EKS, EVTX分析, Go语言, Helm, Parameter Store, Pod安全, Secrets Manager, Secrets Store CSI Driver, Secrets Store CSI Driver Provider AWS, Secret挂载, Systems Manager, Web截图, 云服务, 亚马逊云科技, 凭据管理, 卷挂载, 子域名突变, 容器安全, 容器编排, 密钥轮换, 数据处理, 日志审计, 程序破解