Spring Boot 서비스는 application.yml, application-prod.yml, profile별 설정 파일, 외부 설정 파일 로딩을 자주 사용합니다. 이 패턴 자체는 문제되지 않지만, 운영용 Secret이 저장소에 커밋되면 즉시 유출로 간주해야 합니다.
이런 경우에 참고
application.yml또는application-prod.yml에 DB 비밀번호, 토큰, 서명키가 직접 들어 있는 경우- Spring Boot가 profile 기반 설정 파일을 사용하는 경우
${ENV_VAR}참조와 mounted config file 중 어떤 패턴을 써야 할지 정해야 하는 경우- Kubernetes, VM, secure file delivery 환경에서 Spring 설정 파일을 외부 주입하는 경우
예시
Pattern A: ${ENV_VAR} 기반 주입
환경 변수 주입이 가능하다면 Spring Boot에서는 이 패턴이 가장 단순합니다.
언제 이 패턴을 선택하는가
- Secret 개수가 많지 않고 환경 변수로 주입 가능한 경우
- 배포 시스템이 환경 변수 주입을 표준으로 제공하는 경우
- profile은 유지하되 실제 Secret 값은 외부에서 공급하려는 경우
변경 전
yaml
spring:
datasource:
url: jdbc:postgresql://prod-db.internal:5432/app
username: app_user
password: super-secret-password
jwt:
secret: hardcoded-jwt-secret
변경 후
yaml
spring:
datasource:
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jwt:
secret: ${JWT_SECRET}
Spring 코드 예시
java
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import jakarta.validation.constraints.NotBlank;
@Validated
@ConfigurationProperties(prefix = "jwt")
public class JwtProperties {
@NotBlank
private String secret;
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
}
이 예시는 Jakarta Validation을 사용하는 Spring Boot 환경의 일부입니다. 검증 의존성을 추가하고 @ConfigurationPropertiesScan 또는 @EnableConfigurationProperties로 클래스를 등록하세요. 필수 값이 없거나 비어 있으면 시작을 중단해야 합니다.
Pattern B: mounted application.yml 기반 주입
운영상 application.yml 또는 application-prod.yml 자체를 유지해야 한다면 실제 운영 파일은 런타임에 /config/application.yml 또는 /etc/app/application.yml로 공급합니다.
언제 이 패턴을 선택하는가
- profile별 설정 파일 구조를 유지해야 하는 경우
- 외부 설정 파일 경로를 운영 표준으로 사용하는 경우
- Kubernetes Secret volume, Vault Agent, secure file delivery가 준비되어 있는 경우
저장소에 두는 파일 예시
yaml
spring:
datasource:
url: jdbc:postgresql://prod-db.internal:5432/app
username: app_user
password: "<runtime-provided>"
jwt:
secret: "<runtime-provided>"
런타임 파일 경로 예시
text
/config/application.yml
/etc/app/application.yml
Spring 설정 예시
spring.config.import 또는 Spring Boot의 표준 외부 설정 파일 경로를 사용할 수 있습니다. 중요한 점은 Secret 원문이 저장소가 아니라 런타임에 공급된다는 것입니다.
yaml
spring:
config:
import: file:/etc/app/application.yml
위 파일은 필수이므로 optional:을 사용하지 않습니다. 운영용 application.yml 또는 application-prod.yml은 Git에 커밋하지 않습니다.
개발자 해야 할 일
application.yml또는application-prod.yml에서 실제 Secret 값을 제거합니다.${ENV_VAR}패턴 또는 외부 파일 로딩 패턴 중 현재 서비스 구조에 맞는 방식을 선택합니다.@ConfigurationProperties또는 기존 설정 로딩 구조에서 필수 값 누락 시 fail closed 하도록 구성합니다.- 로그, exception, debug 출력, actuator endpoint에 Secret 값이 노출되지 않도록 점검합니다.
인프라/플랫폼 해야 할 일
- env 패턴이면 환경 변수 주입 체계를 구성합니다.
- file 패턴이면 Secret volume, secure file delivery, 또는 외부 파일 공급 경로를 표준화합니다.
- Helm values, Docker image, ConfigMap에 실제 Secret이 들어가지 않도록 배포 표준을 강제합니다.
- Secret 회전 시 restart 또는 reload 절차를 운영 문서에 포함합니다.
검증 방법
application-prod.yml같은 운영 파일이 저장소에 없는지 확인합니다.${ENV_VAR}또는 mounted file이 실제 배포 환경에서 정상 주입되는지 확인합니다./actuator/env, 디버그 로그, 예외 응답에 Secret 값이 노출되지 않는지 확인합니다.- 필수 Secret 누락 시 애플리케이션이 정상적으로 실패하는지 확인합니다.
자주 하는 실수
application-prod.yml을 저장소에 커밋- Helm values에 Secret 직접 저장
- Docker image에 운영용 설정 파일 bake-in
/actuator/env, debug 로그, 예외 스택에 설정값 노출- 빈 기본값을 사용해 Secret 누락 상태로도 기동되게 방치
담당자에게 안내할 문구 예시
- "Spring Boot 설정 파일에 저장된 실제 Secret은 제거하고,
${ENV_VAR}또는 런타임에 공급되는 외부 설정 파일로 대체하세요." - "
application-prod.yml이 필요하더라도 저장소에는 커밋하지 말고/config/application.yml또는/etc/app/application.yml같은 경로로 런타임에 공급하세요." - "
@ConfigurationProperties또는 기존 설정 로더에서 필수 Secret 누락 시 즉시 실패하도록 구성하고,/actuator/env와 디버그 로그에 값이 노출되지 않도록 확인하세요."