Vault(1) - 하시코프의 시크릿 관리 툴, 볼트(Vault)
볼트는 서버 애플리케이션 운영에 필요한 다양한 보안 정보를 안전하게 다룰 수 있도록 도와주는 시크릿 관리 도구이다. 테라폼(Terraform)으로 유명한 하시코프에서 만들었으며, 서버 - 클라이언트 구조로 동작한다.
볼트의 주된 역할은 비밀번호나 API 키, 엑세스 토큰이나 인증서 등 유출되면 안되는 정보를 안전하게 보관해주는 것이다. 서비스 애플리케이션 개발시 수많은 패스워드 관리를 어떻게 해야 할지 고민되기 마련인데, 볼트는 이런 민감한 정보를 엄격한 보안 룰을 적용하여 관리해 준다.
볼트에 저장된 정보는 배리어(Barrier) 라고 부르는 일종의 방화벽을 통해 보호된다. 배리어 외부로 나가는 데이터는 모두 암호화된 상태로 전송되기 때문에, 유출 위험으로부터 안전하다. 저장된 데이터는 볼트 클라이언트를 통해 참조할 수 있거나 API 호출을 통해 읽어들일 수 있다. 따라서 볼트를 사용하면 소스 코드에 패스워드를 하드 코딩하거나 개발자가 서버 환경 변수에다 직접 넣어줄 필요 없이 서버에 동적으로 주입되도록 처리할 수 있다.
볼트는 어떤 일을 하는가?
볼트가 하는 일은 생각보다 많다. DB 접속을 위한 패스워드나 서버간 통신을 위한 인증 키, OIDC 통신을 통해 획득한 엑세스 토큰, 인증서 등을 암호화된 중앙 저장소에 보관하고, 권한을 가진 사용자나 시스템에만 제한적으로 제공한다. 사용자나 시스템의 접근 권한을 개별 관리할 수 있으며, 해당 정보에 접근한 기록을 남겼다가 보안 감사시 백 데이터로 활용할 수도 있다. DB나 OIDC 시스템을 볼트와 연동할 수 있다면, 볼트 서버가 해당 시스템으로부터 직접 패스워드를 발급받아 사용하도록 처리하기도 한다.
왜 볼트를 도입해야 할까?
볼트와 같은 시크릿 관리 툴 없이 서비스를 운영하는 상황을 떠올려보자. 서비스 운영 팀은 다음과 같은 잠재적 위험성을 해결해야 한다.
- 서버 구동을 위한 설정 파일에 패스워드를 하드코딩해야 한다.
- 소스 컨트롤 시스템 내 소스 파일에 암호화되지 않은 패스워드가 그대로 노출된다.
- 패스워드를 변경하려면 서버 코드 패치가 필요하다.
- 개발자가 퇴사할 때마다 패스워드를 로테이션해야 한다. 따라서 퇴사시 마다 서버를 패치해야 한다.
볼트는 이런 상황을 간단하게 해결해준다. 각종 민감한 정보를 볼트 서버 안에 넣고, 개발자는 볼트에 접근할 권한만 획득한다. 패스워드는 컴포넌트가 API를 통해 동적으로 참조할 수 있으므로 개발자가 직접 알 필요가 없다. 새로운 개발자가 팀에 합류하면 볼트에 접근할 계정만 생성해주면 그 뿐이다.
누군가 퇴사하더라도 별 문제는 없다. 패스워드를 바꿀 필요없이 퇴사자의 볼트 권한만 삭제하면 되기 때문이다. 주기적인 비밀번호 변경 시에도 볼트에 저장된 정보를 새로운 것으로 교체해 두면(이를 ‘로테이션’이라고 한다) 컴포넌트 재기동시 자동으로 변경된 패스워드를 주입받게 되므로 코드를 패치할 필요도 없다. 패스워드 관리가 깔끔해지는 것이다.
물론 볼트를 도입한 초기에는 무척 번거로울 것이다. 정적으로 관리하던 패스워드를 모두 볼트 서버에 등록해야 하고, 각 개발자 별로 권한을 부여해야 한다. 서비스를 개발할 때에도 기존에는 설정 파일 내에 패스워드를 입력하면 되던 것이, 이제는 각 컴포넌트가 볼트 API를 통해 동적으로 비밀번호를 주입받도록 처리해야 한다. 코드 작업량이 늘어나고, 관리 책임이 늘어날 수 밖에 없다. 익숙해지기까지, 동료 개발자들의 투덜거림을 감수해야 할 수도 있다.
하지만 그럼에도 불구하고 볼트를 사용함으로써 얻는 이점은, 사용하지 않음으로 얻는 편리함을 확실히 상회한다. 보안 측면에서 훨씬 성숙한 시스템을 갖출 수 있다는 장점은 무엇보다도 소중하다.
실습, Hello, Vault!
볼트를 실제로 설치하고 사용해 보자. 볼트는 오픈 소스로 공개되어 있으며, 윈도우와 맥, 리눅스 환경을 모두 지원한다. OS에 맞는 파일을 내려받아 압축을 풀고 실행 경로에 넣어주기만 하면 된다.
또, 서버와 클라이언트가 하나의 바이너리로 패키징되어 있기 때문에 설치시 구분할 필요가 없다. 볼트는 기본적으로 CLI 방식이기 때문에 처음에는 낯설겠지만 금세 익숙해지게 될 것이다(물론 웹 콘솔용 플러그인도 존재한다).
1. 도커 컨테이너에 볼트 설치하기
엔터프라이즈 환경에서 볼트는 대부분 리눅스 기반으로 운영된다. 따라서 실제 사용 환경을 익히려면 리눅스에서 볼트 서버를 설치하고 사용해 보는 것이 가장 좋다.
하지만 잠깐의 실습을 위해 실제 리눅스 머신을 갖추기는 현실적으로 어렵다. 대신 추천하는 방식은 버추얼 박스(Virtual Box)같은 하이퍼 바이저를 이용하여 가상 리눅스 머신을 세팅하는 것이다. 혹은 도커가 설치되어 있다면 간단히 리눅스 컨테이너를 만들어 실습해 볼 수도 있다. 여기서는 도커를 이용한 실습 과정을 설명한다. 아무 것도 설치되지 않은 빈 리눅스 컨테이너에 볼트 바이너리 파일을 설치하고, 실행하는 과정을 다룰 예정이다(실습을 위해서는 도커가 설치되어 있어야 한다).
참고로 하시코프에서는 도커 허브(https://hub.docker.com)를 통해 볼트 공식 이미지를 제공한다. 이를 이용하면 도커 환경에서도 손쉽게 볼트 서버를 구동할 수 있다.
1. 최신 우분투 이미지를 이용하여 리눅스 컨테이너를 실행한다.
$ docker run -it -p 8200:8200 ubuntu /bin/sh ⏎
-it
는 컨테이너 생성 후 곧바로 접속하여/bin/sh
셸을 실행시켜주는 옵션이다. 컨테이너 내부로 접속하도록 해준다.-p 8200:8200
은 볼트 서버의 실행 포트인 8200을 컨테이너 외부 포트와 연결하는 옵션이다. 이 옵션을 통해 컨테이너 외부에서도 볼트 서버에 접속할 수 있다.- Ubuntu는 리눅스의 한 종류로, ubuntu 이미지를 이용하여 컨테이너를 생성하면 깡통 상태의 우분투 리눅스가 설치된다.
컨테이너가 실행되면 곧바로 해당 리눅스 내부로 연결된다. 아래와 같이 #
이 표시된다면 리눅스 컨테이너에 접속된 것이다.
$ docker run -it -p 8200:8200 ubuntu /bin/sh ⏎
#
2. 우분투에 unzip과 wget, curl을 설치한다. 먼저, apt-get의 업데이트가 필요하다.
$ apt-get update ⏎
$ apt-get install unzip wget curl ⏎
컨테이너에 사용된 우분투 이미지는 그야말로 깡통이기 때문에, apt-get 정도만 설치되어 있을 뿐 wget, unzip 같은 나머지 툴은 거의 설치되어 있지 않다. 볼트 설치 과정에는 이들이 필요하므로 추가하자. apt-get 업데이트 후 해당 툴들을 설치하면 된다. 참고로 apt-get은 리눅스 계열에서 필요한 소프트웨어를 쉽게 설치할 수 있도록 도와주는 패키지 관리 툴이다.
위 커맨드를 실행하면 다음과 같은 메세지가 출력된다. 설치에 참고하자.
$ apt-get update
Get:1 <http://security.ubuntu.com/ubuntu> focal-security InRelease [109 kB]
Get:2 <http://archive.ubuntu.com/ubuntu> focal InRelease [265 kB]
Get:3 <http://security.ubuntu.com/ubuntu> focal-security/multiverse amd64 Packages [1167 B]
...
Fetched 16.6 MB in 6s (2790 kB/s)
Reading package lists... Done
$ apt-get install wget curl unzip
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
...
Setting up libldap-2.4-2:amd64 (2.4.49+dfsg-2ubuntu1.5) ...
Setting up libcurl4:amd64 (7.68.0-1ubuntu2.4) ...
Setting up curl (7.68.0-1ubuntu2.4) ...
Processing triggers for libc-bin (2.31-0ubuntu9.1) ...
Processing triggers for ca-certificates (20201027ubuntu0.20.04.1) ...
Updating certificates in /etc/ssl/certs...
0 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
3. 볼트 바이너리 패키지를 다운로드한다.
$ wget https://releases.hashicorp.com/vault/1.6.1/vault_1.6.1_linux_amd64.zip ⏎
내려받을 바이너리 파일은 하시코프에서 다운로드 페이지를 통해 제공한다. 해당 패키지는 볼트 서버 및 클라이언트로 사용 가능한 바이너리 파일이 포함되어 있다. 볼트의 신규 버전은 자주 업데이트되고 있으므로 다운로드 전에 미리 최신 버전을 확인하여 그에 맞는 버전을 지정하는 것이 좋다. 하시코프의 공식 링크는 다음과 같다.
https://releases.hashicorp.com/vault/
4. 파일의 압축을 해제한다.
$ unzip vault_1.6.1_linux_amd64.zip ⏎
5. vault 실행 파일을 /usr/bin 아래로 옮겨준다.
$ mv vault /usr/bin ⏎
6. vault를 실행하여 설치 여부를 확인한다. 제대로 설치되었다면 사용 가능한 커맨드 목록이 출력될 것이다.
$ vault ⏎
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
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
path-help Retrieve API help for paths
plugin Interact with Vault plugins and catalog
policy Interact with policies
print Prints runtime configurations
secrets Interact with secrets engines
ssh Initiate an SSH session
token Interact with tokens
볼트의 설치 과정은 바이너리 파일을 내려받는 것이 전부이다. 볼트 실행 파일은 커맨드와 결합하여 동작하며, 커맨드를 생략할 경우 위와 같이 선택 가능한 커맨드와 간단한 설명이 출력되므로 참고할 수 있다.
7. 볼트 서버를 구동한다.
$ vault server -dev -dev-listen-address 0.0.0.0:8200 ⏎
-dev
는 볼트 서버를 개발 모드로 실행하는 옵션이다. 개발 모드는 서비스 모드와 달리 복잡한 설정을 생략할 수 있고 처음부터 봉인이 해제된 unsseal 상태로 실행되기 때문에 간단히 테스트해 볼 수 있다. 하지만 모든 값이 인 메모리에 저장되며 서버 종료시 저장된 시크릿도 함께 삭제되기 때문에, 실제 서비스에는 사용할 수 없다. 개발 모드로 실행했을 때의 특성은 다음과 같다.- 미리 준비된 설정으로 서버가 구동된다(설정을 생략 가능하다)
- 모든 데이터는 인 메모리(in-memory)에 저장된다.
- 데이터는 암호화된다.
- localhost로 listen되며, TLS가 없다.
- 자동으로 봉인이 해제된다(서비스 모드에서는 권한을 가진 이가 모여 봉인을 해제해야 한다)
-dev-listen-address
옵션은 볼트 서버 기동시 사용할 주소와 포트를 지정하는 역할을 한다.
실행 결과는 다음과 같다.
$ vault server -dev-listen-address 0.0.0.0:8200
==> Vault server configuration:
Api Address: <http://0.0.0.0:8200>
Cgo: disabled
Cluster Address: <https://0.0.0.0:8201>
...
2021-01-02T03:10:24.564Z [INFO] secrets.kv.kv_411470d3: collecting keys to upgrade
2021-01-02T03:10:24.564Z [INFO] secrets.kv.kv_411470d3: done collecting keys: num_keys=1
2021-01-02T03:10:24.564Z [INFO] secrets.kv.kv_411470d3: upgrading keys finished
WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory
and starts unsealed with a single unseal key. The root token is already
authenticated to the CLI, so you can immediately begin using Vault.
You may need to set the following environment variable:
$ export VAULT_ADDR='http://0.0.0.0:8200'
The unseal key and root token are displayed below in case you want to
seal/unseal the Vault or re-authenticate.
Unseal Key: <봉인해제를 위한 랜덤키>
Root Token: <볼트 클라이언트 로그인용 토큰>
Development mode should NOT be used in production installations!
실행 결과 메세지 중에서 Unseal Key와 Root Token은 설치시 랜덤하게 생성되는 중요한 키 값이다. 특히 Root Token은 이번 실습의 뒷 부분에서 볼트 로그인시 필요하므로 별도로 관리할 것을 권장한다.
2. 볼트 CLI를 사용하여 서버에 접속하기
또다른 우분투 컨테이너를 세팅하여 볼트 CLI를 설치하고, 방금 구동한 볼트 서버에 접속해 보자. 과정은 거의 동일하지만, 맨 마지막 단계인 vault server -dev ...
커맨드는 필요없다.
$ docker run -it --rm -p 8200:8200 ubuntu /bin/sh ⏎
--rm
는 실행 종료 시 컨테이너를 삭제하는 옵션이다. 테스트용 컨테이너 생성시 사용하면 흔적이 냠지 않아 편리하다.
새로운 우분투 컨테이너 내부에서 다음 과정을 진행한다.
$ apt-get update ⏎
$ apt-get install unzip wget curl ⏎
$ wget <https://releases.hashicorp.com/vault/1.6.1/vault_1.6.1_linux_amd64.zip> ⏎
$ unzip vault_1.6.1_linux_amd64.zip ⏎
$ mv vault /usr/bin ⏎
볼트를 설치했다면, VAULT_ADDR
환경 변수를 설정한다.
$ export VAULT_ADDR=http://<로컬 컴퓨터 IP>:8200 ⏎
VAULT_ADDR
은 볼트 클라이언트가 접속할 서버 주소를 알려주기 위한 환경 변수이다. 도커 환경에서 컨테이너 to 컨테이너로 접속하려면 컨테이너를 실행하는 로컬 머신의 IP를 사용해야 한다.
클라이언트 설정이 모두 끝났다. 이제 볼트 서버에 접속해 보는 일만 남았다.
$ vault status ⏎
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 1
Threshold 1
Version 1.6.1
Storage Type inmem
Cluster Name vault-cluster-7c043066
Cluster ID 3f8a7ec5-7ac5-ceba-0dcc-2db97ae1a87a
HA Enabled false
vault status
커맨드는 볼트 서버에 접속하여 상태 정보를 가져오는 기능을 한다. Sealed
필드의 값이 false인 것으로 보아 볼트 봉인이 해제되어 있음을 알 수 있다(Sealed의 의미는 다음 포스팅에서 알아볼 예정이다)
볼트 서버 접속이 확인되었으므로, 이제 로그인을 해 보자. 사용자나 애플리케이션이 볼트에 저장된 시크릿을 읽거나 쓰기 위해서는 로그인이 필요하다. 이를 위해 볼트는 다양한 로그인 방식을 지원하는데, 대표적으로 username/password 기반 인증이나 토큰 인증이나 LDAP 인증, 또는 깃허브나 OIDC 인증을 이용할 수 있다. 여기서는 토큰 인증을 사용한다(토큰 인증에서는 당연하게도 로그인을 위해 토큰이 필요한데, 앞서 볼트 서버를 실행할 때 출력된 메세지에서 루트 토큰을 복사하여 입력하면 된다).
$ vault login ⏎
Token (will be hidden): # 볼트 서버 실행시 출력된 메세지에서 Root Token 값을 확인하여 입력한다.
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.
Key Value
--- -----
token XXXX
token_accessor JXHtS6DXXXXX0HFWOPQ
token_duration ∞
token_renewable false
token_policies ["root"]
identity_policies []
policies ["root"]
-
로그인을 위해서는
vault login
커맨드를 사용한다. 잘 모르겠다면vault
커맨드만 입력해보자. 사용 가능한 커맨드 목록이 출력될 것이다.만약 인증 방식을 선택하고 싶다면
-method
옵션을 사용한다. 아래는 OIDC 인증을 사용하는 예이다.$ vault login -method=oidc ⏎
-
로그인시 토큰을 입력해야 하는데, 우리는 개발 모드로 실행했기 때문에 루트 토큰을 사용할 수 있다. 앞서 개발 모드로 실행한 볼트 서버 터미널 메세지를 확인하자. 선명하게 출력된 Root Token을 볼 수 있을 것이다. 실제 서비스 환경에서는 토큰을 직접 생성하는 과정이 필요하다.

3. 윈도우 / 맥 OS 환경에서 볼트 설치 및 실행하기
볼트는 윈도우/맥 OS 환경도 지원한다. 윈도우에서는 choco를, 맥에서는 brew를 사용하면 된다. 실제 서비스용 볼트 서버에 접속할 때에는 대부분 본인의 컴퓨터에 직접 볼트 CLI를 설치하는 경우가 많다.
Windows에서)
$ choco install vault -y
macOS에서)
$ brew install vault
설치가 끝나면 VAULT_ADDR 환경 변수에 볼트 서버 주소를 설정한다. 윈도우 환경에서는 sysmtem.cpl
을 실행한 후 시스템 환경 변수에 해당 주소를 등록하고, 맥 OS 환경에서는 export 명령어를 사용하여 VAULT_ADDR 환경 변수를 직접 설정하면 된다.
$ export VAULT_ADDR=http://<자신의 IP 또는 localhost>:8200
만약 볼트 서버가 자신의 도커 컨테이너에서 실행 중이라면 IP 대신 localhost를 주소로 사용할 수 있다. 로컬 컴퓨터에서는 자신의 도커 컨테이너에 localhost로 접근 가능하기 때문이다(만약 다른 컴퓨터에 설치된 볼트 서버에 접속하는 경우라면 당연히 localhost는 사용할 수 없다).
단번에 이해가 가지 않는다면 컨테이너에서 다른 컨테이너로 접근하는 것과, 로컬 컴퓨터에서 자신의 컨테이너로 접근하는 네트워크의 차이를 알아둘 필요가 있다.
- 컨테이너 to 컨테이너 : 반드시 로컬 컴퓨터의 IP 사용
- 로컬 컴퓨터 to 컨테이너 : localhost 또는 127.0.0.1 사용 (물론 로컬 컴퓨터의 IP를 사용해도 된다)
이후의 과정은 동일하다. 어느 환경의 CLI든 볼트 서버에 접속하여 로그인할 수 있을 것이다.
4. 볼트 서버에 시크릿 저장하고 확인하기
이제 실제로 볼트 서버에 시크릿을 저장하고 참조하는 과정을 실습해 보자(여기서 말하는 시크릿은 패스워드, 인증키, 엑세스 토큰, 인증서 등 민감한 정보를 모두 통칭하는 명칭이다). 볼트는 각각의 시크릿을 파일처럼 관리하기 때문에, 저장시 패스를 지정해주어야 한다(물론 이것이 실제 저장 경로를 의미하는 것은 아니다). 이번 실습에서 시크릿 패스의 루트는 공식 경로인 /secret
를 사용한다.
먼저, 시크릿을 저장하는 예이다.
$ vault kv put secret/app/tutorial username=winny password=1234 ⏎
Key Value
--- -----
created_time 2021-01-03T07:00:04.0143405Z
deletion_time n/a
destroyed false
version 4
secret/app/tutorial
패스에 username과 password 두 개의 시크릿을 저장했다. 볼트 서버는 키-값 형태로 시크릿을 저장하며, 하나의 시크릿 패스에 여러 개의 값을 저장할 수 있다. 저장할 때에는 <키=값> 형태로 차례대로 나열하면 된다. 순서는 상관없다.
저장된 값을 확인해보자. 값을 참조하는 방법은 다음과 같다.
$ vault kv get secret/app/tutorial ⏎
====== Metadata ======
Key Value
--- -----
created_time 2021-01-03T07:00:04.0143405Z
deletion_time n/a
destroyed false
version 4
====== Data ======
Key Value
--- -----
password 1234
username winny
조금 전 저장한 된 값이 메타데이터와 함께 출력된다. 저장시간과 삭제 예정 시각, 그리고 삭제 여부 및 버전을 함께 확인할 수 있다. 이들 값은 볼트 백엔드 저장소에 보관되며, 암호화된 상태로 전달된다. 저장소에 보관된 시크릿을 누군가 탈취하더라도 볼트 없이는 값을 디코딩할 수 없다.
특정 필드만 보고 싶다면 -field
옵션을 사용한다.
$ vault kv get -field=username secret/app/tutorial ⏎
winny
-format
옵션을 사용하면 결과값을 json 포맷으로 출력할 수도 있다. json 파싱을 도와주는 jq 툴을 함께 사용하면 편리하다.
$ vault kv get -format=json secret/app/tutorial ⏎
{
"request_id": "e9bf1552-1200-799f-3c4a-fbe81d7b6e50",
"lease_id": "",
"lease_duration": 0,
"renewable": false,
"data": {
"data": {
"password": "1234",
"username": "winny"
},
"metadata": {
"created_time": "2021-01-03T07:28:18.9812519Z",
"deletion_time": "",
"destroyed": false,
"version": 6
}
},
"warnings": null
}
$ vault kv get -format=json secret/app/tutorial | jq -r .data.data.username ⏎
winny
볼트에서 기존 시크릿 패스에 새로운 키와 값을 등록하면 기존 키는 삭제된다. 기존 키와 새로운 키가 나란히 추가되는 개념이 아니므로 주의해야 한다.
$ vault kv put secret/app/tutorial apikey=12345678 ⏎
$ vault kv get secret/app/tutorial ⏎
===== Data =====
Key Value
--- -----
apikey 12345678
하위 패스의 리스트를 볼 때에는 vault kv list
명령을 사용한다. 하위 경로가 많을 때 사용하면 유용하다.
$ vault kv list secret/ ⏎
Keys
----
app/
마지막으로, 저장된 시크릿을 삭제할 때에는 vault kv delete
명령을 사용한다.
$ vault kv delete secret/app/tutorial ⏎
매번 입력하는 kv
는 ‘시크릿 입출력시 kv 엔진을 사용하여 처리하라’는 뜻으로, kv는 볼트에서 제공하는 가장 기본적인 시크릿 관리 엔진이다. 주로 정적인 정보를 직접 키-값 형태로 관리하고자 할 때 사용한다. 이외에도 볼트는 여러 가지 시크릿 엔진을 통해 DB 또는 AWS IAM 등과 연동을 지원한다. 다음 포스팅에서는 이에 대해 알아보도록 하겠다.