在現代雲原生應用程式開發中,保護敏感資料至關重要。本文將詳細介紹如何使用 Terraform 建立和管理 AWS Secrets Manager 中的秘密,並將其安全地整合到 Amazon EKS 叢集中。首先,我們會利用 Terraform 建立 Secrets Manager 的秘密資源,並設定必要的副本區域以提升容錯能力。接著,我們會建立專屬的 IAM 角色和策略,以限制 EKS 叢集對 Secrets Manager 的存取許可權,僅允許讀取特定秘密。然後,我們會安裝 AWS Secrets Manager CSI 提供者,這個提供者扮演了橋樑的角色,讓 Kubernetes Pod 可以直接掛載 Secrets Manager 中的秘密。最後,我們會建立 Kubernetes Secret Provider Class、Service Account 和 Pod,並示範如何將秘密安全地掛載到 Pod 中,讓應用程式得以存取。文章中會提供完整的程式碼範例和操作步驟,並輔以圖表說明,讓讀者更容易理解和實作。此外,我們還會探討如何使用 CloudTrail 和 VPC Flow Logs 進行日誌稽核和監控,以進一步提升安全性。
將 AWS Secrets Manager 與 EKS 整合
利用 Terraform 建立 AWS Secrets Manager
首先,我們需要在 Terraform 中定義 AWS Secrets Manager 的秘密資源。這裡我們會建立一個秘密並指定其副本區域,以確保災難還原能力。
resource "aws_secretsmanager_secret" "ksm_service_token" {
name = "ksm_service_token"
description = "KSM service token secret"
recovery_window_in_days = 30
replica {
region = "eu-central-1"
}
}
在 replica 區塊中,我們指定了秘密的副本區域,這樣可以確保在發生災難時仍能還原秘密。recovery_window_in_days 區塊則定義了設定刪除後可以在多少天內還原該秘密。
接著,我們為這個秘密新增一個版本:
resource "aws_secretsmanager_secret_version" "ksm_service_token_first_version" {
secret_id = aws_secretsmanager_secret.ksm_service_token.id
secret_string = "a-service-token"
}
這是我們第一次與 AWS Secrets Manager 互動。我們建立了一個秘密以及該秘密的版本,其中包含了一個字串。
建立 IAM 角色與策略
接下來,我們需要建立一個具有必要 IAM 憑證的角色:
resource "aws_iam_role" "eks_secret_reader_role" {
name = "eks-secret-reader"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
Federated = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${module.ksm_eks.oidc_provider}"
},
Action = "sts:AssumeRoleWithWebIdentity",
Condition = {
StringEquals = {
"${module.ksm_eks.oidc_provider}:aud" = "sts.amazonaws.com",
"${module.ksm_eks.oidc_provider}:sub" = "system:serviceaccount:default:service-token-reader"
}
}
}
]
})
}
這個角色是為 EKS 特別設計的。它是 Kubernetes 叢集中的工作負載身份對映到的一個角色,具有限制性,無法對其他 AWS 資源進行操作。
接著,我們附加一個 IAM 憑證到這個角色:
resource "aws_iam_role_policy_attachment" "esrrs" {
policy_arn = aws_iam_policy.ksm_service_token_reader.arn
role = aws_iam_role.eks_secret_reader_role.name
}
內容解密:
這段程式碼的主要功能是為 EKS 叢集中的 Pod 提供一個具備讀取 AWS Secrets Manager 中秘密資料許可權的 IAM 偽造身份。
resource "aws_iam_role" "eks_secret_reader_role" {
這行程式碼宣告了一個名為 eks_secret_reader_role 的 IAM 偽造身份。
name = "eks-secret-reader"
指定了該角色的名稱為 eks-secret-reader。
assume_role_policy = jsonencode({
宣告了該角色的信任策略,這是一段 JSON 編碼的策略檔案。
Version = "2012-10-17",
指定了策略檔案的版本為 2012-10-17。
Statement = [
{
宣告了一個陣列包含多條宣告。
Effect = "Allow",
每條宣告中的第一項是效果(Effect),這裡設定為允許(Allow)。
Principal = {
指定了允許的主要方(Principal)。
Federated = ...
表示使用 OpenID Connect(OIDC)聯盟身份驗證。
Action = ...
允許的操作(Action)是 sts:AssumeRoleWithWebIdentity,這表示允許使用 Web 身份假冒該角色。
Condition =
{
條件部分指定了當條件滿足時才允許假冒該角色。
StringEquals =
{
}
使用 StringEquals 條件來比對特定值。
aws_iam_role_policy_attachment`
這行程式碼宣告了一個名為 esrrs 的資源附加策略到 IAM 偽造身份。
policy_arn =
指定了要附加的策略 ARN。
role =
指定了要附加策略的角色名稱。
安裝 AWS Secrets Manager CSI 提供者
接下來,我們需要在 EKS 叢集中安裝 AWS Secrets Manager 的 CSI 提供者。首先,我們需要新增 Secrets Store CSI Driver 外掛:
$ 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
$ 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
以上命令將安裝 Secret Store CSI Driver 和 AWS Secrets Manager CSI 提供者到 EKS 叢集中。
驗證安裝
我們可以透過以下命令來驗證 Secret Store CSI Driver 的安裝狀態:
$ kubectl get daemonset -n kube-system
此圖示展示 DaemonSet 的狀態和詳細資訊。
內容解密:
在 Kubernetes 中,DaemonSet 是一種特殊型別的控制器物件。它會確保在叢集中的每個節點上都執行一個 Pod 的副本。這對於需要在叢集中所有節點上執行某種服務或代理程式非常有用。例如,日誌收集、監控、網路代理等服務通常會使用 DaemonSet。 上面 Plantuml 語法描繪出 DaemonSet 的核心架構與其屬性關係。
執行 Kubernetes 操作
首先,我們需要透過 Secret Provider Class 來對映秘密:
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: service-token
spec:
provider: aws
parameters:
objects: |
- objectName: "arn:aws:secretsmanager:eu-west-1:111111111:secret:service_token-IJ2VLg"
這裡我們將 Kubernetes Secret 與 AWS Secrets Manager 提供的秘密進行對映。
接著,我們需要建立一個具有讀取該秘密許可權的 Kubernetes Service Account:
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
eks.amazonaws.com/role-arn: "arn:aws:iam::111111111:role/eks-secret-reader"
name: service-token-reader
namespace: default
建立 Pod 與掛載秘密
最終,我們建立一個 Pod ,並將秘密掛載到該 Pod 中:
kind: Pod
apiVersion: v1
metadata:
name: nginx
spec:
serviceAccountName: service-token-reader
containers:
- image: nginx
name: nginx
volumeMounts:
- name: secret-from-asm
mountPath: "/mnt/secrets-store"
readOnly: true
volumes:
- name: secret-from-asm
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "service-token"
內容解密:
此圖示展示 Kubernetes Pod 與 ServiceAccount 和 Volumes 的關係
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title EKS 安全整合 AWS Secrets Manager 實戰
package "Kubernetes Cluster" {
package "Control Plane" {
component [API Server] as api
component [Controller Manager] as cm
component [Scheduler] as sched
database [etcd] as etcd
}
package "Worker Nodes" {
component [Kubelet] as kubelet
component [Kube-proxy] as proxy
package "Pods" {
component [Container 1] as c1
component [Container 2] as c2
}
}
}
api --> etcd : 儲存狀態
api --> cm : 控制迴圈
api --> sched : 調度決策
api --> kubelet : 指令下達
kubelet --> c1
kubelet --> c2
proxy --> c1 : 網路代理
proxy --> c2
note right of api
核心 API 入口
所有操作經由此處
end note
@enduml此段程式碼定義了一個名為 nginx 的 Pod, 語法如下:
kind : Pod;
apiVersion : v1;
metadata 包含了Pod 的 metadata資訊, 標題如下:
metadata :
name : nginx;
spec 包含了Pod 的規格, 標題如下:
spec :
serviceAccountName : service-token-reader;
containers : [{image : nginx, name : nginx}];
volumeMounts :
[
{name : secret-from-asm, mountPath : "/mnt/secrets-store", readOnly : true}
];
volumes 包含了Volumes 的規格, 標題如下:
volumes :
[
{name : secret-from-asm,
csi :
{driver : secrets-store.csi.k8s.io,
readOnly : true,
volumeAttributes :
{secretProviderClass : service-token}
}
}
];
在這段程式碼中, ServiceAccountName 指向具備 AWS IAM Role 許可權且能從 AWS Secrets Manager 提取秘密資料之 ServiceAccount。Containers 裡面定義了一個名稱為 nginx 的容器, volumeMounts 裡面則將名稱為 secret-from-asm 的 Volume Mount 上。最後 Volumes 中則定義了一個名稱為 secret-from-asm 的 Volume , Volume 是透過 CSI Driver 的方式以 ReadOnly 模式掛載上去並且附加了一些 Volume 屬性 (例如說SecretProviderClass),透過以上方式即可將從 AWS Secrets Manager 提取到之秘密資料掛載於該容器內並且位於 /mnt/secrets-store/ 路徑下。
測試掛載後的秘密
最後,我們可以透過以下命令來測試掛載後的秘密:
$ kubectl exec -it nginx cat /mnt/secrets-store/
日誌稽核與監控
完成上述步驟後,我們已經成功地建立並安全地存取 AWS Secrets Manager 中的秘密。接下來,我們可以透過 CloudTrail 和 VPC Flow Logs 提供更詳細的日誌記錄與監控。
透過 CloudTrail ,可以記錄所有對 AWS Secrets Manager 操作的 API 請求和回應。此外,VPC Flow Logs 則可以記錄所有進出 VPC 的網路流量。
總結來說,透過以上步驟,我們成功地將 AWS Secrets Manager 與 EKS 整合在一起,並確保了秘密資料的安全存取和管理。