일부 서비스는 애플리케이션 구조상 config.yaml, application.yml, settings.json 같은 설정 파일 자체를 유지해야 합니다. 이 경우에도 Secret 원문을 저장소에 커밋하면 안 되며, 실제 설정 파일은 런타임에 안전한 소스에서 생성하거나 마운트해야 합니다.
이런 경우에 참고
- 환경 변수만으로 설정 구조를 바꾸기 어려운 경우
- 애플리케이션이 특정 파일 경로의 설정 파일을 반드시 읽는 경우
config.yaml.example는 필요하지만 실제config.yaml도 운영에 필요한 경우- Kubernetes, Vault Agent, CSI driver, 배포 시스템의 secure file delivery를 사용할 수 있는 경우
기본 권장 패턴
- 저장소에는
config.yaml.example만 둡니다. - 실제
config.yaml은 Git에 커밋하지 않습니다. - 실제 파일은 Kubernetes Secret volume, CSI/Vault Agent, 배포 시스템의 secure file delivery를 통해 런타임에 생성하거나 마운트합니다.
- 애플리케이션은 지정된 파일 경로에서 설정을 읽되, 파일이 없거나 필수 Secret이 비어 있으면 fail closed로 종료합니다.
언제 이 패턴을 선택하는가
- 애플리케이션이 특정 파일 경로의 설정 파일을 반드시 읽어야 하는 경우
- 환경 변수만으로 계층형 설정 구조를 표현하기 어려운 경우
- 배포 플랫폼이 Secret file mount 또는 secure file delivery를 표준으로 제공하는 경우
금지 패턴
- 운영용
config.yaml을 저장소에 커밋 - Secret이 포함된
config.yaml을 ConfigMap에 저장 - Docker image 안에
config.yaml을 bake-in Dockerfile에서 운영용config.yaml을COPY- 수동 배포 파일을 기본 운영 패턴으로 문서화
권장 구조
text
config.yaml.example # 샘플 설정만 포함, Git 커밋 가능
config.yaml # 실제 운영 설정, Git 커밋 금지
.gitignore # config.yaml 포함
/etc/app/config.yaml # 런타임에 마운트되거나 생성되는 실제 설정 파일 예시
예시
변경 전
yaml
database:
host: prod-db.internal
user: app_user
password: super-secret-password
jwt:
secret: hardcoded-jwt-secret
위와 같은 파일이 저장소에 포함되어 있으면 Secret 유출로 간주해야 합니다.
변경 후
저장소에 두는 파일: config.yaml.example
yaml
database:
host: prod-db.internal
user: app_user
password: "<runtime-provided>"
jwt:
secret: "<runtime-provided>"
런타임 파일 경로 예시
text
/etc/app/config.yaml
이 파일은 Kubernetes Secret volume, Vault Agent/CSI, 또는 배포 시스템의 secure file delivery가 런타임에 생성하거나 마운트합니다.
파일 권한 가이드
- 설정 파일은 애플리케이션 실행 사용자만 읽을 수 있도록 최소 권한으로 설정합니다.
- world-readable 권한을 금지합니다.
- owner와 group을 운영 표준에 맞게 제한합니다.
일반적인 쓰기 가능한 파일의 권한 설정 예시입니다. Kubernetes Secret volume처럼 읽기 전용인 마운트는 볼륨 설정에서 권한과 소유권을 구성하세요.
bash
chmod 600 /etc/app/config.yaml
chown appuser:appgroup /etc/app/config.yaml
Kubernetes 예시
Secret또는 외부 Secret 연동 리소스에 설정 파일 내용을 저장합니다.- Pod에는 Secret volume으로
/etc/app/config.yaml을 마운트합니다. ConfigMap으로 대체하지 않습니다.
개발자 해야 할 일
- 저장소에 있는 실제 설정 파일에서 Secret 값을 제거합니다.
- 애플리케이션이 런타임 파일 경로를 읽도록 유지하되, 파일 누락과 필수 값 누락 시 즉시 실패하게 수정합니다.
- 설정 객체 전체를 로그, panic, debug dump로 출력하지 않도록 수정합니다.
- Secret 회전 시 reload 또는 restart가 필요한지 애플리케이션 동작을 문서화합니다.
인프라/플랫폼 해야 할 일
- 실제 설정 파일을 Kubernetes Secret volume, CSI/Vault Agent, 또는 secure file delivery로 공급합니다.
- ConfigMap, Docker image bake-in,
Dockerfile COPY를 사용하지 않도록 배포 표준을 정합니다. - 파일 owner, group, 권한을 최소화합니다.
- Secret 회전 시 파일 갱신과 restart/reload 절차를 운영 문서에 포함합니다.
검증 방법
- 저장소에 운영용 설정 파일이 없는지 확인합니다.
- 컨테이너 또는 호스트에서 설정 파일 권한이 최소화되어 있는지 확인합니다.
- 애플리케이션이 파일 누락 시 정상적으로 실패하는지 확인합니다.
- 설정 값이 로그, actuator, 예외 메시지에 노출되지 않는지 확인합니다.
자주 하는 실수
- 운영용
config.yaml을 Git에 커밋 - Secret이 들어 있는 설정 파일을 ConfigMap에 저장
- Docker image에 설정 파일 bake-in
- 파일 권한을 넓게 열어 두고 world-readable 상태로 배포
- Secret rotation은 했지만 reload/restart 절차를 정의하지 않음
담당자 조치 절차
노출된 자격 증명은 아래 파일 정리나 배포 변경을 기다리지 말고 먼저 폐기·비활성화하고 새 값으로 교체하세요.
- 저장소에 커밋된 실제
config.yaml에서 Secret 값을 제거합니다. - 저장소에는
config.yaml.example만 남기고 실제config.yaml은.gitignore에 추가합니다. - 실제
config.yaml은 Kubernetes Secret volume, CSI/Vault Agent, 또는 secure file delivery로 런타임에 공급합니다. - 애플리케이션은 지정 파일 경로를 읽도록 유지하되, 파일 누락이나 필수 Secret 누락 시 즉시 종료하도록 수정합니다.
- 설정 파일 권한을 최소화하고 world-readable 상태를 제거합니다.
- 디버그 로그, panic, 설정 dump에 Secret 값이 노출되지 않도록 마스킹합니다.
- Secret 회전 시 파일 갱신 후 reload 또는 restart 절차를 운영 문서에 명시합니다.
- 기존 값이 저장소에 노출되었다면 폐기 및 재발급합니다.
담당자에게 안내할 문구 예시
- "
config.yaml형식을 유지해야 하더라도 실제 운영 파일은 Git에 커밋하지 말고 런타임에 안전하게 마운트하세요." - "저장소에는
config.yaml.example만 남기고, 실제config.yaml은 Kubernetes Secret volume 또는 배포 시스템의 secure file delivery로 공급하세요." - "설정 파일이 없거나 필수 Secret이 비어 있으면 애플리케이션이 즉시 실패하도록 구성하세요."
- "Secret이 포함된
config.yaml을 Docker image나 ConfigMap에 포함시키지 마세요."
운영 체크리스트
.gitignore에 실제 설정 파일이 포함되어 있는가config.yaml.example와 실제config.yaml의 역할이 분리되어 있는가- 설정 파일 권한이 최소화되어 있는가
- 로그, panic, debug dump에 전체 설정 객체를 출력하지 않는가
- Secret rotation 시 reload 또는 restart 절차가 정의되어 있는가
- 예외적으로 수동 배치를 하더라도 기본 권장 방식이 아닌 fallback으로만 관리하는가