Kubernetes

External Secret으로 AWS Secrets Manager와 EKS Secret 동기화하기

mokpolar 2025. 3. 15. 18:53
반응형

안녕하세요?

 

AEWS 6주차 과제로 AWS Secrets Manager로 Kubernetes Secret 관리하기라는 내용을 다뤄보겠습니다.

 

학습자료는 EKS Workshop의 Managing Secrets with AWS Secrets Manager 를 참고하였습니다.

 

Kubernetes Secret의 관리에 대해서

Kubernetes Secret이 뭔가요?

Secrets | Kubernetes 해당 공식 문서를 참고하겠습니다.

시크릿은 암호, 토큰 또는 키와 같은 소량의 중요한 데이터를 포함하는 오브젝트이다. 
이를 사용하지 않으면 중요한 정보가 파드 명세나 컨테이너 이미지에 포함될 수 있다. 
시크릿을 사용한다는 것은 사용자의 기밀 데이터를 애플리케이션 코드에 넣을 필요가 없음을 뜻한다.

 

Secret을 통해서 암호, 토큰, 키를 애플리케이션 파드와 독립적으로 관리할 수 있습니다.
예를 들어 이런 형태의 Secret 매니페스트를 통해 생성할 수 있습니다.


Key:Value 쌍으로 저장되는 형태인 것을 볼 수 있습니다.

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  USER_NAME: YWRtaW4=
  PASSWORD: MWYyZDFlMmU2N2Rm

 

위와 같이 USER_NAME, PASSWORD를 따로 정의하면 Pod에 주입할 수 있게 됩니다.
아래와 같이 생성한 secret을 Pod에 바인딩하는 형태로 동작합니다.

apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/sh", "-c", "env" ]
      envFrom:
      - secretRef:
          name: mysecret
  restartPolicy: Never

 

Secret을 사용하는 방법은 3가지가 있습니다.

  1. Pod에 직접적으로 마운트해서 파일 처럼 사용하기
  2. 마운트해서 환경 변수로 사용하기
  3. Pod image를 가져올 때 Kubelet이 pull할 때 필요한 Credential을 Secret으로 제공해서 Private repo에 접근하기

이렇게보면 Secret을 사용해서 편리하게 암호들을 관리할 수 있을 것 같습니다만,
문제가 있습니다.

 

Secret은 Kubernets 는 etcd에 저장되는데, base64 로 인코딩된 값이 평문으로 그대로 저장되기 때문입니다.

또한 Secret 매니페스트를 관리하기 위해서 GitOps등을 사용할 때, 이 값이 리포지토리에 그대로 노출된다는 문제도 있습니다.

 

그래서 클러스터 외부에서 Secret에 대한 값을 주입해줄 필요가 있습니다.

 

AWS Secret Manager가 뭔가요?

AWS Secrets Manager란 무엇인가요? - AWS Secrets Manager 해당 문서를 참고하겠습니다.

AWS Secrets Manager 는 수명 주기 전반에 걸쳐 데이터베이스 자격 증명, 애플리케이션 자격 증명, 
OAuth 토큰, API 키 및 기타 보안 암호를 관리, 검색 및 교체하는 데 도움이 됩니다. 

많은 AWS 서비스가 Secrets Manager에 보안 암호를 저장하고 사용합니다.

Secrets Manager를 사용하면 더 이상 애플리케이션 소스 코드에 하드 코딩된 보안 인증 정보가 필요하지 않으므로 
보안 태세를 개선할 수 있습니다. 

Secrets Manager에 보안 인증 정보를 저장하면 애플리케이션 또는 구성 요소를 조사할 수 있는 누군가로 인해 
손상될 가능성을 방지할 수 있습니다. 

하드 코딩된 보안 인증 정보를 Secrets Manager 서비스에 대한 런타임 호출로 대체하여 필요할 때 
동적으로 보안 인증 정보를 검색합니다.

Secrets Manager를 사용하면 암호에 대한 자동 교체 일정을 구성할 수 있습니다. 
따라서 단기 보안 암호로 장기 보안 암호를 교체할 수 있어 손상 위험이 크게 줄어듭니다. 
보안 인증 정보가 더 이상 애플리케이션에 저장되지 않으므로 보안 인증 정보를 교체할 때 
더 이상 애플리케이션을 업데이트하거나 애플리케이션 클라이언트에 변경 사항을 배포하지 않아도 됩니다.

 

위에서 언급한 Kubernetes의 문제 때문에 별도로 암호, 토큰 등을 관리해야 할 필요성이 있습니다.

 

그래서 AWS SecretManager를 통해 별도로 관리를 하고 이를 Kubernetes Pod에 주입시키는 방법을 사용하면 Secret을 Git Repo에서 관리하는 것보다 좀 더 안전할 것입니다.

 

그리고 AWS Secret Manager에서 우리가 사용할 암호나 토큰 등을 저장했다면 이것을 Kubernetes Secret과 동기화 할 방법이 필요합니다.

 

그 중 하나가 아래 설명할 External Secret Operator입니다.

External Secret Operator가 뭔가요?


External Secret Operator 공식문서
External Secret Operator를 이용하면 AWS Secret Manager와 Kubernetes의 Secret을 통합할 수 있습니다.

 

