본문 바로가기
Orchestration/Kubernetes

Kubernetes에서 External Secrets Operator(ESO)로 안전하게 Secrets 관리하기

by wlsdn3004 2025. 3. 26.
728x90
반응형

Kubernetes에서 Secret 관리의 현실

Kubernetes 환경에서 Secret 관리는 보안과 운영 측면에서 매우 중요하다.

Kubernetes를 운영하다 보면 애플리케이션에 필요한 다양한 자격 증명, 토큰, API Key, ARN 정보 등을 Helm Chart나 CI 파이프라인에 넣어야 하는 상황이 자주 발생한다. 이때 많은 팀들이 빠르고 간편하다는 이유로 값을 하드코딩하거나, Kubernetes Secret을 수동으로 만들어 사용한다.

 

예를 들어, Helm chart의 values.yaml 파일에 AWS 리소스의 ARN이나 API 키를 그대로 적어 사용하는 경우, Jenkins와 같은 CI/CD 도구에 Secrets Manager의 자격 증명을 수동으로 등록한 경우, Secret 값이 변경될 때마다 직접 찾아서 수정하고 배포까지 다시 진행해야 하는 경우가 있는데, 이러한 방식은 단기적으로는 편해 보일 수 있지만, 시간이 지나면 다음과 같은 문제가 발생할 수 있다.

  • 보안 리스크: Git에 민감 정보가 포함될 수 있고, 공유된 자격 증명이 노출될 가능성 증가
  • 운영 불편: Secret이 바뀔 때마다 여러 배포 환경에 수동으로 반영해야 하는 번거로움
  • 자동화 한계: GitOps나 Helm 배포 자동화와 충돌하여 관리 및 운영 복잡성가 증가

 

기존에 사용하던 kubernetes-external-secrets(KES)의 한계

과거에는 kubernetes-external-secrets (KES)라는 오픈소스를 통해 AWS Secrets Manager나 Parameter Store에서 Secret을 가져와 Kubernetes Secret으로 동기화하는 방식이 많이 사용되었다.
그러나 KES는 구조적인 한계와 확장성 부족의 문제로 현재 공식적으로 deprecated 되어 유지보수가 중단되었다.

 

대신, 이에 대한 공식적인 후속 프로젝트로 CNCF에서 관리하는 External Secrets Operator(ESO)가 등장했다.

참고

KES에서 ESO로 마이그레이션 한다면 ESO 마이그레이션 도구를 사용하여 전환하는 것이 권장된다.

 

External Secrets Operator 란?

External Secrets Operator(이하 ESO)는 AWS Secrets Manager, HashiCorp Vault, Google Secrets Manager 등 다양한 외부 비밀 관리 시스템을 통해 Kubernetes 환경에서 Secret을 쉽게 관리할 수 있도록 도와주는 오픈소스 솔루션이다.

 

ESO의 가장 큰 장점은 외부 API를 통해 비밀 정보를 자동으로 동기화하여 Kubernetes Secrets을 사용할 수 있다는 점이다. 이를 통해 외부 시스템에서 Secret이 변경되면, Kubernetes에서 자동으로 이를 반영할 수 있어 운영 부담을 크게 줄일 수 있다.

위 그림에서 ESO 관련 리소스들의 역할은 다음과 같다.

SecretStore

  • 외부 비밀 관리 시스템과 연결하고, Secret 값을 가져올 수 있도록 인증 정보와 접근 방법을 정의하는 리소스.
  • SecretStore는 네임스페이스에 종속적인 반면, ClusterSecretStore는 클러스터 수준에서 비밀 관리 시스템 설정을 제공한다.

ExternalSecret

  • 외부 비밀 관리 시스템에서 어떤 Secret을 가져와 Kubernetes Secret으로 생성 또는 동기화할지 정의하는 리소스.

ESO(External Secrets Operator)

  • 외부 비밀 관리 시스템과 Kubernetes 간의 Secret을 동기화하는 컨트롤러.

 

 

본 글에서는 실습을 통해 ESO를 구성하고, AWS Secrets Manager와 EKS 클러스터 내에서 Kubernetes Secrets을 동기화하는 과정을 다룬다.

 

 

구성 환경

  • Amazon EKS v1.30
  • Helm cli : 3.17.2

