상세 컨텐츠

본문 제목

SOPS/AGE로 파일에서 크레덴셜만 암호화하기

OpenCSP/운영 관련

by miiml 2026. 3. 15. 17:32

본문

 

쿠버네티스 클러스터 내부를 코드로 구성하기 위해 FluxCD를 사용하고 있다.

GitHub 레포지토리에 yaml 파일들을 올리고 FluxCD에 GitRepository CR 추가하면

주기적으로 코드를 가져와서 내부 구성을 자동 관리해주도록 설정해뒀다.

 

근데 문제는 진행중인 프로젝트(오픈소스) 특성상 레포지토리를 Public으로 열어둬야하고,

일부 툴들은 helm value는 마스터 키나 크레덴셜 입력을 필수로 해줘야해서 크레덴셜 키가 인터넷에 노출된다는 점이었다.

 

이런 문제를 해결하는 여러 방법이 있지만 제일 가벼운(?) 방식으로 해결하기로 했다.

AGE를 통해 암호화 키를 생성하고, SOPS(Secrets OPerationS)를 사용해서 해당 키를 사용해 크레덴셜 부분만 암호화 (Public/ Private key 사용)

** SOPS : YAML, JSON, ENV, INI 등 다양한 포맷의 설정 파일 내 민감 정보(비밀번호, API 키 등)를 암호화하여 저장하는 오픈소스 CLI 도구, age는 gpg랑 비슷한 포지션인데 더 가볍고 빠르고 직관적이라 sops 공식 문서에서도 해당 옵션을 권장한다고 함

 

그리고 FluxCD가 암호화된 파일을 그대로 가져가서 알고 있는 Private key로 클러스터 내부에서 '복호화 -> 자동 구성' 하는 방식임.

 

이렇게 하면 민감한 정보가 포함된 yaml파일들을 public으로 올릴 수도 있고 별도의 서비스나 툴을 새로 구성할 필요없어서 좋은거 같음

 

 


맥북 기준으로 사용 방법 좀 설명하면, 우선 brew로 age, sops 설치해주고

brew install age sops

 

 

age 키를 생성해준다. 

생성된 키 보면 public key랑 AGE-SECRET-KEY~ 가 있는데, AGE-SECRET-KEY는 외부로 노출되면 안됨

age-keygen -o age.agekey

 

 

쿠버네티스 secret에 파일 통째로 등록

cat age.agekey | kubectl create secret generic sops-age \
  --namespace=flux-system \
  --from-file=age.agekey=/dev/stdin

 

 

확인해보면 잘 등록된 거 같다.

ubuntu@k3s:~$ k get secret -n flux-system | grep sops
sops-age                                          Opaque                                1      82s
ubuntu@k3s:~$ k describe secret sops-age -n flux-system
Name:         sops-age
Namespace:    flux-system
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
age.agekey:  75 bytes

 

 

이제 클러스터에서 할 수 있는건 다 했고,

FluxCD 한테도 어떤 파일이 암호화되었는지 어디를 복호화해야하는지 알려줘야 한다.

 


Flux가 참조하는 레포지토리 루트에 .sops.yaml 파일을 생성해주고

creation_rules:
  - path_regex: cluster/.*secret.*\.yaml
    age: age1z4t56we62raeue0w02lcykg7pxt3phtq3j5laju8fng7jlw7lers0hvnpq (위에서 생성된 키)

 

Flux 사용해서 Git Repository 연결하면 커밋되는 파일들(gotk-sync.yaml)이 있는데, 내 프로젝트에서는 프로비저닝 순서를 보장하기위해 layer.yaml을 추가해줘서 거기에 복호화 관련 옵션들을 추가해줬다. (decryption 부분)

---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: cluster-infrastructure
  namespace: flux-system
spec:
  dependsOn:
    - name: cluster-system-configs
  interval: 10m
  path: ./cluster/infrastructure
  prune: true
  sourceRef:
    kind: GitRepository
    name: flux-system
  decryption: # 이 부분
    provider: sops
    secretRef:
      name: sops-age # 아까 클러스터에서 생성해둔 age 키의 secret 이름
  wait: true
  timeout: 5m

 

프로젝트 루트 경로에서 아래 명령어로 파일 지정해서 암호화해주고 커밋해준다.

sops -e -i cluster/infrastructure/lago/secret.yaml

 

직접 암호화해서 깃에 올리는 방식이라 사람이 실수로 올릴 수도 있어서 주의가 필요함.

방지하려면 secret.yaml 을 .gitginore에 추가해서 막고 암호화된 파일명만 바꿔서 올리던지 PR 생성하면 action으로 자동 수정해주는 등으로 처리해야 될 거 같다. 

 

 

로컬에서 복호화하는 방법은 아래 더보기 참고

더보기

암호화에 사용한 age.agekey 파일은 ~/.config/sops/age 에 옮겨두거나 환경변수에 경로를 지정해주면 됨

sops -d cluster/infrastructure/lago/secret.yaml

 

그리고 -i 옵션 없으면 결과가 파일에 적용 되지 않고 출력으로만 나온다. 

sops -d cluster/infrastructure/lago/secret.yaml > cluster/infrastructure/lago/secret.decrypted.yaml

 

반대로 암호화도 이렇게 해주면

sops -e cluster/infrastructure/lago/secret.decrypted.yaml > cluster/infrastructure/lago/secret.yaml

 

secret.decrypted.yaml만 .gitignore에 추가해두고 사용할 수 있다.

관련글 더보기