Spring application.yml

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와 디버그 로그에 값이 노출되지 않도록 확인하세요."

관련 문서