외주 개발자가 떠나는 날의 SOPS
이 실습은 VM 에서 돕니다
우분투 24.04 VM 한 대입니다. root 이고 systemd 와 커널 기능이 그대로 있습니다. 실습 파드에는 capability 가 없어 이 실습이 다루는 설정을 바꿀 수 없기 때문에 VM 으로 돕니다. 처음 뜨는 데 몇 분이 걸립니다. 세션이 끝나면 VM 은 통째로 사라지므로 남기고 싶은 것은 화면에서 복사해 두세요.
목표
SOPS 와 age 로 쿠버네티스 Secret 매니페스트의 값만 암호화해 Git 에 두고, 서비스가 자기 키로 그것을 읽게 합니다. 함께 일하던 외주 개발자가 떠날 때 열쇠를 거두는 절차를 끝까지 해 봅니다.
왜 중요한가
비밀값을 Git 밖에 두면 배포 설정과 비밀값이 서로 다른 속도로 바뀌고, 결국 "이 버전에 맞는 비밀번호가 무엇이었나" 를 아무도 모르게 됩니다. SOPS 는 파일 구조와 키 이름은 그대로 두고 값만 암호화해서, 비밀값도 리뷰와 기록을 받게 합니다.
대신 새 질문이 생깁니다. 누가 읽을 수 있는가, 그리고 그 사람이 떠나면 무엇을 바꿔야 하는가. 수신자 목록에서 이름을 지우는 것만으로는 부족합니다. 떠난 사람은 옛 파일과 그 안의 데이터 키, 그리고 평문을 이미 봤을 수 있습니다.
단계
- 운영자 age 키 쌍을
/root/keys/ops.agekey·/root/keys/ops.pub로 만듭니다. /root/deploy/.sops.yaml에 경로·암호화 범위·수신자 셋을 적습니다.secrets/payapi.yaml을 제자리에서 암호화합니다.- 커밋하고, 평문이 기록 어디에도 없게 합니다.
cfgapp을 다시 띄워 서비스 키로 읽게 합니다.- contractor 를 빼고 수신자를 갱신하고 데이터 키를 회전합니다.
- DB 비밀번호 자체를 바꾸고, 파일과 서비스에 반영해 커밋합니다.
참고
- 복호화 키는
SOPS_AGE_KEY_FILE환경 변수로 고릅니다. 예:SOPS_AGE_KEY_FILE=/root/keys/ops.agekey sops -d secrets/payapi.yaml cfgapp의 상태는curl -s http://127.0.0.1:8090/로, 기록은journalctl -u cfgapp로 봅니다.- 흔한 실수: 평문 파일을 먼저 커밋하고 그다음 암호화하는 것. 기록에 평문이 남습니다.
- 흔한 실수: 6단계에서 updatekeys 만 하는 것. 데이터 키가 그대로라 떠난 사람이 꺼내 둔 키로 새 파일도 열립니다.
운영자 키를 만든다
age 키 쌍을 만들어 비밀 키를 /root/keys/ops.agekey(권한 600)에, 공개 키를 /root/keys/ops.pub 에 저장하세요. 서비스와 외주 개발자의 공개 키는 같은 디렉터리에 이미 있습니다.
age 의 키 생성 도구는 비밀 키 파일을 쓰면서 공개 키를 화면에 알려 줍니다. 비밀 키 파일에서 공개 키를 다시 뽑는 옵션도 있습니다.
무엇을 누구에게 암호화할지 적는다
/root/deploy/.sops.yaml 에 규칙 하나를 두세요. secrets/ 아래의 .yaml 파일에 적용되고(path_regex), data 와 stringData 아래의 값만 암호화하며(encrypted_regex), 수신자는 ops·cfgapp·contractor 세 공개 키입니다.
규칙은 creation_rules 목록의 한 항목입니다. 여러 age 공개 키는 쉼표로 이어 한 문자열로 적습니다. 값만 암호화하면 리뷰어가 무엇이 바뀌었는지 키 이름으로 볼 수 있습니다.
값만 암호화한다
/root/deploy/secrets/payapi.yaml 를 제자리에서 암호화하세요. stringData 의 값은 ENC[ 로 시작해야 하고, metadata.name 같은 다른 필드는 그대로 읽혀야 합니다.
규칙 파일은 sops 가 파일 경로를 보고 스스로 찾습니다. 제자리 암호화 옵션을 쓰면 평문 파일이 따로 남지 않습니다.
커밋하고 기록을 확인한다
규칙 파일과 암호화된 Secret 을 커밋하세요. 저장소의 어느 객체에도 평문 값이 없어야 합니다.
커밋하기 전에 git diff --cached 로 무엇이 올라가는지 보세요. 평문을 한 번이라도 커밋했다면 앞 실습에서 본 것처럼 기록에 남습니다.
서비스가 자기 키로 읽는다
cfgapp 서비스를 다시 띄워 암호화된 파일을 자기 키(/etc/cfgapp/age.key)로 읽게 하세요. http://127.0.0.1:8090/ 이 200 과 "ok": true 를 돌려줘야 합니다.
이 서비스는 기동할 때 한 번 복호화합니다. 수신자 목록에 서비스의 공개 키가 없으면 여기서 실패합니다.
떠난 사람의 열쇠를 거둔다
외주 계약이 끝났습니다. 먼저 contractor 키(/var/lib/labhub-isec/contractor.agekey)로 지금 파일이 열리는 것을 확인한 뒤, .sops.yaml 에서 contractor 를 빼고 파일의 수신자를 갱신하고 데이터 키까지 회전하세요. contractor 키로는 열리지 않고 ops·cfgapp 키로는 열려야 합니다.
수신자 갱신(updatekeys)은 데이터 키를 새 수신자에게 다시 봉할 뿐이라, 떠난 사람이 이미 꺼내 둔 데이터 키는 여전히 맞습니다. 값의 암호문을 새 데이터 키로 바꾸는 명령이 따로 있습니다.
값 자체를 바꾸고 다시 띄운다
떠난 사람은 평문을 이미 봤습니다. 새 DB 비밀번호를 만들어 cfgdb-passwd <새 값> 으로 DB 에 적용하고, 암호화된 파일의 stringData.db_password 를 같은 값으로 바꿔 커밋한 뒤 cfgapp 이 새 값으로 도는지 확인하세요.
암호화된 파일은 sops <파일> 로 편집기에서 열거나, 값 하나를 바꾸는 하위 명령(set)으로 고칩니다. 서비스는 기동할 때만 읽습니다. 커밋까지 해야 다음 배포가 같은 값을 씁니다.