설치 버전

  • ESO Helm Chart : 0.15.0
  • ESO Helm APP : 0.15.0

전제 조건

  • AWS 계정
  • AWS Secrets Manager 생성
  • AWS Iam Role 생성
  • Amazon EKS 클러스터

 

 

1.  External Secrets Operator 설치


Helm을 사용하여 설치한다.

$ helm repo add external-secrets https://charts.external-secrets.io
"external-secrets" has been added to your repositories

$ helm install external-secrets \
   external-secrets/external-secrets \
    -n external-secrets \
    --create-namespace

 

설치가 완료되면 ESO의 Custom Resource Definitions(CRD)가 생성된다.

$ kubectl get crds | grep external-secrets

 

ESO가 정상적으로 구동되었는지 확인하기 위해 external-secrets 네임스페이스의 Pod 상태를 조회한다.

$ kubectl get po -n external-secrets
NAME                                                READY   STATUS    RESTARTS   AGE
external-secrets-68655687d8-ztbk6                   1/1     Running   0          62s
external-secrets-cert-controller-7fcdd69449-ccppg   1/1     Running   0          62s
external-secrets-webhook-5fcb64844c-nwtnx           1/1     Running   0          62s

 

 

2. AWS IRSA와 SecretStore 설정을 통한 AWS Secrets Manager 권한 획득


참고

AWS Secrets Manager와 IAM Role 생성 과정은 생략한다

 

ESO에서 AWS IAM Role을 사용하여 AWS Secrets Manager에 접근하려면 IRSA(AWS IAM Roles for Service Accounts) 설정이 필요하다.

 

먼저, AWS IAM Role과 AWS Secrets Manager를 생성한 후, 해당 IAM Role에 다음과 같은 정책을 추가한다.
이때, Resource 항목에는 각 환경에 맞는 AWS Secrets Manager의 ARN을 입력하면 된다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "secretsmanager:ListSecrets",
                "secretsmanager:BatchGetSecretValue"
            ],
            "Effect": "Allow",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetResourcePolicy",
                "secretsmanager:GetSecretValue",
                "secretsmanager:DescribeSecret",
                "secretsmanager:ListSecretVersionIds"
            ],
            "Resource": [
                "arn:aws:secretsmanager:ap-northeast-2:{account}:secret*"
            ]
        }
    ]
}

 

앞에서 생성한 IAM Role에 다음과 같이 신뢰 관계(Trust Relationships)을 설정한다.

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

 

external-secrets Pod가 IAM Role 권한을 얻을 수 있도록 해당 Pod가 사용하는 ServiceAccount에 IAM Role 정보를 추가한다.

$ kubectl edit sa -n external-secrets external-secrets
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    #... 추가
    eks.amazonaws.com/role-arn: arn:aws:iam::{account}:role/external-secrets
    #... 중략

 

AWS Secrets Manager와 연결을 위해 SecretStore 리소스를 배포한다.

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secretsmanager
  namespace: external-secrets
spec:
  provider:
    aws:
      service: SecretsManager
      region: ap-northeast-2
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets

 

SecretStore 리소스가 AWS Secrets Manager와 정상적으로 연결되었는지 확인한다.

다음과 같이 STATUS가 Valid이고, READY가 True이면 정상적으로 연결된 것이다.

$ kubectl get secretstores.external-secrets.io -n external-secrets
NAME                 AGE   STATUS   CAPABILITIES   READY
aws-secretsmanager   96m   Valid    ReadWrite      True

 

 

3. ExternalSecret 설정 및 Kubernetes Secret 자동 생성


EKS에서 AWS Secrets Manager 기반으로 Kubernetes Secret을 생성할 수 있게 ExternalSecret을 생성한다.

주요 설정 내용은 다음과 같다.

  • target
    • name : EKS 클러스터 내에서 생성될 Kubernetes Secret 이름
  • data
    • secretKey : Kubernetes Secret 내에서 사용할 키 이름
    • remoteRef
      • key : AWS Secrets Manager Secret 이름
      • property : AWS Secrets Manager에서 가져올 Secret key 이름

ExternalSecret의 YAML 내용을 다음과 같이 작성한 후 kubectl 명령어를 사용하여 적용한다.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: example
  namespace: external-secrets
