Study_note
[kubernetes]AWS Secrets Manager를 통한 쿠버네티스 Secret관리 본문
Kuernetes Secret
Secret은 상대적으로 configmap과 비교가 많이 되는데 configmap이 쿠버네티스에서 특정 정보를 보관, 저장하는 오브젝트라 하면 Secret은 보안 정보를 보관, 저장하는 오브젝트이다.
하지만 Secret은 보안 정보를 보관, 저장한다하여 암호화된채로 저장 되지 않고 Base64로 인코딩 되어서 보관되어지며 해당 파일을 가진 모든 사람이 해당 시크릿 파일에 접근 가능하여 굉장히 보안적으로 취약한 보관 방법이다.
이런 보안취약점 때문에 AWS Secrets Manager를 사용하여 Secrets Manager 내 저장되어있는 암호들을 Kuernetes Secret의 동기화여 사용하며 해당 시크릿을 파드에 마운트하여 사용한다.
AWS Secrets Manager
aws 제공해주는 리소스로 RDS 및 그 외 암호같이 보안적인 내용들을 코드상에 직접 입력하는 것이 아니라, 원격에서 안전하게 관리할 수 있도록 지원해주는 리소스이다.
그럼 어떻게 AWS Secrets Manager와 Kuernetes Secret를 어떻게 동기화하고 마운트 할까?
이런 동작들은 Secrets Store CSI Driver를 사용하여 동기화 가능하다.
Secrets Store CSI Driver (CSI - Container Storage Interface)
Secret Store 서비스의 시크릿을 쿠버네티스의 파드에 마운트 할 수 있게 해주는 도구로
aws 에서는 Secrets Store CSI Driver에대한 AWS 보안 암호 및 구성 공급자(ASCP)를 사용하여 한다.
AWS 내부적으로 ASCP를 사용하여 Secrets Manager의 보안 암호를 EKS 포드에 권한 및 생성 순서는 아래와 같다
1. IAM 역할, 정책을 사용하여 Secrets Manager에 대한 액세스를 클러스터의 특정 Amazon EKS 포드로 제한 (IRSA 생성)
2. ASCP는 포드 자격 증명을 검색하고 IAM 역할에 대한 자격 증명을 교환
3. ASCP는 포드의 IAM 역할을 가정한 후 해당 역할에 대해 인증된 Secrets Manager에서 보안 암호를 검색 가능
생성
우선 CSI 설치
필자는 Helm으로 설치하고 생성 시 values.secrets-store.yaml 파일을 만들어 오버라이딩 했다 (Helm 글 참고)
values.secrets-store.yaml 은 아래와 같다.
syncSecret:
enabled: true
enableSecretRotation: true
syncSecret : Secrets Manager와 Kuernetes Secret을 동기화 옵션
enableSecretRotation : 2분마다(default 값) Secret 확인 변경 점 있을 시 재반영
다음으로 Helm사용하여 csi 사용하고 ASCP는 YAML을 직접 가져와 생성했다.
helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts -f values.secrets-store.yaml
helm install -n kube-system csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver
kubectl apply -f https://raw.githubusercontent.com/aws/secrets-store-csi-driver-provider-aws/main/deployment/aws-provider-installer.yaml
Secrets Manager 생성
새 보안 암호 저장 클릭 -> 다른 유형의 보안 암호 클릭 후 아래에 키/값에 시크릿 생성 -> 보안 암호 이름 eks/secret으로 생성
# 주의해야 할 점이 워커노드들이 인터넷에 액세스할 수 없는 프라이빗 서브넷일 경우 Secrets Manager에 액세스하려면 vpc 엔드포인트를 설정해야 한다.
IAM ServiceAccount 생성 (IRSA 참고)
방금 만든 Secrets Manager 접근 권한이 필요하기 때문에 IRSA 생성
우선 쿠버네티스 외부 리소스를 사용하기 때문에 OIDC 생성
eksctl utils associate-iam-oidc-provider --region="$REGION" --cluster="$CLUSTERNAME" --approve
생성할 IRSA에 할당할 권한 생성
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",
# 시크릿을 Get / Describe할 수 있는 권한을 허용
"Action": ["secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret"],
# 생성한 secretsmanager arn 입력
"Resource": ["arn:*:secretsmanager:*:*:secret:eks/secret-??????"]
} ]
}')
생성한 권한을 할당한 IRSA 생성
eksctl create iamserviceaccount --name nginx-deployment-sa \
--region="$REGION" --cluster "$CLUSTERNAME" \
--attach-policy-arn "$POLICY_ARN" \ # 방금 생성한 정책에 대한 ARN 입력
--approve \
--override-existing-serviceaccounts
처음에 생성했던 ASCP를 통해 Secrets Driver 생성
apiVersion: secrets-store.csi.x-k8s.io/v1alpha1
kind: SecretProviderClass
metadata:
name: my-secret-provider
spec:
provider: aws
secretObjects:
- secretName: my-secret # 쿠버네티스에서 생성하고 파드에 마운트 할 secret 네임
type: Opaque
data:
- key: "USERNAME" # 시크릿에 주입할 키 (my-secret이라는 시크릿에 USERNAME 키와 그 값이 주입된다.)
objectName: "USERNAME" # 볼륨에 마운트되어 있는 파일이름 (아래의 jmesPath/objectAlias)
- key: "PASSWORD"
objectName: "PASSWORD"
parameters:
objects: |
- objectName: "eks/secret" # 2번에서 등록한 시크릿 네임
objectType: "secretsmanager"
jmesPath:
- path: "USERNAME"
objectAlias: "USERNAME"
- path: "PASSWORD"
objectAlias: "PASSWORD"
objectName : 보안 암호의 이름 또는 전체 ARN ARN을 사용하는 경우 objectType을 생략 가능
objectType : objectName으로 Secrets Manager ARN을 사용하지 않는 경우에 필요
jmesPath : EKS에 마운트할 파일에 대한 보안 암호의 키 맵으로 secret키에서 각 키를 분리해주기 위해 각 키마다 파일을 분리하며 이 필드를 사용하는 경우path및objectAlias하위 필드를 포함해야 한다.
path : 보안 암호 값의 JSON에 있는 키/값 쌍의 키 값 (시크릿 매니저에 있는 키 값)
objectAlias : 볼륨에 마운트할 이름
Secrets Driver와 IRSA등 필요한것들은 다 생성해서 Deployment를 생성하면서 연결하여 배포
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
serviceAccountName: nginx-deployment-sa2 # 생성했던 IRSA 이름
volumes: # 볼륨 정의
- name: secrets-store-inline
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "my-secret-provider" # 방금 생성했던 secret provider의 이름
containers:
- name: nginx-deployment
image: nginx
ports:
- containerPort: 80
envFrom:
- secretRef:
name: my-secret # secret provider에서 설정한 secret의 이름
volumeMounts: # 마운트 path 정의
- name: secrets-store-inline # 볼륨 정의했던 이름과 동일해야한다.
mountPath: "/mnt/secrets-store"
readOnly: true
생성 되어진 파드에 원격 접속 후 마운트 했던 경로에 eks_secret파일을 확인하면 Secrets Manager에 넣어준 값 도출 가능
생성한 secret과 IRSA를 확인 하면 아래처럼 제대로 생성된것을 확인 가능
------------------------------
작업 도중 겪은 에러들
Secrets Driver 생성 시 Secrets Manager 키 값에 username이 없어 에러 발생
-> 오타로 인해 Secrets Manager 키 값이 USERNAM이였다
secretmanager에대한 권한이 없다
IRSA에 연결할 secretmanager에 대한 권한이 다른 secretmanager에 ARN로 연결되어서 권한할당 불가능
-> 권한을 재생성 후 IRSA 다시 연결하여 해결
SecretProviderClass 이름 잘못 입력
Secrets Driver에서 생성한 SecretProviderClass 이름과 Deployment에 마운트한 이름과 달라 에러 발생
-> 이것또한 오타로 인해 에러 발생 이름 수정 후 해결
참조
https://docs.aws.amazon.com/ko_kr/secretsmanager/latest/userguide/integrating_csi_driver.html
'Kubernetes' 카테고리의 다른 글
[kubernetes] 쿠버네티스 플러그인 패키지 관리자 Krew 및 자주 사용하는 플러그인 tree, neat 사용 (0) | 2022.12.01 |
---|---|
[kubernetes] EFS를 POD에 동적 마운팅 (0) | 2022.11.30 |
[kubernetes]IRSA 및 OIDC, Serviceaccount, IAM workflow (1) | 2022.11.21 |
[kubernetes] Helm Override 및 Upgrade (0) | 2022.11.17 |
[kubernetes] Helm 및 chart 커스텀 (0) | 2022.11.16 |