External Secrets
External Secrets는 시크릿 관리를 강화하는 Kubernetes 오퍼레이터예요. CloudNativePG와 함께 사용해 PostgreSQL 사용자 패스워드를 자동 관리하고, 외부 KMS와 통합하는 방법을 예시로 살펴볼게요.
출처: 문서
본문
External Secrets는 TAG Security의 후원으로 2022년에 승인된 CNCF Sandbox 프로젝트예요.
소개
**External Secrets Operator(ESO)**는 시크릿의 저장을 Kubernetes 자체로부터 분리해 시크릿 관리를 강화하는 Kubernetes 오퍼레이터예요. 외부 시크릿 관리 시스템과 네이티브 Kubernetes Secret 리소스 사이의 원활한 동기화를 가능하게 해요.
ESO는 다양한 백엔드를 지원해요:
…그리고 더 많아요. 지원되는 프로바이더의 전체 최신 목록은 공식 External Secrets 문서를 참고하세요.
PostgreSQL 및 CloudNativePG와의 통합
PostgreSQL 데이터베이스와 관련해 External Secrets는 두 가지 주요 사용 사례로 CloudNativePG와 원활하게 통합돼요:
-
자동 패스워드 관리: ESO는 Kubernetes
Secret리소스에 저장된 데이터베이스 사용자 패스워드의 자동 생성과 회전을 처리할 수 있어, 클러스터 안에서 실행되는 애플리케이션이 항상 최신 자격 증명에 접근할 수 있게 보장해요. -
크로스 플랫폼 시크릿 접근:
SecretStore리소스를 통해 외부 KMS(Key Management Service)와 패스워드를 투명하게 동기화할 수 있어요. 이를 통해 Kubernetes 클러스터 외부의 애플리케이션과 개발자(쿠버네티스 시크릿에 접근하지 못할 수 있는 사람들)가 외부 KMS에서 직접 데이터베이스 자격 증명을 검색할 수 있게 해줘요.
예시: External Secrets로 자동 패스워드 관리
퀵스타트 가이드의 cluster-example Postgres 클러스터에서 app 사용자의 패스워드를 24시간마다 자동 회전하는 방법을 살펴볼게요.
:::info[Important]
진행하기 전에 cluster-example Postgres 클러스터가 환경에서 실행 중인지 확인해 주세요.
:::
기본적으로 CloudNativePG는 cluster-example 클러스터의 app 사용자 자격 증명이 포함된 cluster-example-app이라는 Kubernetes Secret을 생성하고 관리해요. 자세한 내용은 "Connecting from an application" 섹션에서 읽을 수 있어요.
External Secrets의 목표는:
- 패스워드를 생성하는 방법을 지정하는
Password생성기를 정의하는 것. password와pgpass필드만 업데이트해cluster-example-app시크릿을 동기화 상태로 유지하는ExternalSecret리소스를 만드는 것.
패스워드 생성기 만들기
다음 예시는 default 네임스페이스에 pg-password-generator라는 Password 생성기 리소스를 만들어요. 필요에 따라 이름과 속성을 커스터마이즈할 수 있어요:
apiVersion: generators.external-secrets.io/v1alpha1
kind: Password
metadata:
name: pg-password-generator
spec:
length: 42
digits: 5
symbols: 5
symbolCharacters: "-_$@"
noUpper: false
allowRepeat: true
이 사양은 생성된 패스워드의 특성을 정의하며, 길이와 숫자, 기호, 대문자 포함 여부가 포함돼요.
외부 시크릿(External Secret) 만들기
아래 예시는 cluster-example-app-secret이라는 ExternalSecret 리소스를 만들어 24시간마다 패스워드를 갱신해요. Merge 정책을 사용해 cluster-example-app 시크릿의 지정된 필드(password, pgpass, jdbc-uri, uri)만 업데이트해요.
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: cluster-example-app-secret
spec:
refreshInterval: "24h"
target:
name: cluster-example-app
creationPolicy: Merge
template:
metadata:
labels:
cnpg.io/reload: "true"
data:
password: "{{ .password }}"
pgpass: "cluster-example-rw:5432:app:app:{{ .password }}"
jdbc-uri: "jdbc:postgresql://cluster-example-rw.default:5432/app?password={{ .password }}&user=app"
uri: "postgresql://app:{{ .password }}@cluster-example-rw.default:5432/app"
dataFrom:
- sourceRef:
generatorRef:
apiVersion: generators.external-secrets.io/v1alpha1
kind: Password
name: pg-password-generator
cnpg.io/reload: "true" 레이블은 시크릿이 변경될 때 CloudNativePG가 데이터베이스의 사용자 패스워드 리로드를 트리거하도록 보장해요.
구성 검증하기
ExternalSecret가 올바르게 동기화되고 있는지 확인하려면:
kubectl get es cluster-example-app-secret
패스워드가 실시간으로 갱신되는 것을 관찰하려면 refreshInterval을 임시로 30s로 줄이고 다음 명령을 반복 실행해 보세요:
kubectl get secret cluster-example-app \
-o jsonpath="{.data.password}" | base64 -d
패스워드가 30초마다 변경되는 것을 볼 수 있어야 하며, 회전이 올바르게 동작하고 있음을 확인할 수 있어요.
더 많은 것들
위 예시는 CloudNativePG가 생성한 기본 cluster-example-app 시크릿에 초점을 맞추지만, 같은 접근 방식을 직접 만든 임의의 커스텀 시크릿이나 PostgreSQL 사용자에 확장해 패스워드를 주기적으로 회전시킬 수 있어요.
예시: 외부 KMS와의 통합
CNCF 생태계에서 가장 널리 사용되는 KMS(Key Management Service) 프로바이더 중 하나는 HashiCorp Vault예요. Vault가 Business Source License(BUSL)로 라이선스되지만, 완전히 호환되고 활발히 유지 관리되는 오픈소스 대안인 OpenBao가 있어요. OpenBao는 HashiCorp Vault와 동일한 모든 인터페이스를 지원하므로 진정한 드롭인 대체물이에요.
이 예시에서는 CloudNativePG, External Secrets Operator, HashiCorp Vault를 통합해 PostgreSQL 패스워드를 자동으로 회전하고 Vault에 안전하게 저장하는 방법을 보여줄게요.
:::info[Important] 이 예시는 HashiCorp Vault가 이미 설치되어 있고 환경에서 제대로 구성되었으며, 팀이 이를 운영하는 데 필요한 전문성을 갖추고 있다고 가정해요. Vault를 배포하는 방법은 다양하며, 이를 자세히 다루는 것은 CloudNativePG의 범위를 벗어나요. Vault를 Kubernetes 안에서 실행하는 것도 가능하지만, 보통은 외부에 배포돼요. 자세한 지침은 HashiCorp Vault 문서를 참고하세요. :::
이전 예시에서 이어서, 이제 Vault와의 통합을 완성하는 데 필요한 SecretStore와 PushSecret 리소스를 만들게요.
SecretStore 만들기
이 예시에서는 HashiCorp Vault가 네임스페이스 안에서 http://vault.vault.svc:8200으로 접근 가능하고, 같은 네임스페이스에 Vault 인증에 사용할 토큰이 포함된 vault-token이라는 Kubernetes Secret이 존재한다고 가정해요.
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "http://vault.vault.svc:8200"
path: "secrets"
# Specifies the Vault KV secret engine version ("v1" or "v2").
# Defaults to "v2" if not set.
version: "v2"
auth:
# References a Kubernetes Secret that contains the Vault token.
# See: https://www.vaultproject.io/docs/auth/token
tokenSecretRef:
name: "vault-token"
key: "token"
---
apiVersion: v1
kind: Secret
metadata:
name: vault-token
data:
token: aHZzLioqKioqKio= # hvs.*******
이 구성은 vault-backend라는 SecretStore 리소스를 만들어요.
:::info[Important] 이 예시는 API와 CLI 사용 사례 테스트에 적합한 기본 토큰 기반 인증을 사용해요. Vault에서 기본적으로 활성화된 방법이지만 프로덕션 환경에는 권장되지 않아요. 프로덕션에서는 더 안전한 인증 방법을 고려하세요. 지원되는 인증 메커니즘의 전체 목록은 External Secrets Operator 문서를 참고하세요. :::
:::info
HashiCorp Vault는 secrets 경로에 버전 v2의 KV 시크릿 엔진이 활성화되어 있어야 해요. Vault 인스턴스가 다른 경로나 버전을 사용한다면 path와 version 필드를 그에 맞게 업데이트해야 해요.
:::
PushSecret 만들기
PushSecret 리소스는 Kubernetes Secret을 HashiCorp Vault로 푸시하는 데 사용돼요. 이 단순화된 예시에서는 샘플 클러스터 cluster-example의 app 사용자 자격 증명을 푸시할게요.
PushSecret 구성에 대한 자세한 내용은 External Secrets Operator 문서를 참고하세요.
apiVersion: external-secrets.io/v1alpha1
kind: PushSecret
metadata:
name: pushsecret-example
spec:
deletionPolicy: Delete
refreshInterval: 24h
secretStoreRefs:
- name: vault-backend
kind: SecretStore
selector:
secret:
name: cluster-example-app
data:
- match:
remoteRef:
remoteKey: cluster-example-app
이 예시에서 PushSecret 리소스는 External Secrets Operator에게 (이전 예시의) cluster-example-app이라는 Kubernetes Secret을 HashiCorp Vault로 푸시하도록 지시해요. remoteKey는 Vault에서 시크릿이 저장될 이름을 정의하며, vault-backend라는 SecretStore를 사용해요.
구성 검증하기
PushSecret가 올바르게 동작하는지 확인하려면 HashiCorp Vault UI로 이동해 secrets 경로의 kv 시크릿 엔진에서 위에서 정의한 remoteKey에 해당하는 cluster-example-app이라는 시크릿을 찾을 수 있을 거예요.