LabHub

KCSA — 쿠버네티스 보안 어소시에이트 · 플랫폼 보안 · 이론

시크릿 — 숨기는 것보다 폐기가 빠른 게 낫다

LabHub 에서 이어서 보기

한 줄 요약

시크릿 관리의 성숙도는 "얼마나 잘 숨겼는가"가 아니라 **"유출됐다고 가정할 때 폐기하고 교체하는 데
몇 분 걸리는가"** 로 측정됩니다. 이 관점 하나가 우선순위 전부를 바꿉니다.

왜 이게 필요했나

표준 조언은 이렇습니다 — "코드에 하드코딩하지 말고 환경변수로, .env.gitignore 에."
이 조언이 막는 위협은 딱 하나입니다. 소스 저장소에 평문으로 들어가는 것.

실제 유출 경로는 훨씬 많습니다. 프로세스 메모리, 크래시 리포트, CI 로그, 이미지 레이어,
APM 대시보드, 잘못 노출된 디버그 엔드포인트. 환경변수로 옮기는 것만으로는 이 중 어느 것도 막히지 않습니다.

어떻게 동작하나

환경변수는 저장 위치가 아니라 전달 방식이다

.env 파일을 로드한 뒤 삭제해도 소용없습니다. 값은 이미 커널이 프로세스마다 들고 있고,
리눅스에서는 그것을 파일처럼 읽을 수 있습니다.

tr '\0' '\n' < /proc/2841/environ

같은 UID 이거나 root 면 그대로 읽힙니다. 컨테이너 안이라고 다르지 않습니다 —
**같은 파드의 사이드카, 노드에 접근할 수 있는 사람, kubectl debug --target=app 으로 붙은
디버그 컨테이너가 전부 같은 것을 봅니다.**

환경변수가 새는 다른 경로도 정리해 두면 좋습니다.

쿠버네티스 Secret 의 실제 위치

저장 방식을 고르는 세 가지 기준

"암호화되는가"는 좋은 기준이 아닙니다. 대신 이 셋을 물으세요.

1. 노출 경로가 몇 개인가
2. 폐기와 교체가 몇 분 걸리는가
3. 누가 언제 읽었는지 알 수 있는가

이 기준으로 보면 저장 방식의 순서는 "암호화 강도" 순이 아니라 "사고 시 대응 시간이 짧아지는" 순
으로 정렬됩니다.

최선은 정적 키를 만들지 않는 것

로테이션은 운영 절차가 아니라 설계 속성이다

가장 중요한 문장입니다 — **코드가 키 하나만 가정하면 아무리 좋은 시크릿 매니저를 써도 실제 교체는
일어나지 않습니다.**

웹훅 검증이 SECRET 하나만 본다면, 키를 바꾸는 순간 이전 키로 서명된 요청이 전부 거부됩니다.
전송 측과 수신 측을 원자적으로 동시에 바꿀 방법이 없으니 사실상 교체 불가능해지고,
그래서 아무도 로테이션을 하지 않게 됩니다.

해법은 서명과 검증의 분리입니다. 서명은 현재 키 하나로 하되, 검증은 유효한 키 집합 전체
대해 시도합니다. 환경변수 이름이 단수에서 복수로 바뀌는 것(WEBHOOK_SECRETWEBHOOK_SECRETS)이
이 설계의 표식입니다. JWT 라면 kid 헤더 + JWKS 가 같은 일을 합니다 —
발급자가 JWKS 에 새 키를 먼저 공개하고, 검증자가 가져간 뒤에 서명 키를 바꿉니다.

그리고 관측. 어떤 키로 검증이 성공했는지 key_index 같은 태그로 지표를 남기면,
"구 키로 서명된 요청이 정말 사라졌는가"를 추측이 아니라 그래프로 확인하고 안전하게 옛 키를 지울 수 있습니다.

탐지보다 대응 시간이 먼저

사전 커밋 훅은 로컬 설정이라 git commit --no-verify 한 번이면 우회되고, 새로 합류한 사람은
훅을 설치하지 않은 채 며칠을 보냅니다. 실제 강제력은 서버 쪽 푸시 보호에서 나옵니다.

그리고 탐지에 투자하기 전에 물어야 할 질문이 있습니다 —
"이 시크릿이 노출됐다고 가정할 때 폐기와 교체에 몇 분이 걸리는가."
이 숫자가 크면 탐지를 아무리 촘촘히 해도 사고 시 손실은 줄지 않습니다.

현장에서 만나는 모습

저자가 정리한 사고 대응 순서가 이 관점의 결정판입니다. 시크릿이 이미 커밋된 상황에서
가장 흔한 반응은 히스토리 재작성인데 순서가 틀렸습니다.

공개 저장소에 푸시된 자격증명은 수 초에서 수 분 안에 자동화된 스캐너에 수집됩니다.
그리고 히스토리를 지워도 남는 것들이 있습니다 — 포크된 저장소, 이미 클론한 개발자들의 로컬 사본,
플랫폼이 보관하는 PR 참조(커밋 해시를 알면 여전히 접근 가능한 경우가 많습니다),
검색 엔진과 코드 검색 서비스의 캐시, CI 캐시와 아티팩트.
게다가 정리 후 누군가 기존 클론에서 그대로 푸시하면 지운 커밋이 되살아납니다.

그래서 순서는 폐기 → 영향 조사 → 히스토리 정리 입니다.
AWS 키라면 새 키 생성 → 배포 → 구 키 Inactive → 삭제 순으로 무중단 교체하고,
CloudTrail 로 그 키가 어디서 쓰였는지 조사합니다.
낯선 IP, 평소 쓰지 않던 리전, 예상 밖의 API 호출이 침해 판단의 신호입니다.
"히스토리 재작성은 유출을 되돌리는 조치가 아니라 재발을 줄이는 위생 작업입니다."

External Secrets Operator 같은 도구를 써도 클러스터 위생이 면제되지 않는다는 지적도 중요합니다.
ExternalSecret 매니페스트는 값이 없으니 Git 에 커밋해도 되지만,
오퍼레이터가 결국 값을 클러스터 Secret 으로 실체화하므로 RBAC 과 저장 시 암호화 문제는
그대로 남습니다.

다음 실습에서 할 것

다음 실습에서 PSA 라벨을 직접 붙이고 restricted 위반 파드가 거부되는 것을 확인합니다.
시크릿 자체는 실습보다 퀴즈로 다루지만, 앞 모듈 실습에서 작성한 EncryptionConfiguration 이
바로 여기서 말한 "etcd 평문 저장" 문제의 해답이라는 점을 다시 확인해 두세요.