Study/K8s

External Secrets Operator를 통한 Kubernetes Secret 관리 (feat. AWS Secrets Manager)

Omoknooni 2024. 8. 28. 12:56

Kubernetes에는 Secrets라는 리소스가 존재한다. Secrets에는 Application에서 사용하는 Credential과 같은 민감한 정보를 저장하여 관리하도록 한다.

하지만 Secrets은 이름과는 다르게 정말 완벽한 비밀은 아니다. 이러한 Secrets는 Kubernetes 클러스터 Control plane에 위치한 etcd에 저장되는데, base64로 인코딩된 값이 평문으로 그대로 저장되기 때문이다.

 

Secrets를 정말 Secret답게 사용하기 위해 보통 크게 3가지 방법을 사용하는 것이 제시된다.

  1. RBAC를 활용해 Secrets에 접근할 수 있는 User를 제한하는 방법
  2. 외부 Secret Management 서비스를 이용해 저장 후, 클러스터로 가져와서 사용하는 방법
  3. Secrets가 저장되는 클러스터의 ETCD에 저장될때 암호화하는 방법 (ETCD 암호화)

이번에는 Secrets 리소스를 AWS Secrets Manager와 같은 외부 서비스와 연동시켜 사용해보도록 한다.

 

External Secrets Operator

ESO(External Secrets Operator)는 AWS의 Secrets Manager, GCP의 Secret Manager, Azure의 Key Vault와 같은 퍼블릭 클라우드 서비스가 제공하는 민감정보들을 관리할 수 있는 서비스에서 값을 가져와 Kubernetes의 Secrets로 주입할 수 있도록 도와주는 오픈소스 툴이다. 

 

GitHub - external-secrets/external-secrets: External Secrets Operator reads information from a third-party service like AWS Secr

External Secrets Operator reads information from a third-party service like AWS Secrets Manager and automatically injects the values as Kubernetes Secrets. - external-secrets/external-secrets

github.com

 

ESO의 목적은 외부 Secret Management 서비스 API를 이용해 Secret들을 Kubernetes 클러스터의 Secret과 동기화하는 것이다.

 

 

ESO는 CustomResourceDefinitions(CRD)을 통해 SecretStore ExternalSecrets 리소스를 정의한다.

SecretStore로는 접근할 secret provider의 정보를 정의하고, ExternalSecrets로는 SecretStore로 부터 가져온 Secret을 관리한다.

 

또한 위 두가지 리소스(SecretStore, ExternalSecret)을 클러스터 단위로 선언해 클러스터 전체에 적용할 수 있는  ClusterSecretStoreClusterExternalSecret 리소스도 존재한다. 

 

클러스터에 ESO 설치

먼저, 외부 Secrets Management 서비스와 Kubernetes의 Secret을 연동하기 위해 ESO를 클러스터에 설치해야한다.

helm 차트를 이용해 클러스터에 ESO를 설치한다.

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

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

 

ESO를 설치하면 클러스터에 SecretStore와 ExternalSecrets CRD를 비롯해 Cert controller와 같은 ESO 관련 pod 리소스가 구축되는것을 확인할 수 있다.

 

AWS Secrets Manager에 Secrets 등록

ESO 연동 테스트를 위해 간단하게 AWS Secrets Manager에 Secret을 하나 등록해보도록 한다.

 

 

ServiceAccount & IRSA 생성

kubernetes 클러스터에서 AWS 서비스인 Secrets Manager에 접근해야 하기에 권한이 필요하다. 이 권한은 ServiceAccount에 IRSA를 연결함으로써 할당받도록 한다.

EKS 클러스터에 대한 IRSA 설정은 아래 글을 참고하도록 한다.

 

EKS 클러스터에서의 IAM Role, IRSA (IAM Roles for ServiceAccount)

EC2 인스턴스내에서 AWS 서비스에 접근하기 위해서는 IAM User의 AccessKey Credential을 가져와서 사용하거나, 인스턴스에 IAM Role을 지정한 뒤 사용하고는 했다.그렇다면 Kubernetes의 클러스터에 존재하는

blog.omoknooni.me

 

IRSA로 연결할 IAM Role은 Secrets Manager에 접근가능해야하기에 다음과 같은 정책을 연결한다.

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

 

 

IAM Role과 Policy를 연결한 뒤, 클러스터에서 eso-irsa 이름의 SA를 생성한다.

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: [IRSA 연결할 Role]
  name: eso-irsa

 

외부에 보관된 Secret 획득 및 저장

AWS Secrets Manager에 저장된 Secret을 가져오기 위해 SecretStore와 ExternalSecret 리소스를 생성한다.

클러스터가 Secret을 fetch해올 외부 Management 서비스를 선언한 SecretStore를 다음과 같이 생성해준다.

(클러스터 전체에서 접근가능한 ClusterSecretStore를 생성하는 경우, kind만 변경해주면 된다.)

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secretmanager
spec:
  provider:
    aws:
      service: SecretsManager
      region: ap-northeast-2
      auth:
        jwt:
          serviceAccountRef:      # IRSA 연결한 ServiceAccount
            name: eso-irsa

 

 

그리고 이 SecretStore에서 가져올 Secret 데이터를 선언한 ExternalSecret을 다음과 같이 생성해준다.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: eso-secret
spec:
  secretStoreRef:            # Secret을 가져올 SecretStore 작성
    name: aws-secretmanager
    kind: SecretStore
  refreshInterval: 1h
  target:
    name: secret-from-eso    # 가져온 값으로 클러스터에 생성할 Secret object의 이름
  data:
    - secretKey: api-key
      remoteRef:             # Secrets Manager에 저장된 Secret
        key: eso-test
        property: APIKey

 

 

ExternalSecret 리소스까지 생성하면, 클러스터의 Secrets 리소스를 확인하면 SecretStore에서 가져온 Secret이 하나 생성된 것을 확인할 수 있다.

 

이 Secret을 사용하는 Pod를 생성하고 내용을 확인할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  namespace: default
spec:
  containers:
  - name: my-container
    image: nginx
    env:
    - name: API_KEY
      valueFrom:
        secretKeyRef:        # 클러스터의 Secret object를 참조
          name: secret-from-eso
          key: api-key

 

Pod에 접속 후 환경변수로 가져온 Secret object의 내용을 확인할 수 있다.

 

 

이렇게 클러스터와 외부 Secret Management 서비스와 연동해 Management 서비스에 저장된 Secret을 클러스터로 가져오고 갱신하는 과정을 알아보았다.

하지만 이렇게 가져온 Secret도 클러스터상에서는 base64 인코딩된 평문으로 저장되기에 여전히 노출에 대한 문제는 존재한다.

다음에는 클러스터의 Secret 리소스를 암호화할 수 있는 저장소 ETCD의 암호화를 알아보도록 한다.