티스토리 뷰

개발/Vault

Vault 컨셉과 기능 소개

Jaeyeon Baek 2024. 9. 29. 21:35

HashiCorp Vault2014년 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 주소로 접속을 해봅니다.

토큰은 터미널에서 입력한 root를 입력해 줍니다

 

이제 메뉴를 천천히 둘러볼 시간입니다.

메뉴가 단촐합니다. 그만큼 메뉴가 복잡하지 않아요

 

# Secrets Engines

처음 메뉴에 들어가서 보면 cubbyhole과 secret을 지원합니다. 두 가지 타입 모두 key/value 형태로 값을 저장하는데 가장 큰 차이점은 cubbyhole은 버전 관리 기능이 없습니다. 반면 secret에는 있고요.

Secrets Engines 목록

 

여기서 cubbyhole과 secret 각각에 데이터를 넣어두고 읽어보도록 하겠습니다. 먼저 secret을 만들어 보겠습니다. 화면 우측 상단에 Create secret을 눌러주세요.

secret 생성

 

아래와 같이 값을 넣어줄 수 있을 겁니다.

필요한 Secret data를 추가해 줍니다

 

여기서 Path for this secret은 디렉터리 구조처럼 사용할 수 있습니다. 생성이 완료되면 아래 화면처럼 데이터를 볼 수 있는데 여기서 버전 관리를 할 수 있습니다.

Create new version을 통해 신규 버전 발행 가능

 

그리고 상단 메뉴를 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에 데이터베이스를 추가해 주도록 합니다.

Secrets Engines메뉴에서 Enable a Secrets Engine을 선택합니다

 

Default Lease와 Max Lease을 일단 둘 다 3600으로 설정하고 활성화시켜 봅니다.

TTL을 활성화 시키고 Enable engine을 클릭합니다

 

이제 생성된 database engine 메뉴에서 데이터베이스를 연결합니다. 여기 예제에서는 미리 docker를 통해 postgres를 설치해 뒀습니다.

중앙에 +Connect a database를 선택합니다

 

필요한 값들을 채워 넣고 Create database를 선택합니다.

필요한 값을 넣습니다. 나중에 수정도 가능하니 편하게 선택하시면 됩니다

 

그럼 바로 아래와 같은 팝업이 출력됩니다. 우리가 넣은 계정의 패스워드를 Rotate 시킬 거냐는 질문인데 일단 여기서는 Rotate 하지 않는 것으로 선택하겠습니다. 

임시로 띄워놓은 Vault에서 postgres 패스워드를 변경하면 곤란해요

 

모든 준비가 됐습니다. 이제 Add role을 통해 사용자를 관리할 수 있습니다.

Add role을 선택하면 됩니다

 

Role은 static과 dynamic 두 가지 타입을 지원합니다. 

Type of role

 

static은 아래와 같은 특징이 있습니다.

  • 이미 존재하는 계정에 대해 패스워드를 로테이트 시킴
  • 애플리케이션에서 role을 요청했을 때 동일한 사용자 계정을 사용할 수 있음

반면 dynamic은 아래와 같습니다.

  • 요청이 들어올 때마다 새로운 계정을 생성합니다. 즉, 모든 계정이 임시입니다

 

얼마나 많은 요청이 동시다발적으로 Vault로 들어오는지가 관건일 것 같습니다. 결국 Vault 가 동작하는 환경과 데이터베이스의 스펙에 따라 성능을 얼마나 뽑아낼 수 있는지가 중요하지 않을까 싶습니다. 어떤 타입을 사용하던지 애플리케이션에서 캐싱해야 할 거라서 계정 생성과 로테이션에 대한 비용만 생각하면 될 것 같네요.

여기서는 아래와 같은 설정으로 진행합니다.

dynamic role 설정

 

여기서 몇 가지 알아야 하는 게 있어서 정리합니다.

  • 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로 이동해 주세요.

Tools 메뉴로 이동합니다

 

그리고 API Explorer로 이동하면 아래와 같이 Vault API 스웨거로 이동됩니다. 

모든 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