티스토리 뷰
HashiCorp Vault는 2014년 4월에 발표됐습니다. Vault는 토큰, 비밀번호, 인증서, 암호화 키에 대한 액세스를 보호하고 저장하며 엄격하게 제어하여 비밀 및 기타 민감한 데이터를 보호하기 위해 사용하는 솔루션입니다. 컨셉을 이해하려면 HashiCorp 블로그 글을 읽어보세요.
이번 글에서는 Vault를 설치하고 기본 컨셉에 대해 살펴보도록 하겠습니다. Vault를 쓰면 좋겠다고 생각한 첫 번째 이유가 데이터베이스 접속 정보 관리인데요. 아래와 같은 흐름으로 사용이 가능합니다. Vault의 가장 기본적인 사용 예시입니다.
CSP에 있는 Secrets Manager와 비슷합니다. 하지만 Vault의 경우 필요한 순간에 데이터베이스 계정을 "생성"해서 줄 수 있습니다. 그것이 가장 큰 차이점입니다.
Vault를 사용하는 부분으로 넘어가서 살펴보겠습니다. 일단 설치를 해야 하는데요. MacOS에서 아래와 같이 설치가 가능합니다. 다른 OS에서 설치 방법은 여기, helm을 통한 배포는 여기를 참고해 주세요.
brew tap hashicorp/tap
brew install hashicorp/tap/vault
이제 터미널에서 vault 명령어를 사용할 수 있습니다.
$ vault help
Usage: vault <command> [args]
Common commands:
read Read data and retrieves secrets
write Write data, configuration, and secrets
delete Delete secrets and configuration
list List data or secrets
login Authenticate locally
agent Start a Vault agent
server Start a Vault server
status Print seal and HA status
unwrap Unwrap a wrapped secret
Other commands:
audit Interact with audit devices
auth Interact with auth methods
debug Runs the debug command
events
hcp
kv Interact with Vault's Key-Value storage
lease Interact with leases
monitor Stream log messages from a Vault server
namespace Interact with namespaces
operator Perform operator-specific tasks
patch Patch data, configuration, and secrets
path-help Retrieve API help for paths
pki Interact with Vault's PKI Secrets Engine
plugin Interact with Vault plugins and catalog
policy Interact with policies
print Prints runtime configurations
proxy Start a Vault Proxy
secrets Interact with secrets engines
ssh Initiate an SSH session
token Interact with tokens
transform Interact with Vault's Transform Secrets Engine
transit Interact with Vault's Transit Secrets Engine
version-history Prints the version history of the target Vault server
터미널에서 대부분의 작업을 할 수 있지만 우선 GUI를 통해서 친해져 보도록 하죠. 아래와 같이 서버를 띄우면 브라우저를 통해 Vault를 만날 수 있습니다. 어쩌면 당연한 이야기지만 터미널에서만 가능한 명령도 존재하기 때문에 모든 것을 GUI로 해결하려고 하면 안 됩니다.
vault server -dev -dev-root-token-id="root"
여기서 -dev-root-token-id를 입력하지 않아도 괜찮습니다. 입력하지 않을 경우 랜덤 토큰이 생성되고, vault 실행 화면에서 바로 확인하실 수 있을 겁니다. 명령어를 실행하셨으면 http://127.0.0.1:8200 주소로 접속을 해봅니다.
이제 메뉴를 천천히 둘러볼 시간입니다.
# Secrets Engines
처음 메뉴에 들어가서 보면 cubbyhole과 secret을 지원합니다. 두 가지 타입 모두 key/value 형태로 값을 저장하는데 가장 큰 차이점은 cubbyhole은 버전 관리 기능이 없습니다. 반면 secret에는 있고요.
여기서 cubbyhole과 secret 각각에 데이터를 넣어두고 읽어보도록 하겠습니다. 먼저 secret을 만들어 보겠습니다. 화면 우측 상단에 Create secret을 눌러주세요.
아래와 같이 값을 넣어줄 수 있을 겁니다.
여기서 Path for this secret은 디렉터리 구조처럼 사용할 수 있습니다. 생성이 완료되면 아래 화면처럼 데이터를 볼 수 있는데 여기서 버전 관리를 할 수 있습니다.
그리고 상단 메뉴를 Paths로 이동하면 API 호출 경로를 확인할 수 있습니다.
터미널에 명령어를 복사해서 붙여 넣었는데 아래와 같은 오류가 발생하는 경우가 있습니다. 아마 이 글을 그대로 따라 했다면 무조건 발생할 겁니다.
$ vault kv get -mount="secret" "/redshift/team/payment"
WARNING! VAULT_ADDR and -address unset. Defaulting to https://127.0.0.1:8200.
Get "https://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret": http: server gave HTTP response to HTTPS client
그때는 환경변수를 통해 Vault의 주소를 http로 지정해 주면 됩니다.
export VAULT_ADDR='http://127.0.0.1:8200'
이제 값을 확인하실 수 있습니다.
$ vault kv get -mount="secret" "redshift/team/payment"
========= Secret Path =========
secret/data/redshift/team/payment
======= Metadata =======
Key Value
--- -----
created_time 2024-09-05T06:27:53.280911Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1
====== Data ======
Key Value
--- -----
host localhost:1234
id developer
password Password123!
미리 값을 세팅해 둔 cubbyhole는 아래와 같이 출력됩니다.
$ vault read cubbyhole/redshift/team/admin
Key Value
--- -----
id admin
password Password123!@#
당연히 두 가지 모두 API를 통해 값을 가져올 수 있습니다. 애플리케이션에서 이용하면 되겠죠? 아래는 cubbyhole 정보 예시입니다.
$ curl --header "X-Vault-Token: root" --request GET http://127.0.0.1:8200/v1/cubbyhole/redshift/team/payment
{"request_id":"0817688f-7e16-9af3-5a2b-c8d713924b60","lease_id":"","renewable":false,"lease_duration":0,"data":{"id":"admin","password":"Password123!@#"},"wrap_info":null,"warnings":null,"auth":null,"mount_type":"cubbyhole"}
다음으로 secret입니다.
$ curl --header "X-Vault-Token: root" --request GET http://127.0.0.1:8200/v1/secret/data/redshift/team/payment
{"request_id":"edb12faa-3edc-e653-16dc-c5559f554892","lease_id":"","renewable":false,"lease_duration":0,"data":{"data":{"host":"localhost:1234","id":"developer","password":"Password123!"},"metadata":{"created_time":"2024-09-05T06:27:53.280911Z","custom_metadata":null,"deletion_time":"","destroyed":false,"version":1}},"wrap_info":null,"warnings":null,"auth":null,"mount_type":"kv"}
버전 관리를 할 수 있느냐 없느냐에서 느끼셨겠지만 대체로 cubbyhole은 생명주기가 짧은 기밀 정보를 저장할 때 사용하고 secret은 좀 더 길게 저장할 때 사용한다고 하네요 ( 둘 다 expire 설정을 할 수 있습니다 )
# 데이터베이스 패스워드 회전(Rotate)
위에서 설명한 secret과 cubbyhole은 자체적으로 Rotate가 안 됩니다. 예상컨대 key/value 형태라서 무엇을 어떤 기준으로 rotate 시켜야 할지 판단할 수 없기 때문 같네요. 하지만 데이터베이스는 가능합니다. 먼저 Secrets Engines에 데이터베이스를 추가해 주도록 합니다.
Default Lease와 Max Lease을 일단 둘 다 3600으로 설정하고 활성화시켜 봅니다.
이제 생성된 database engine 메뉴에서 데이터베이스를 연결합니다. 여기 예제에서는 미리 docker를 통해 postgres를 설치해 뒀습니다.
필요한 값들을 채워 넣고 Create database를 선택합니다.
그럼 바로 아래와 같은 팝업이 출력됩니다. 우리가 넣은 계정의 패스워드를 Rotate 시킬 거냐는 질문인데 일단 여기서는 Rotate 하지 않는 것으로 선택하겠습니다.
모든 준비가 됐습니다. 이제 Add role을 통해 사용자를 관리할 수 있습니다.
Role은 static과 dynamic 두 가지 타입을 지원합니다.
static은 아래와 같은 특징이 있습니다.
- 이미 존재하는 계정에 대해 패스워드를 로테이트 시킴
- 애플리케이션에서 role을 요청했을 때 동일한 사용자 계정을 사용할 수 있음
반면 dynamic은 아래와 같습니다.
- 요청이 들어올 때마다 새로운 계정을 생성합니다. 즉, 모든 계정이 임시입니다
얼마나 많은 요청이 동시다발적으로 Vault로 들어오는지가 관건일 것 같습니다. 결국 Vault 가 동작하는 환경과 데이터베이스의 스펙에 따라 성능을 얼마나 뽑아낼 수 있는지가 중요하지 않을까 싶습니다. 어떤 타입을 사용하던지 애플리케이션에서 캐싱해야 할 거라서 계정 생성과 로테이션에 대한 비용만 생각하면 될 것 같네요.
여기서는 아래와 같은 설정으로 진행합니다.
여기서 몇 가지 알아야 하는 게 있어서 정리합니다.
- Generated credentials’s Time-to-Live (TTL)
해당 계정이 유효한 시간입니다. 시간이 지나면 만료되면 아래와 같이 쿼리 실행 시 오류가 발생합니다.
connection to server at "localhost" (::1), port 55000 failed: FATAL: password authentication failed for user "v-token-dynamic--BXV4s1lfZl53vVHYQ64h-1725528870"
- Generated credentials’s maximum Time-to-Live (Max TTL)
dynamic으로 생성된 계정은 유효 시간을 늘릴 수 있습니다. renew를 통해 갱신할 수 있는데 갱신을 하더라도 최대 이 시간은 넘지 못합니다. - Statements
postgres를 사용 시 아래와 같은 권한을 줘야 합니다
CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}' INHERIT;
자, 이렇게 Role까지 설정하고 나면 이제 애플리케이션에서 Vault를 통해 계정을 생성할 수 있습니다. renew 시에는 lease_id가 활용됩니다.
$ curl --header "X-Vault-Token: root" --request GET http://127.0.0.1:8200/v1/database/creds/dynamic-role
{"request_id":"12109ecb-28c8-ac48-0032-523e374453b2","lease_id":"database/creds/dynamic-role/lonV7toJrRzJhVDYuC3q9TI6","renewable":true,"lease_duration":60,"data":{"password":"Fc20-051rMSwgu6y7Zxk","username":"v-token-dynamic--2ZcCIhb3wL0raCagH9NK-1725529510"},"wrap_info":null,"warnings":null,"auth":null,"mount_type":"database"}
여기 있는 username과 password를 통해 데이터베이스에 접속하시면 됩니다.
$ psql -h localhost -p 55000 -d postgres -U v-token-dynamic--2ZcCIhb3wL0raCagH9NK-1725529510
Password for user v-token-dynamic--2ZcCIhb3wL0raCagH9NK-1725529510:
psql (14.12 (Homebrew), server 16.4 (Debian 16.4-1.pgdg120+1))
WARNING: psql major version 14, server major version 16.
Some psql features might not work.
Type "help" for help.
postgres=> \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+----------+----------+------------+------------+-----------------------
postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =Tc/postgres +
| | | | | postgres=CTc/postgres
template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +
| | | | | postgres=CTc/postgres
(3 rows)
# Swagger
설정은 이렇게 UI를 통해서 할 수도 있지만 CLI로도 가능합니다. 앞서 살펴봤던 vault 명령어를 터미널에서 입력해서요. 여기에 추가로 모든 기능은 API를 통해 제어 가능합니다. 실제 현업으로 가게 되면 데이터베이스 정보를 얻거나 자격 증명을 갱신하는 것도 API를 통해 진행되는데요. 놀랍게도 Vault를 설치하게 되면 UI 안에서 Swagger를 만나볼 수 있습니다.
우선 좌측 메뉴에서 Tools로 이동해 주세요.
그리고 API Explorer로 이동하면 아래와 같이 Vault API 스웨거로 이동됩니다.
# 마치며
일을 하면서 데이터베이스 접속 권한은 항상 화두입니다. 개발팀은 권한을 얻고자 하고, 데이터베이스를 관리하는 팀은 지키고자 하고요. Vault와 같은 도구를 이용하면 이런 문제가 해결됩니다. 계정을 발급하고 언제 테스트가 끝나는지 기다리고 확인할 필요가 없습니다. 정해진 시간이 되면 만료될 테니까요. 애플리케이션에서 사용하는 패스워드도 보안 규격에 따라 주기적으로 갱신을 해줘야 하는데 신경 쓸 필요가 없어집니다. Vault가 알아서 rotate 시키거나 혹은 임시 자격 발급이기 때문에 알아서 파기되니까요. AWS나 Google Cloud와 같은 CSP의 IAM도 마찬가지입니다. Vault를 통해 보안을 한 단계 더 진화시킬 수 있습니다. 그런데 문제는 역시 돈입니다. 직접 구축해서 쓰려면 Vault가 설치된 서버의 보안과 가용성을 챙겨야 하는데 이게 쉽지 않거든요. 모든 Secrets을 Vault로 제공하는데 가용성이 무너졌다고 생각해 보세요. 끔찍한 상황일 겁니다. 이런 상황을 피하려면 클라우드를 통해 제공하는 Managed Vault를 사용하면 되지만 여기는 비용이 끔찍합니다. 결국 trade-off인데 두 개를 비교해 보고 올바른 선택을 할 수 있기를 바랍니다. (저 자신에게 하는 말)
'개발 > Vault' 카테고리의 다른 글
AWS Secrets engine 소개와 사용방법 (0) | 2024.10.07 |
---|
- Total
- Today
- Yesterday