💡 Vault - HashCorp 사에서 만든 Vault는 다양한 환경에서 애플리케이션의 외부 비밀 속성(예: 데이터베이스 비밀번호, API 키 등)을 외부화된 구성으로 중앙에서 관리할 수 있습니다.
- Spring Boot 환경에서 Vault로부터 시크릿 정보를 읽어오며 Valut에 시크릿 정보를 쓰는 것도 가능합니다. 이러한 방식으로 애플리케이션의 중요한 정보는 코드에서 분리되어 보안이 보장됩니다. - 기밀정보의 동적인 제공, 중앙 집중식 시크릿 관리, 즉각적인 액세스 제어, 감사 추적 기능 등을 제공하여, 기업의 보안 정책을 준수하는 데 도움이 됩니다.
1. Vault의 특징
특징
설명
기밀정보의 동적 제공
애플리케이션이 필요에 따라 비밀정보를 요청하고, Vault는 요청된 정보를 제공합니다.
중앙 집중식 시크릿 관리
모든 애플리케이션의 시크릿 정보를 중앙에서 관리할 수 있습니다.
즉각적인 액세스 제어
애플리케이션이 Vault에 접근 할 수 있는 ‘특정 토큰 아이디’를 기반으로 Vault 서비스를 호출하고, Vault는 해당 '특정 토큰 아이디'를 수신한 뒤 요청된 '비밀정보'를 제공합니다.
감사 추적 기능
Vault는 모든 액세스 요청을 로깅하여 감사를 위한 추적이 가능합니다.
2. Vault 인증 방식
💡 Vault 인증 방식
- Vault에 접근하기 위한 인증 방식을 의미합니다. Root Token, 정책 기반 토큰 (Policy-based Token), AppRole 등의 인증 방식을 통해서 Vault를 접근합니다.
인증 방식
설명
Root Token
- 모든 권한을 가진 ‘루트 권한 토큰’을 이용하는 인증 방식이며, 정책, 인증 방식, 시크릿 엔진, 모든 데이터 접근 가능한 인증 방식입니다. - 절대적인 권한을 가지고 있기에, 직접적으로 애플리케이션 레벨에서 인증방식으로 사용되지 않습니다.
정책 기반 토큰 (Policy-based Token)
정책(policy)에 의해 접근 범위가 제한된 ‘토큰’을 이용하는 인증 방식이며, 정책 범위 별로 강제 제한된 권한을 부여하고 접근하는 인증방식 입니다.
AppRole
- Role 별로 지정된 정책(Policy)에 따라서 접근 범위가 제한되며, ‘RoleID, Secret ID’에 따라 ‘토큰’을 발급받아서 접근 가능한 인증 방식입니다. - 해당 Role은 운영환경 혹은 팀별로 접근을 제어할 수 있으며, 사용자는 Role Id, Secret Id를 통해 인증만 수행하고, 이후 토큰에 대해서는 머신이 이를 갱신/관리하기에 별도의 Token 재발급 과정을 수행하지 않습니다.
3. Vault 수행 과정
Baeldung on KotlinSpring Boot ConnectionDetails Abstraction ❘ Baeldung
💡 Vault 수행 과정 1. Spring Boot → Hashicorp Vault: Secret Request(비밀정보 요청) - Spring Boot 애플리케이션에서는 서버를 실행할 때, Valut에 접근할 수 있는 ‘특정 토큰 아이디’(Secret Request)’ 를 기반으로 Vault 서비스를 호출하여 ‘비밀 정보(Secret)’를 검색합니다.
2. Hashicorp Vault → Spring Boot: Secret Response(비밀정보 응답) - Valut 서비스는 Spring Boot 애플리케이션의 ‘특정 토큰 아이디’(Secret Request)’를 수신하고 요청된 ‘비밀 정보(Secret)’를 전달합니다. - 이 단계를 통해서 애플리케이션은 필요한 시크릿 정보를 안전하게 받아올 수 있습니다.
3. Spring Boot → Remote Service : Connection Request(커넥션 요청) - Spring Boot 애플리케이션이 원격 서비스와 연결을 시도하는 과정을 의미합니다. - 이 단계에서 애플리케이션은 Vault에서 안전하게 가져온 비밀 정보(예: 데이터베이스 비밀번호, API 키 등)를 사용하여 원격 서비스에 연결 요청을 보냅니다. - 이렇게 함으로써, 중요한 정보를 코드 내에 직접 작성하거나 노출시키지 않아도 되어 보안을 더 효과적으로 관리할 수 있습니다.
4. Remote Service → Spring Boot : Connection Response(커넥션 연결) - Vault 서비스가 Spring Boot 애플리케이션의 Secret Request를 수신하고, 요청된 '비밀 정보'를 애플리케이션으로 전송하는 단계입니다. - 이 단계를 통해 애플리케이션은 필요한 시크릿 정보를 안전하게 받아올 수 있습니다.
2) Vault 인증 방식의 강점
1. 소스코드 내에 민감정보 누출을 줄일 수 있습니다
💡 소스코드 내에 민감정보 누출을 줄일 수 있습니다
- 전체 프로젝트 내에서 소스코드의 민감 정보는 포함되지 않고, 최초 서버 실행 시 Vault Secret Engine에서 불러온 값을 토대로 모두 조회해 옵니다. - 만약 소스코드가 누출이 되더라도 주요한 민감정보에 대한 접근 권한을 가지고 있기에 누출이 발생하지 않습니다.
2. 분산된 민감 정보를 중앙 관리할 수 있습니다.
💡 분산된 민감 정보를 중앙 관리할 수 있습니다.
- 해당 강점은 단순히 소스코드 내에서 보안 취약점을 숨기는 수준이 아니라 ‘애플리케이션 구조’와 ‘보안 책임’ 자체를 분리하는 과정입니다. - 소스코드 내에 하드코딩 되어 있던 키 값들을 일괄적으로 Vault로 관리함으로써 분리된 ‘보안 책임’을 일괄 관리가 가능합니다.
💡 아래와 같이 각각 분리된 키들이 있습니다.
💡 아래와 같이 KV Secret Engine 내에서 일괄된 키들을 관리할 수 있습니다.
3. 접근 권한을 주기적으로 회전(Rotation)하고, 필요시 즉시 폐기할 수 있어서 보안 사고 발생 시 피해를 최소화할 수 있습니다.
💡 접근 권한을 주기적으로 회전(Rotation)하고, 필요 시 즉시 폐기할 수 있어서 보안 사고 발생 시 피해를 최소화할 수 있습니다.
- AppRole 방식을 이용하면 Secret ID에 대해서 TTL(Time To Live), Num of uses(사용 횟수 제한)을 지정할 수 있습니다. - 그리고, 만약 보안 사고 발생 시, Secret Id를 즉시 만료하여서 동적으로 노출이 되어도 피해가 최소화될 수 있습니다.
💡 아래와 같이 최초 Secret Id를 발급하였습니다.
💡정상적으로 Vault를 접속해서 KV 데이터를 조회해 왔습니다.
💡 아래와 같이 즉시 만료를 하였습니다.
# 특정 AppRole의 secret_id 만료
vault write auth/approle/role/dev-role/secret-id/destroy secret_id=<SECRET_ID>
# or
# 모든 AppRole의 secret_id 만료
$ vault write auth/approle/role/my-role/secret-id/destroy-all
💡 서버에서 Vault 접근 시 만료가 됨을 확인할 수 있습니다.
4. 각 시스템/팀별로 Role을 나눌 수 있습니다.
💡 각 시스템/팀별로 Role을 나눌 수 있습니다.
- dev-role, qa-role, prd-role, cicd-role, backend-role 등 역할 단위로 정책(Policy), TTL, Secret ID 발급 방식을 개별 설정할 수 있어, 운영 및 DevOps 환경에서 권한 관리와 보안 정책을 유연하게 적용할 수 있습니다. - 또한 팀별로 Vault 접근 권한을 명확히 제한함으로써, 필요 최소한의 권한만 부여하는 원칙(Least Privilege)을 효과적으로 구현할 수 있습니다. - 예를 들어서, dev-role의 경우는 kv2_dev에 대해서만 접근 가능 및 읽기, 쓰기, 수정이 가능하다고 Role을 지정할 수 있습니다. 그리고 prd-role의 경우는 오직 읽기 권한만 가능하다고 Role을 지정할 수 있습니다.
3) App Role 인증방식
💡 App Role 인증방식
- 조직 내 팀, 서비스, 환경 단위로 '접근 가능한 권한을 분리'하여서 Vault에서 정책(Policy)을 정의하고, 이를 기반으로 AppRole 기준으로 각각 Role ID, Secret ID를 발급받아서 Vault에 인증을 요청하면 Vault에서는 토큰을 발급해 주는 인증 방식을 의미합니다. - 사용자는 Role Id, Secret Id를 통해 인증만 수행하고, 이후 토큰에 대해서는 머신이 이를 갱신/관리하기에 별도의 Token 재발급 과정을 수행하지 않습니다 - 예를 들어서, 개발팀, DevOps, 백엔드 서버, CI/CD 파이프라인, 특정 마이크로서비스 등 주체별로 AppRole을 각각 나누어서 각각의 정책(Policy) 및 접근 권한을 부여하여 관리합니다.
💡 Role ID - AppRole의 고유 식별자로 어떤 역할을 수행하는지를 나타내는 아이디입니다. - 예를 들어서, 해당 Role ID 값에 따라서 조직 내 팀, 서비스, 환경 단위로 분류가 가능하며, DevOps, 백엔드 서버, CI/CD 파이프라인, 특정 마이크로서비스 등 주체별로 각각 나누어서 어떤 역할을 수행할지 정의합니다. - 해당 값은 역할이 무엇인지를 의미하는 ID와 같기에 유출되어도 단독으로는 아무 권한도 없습니다.
2. Secret ID
💡 Secret ID - Vault AppRole 인증에서 사용하는 비밀 키로, 해당 요청 주체가 특정 Role에 접근할 권한을 가지고 있음을 증명하는 값을 의미합니다. 또한, Role ID와 함께 사용되어야만 인증이 가능하며, 하나의 Role ID에 대해 여러 개의 Secret ID를 동시에 발급할 수 있습니다. - Secret ID는 새로 발급된다고 해서 기존 Secret ID가 자동으로 폐기되지는 않으며, TTL 만료 또는 명시적인 revoke가 이루어져야만 무효화됩니다. - 보안을 위해 Secret ID는 짧은 TTL 또는 Num of uses(사용 횟수 제한)으로 설정하여 필요한 순간에만 발급·사용되는 소모성 인증 수단으로 사용하는 것이 권장됩니다.
💡 아래와 같이 최초 Secret Id를 발급하였습니다.
💡 정상적으로 Vault를 접속해서 KV 데이터를 조회해 왔습니다.
3. Token
💡 Token
- 발급받은 Role ID와 Secret ID를 기반으로 Vault에 로그인을 수행하면, 토큰이 발급됩니다. 이는 요청을 하는 주체가 어떤 정책으로 얼마나, 언제까지 접근할 수 있는지를 증명하는 값을 의미합니다. - 실제 애플리케이션에서는 직접적으로 토큰을 발급받는 것이 아닌, Role ID와 Secret ID를 기반으로 자격증명을 요청하면 Vault 내에서 토큰을 발급하여 인증을 수행합니다. - 그렇기에, 애플리케이션은 Secret ID를 이용해 최초 한 번만 인증하고 이후에는 발급된 토큰을 사용해 Vault에 접근하며 토큰은 TTL에 따라 자동으로 만료되거나 갱신됩니다.
4. AppRole 프로세스
💡 AppRole 프로세스 1. Spring Boot 서버 시작: SpringApplication.run() - 최초 Spring Boot 서버가 실행이 됩니다.
2. Spring Cloud Vault 자동 구성 로드: bootstrap.yml - Spring Cloud Bootstrap에서 최초 Vault 설정에 대한 자동 구성이 로드가 됩니다.
3. Vault에 '인증(AppRole)' 요청 수행(Role Id, Secret Id) - Vault의 인증 방식 중 AppRole을 이용한 방식에서 사전에 팀별로 발급된 Role Id, Secret Id를 통해서 Vault에 인증을 요청합니다.
4. Vault '인증 정보' 검증 - Vault에서는 유효한 토큰인지에 대해 인증 정보에 대해 검증을 합니다. - 성공 시, KV Secret Engine에 접근합니다. - 실패 시, 서버 시작 실패와 종료를 합니다.
5. 조회된 Secret을 Spring Boot에 응답 - KV Secret Engine에서 조회된 정보를 응답으로 전달합니다
6. 응답받은 Secret을 Spring Environment(PropertySource)에 자동 주입 - Spring Boot에서는 전달받은 Secret을 Spring Environment(PropertySource)에 자동 주입을 합니다.
7. @ConfigurationProperties VaultKVProperties.java 객체와 자동 매핑 - 추후에 구성할 VaultKVProperties.java 파일 내에 사전에 KV와 같은 형태로 구성한 멤버 변수 각각에 데이터가 매핑이 됩니다.
8. Spring Boot 서버 실행 완료 - 최종 작업이 완료되어서 Spring Boot 서버가 실행이 완료가 됩니다.
4) Root Token 인증 방식 vs 정책 기반 토큰 인증 방식 vs AppRole 인증 방식
구분
Root Token
정책 기반 토큰 인증 방식
AppRole 인증 방식
권한(Privilege)
Vault 전체 권한(슈퍼 관리자). 모든 작업 허용
정책(policy) 범위에서만 권한 허용
정책(policy)로 제한되며, Role에 부여된 권한만 사용
보안 위험도
극도로 높음 (유출 시 Vault 완전 장악)
비교적 낮음 (정책으로 제한)
매우 낮음. RoleID + SecretID 조합이 필요하고, SecretID TTL/1회용 설정 가능
용도
초기 구성, 위험한 관리 작업
일반 사용자/서비스가 직접 토큰을 받아서 사용
서버/애플리케이션이 자동 인증할 때 사용 (사람이 아닌 머신/서비스용)
TTL / 만료 설정
기본 없음. 수동 회전 필요
TTL, Max TTL, Renewable 자유 설정
SecretID/Token TTL, Max TTL, Renewable 모두 세밀하게 설정 가능
정책 적용 방식
정책 무시하고 모든 권한 가능
정책 기반으로 반드시 제한
Role에 연결된 정책의 권한으로 제한
사용 주체
초기 관리자
개발자, 운영자, 서비스 계정
백엔드 서버, 배치 서버, 컨테이너, CICD — 사람이 아닌 머신
재발급 / 갱신
거의 없음
TTL 만료 시 재발급 가능
SecretID 자동 생성 가능, 주기적 Rotation 가능, 토큰도 자동 갱신 가능
발급 방법
초기화 시 자동 생성
vault token create 또는 Auth Method(OIDC, GitHub 등)
SecretID 생성 → 서버가 RoleID + SecretID로 로그인 하여 Token 자동 발급
운영 관점
일반 운영에서 절대 사용 금지
사용 가능하지만 사람이 직접 관리해야 하는 불편함 있음
자동화에 최적, 서버 재기동해도 RoleID/SecretID로 자동 로그인
Audit Logging
모든 작업 기록되나 너무 광범위
정책 기반이라 세밀한 기록 가능
RoleID 노출되어도 SecretID 없으면 무용지물 → 가장 안전한 로깅 가능
토큰 폐기(Revocation)
폐기하면 복구가 복잡
토큰만 폐기 가능
RoleID/SecretID/발급된 토큰 각각 독립적으로 폐기 가능
적합한 상황
초기 설정, 정책 생성, 비상 상황
단순 테스트, 개발자가 직접 토큰 사용 시
운영 서버 인증, 자동화, 배포 파이프라인, 백엔드 서비스 연동
장점
무제한 권한
설정이 직관적
매우 안전, 자동화 가능, SecretID 1회용/TTL로 보안 극대화
단점
너무 위험함
장기적으로 운영 자동화에 적합하지 않음
개념이 조금 복잡. RoleID + SecretID 관리 필요
5) App Role 구성 -1 : AppRole 활성화 & Policy 생성
1. Vault 탭에서 Access를 선택합니다.
2. Authentication Methods 내에서 ‘Enable new method’를 선택합니다.
- 사전 단계에서 발급받은 VAULT_APP_ROLE_ID, VAULT_APP_ROLE_SECRET 값을 환경 변수로 추가하였고 이를 불러옵니다.
spring:
cloud:
vault:
uri: http://localhost:8200
# 인증방식 : APPID, APPROLE, AWS_EC2, AWS_IAM, AZURE_MSI, CERT, CUBBYHOLE, GCP_GCE, GCP_IAM, KUBERNETES, NONE, PCF, TOKEN;
authentication: APPROLE # Vault 인증 방식 (TOKEN, APPROLE, APPID 등)
connection-timeout: 5000 # Vault 서버 연결 시도 타임아웃(ms)
read-timeout: 15000 # Vault 데이터 읽기 타임아웃(ms)
app-role:
role-id: ${VAULT_APP_ROLE_ID} # 환경변수에서 Vault 접근 role-id를 불러옴
secret-id: ${VAULT_APP_ROLE_SECRET} # 환경변수에서 Vault 접근 role-secret를 불러옴
role: dev-role # 사전에 할당된 role을 불러옴
kv:
enabled: true # KV(키-값) 비밀 엔진 사용 여부
backend: kv2 # Vault에서 생성한 엔진 이름(ex: kv2), 반드시 일치해야 함
# 실제 Vault 경로가 "kv2/" 라면 backend도 kv2로 설정해야 함
default-context: loc # 기본 Secret 경로 (예: kv2/loc/data/application 같은 구조가 됨)
# 즉, Vault UI에서는 /kv2/data/loc 경로와 매핑됨
application-name: '' # Spring 애플리케이션 이름을 Secret 경로에 추가할지 여부
# 빈 값이면 "default-context" 바로 아래에서 조회함
# 예: default: '' → kv2/loc/data
5. 서버 실행
💡 서버 실행
- 정상적으로 오류 없이 서버가 실행하였습니다.
6. Vault 값과 객체 매핑
💡 Vault 값과 객체 매핑
- Vault에서 읽어온 값을 자바 클래스 필드에 자동 바인딩을 하는 클래스입니다.
- @Getter, @Setter 통해서 멤버 변수에 접근할 수 있도록 어노테이션을 지정하였습니다. - @Component를 빈으로 등록하여서, 다른 서비스나 컴포넌트에 주입하여 사용이 가능하도록 하였습니다. - @ConfigurationProperties(prefix = "vault") : 상위 prefix 내에 하위에 동일한 이름으로 각각 자동 바인딩이 됩니다.