spec:
  refreshInterval: 1m
  secretStoreRef:
    name: aws-secretsmanager
    kind: SecretStore
  target:
    name: awssecret
    creationPolicy: Owner
  data:
  - secretKey: key
    remoteRef:
      key: {AWS Secrets Manager name}
      property: {AWS Secrets Manager Secret key}

 

ExternalSecret을 적용 후 상태를 확인하면 "SecretSynced" 상태가 표시된다. 이 상태는 ExternalSecret 리소스가 정상적으로 AWS Secrets Manager에서 Secret을 가져와 Kubernetes Secret으로 동기화되었음을 의미한다.

$ kubectl get externalsecrets.external-secrets.io -n external-secrets
NAME      STORETYPE     STORE                REFRESH INTERVAL   STATUS         READY
example   SecretStore   aws-secretsmanager   1m                 SecretSynced   True

 

이전에 설정한 ExternalSecret의 spec에 따라 "awssecret" Kubernetes Secret이 정상적으로 생성되었는지 확인한다.

$ kubectl get secrets -n external-secrets awssecret
NAME        TYPE     DATA   AGE
awssecret   Opaque   1      13m

주의

만약 생성이 안된다면 Kubernetes RBAC 및 AWS Iam Role 권한을 확인한다.

기본적으로 설치 과정에서 ClusterRole, ClusterRoleBinding, Role, RoleBinding 리소스가 자동으로 설정된다.

 

AWS Secrets Manager에서 설정한 값과 같은지 확인하려면, kubectl 명령어로 생성된 Kubernetes Secret을 확인하고, AWS Secrets Manager에서 해당 Secret을 직접 확인하여 값을 비교할 수 있다.

  • AWS Secrets Manasger 

  • Kubernetes Secret 
$ kubectl get secrets -n external-secrets awssecret -ojsonpath="{.data.key}" | base64 -d
test

 

이제부터 AWS Secrets Manager에서 Secret 값을 변경하면 Kubernetes Secret의 값도 변경된다.

 

응용 1.

AWS Secrets Manager의 모든 Key-Value를 Kubernetes Secret으로 한 번에 가져오려면 다음과 같이 설정한다.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: example
  namespace: external-secrets
spec:
  refreshInterval: 1m
  secretStoreRef:
    name: aws-secretsmanager
    kind: SecretStore
  target:
    name: awssecret
    creationPolicy: Owner
  dataFrom:
    - extract:
        key: {AWS Secrets Manager name}

 

응용 2.

AWS Secrets Manager의 모든 Key 중 특정 Key만 선택해서 가져오려면 다음과 같이 설정한다.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: example
  namespace: external-secrets
spec:
  refreshInterval: 1m
  secretStoreRef:
    name: aws-secretsmanager
    kind: SecretStore
  target:
    name: awssecret
    creationPolicy: Owner
    template:
      data:
        key1: "{{ .key_name1 }}"
        key2: "{{ .key_namme2 }}"    
  dataFrom:
    - extract:
        key: {AWS Secrets Manager name}

 

 

마무리


이번 글에서는 External Secrets Operator (ESO)를 사용하여 AWS Secrets Manager와 EKS 클러스터 간에 비밀 정보를 자동으로 동기화하는 방법을 실습해 보았다. 이와 같은 자동화된 비밀 관리 방식은 보안성과 운영 효율성을 크게 향상시키고, 비밀 정보의 갱신이 자동으로 이루어지기 때문에 운영자 입장에서 수동 갱신에 대한 부담을 줄이는 데 큰 도움이 된다.

 

ESO와 유사한 기능을 제공하는 도구로  VSO(Vault Secrets Operator)가 있다. VSO와 ESO는 비슷한 목적을 가지고 있지만, 주요 차이점은 VSO는 HashiCorp Vault와의 통합에 특화되어 있는 반면, ESO는 여러 외부 비밀 관리 시스템과의 연동을 지원한다는 점이다.

 

이러한 점에서 ESO는 Kubernetes 기반의 인프라 환경에서 유연하고 확장성 있는 Secret 관리 전략을 구현하고자 하는 조직에게는 좋은 선택지가 될 수 있다.

반응형

댓글