시크릿 매니저 통합: 설계 기록¶
이 문서는 시크릿 매니저에서 credential 참조를 해석하기 위해 출시된 설계를 기록합니다 (이슈 #2413). 첫 릴리스는 참조 체계, 교체 가능한 리졸버 트레이트, 그리고 HashiCorp Vault 프로바이더 하나를 제공합니다. AWS Secrets Manager, AWS KMS, GCP Secret Manager 리졸버는 후속 작업으로 남아 있습니다.
현재¶
시크릿은 다음 방법으로 라우터에 도달합니다.
- 환경 변수 대체:
src/infrastructure/config/secrets.rs의resolve_secret_value가 설정 값 안의${VAR},$VAR,${VAR:-default}를 확장합니다. - 로컬 파일: 백엔드
auth.key_file(예: 서비스 계정 JSON)과, 클라이언트 키를 위한api_keys.api_keys_file입니다. - 허브가 관리하는 백엔드 credential: 백엔드 동기화를 켠 허브 등록 라우터에서는 허브가 작성한 백엔드 스냅샷이 credential 참조를 담고, 허브가 이를 해석해 전달합니다(
src/control_plane/backend_sync.rs, #1261). 이 경로는 아래 설계와 별개로 유지됩니다. - 시크릿 매니저 참조: credential 필드에
secret://<path>#<field>를 넣으면, 라우터가 설정 로드 시점과 핫 리로드 시점에 설정된 프로바이더에서 값을 해석합니다. 프로바이더는 이 두 시점에만 접속하며, 요청마다 접속하지 않습니다.
출시된 설계¶
설정¶
secrets:
provider: vault
vault:
address: https://vault.example.com
token: ${VAULT_TOKEN}
mount: secret
kv_version: 2
namespace: null
timeout_seconds: 10
ca_cert_file: null
- 이 섹션은 선택 사항입니다. 섹션이 없고 참조도 없으면 라우터는 프로바이더를 호출하지 않으며 기존과 똑같이 동작합니다.
provider의 기본값은vault이며, 현재 구현된 프로바이더는 이것 하나입니다.address는httpsURL이어야 합니다. 평문http는 루프백 호스트(localhost,127.0.0.1,::1)에서만 허용됩니다.token자체에${VAR}대체를 사용할 수 있습니다. 이 값은 모든 Admin 보고서와Debug출력에서 마스킹됩니다.mount의 기본값은secret이며, 앞이나 뒤에/를 붙일 수 없고..나 공백,\,%를 포함할 수 없습니다. 뒤의 두 문자는 참조 경로와 동일한 URL 정규화 규칙으로 거부합니다.kv_version은1또는2입니다.timeout_seconds는 1부터 120까지이고 기본값은 10입니다.namespace는X-Vault-Namespace로 전송됩니다.ca_cert_file은 사설 CA용 PEM 루트를 추가합니다.- 형식이 잘못된 섹션은 경고가 아니라 로드 오류입니다.
참조 문법¶
- 체계 마커는 정확히
secret://입니다. 문자열 전체가 이 마커로 시작할 때만 참조로 취급하므로,Bearer secret://...는 리터럴로 남습니다. - 문법은
secret://<path>[#<field>]입니다.<path>는mount기준 상대 경로이며, 비어 있지 않고, 공백이나 제어 문자, 선행/,..세그먼트,?,\,%를 포함하지 않습니다. 뒤의 두 문자를 거부하는 이유는 URL 정규화가 백슬래시를 경로 구분자로, 퍼센트 인코딩된 점 세그먼트를..로 바꾸어 요청을mount밖으로 옮길 수 있기 때문입니다. <field>는 시크릿 데이터 객체의 최상위 키를 선택하며, Vault에서는 필수입니다. 라우터는 필드가 하나뿐이라고 추측하지 않습니다.- 해석된 값은 JSON 문자열이어야 합니다. 필드가 없거나, 문자열이 아니거나, 경로가 비었거나, 객체가 아닌 값에 필드를 지정하면 모두 오류입니다.
- Vault KV v2는
GET <address>/v1/<mount>/data/<path>를 읽고data.data에서 필드를 꺼냅니다. KV v1은GET <address>/v1/<mount>/<path>를 읽고data에서 꺼냅니다. 두 경우 모두X-Vault-Token과, 설정된 경우X-Vault-Namespace를 보냅니다.
참조를 허용하는 필드¶
- 설정을 마스킹하는 규칙과 동일한 기준으로 판단한 credential 위치에만 허용합니다.
api_key,secret,token,password,key_file, AWS 및 OAuth credential 필드,redis.url, 헤더 맵,prompt_logging엔드포인트와 패턴,alerting.webhook.url,rate_limiting.scoped_bypass_keys[].key가 여기에 해당합니다. api_keys섹션은 별도로 처리합니다. 항목의key,api_key,value,secret은 해석하지만,id,name등 메타데이터는 해석하지 않습니다.- 그 밖의 위치에 있는
secret://값은 필드 경로를 밝히는 로드 오류입니다. 해석되지도, 리터럴로 남지도 않습니다. secrets섹션 자체 안의 참조는 부트스트랩 순환이 되므로 로드 오류입니다.
해석 시점¶
- 설정 로드 중,
${VAR}대체 뒤이자 타입 역직렬화 앞에서 해석합니다. 시작, 핫 리로드, Admin 히스토리 복원, 그리고 CLI의 유효 설정 경로(config validate,config show --resolved,config diff)가 모두 참조를 해석합니다. - 기본
config show, MCP 경로, 마스킹된 설정 히스토리 화면은 참조를 리터럴로 유지하며 프로바이더에 접속하지 않습니다. - 타입이 지정된
Config에secret://로 시작하는 문자열이 남아 있으면 최종 검증 가드가 이를 거부합니다. 파일 로더를 우회하는 Admin API, WebUI, 컨트롤 플레인 변경을 잡기 위한 장치입니다.
실패 정책¶
- 모든 실패는 점 표기 필드 경로와 참조 경로 및 필드를 밝히는 로드 오류입니다. 메시지에는 해석된 값이 절대 포함되지 않습니다. 예시는
backends[0].api_key: secret reference secret://openai#api_key could not be resolved: ...입니다. - 해석할 수 없는 참조는 리터럴 문자열로 대체되지 않으므로, 라우터는 실패 시 차단(fail closed) 방식으로 동작합니다.
- 리로드가 실패하면 이전 설정을 유지하므로, 트래픽은 마지막으로 정상 동작한 credential을 계속 사용합니다.
secrets섹션이 없는데 참조가 있으면 로드 오류입니다.- 런타임은 프로바이더 이름과 해석 건수만 기록하며, 값은 기록하지 않습니다.
마스킹¶
- 마스킹 규칙은 참조가 나타날 수 있는 모든 위치를 이미 포함합니다. 따라서 credential이 아닌 위치의 참조는 보고서에 마스킹 없이 노출되는 값이 아니라 오류가 됩니다.
secrets.vault.token은 기존token규칙으로 마스킹되며,provider,address등 나머지 비밀 아닌 설정은 그대로 읽을 수 있습니다.- 해석된 값은 로그,
Debug출력, Admin 설정 GET 및 내보내기, 설정 보고서,config diff출력에 나타나지 않습니다.
로테이션¶
- 로드할 때마다 시크릿을 다시 읽습니다. 로드 간 캐시도, TTL 캐시도 없습니다.
- 로테이션된 시크릿은 재시작 없이 다음 리로드에서 반영됩니다.
- 해석된 값은 실행 중인
Config에만 존재하며 디스크에 다시 기록되지 않습니다.
빌드 피처¶
secret-manager는 Cargo 피처로,full과 공식 릴리스 바이너리에 포함되며 크레이트 의존성을 추가하지 않습니다. 리졸버는 기존reqwest를 사용합니다.secrets설정 타입은 항상 컴파일되므로, 설정 파일은 어떤 피처 조합에서도 동일하게 검증됩니다.- 피처 없이 빌드한 바이너리는 런타임이 비활성임을 경고하며,
secret://값은 여전히 로드 오류입니다. - 이 피처는
embed에 포함되지 않으며,embed의 의존성 그래프는 바뀌지 않습니다.
Admin과 MCP¶
secrets섹션은 시작 시점 소유입니다.PUT과PATCH /admin/config/secrets는409 STARTUP_OWNED_SECTION을 반환하며, 이 섹션을 바꾸는 적용 및 롤백 후보도 같은 응답을 받습니다.- 설정 내보내기에서는 이 섹션을 제외하고, 가져오기 후보가 섹션을 생략하면 실행 중인 섹션을 유지합니다.
- MCP 핫 리로드 표면은
secrets를requires_restart로 분류합니다. - Admin API와 WebUI 변경으로는 참조를 설정할 수 없습니다. 런타임 API가 참조를 해석할 수 없기 때문입니다. 소스 설정을 수정한 뒤 리로드하십시오.
남은 프로바이더¶
- AWS Secrets Manager, AWS KMS, GCP Secret Manager 리졸버는 구현되어 있지 않습니다.
- 새 프로바이더를 추가하려면
SecretResolver트레이트를 구현하고provider열거형과 팩토리를 확장하면 됩니다. 참조 체계, 트리 순회, 실패 정책, 마스킹 규칙은 그대로 유지됩니다.