이 Operator는 추상화 계층을 통해 전체 라이프사이클을 관리하면서 AWS Secret Manager의 Secret을 Kubernetes의 Secret으로 동기화합니다.

 

이 Operator를 사용하면 자동으로 시크릿 매니저의 값을 Kubernetes Secret에 주입할 수 있습니다.

 

 

SecretStore, ExternalSecret이라는 2개의 CRD를 정의합니다.

  • SecretStore : secret provider 정보 정의
  • ExternalSecret : SecretStore에서 가져온 정보 관리

External Secret Operator로 AWS SecretManager 연동해보기

권한 설정

 

Secret Manager의 secret에 접근하기 위한 권한을 생성합니다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue",
                "secretsmanager:DescribeSecret"
            ],
            "Resource": [
                "arn:aws:secretsmanager:us-weset-2:xxxxxxxxxxxxx:secret:your/secret/credentials*"
            ]
        }
    ]
}

 

Service Account를 통해 AWS IAM Role을 사용할 수 있도록 IAM Role을 생성합니다.

{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Federated": "arn:aws:iam::xxxxxxxxx:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/xxxxxxxxxxxxxx"
        },
        "Action": "sts:AssumeRoleWithWebIdentity",
        "Condition": {
          "StringEquals": {
            "oidc.eks.us-west-2.amazonaws.com/id/xxxxxxxxxxxxxx:sub": "system:serviceaccount:external-secrets:external-secrets"
          }
        }
      }
    ]
}

 

External Secret Operator 설치

 

설치 방식은 공식 문서를 참고하겠습니다.
Getting started - External Secrets Operator

helm repo add external-secrets https://charts.external-secrets.io

helm install external-secrets \
   external-secrets/external-secrets \
    -n external-secrets \
    --create-namespace \
  # --set installCRDs=false

 

helm value에는 IRSA를 위한 annotation 정보도 포함해줘야 합니다.
위에서 생성한 Role 정보를 넣어줍니다.

serviceAccount:
  create: true
  name: external-secrets
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::xxxxxxxx:role/eks-external-secrets-role

 

그러면 관련 Pod 들이 생성된 것을 볼 수 있습니다.

k get pod -A | grep external-secret

external-secrets     external-secrets-xxxxxxxx                   1/1     Running            0          5m
external-secrets     external-secrets-xxxxxxxx                   1/1     Running            0          5m
external-secrets     external-secrets-cert-controller-xxxxxxxx   1/1     Running            0          5m
external-secrets     external-secrets-cert-controller-xxxxxxxx   1/1     Running            0          5m
external-secrets     external-secrets-webhook-xxxxxxxx           1/1     Running            0          5m
external-secrets     external-secrets-webhook-xxxxxxxx           1/1     Running            0          5m

그리고 AWS Secret Manager에 Secret을 하나 등록합니다.
key value 형태로 값을 등록해볼 수 있습니다.

 

your/secret/credential 이라는 이름으로 생성했다고 가정하겠습니다.

 

 

k -n external-secrets get sa | grep external
external-dns                                                      0        20m
external-secrets                                                  0         20m
external-secrets-cert-controller                                  0         20m
external-secrets-webhook                                          0         20m

 

Cluster Secret Store를 생성합니다.

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: "cluster-secret-store"
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: "external-secrets"
            namespace: "external-secrets"
kubectl get clustersecretstores.external-secrets.io
kubectl get clustersecretstores.external-secrets.io cluster-secret-store  -o yaml | yq '.spec'
provider:
  aws:
    auth:
      jwt:
        serviceAccountRef:
          name: external-secrets
          namespace: external-secrets
    region: us-east-1
    service: SecretsManager

 

ClusterSecretStore는 서비스 계정으로 참조되는 JSON 웹 토큰(JWT)을 사용하여 AWS Secret Manager로 인증합니다.

 

그 다음, AWS Secret Manager에서 어떤 데이터를 가져와야 하는지, 그리고 그 데이터를 어떻게 Kubernetes Secret 으로 변환해야 하는지를 정의하는 ExternalSecret을 생성해야 합니다.

 

예를 들어 아래와 같은 Application의 Key를 저장했다고 해보겠습니다.

생성한 cluster-secret-store를 참조합니다. 

 

그리고 생성 대상이 되는 secret을 target에 넣어줍니다. 

secret에 들어가는 데이터는 AWS SecreManager에 입력한 값입니다.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: example-db-secret
spec:
  secretStoreRef:
    kind: ClusterSecretStore
    name: cluster-secret-store
  target:
    name: db-secret
    creationPolicy: Owner
  data:
    - secretKey: username
      remoteRef:
        key: your/secret/credential
        property: username
    - secretKey: password
      remoteRef:
        key: your/secret/credential
        property: password

 

그러면 입력한 내용과 같이 아래와 같은 secret이 생성됩니다.

이 secret을 처음 소개했던 내용과 같이 pod에 바인딩해서 사용할 수 있습니다.

apiVersion: v1
kind: Secret
metadata:
  name: db-secret
  namespace: default
type: Opaque
data:
  username: ZGJ1c2Vy
  password: c3VwZXJzZWNyZXQxMjMh
반응형