Vault Secrets Operator refreshAfter — HMAC 기반 변경 감지

보안2분조회

Vault Secrets Operator(VSO)는 Vault 에 저장된 시크릿을 읽어 쿠버네티스 Secret 오브젝트로 동기화해 주는 오퍼레이터입니다. 이걸로 시크릿을 GitOps 로 관리하면서 VaultStaticSecretrefreshAfter: 30s 를 걸었습니다. 30초마다 Vault 를 폴링한다는 뜻인데, 부하가 걱정될 만한 숫자였습니다.

kind: VaultStaticSecret
spec:
  mount: vso-secrets
  path: <namespace>/<app>/.env
  type: kv-v2
  refreshAfter: 30s          # 30초마다 Vault 폴링
  hmacSecretData: true       # 이게 핵심
  destination:
    create: true
    name: <secret-name>
    overwrite: true

Vault 폴링 자체는 비용 아님

refreshAfter: 30s, 파드 10개 기준으로 계산하면 이렇습니다.

분당  10개 × 2회 = 20 호출
하루  28,800 호출

Vault 가 초당 수천 요청을 처리하는 걸 감안하면 무시해도 되는 수준입니다.

진짜 비용은 Kubernetes Secret 쓰기

hmacSecretData: true 가 하는 일이 여기서 나옵니다. 30초마다 Vault 를 읽지만, 값이 안 바뀌었으면 Kubernetes Secret 을 갱신하지 않습니다.

1. 30초마다 Vault kv-v2에서 현재 값 읽기
2. 읽어온 값의 HMAC 계산
3. 이전에 저장한 HMAC과 비교
4. 같으면: 아무것도 안 함 (K8s API 호출 없음)
5. 다르면: K8s Secret 업데이트

비교에 쓰는 해시는 리소스 상태에 남습니다.

kubectl get vaultstaticsecret <vss-name> -n <namespace> \
  -o jsonpath='{.status.secretMAC}'

hmacSecretData 가 없으면 값이 같아도 매번 Secret 을 덮어씁니다. 그러면 불필요한 Kubernetes API write 가 생기고, Deployment 가 Secret 변경을 감지해서 파드가 재시작합니다. 30초마다 시크릿을 다시 읽는 게 아니라 30초마다 파드가 재시작할 수 있는 구성이 됩니다.

30초가 항상 맞는 값은 아님

시크릿을 바꿨을 때 빠르게 반영돼야 하는 워크로드라 30초로 잡았습니다. 일반적인 프로덕션 서비스라면 refreshAfter: 5m 이나 1h 가 적절합니다.

폴링 주기를 줄이는 판단의 기준은 Vault 부하가 아니라 얼마나 빨리 반영돼야 하는가 입니다. 부하 쪽은 hmacSecretData 가 막아주고 있습니다.

참고

  1. 불러오는 중