Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.

HashiCorp의 비밀정보 관리 도구 Vault의 사용

HashiCorp의 비밀정보 관리 도구 Vault의 구성에서 Vault의 기본적인 개념과 서버를 구성하는 방법을 살펴보았다. 이 설명도 꽤 길었지만, 아직 Vault를 어떻게 사용하는지 감이 잘 안 올 것이라고 생각하는데 이번에는 실제로 Vault를 어떻게 사용하는지 살펴보자.

Vault 서버에서 데이터 읽고 쓰기

앞에서 초기화를 할 때 root token이라는 것을 하나 받았다. 이는 Vault 서버의 루트 권한이 있는(서버의 모든 작업을 할 수 있고 sealed 상태로 바꿀 수도 있지만, unseal을 할 수는 없다.) 토큰이다. 일단 이 토큰으로 Vault를 사용해 보자. 토큰은 VAULT_TOKEN 환경변수에 저장하면 vault 클라이언트에서 사용할 수 있다.

$ export VAULT_TOKEN=c0ccbf61-cd7b-bd98-dabe-f319247cedd8

앞에서 비밀정보를 저장하려면 시크릿 백엔드를 만들어야 한다고 했는데 이 시크릿 백엔드를 파일시스템처럼 마운트해서 사용한다. 마운트를 하면 경로가 생성되고 그 경로에 원하는 정보를 저장하면 된다. 기본적으로 마운트된 백엔드가 있으니 vault mounts로 확인해 보자.

$ vault mounts
Path        Type       Default TTL  Max TTL  Description
cubbyhole/  cubbyhole  n/a          n/a      per-token private secret storage
secret/     generic    system       system   generic secret storage
sys/        system     n/a          n/a      system endpoints used for control, policy and debugging

cubbyhole/는 토큰마다 생성되는 스토리지로 연습용이라고 생각하면 된다. secret/gerneric 타입이라고 되어 있는데 이는 generic 시크릿 백엔드라는 의미이고 바로 사용을 할 수 있도록 기본적으로 하나가 있는 것이다. 추가설명을 하자면 generic 시크릿 백엔드는 secret 경로에 마운트한 것이다. sys/는 시스템용으로 ACL 정책이나 관리 정보가 여기에 저장된다.

secret에 시크릿 백엔드가 마운트되어 있으므로 여기에 구분하기 쉬운 경로를 생성해서 vault write로 데이터를 저장하면 된다.

vault write secret/database/postgres host=127.0.0.1 \
  port=5432 db=test username=postgres password=postgres \
  desc="테스트용 데이터베이스"
Success! Data written to: secret/database/postgres

데이터를 읽어올 때는 vault read 데이터가 저장된 경로를 지정하면 된다.

$ vault read secret/database/postgres
Key               Value
---               -----
refresh_interval  768h0m0s
db                test
desc              테스트용 데이터베이스
host              127.0.0.1
password          postgres
port              5432
username          postgres

$ vault read -format=json secret/database/postgres
{
  "request_id": "f0775db9-82d9-2fa2-5052-e999a0b3eed2",
  "lease_id": "",
  "lease_duration": 2764800,
  "renewable": false,
  "data": {
    "db": "test",
    "desc": "테스트용 데이터베이스",
    "host": "127.0.0.1",
    "password": "postgres",
    "port": "5432",
    "username": "postgres"
  },
  "warnings": null
}

결과를 JSON으로 받고 싶다면 -format=json 옵션을 추가하면 된다.

원하는 경로를 찾기가 어려울 때는 vault list를 사용하면 하위에 있는 경로를 볼 수 있다.

$ vault list secret
Keys
----
database/

$ vault list secret/database
Keys
----
postgres

데이터 관리를 쉽게 할 수 있도록 새로운 시크릿 백엔드를 연결하고 싶다면 vault mount를 사용하면 된다. 다음은 account/ 경로에 generic 시크릿 백엔드를 연결한 것이다.

$ vault mount -path=account generic
Successfully mounted 'generic' at 'account'!

$ vault mounts
Path        Type       Default TTL  Max TTL  Description
account/     generic    system       system
cubbyhole/  cubbyhole  n/a          n/a      per-token private secret storage
secret/     generic    system       system   generic secret storage
sys/        system     n/a          n/a      system endpoints used for control, policy and debugging

ACL 정책

여태까지는 루트 토큰을 사용했는데 루트 토큰은 권한이 너무 많으므로 일반적인 사용사례를 생각하면 이 토큰은 관리용으로 보관해 두고 개인이나 애플리케이션별로 토큰 등을 발급해서(혹은 인증 백엔드로 발급할 수 있게 해서) 사용할 것이다. 그러자면 ACL을 설정해서 사용자가 접근 가능한 정보만 볼 수 있도록 해야 한다. Vault에서는 이를 정책(policy)이라고 부른다.

$ vault policies
default
root

기본으로 생성된 정책은 rootdefault가 있다.

새로운 권한을 만들려면 먼저 정책을 hcl 파일(혹은 JSON 파일)로 작성해야 한다. 아래의 내용으로 policy.hcl 파일을 만들어 보자.

path "account/*" {
  capabilities = ["create", "update", "read"]
}

이는 account/ 하위의 모든 경로에서 생성, 수정, 읽기가 가능하다는 정책이다. capabilities에는 다음의 내용

  • create: 경로에 값을 생성한다.
  • read: 경로의 값을 읽는다.
  • update: 경로의 값을 바꾼다.
  • delete: 경로의 값을 제거한다.
  • list: 경로에서 키의 이름을 읽는다. list로 조회할 때 반환되는 키 정보는 정책에 따라 필터링 되지 않으므로 키 이름에 민감한 정보를 넣지 않도록 조심해야 한다.
  • sudo: root로 보호된 경로를 얻는다. sudo는 특수 기능을 부가하는 것이므로 별도로 read, update 등을 지정해 주어야 한다.
  • deny: 해당 경로에 접근을 차단한다. 이는 sudo나 다른 설정과 관계없이 모두 차단한다.

앞에서는 path를 하나만 설정했지만 여러 개 설정해서 복잡한 경로에도 특정 부분만 권한을 주거나 권한을 빼거나 할 수 있다. 이제 앞에서 만든 policy.hcl 파일을 이용해서 정책을 생성해보자.

$ vault policy-write owner policy.hcl
Policy 'owner' written.

$ vault policies
owner
default
root

위 명령어는 owner라는 이름으로 정책을 생성한 것이다. 구분하기 쉽게 정책 이름을 잘 정하면 좋고 정책이 잘 적용된 것을 볼 수 있다.

인증 백엔드

앞에서도 설명했지만 인증 백엔드를 사용하려면 새로운 인증을 추가해야 한다. Vault에서 제공하는 인증 백엔드 중에 자신에게 적합한 타입을 골라서 사용하면 되고 꼭 하나만 사용해야 하는 것은 아니다.

$ vault auth -methods
Path    Type   Default TTL  Max TTL  Description
token/  token  system       system   token based credentials

vault auth -methods 명령어로 사용 중인 인증 백엔드의 목록을 볼 수 있다. 여태까지 토큰 인증 백엔드는 기본적으로 포함되어 있다. 새로운 토큰을 발급받으려면 vault token-create를 사용하면 된다.

$ vault token-create
Key             Value
---             -----
token           681b522a-5689-519f-bde0-3cddf334f35a
token_accessor  91e7588f-a0a0-70aa-6cca-0583fc2f8cbe
token_duration  0s
token_renewable false
token_policies  [root]

token의 토큰을 사용하면 되고 token_accessor의 용도는 정확히 모르겠다. 여기서는 현재 사용 중인 토큰이 루트 토큰이므로 같은 정책인 root 정책이 적용된 것을 볼 수 있다. 앞에서 만든 owner 정책으로 토큰을 생성하려면 -policy 옵션을 사용하면 된다.

vault token-create -policy=owner
Key             Value
---             -----
token           276fbe3f-4026-d054-8eae-c2897ecba1b8
token_accessor  8548431b-95ef-db7b-2c9d-fb8b6c57a566
token_duration  768h0m0s
token_renewable true
token_policies  [default owner]

여태까지는 VAULT_TOKEN 환경변수로 토큰을 지정했지만 vault auth 명령어로 로그인할 수도 있다.

$ vault auth 276fbe3f-4026-d054-8eae-c2897ecba1b8
Successfully authenticated! You are now logged in.
token: 276fbe3f-4026-d054-8eae-c2897ecba1b8
token_duration: 2764532
token_policies: [default owner]

이 토큰은 account/에 권한이 있는 owner 정책이 적용된 토큰이므로 실제로 사용을 해보자.

$ vault write account/twitter username=outsideris password=test
Success! Data written to: account/twitter

$ vault read account/twitter
Key               Value
---               -----
refresh_interval  768h0m0s
password          test
username          outsideris

목록 조회 기능이나 다른 경로를 사용하려고 하면 권한 거부가 일어나는 것을 볼 수 있다.

$ vault list account
Error reading account/: Error making API request.

URL: GET http://127.0.0.1:8200/v1/account/?list=true
Code: 403. Errors:

* permission denied

$ vault read secret/database/postgres
Error reading secret/database/postgres: Error making API request.

URL: GET http://127.0.0.1:8200/v1/secret/database/postgres
Code: 403. Errors:

* permission denied

추가로 새로운 인증 백엔드를 추가해보자. 여기서는 간단한 사용자명/비밀번호 인증 백엔드를 추가해 보겠다. owner에는 백엔드를 추가할 기능이 없으므로 앞에서 사용하던 루트 토큰을 다시 사용해보자.

$ vault auth-enable userpass
Successfully enabled 'userpass' at 'userpass'!

userpass 인증 백엔드가 활성화되었으므로 이를 통해 새로운 사용자를 추가할 수 있다.

$ vault write auth/userpass/users/outsider password=asdf policies=owner
Success! Data written to: auth/userpass/users/outsider

이제 생성한 계정으로 로그인을 해보자. vault auth만 사용하면 토큰 인증이므로 -method=userpass를 지정해야 한다.

$ vault auth -method=userpass username=outsider password=asdf
Successfully authenticated! You are now logged in.
The token below is already saved in the session. You do not
need to "vault auth" again with the token.
token: ae75f594-70a0-ae40-af7d-2449e0d1f46f
token_duration: 2764800
token_policies: [default owner]

성공적으로 인증이 되고 토큰이 발급된 것을 볼 수 있다.

에필로그

최근에 새로운 도구를 여러 가지 만져보고 있지만, Vault는 꽤 맘에 드는 것 중 하나이다. 실제로 사용하려면 경로 정책도 정해야 하고 개발단계나 CI, 배포 등에서 사용하기 위해 Vault와 통합하는 작업을 해야 하지만 보안에 집중한 도구라서 이 부분에서 신뢰가 가고 사용성도 상당히 고려했다는 느낌이 든다. 여기서는 모든 명령어를 vault 클라이언트를 사용했지만, 이 모든 기능은 RESTful API가 동시에 제공되기 때문에 용량이 꽤 되는 Vault를 설치하기 부담되는 곳에서도 curl 등으로 정보를 얼마든지 조회할 수 있다. 설명이 꽤 길었지만, 상당히 잘 만들어진 도구라고 생각한다.

보안이 중요하다는 부분에는 누구나 동의하겠지만, 보안을 강화하려면 노력과 비용이 많이 들고 대부분은 이 노력과 비용을 실무자한테 전가하기 때문에 불편한 보안 장벽 내에서 생산성을 내기 위해서 오히려 보안이 약해지는 문제가 생긴다고 본다. Vault 같은 도구로 개발환경이나 CI, 배포에 잘 통합을 한다면 기존과 거의 차이 없는 환경에서 비밀번호를 주기적으로 바꾼다거나 실수로라도 외부에 유출되지 않도록 잘 관리한다거나 하는 것이 가능할 것이라고 본다.

2017/01/18 08:30 2017/01/18 08:30

HashiCorp의 비밀정보 관리 도구 Vault의 구성

얼마 전에 HashiCorpTerraform에 대해서 올렸는데 이번에는 다른 제품인 Vault에 대해 설명하게 되었다. HashiCorp를 좋아하긴 하지만 어쩌다 보니 HashiCorp의 도구를 연달아 사용해 보게 되었다.

Vault

Vault는 비밀 정보 즉, 공개되면 안 되는 비밀번호, API 키, 토큰 등을 저장하고 관리하는 도구이다.

Vault 홈페이지

개발하면 꽤 많은 비밀 정보를 사용하게 된다. 여기서 비밀 정보라는 것은 애플리케이션 등에서는 사용해야 하는데 외부에 노출이 되면 안 되는 정보를 얘기하는 것이다. 대표적으로 데이터베이스 계정명과 비밀번호가 있고(URL은 보통 접근 안 되게 차단하지만 이마저도 외부에 공개되어서 좋을 건 없다.), 외부 API를 사용한다면 엑세스 토큰이 있을 수도 있고 서버에 접근하기 위한 SSH 비밀키도 있다. 최소한 내가 경험해 본 프로젝트에서는 이중 비밀번호나 엑세스 토큰은 대부분 소스코드에 하드 코딩해서 사용하고 SSH키는 그냥 개발자의 PC에 (잘 관리할 것이라고 믿고) 저장되어 있고 개발이나 업무에 필요한 계정정보 등은 구글 스프레드시트 등에 관리되는 것이 대부분이었다.

이런 정보를 관리해주는 도구라고 생각하면 된다. 계정 정보 같은 건 구글 스프레드시트가 아니더라도 1Password 같은 솔루션도 있고 여기서는 개발에 대한 부분만 얘기해보자. 보통 이렇게 관리하는 것에 큰 문제가 없기는 하다. 코드에 하드 코딩 되어 있으면 사용하기도 쉽고 변경할 때는 업데이트하면 그만이기는 하지만 소스코드는 생각보다 관리하기가 쉽지 않다. GitHub 등의 공개 저장소의 실수로 올리는 경우를 제외하더라도 소스코드는 대부분 관련 개발자의 PC에 저장되어 있는데 악의적인 누출을 제외하더라도 개개인의 PC까지 다 제어하기는 쉽지 않기 때문에 유출될 가능성도 있고 코드에 하드 코딩 되어 있으므로 이러한 비밀정보를 바꾸기도 쉽지 않다.

프로젝트가 간단하다면 괜찮지만, 프로젝트 수가 많고 사람 수도 많아진다면 문제는 더욱 커진다. Vault는 이러한 정보를 안전하게 보관하고 관리해 주는 도구라고 생각하면 된다. 좀 더 쉽게 얘기하면 Vault에 이 정보를 저장해 놓고 애플리케이션이나 서버에서는 가져다가 사용하겠다는 의미이다. 최근 작업에서 이러한 솔루션이 필요하다는 생각을 하게 되어서 본격적으로 살펴보게 되었다.

Vault에서 사용하는 용어

Vault를 자세히 살펴보기 전에 이해를 돕기 위해서 용어 설명을 해야 할 것 같다. Vault를 사용할 때 기능을 확장할 수 있는데 이를 백엔드(Backend)라고 통칭해서 부른다.

스토리지 백엔드(Storage Backend)

Vault의 정보를 저장하면 Vault가 이를 어딘가에는 저장해야 하는데 이를 스토리지 백엔드라고 부른다. 스토리지 백엔드를 PostgreSQL로 설정하면 정보를 PostgreSQL에 저장하고 S3로 설정하면 파일에 저장한다. Consul, DynamoDB, S3, Google Cloud Storage, Azure, MySQL, PostgreSQL 등 다양한 백엔드를 지원하고 있다.

시크릿 백엔드(Secret Backend)

시크릿 백엔드는 문서를 보면 꽤 많은 시크릿 백엔드가 있다. 대표적으로 Generic과 Cubbyhole이 있는데 Gerneric 시크릿 백엔드를 사용할 때 정보를 저장하면 위의 스토리지 백엔드로 설정한 물리 스토리지에 정보를 저장하게 된다. Cubbyhole는 Gerneric과 똑같이 동작하지만, 연습용 플레이그라운드라고 생각하면 된다. 일반적으로 비밀정보를 저장할 때 생각하게 되는 username/password 등을 저장한다고 하면 이 Generic을 사용하면 된다.

문서를 보면 이 외에 AWS, MySQL, PostgreSQL 등 많은 시크릿 백엔드가 있는 걸 볼 수 있다. 다 테스트해 본 것은 아니지만 내가 본대로는 Generic과 Cubbyhole을 제외한 나머지 시크릿 백엔드는 해당 서비스에 연결해서 동적으로 동작하는 백엔드이다. 이해가 안될 텐데 예를 들어 보자.

AWS 시크릿 백엔드를 사용한다고 하면 AWS에서 API 키와 시크릿을 만들어서 Vault에 이 정보로 AWS 시크릿 백엔드를 설정한다. 그러면 AWS 키가 필요할 때 이 백엔드에 요청을 하면 AWS에 접속해서 새로운 Role을 만들고 그 엑세스 키와 시크릿 정보를 관리해 주게 된다. 즉 요청할 때마다 새로운 IAM Role이 생성되어 반환해 준다.

MySQL이나 PostgreSQL같은 데이터베이스도 마찬가지인데 데이터베이스의 계정 정보로 해당 시크릿 백엔드를 구성하면 요청할 때 데이터베이스에서 새로운 계정을 만들고 expire도 설정하고 이 계정 정보를 돌려준다.

즉, 비밀정보를 관리할 수 있는 권한을 Vault에 주고 Vault가 필요에 따라 새로 만들어 줌으로써 계정이나 비밀정보를 관리할 수 있게 하겠다는 것이다. 처음에 이 개념을 보았을 때는 "어째서?"라는 느낌이었지만 현실에서 데이터베이스의 계정이나 비밀번호를 운영 중에 바꾸는 것을 한 번도 본적이 없다는 점을 생각해 보면 이런 Vault의 관리하에서는 가능하겠다는 생각이 들었다.

인증 백엔드(Auth Backend)

Vault에 중요한 정보를 관리하기 시작했으면 이 Vault에 접근할 수 있는 권한을 제어해야 하는데 이게 인증 백엔드이다. 인증 백엔드는 username/password도 있고 Token 백엔드도 있어서 필요에 따라 사람이나 애플리케이션 등에서 사용하도록 만들 수 있다. 그 외에 LDAP이나 GitHub 등을 연결할 수 있다. 회사에서 LDAP를 쓰고 있다면 LDAP auth backend를 연결해서 사용하게 될 테고 이때 퇴사를 해서 LDAP 계정을 삭제한다면 더는 Vault에 접근할 수 없으므로 관리 포인트를 줄일 수 있다. GitHub 같은 경우는 GitHub의 특정 org에 연결해서 해당 org의 사용자만 Vault에 접근할 수 있게 하는 식으로 연결할 수 있다.

Vault 설치

다운로드 사이트에서 OS에 맞는 파일을 다운로드받고 압축을 풀어서 경로에 넣어주면 설치가 끝이다. 정상적으로 설정되었다면 다음과 같이 버전을 확인할 수 있다. 현재 최신 버전은 v0.6.4다.(HashiCorp는 버전 업그레이드에 아주 짜다.)

$ vault -v
Vault v0.6.4 ('f4adc7fa960ed8e828f94bc6785bcdbae8d1b263')


Vault 서버 설정

위에서 설치한 vault는 서버이면서 클라이언트이다. 클라이언트를 보기 전에 서버가 필요하므로 먼저 서버 설정을 해보자. Vault 서버의 고가용성을 보장하려면 HashiCorp가 만든 Consul을 사용해야 하지만 설정이 복잡해지므로 여기서는 PostgreSQL을 사용해보자. 로컬 5432 포트로 PostgreSQL v9.6.1이 떠 있다고 할 때 vault_test라는 데이터베이스를 만들고(CREATE DATABASE vault_test;) 다음 명령어로 테이블과 인덱스를 미리 생성해야 한다.

CREATE TABLE vault_kv_store (
  parent_path TEXT COLLATE "C" NOT NULL,
  path        TEXT COLLATE "C",
  key         TEXT COLLATE "C",
  value       BYTEA,
  CONSTRAINT pkey PRIMARY KEY (path, key)
);

CREATE INDEX parent_path_idx ON vault_kv_store (parent_path);

데이터베이스 준비가 완료되었으면 다음의 내용으로 config.hcl 파일을 만든다.(파일명을 중요치 않다.)

backend "postgresql" {
  connection_url = "postgres://postgres:postgres@127.0.0.1:5432/vault_test?sslmode=disable"
}

listener "tcp" {
  address = "0.0.0.0:8200"
  tls_disable = 1
}

설정을 설명하자면 스토리지 백엔드로 PostgreSQL을 설정해서 로컬에 띄워놓은 디비를 지정했다. 마지막에 sslmode=disable이 붙인 이유는 PostgreSQL의 SSL이 기본으로 활성화되어 있지 않으므로 Vault가 보안 문제로 오류를 던지므로 수동으로 끈 것이다. 이는 물론 테스트라서 그렇고 프로덕션이라면 당연히 SSL을 고려해야 할 것이다.

listener "tcp"는 이 서버에 접속할 포트를 여는 것이다. 이 설정에 따라 8200 포트로 서버에 접속할 수 있고 여기서는 테스트이므로 tls를 비활성화했다.(프로덕션에서도 보통은 Vault 앞의 로드밸런서나 웹서버에서 HTTPS 인증을 구성해서 사용할 것이므로 프로덕션에서도 상황에 따라서는 끌 수 있다.)

이제 Vault 서버를 띄워보자. 서버를 띄울 때는 vault server 명령어를 사용하고 -config 옵션으로 위에서 작성한 config.hcl 파일을 지정하면 된다.

$ vault server -config=config.hcl
==> WARNING: mlock not supported on this system!

  An `mlockall(2)`-like syscall to prevent memory from being
  swapped to disk is not supported on this system. Running
  Vault on an mlockall(2) enabled system is much more secure.

==> Vault server configuration:

                 Backend: postgresql
                     Cgo: disabled
              Listener 1: tcp (addr: "0.0.0.0:8200", cluster address: "", tls: "disabled")
               Log Level: info
                   Mlock: supported: false, enabled: false
                 Version: Vault v0.6.4
             Version Sha: f4adc7fa960ed8e828f94bc6785bcdbae8d1b263

==> Vault server started! Log data will stream in below:

WARNING: mlock not supported on this system! 경고는 테스트를 macOS에서 해서 나온 것이다. 메시지로 추정하건대 서버의 메모리를 탈취해서 비밀 정보를 뺏어가는 공격을 막기 위한 것으로 보인다. 그래서 프로덕션에서는 mlockall(2)를 활성화하기 위해 root 권한으로 실행해야 한다. 그 아래 구성할 설정 정보가 나오고 이제 이 서버를 사용하면 하단에 로그가 출력된다.

Vault 서버 초기화

서버를 처음 띄웠으므로 Vault를 초기화해야 사용할 수 있다. 서버 구성은 이게 끝이고 나머지 작업은 모두 클라이언트에서 하게 된다. 클라이언트가 서버를 찾을 수 있도록 VAULT_ADDR 환경변수에 서버 경로를 지정한다.

$ export VAULT_ADDR='http://52.198.157.64:8200'

Vault의 서버 상태를 확인하는 명령어인 vault status를 실행하면 서버가 초기화되지 않았다고 오류가 나온다.

$ vault status
Error checking seal status: Error making API request.

URL: GET http://127.0.0.1:8200/v1/sys/seal-status
Code: 400. Errors:

* server is not yet initialized

초기화는 vault init 명령어를 사용한다.

$ vault init
Unseal Key 1: 986qtg5W7YOewHvLxj/0M42TkfjAY/+uIu2MJfoRFMkB
Unseal Key 2: 2v7+to7s3s6E/ZOMBN8PNkm/a8QdUyrSF++FiNamkhoC
Unseal Key 3: Noc5jbbGOgTZsWcoGX+ClgDETjUytTjCYG+0l2D+65oD
Unseal Key 4: iv9I1FB6rHyU5yZEx3NSF7n0c1iWNtlNVyC+Q/rrvNQE
Unseal Key 5: ZoaP72hQSLbJq9Lg2tPft/CPVqm50MtdIKCPXEyzxVQF
Initial Root Token: c0ccbf61-cd7b-bd98-dabe-f319247cedd8

Vault initialized with 5 keys and a key threshold of 3. Please
securely distribute the above keys. When the Vault is re-sealed,
restarted, or stopped, you must provide at least 3 of these keys
to unseal it again.

Vault does not store the master key. Without at least 3 keys,
your Vault will remain permanently sealed.

긴 로그가 나왔는데 정리하면 Unseal Key를 5개 주고 Root Token을 하나 준다.

Seal/Unseal

초기화는 서버를 껐다 키더라도 서버에서 단 한 번만 실행하면 된다. 초기화를 하더라도 Vault 서버는 Sealed상태가 된다. 즉, 잠겨있어서 Vault 서버에서 정보를 읽는 등의 작업을 할 수 없으므로 서버를 Unsealed상태로 만들어야 한다. 이는 PC의 잠금 상태와 잠금 해제상태를 생각하면 된다. 이때 사용하는 것이 초기화할 때 알려준 Unseal Key이다. 위의 메시지를 보면 Unseal Key가 5개이고 스레스홀드가 3인 걸 볼 수 있는데 이 5개의 키 중 3개를 제공해야 Unseal을 할 수 있다.

$ vault status
Sealed: true
Key Shares: 5
Key Threshold: 3
Unseal Progress: 0
Version: 0.6.4

High-Availability Enabled: false

상태를 확인해 보면 Sealedtrue인 걸 볼 수 있다. 이제 unseal을 해보자. unseal은 vault unseal 명령어를 사용한다.

$ vault unseal
Key (will be hidden):
Sealed: true
Key Shares: 5
Key Threshold: 3
Unseal Progress: 1

$ vault unseal
Key (will be hidden):
Sealed: true
Key Shares: 5
Key Threshold: 3
Unseal Progress: 2

$ vault unseal
Key (will be hidden):
Sealed: false
Key Shares: 5
Key Threshold: 3
Unseal Progress: 0

$ vault status
Sealed: false
Key Shares: 5
Key Threshold: 3
Unseal Progress: 0
Version: 0.6.4
Cluster Name: vault-cluster-fdb80367
Cluster ID: edf425f4-9350-d914-80d5-b3b3a91e0971

High-Availability Enabled: false

unseal을 시도하면 키를 입력하라고 나오고 초기화 때 받은 키를 넣으면 된다. 위에서 보면 총 3번 실행했고 실행할 때마다 Unseal Progress의 숫자가 줄어드는 것을 볼 수 있다. unseal이 완료되고 상태를 확인해 보면 Sealedfalse가 된 것을 볼 수 있다. unseal을 할 때는 키의 순서 등은 전혀 상관이 없고 5개 중 3개만 제공하면 된다.

총 Unseal key의 개수와 스레스홀드를 바꾸고 싶다면 초기화할 때 vault init -key-shares=3 -key-threshold=2처럼 숫자를 지정하면 된다. 이는 키가 총 3개이고 2개를 입력하면 unseal을 할 수 있다는 의미이다.

여기서는 연속적으로 unseal을 3번 실행했지만, 실제 시나리오를 생각하면 저 키를 5명이 나누어 갖고 3명이 unseal을 하면 Vault 서버가 열린다는 의미가 된다. Vault에 중요한 정보를 다 담아놓았으므로 서버를 재시작하거나(이때는 항상 sealed가 된다.) 보안 문제가 있어서 수동으로 막아야 해서 sealed 상태로 바꾸었을 때 다시 서버를 열려면 unseal 단계를 거쳐야 한다. 이는 꽤 괜찮은 방법이라고 생각한다. 초기화를 할 때 5명이 키를 나누어 가지고 있다가 중요한 때에 이 중 3명이 unseal로 바꿀 수 있게 되는 것이다. 위에서는 3번 연속실행했지만, unseal 과정은 서버가 기억하고 있으므로 한자리에 모일 필요 없이 각자의 자리에서 unseal을 하기만 하면 된다. 즉, 스토리지 백엔드에도 암호화되어서 저장되어 있고 이를 복호화하려면 이 unseal 단계가 필수적이 된다. 설명에 따르면 저 키 3개가 없으면 Vault 서버도 복호화키를 만들어 낼 수 없다고 한다. 그러므로 DB가 통째로 털려도 Vault 서버가 통째로 털려도 이 키 3개가 없다면 공격자는 정보를 볼 수 없게 된다.

하지만 더 현실적으로 생각해 보자. 이런 작업을 한두 번 해보는 게 아니지 않은가! 5명이 이 키를 나누어 가진 뒤 어떻게 보관하게 할 것이냐 하는 문제를 제쳐놓고 지금까지의 경험으로 보건대 초기화를 하고 이 5개의 키를 한꺼번에 관리할 가능성이 훨씬 크다. 탈취당하는 경우보다 운영하면서 unseal 하는 경우가 많을 텐데 어떻게 이 5명 중 3번을 매번 찾겠는가.

그래서 Vault는 초기화를 PGP로 암호화하는 방법을 제공하고 있고 사용상의 편의를 위해서 keybase.io를 직접 이용할 수 있도록 해놓았다. 다음과 같이 -pgp-keys로 keybase의 계정명을 제공하면 이 공개키로 초기화를 하게 되고 unseal을 할 때는 이 사람들의 비밀키를 제공하면 unseal을 할 수 있다.

$ vault init \
  -pgp-keys="keybase:outsideris,keybase:james,keybase:jefferai,keybase:vishalnayak,keybase:sethvargo"

이 방법을 이용하면 자연스럽게 Unseal key를 나눠 가질 수 있다. 회사의 중요한 5명이 이 방법을 사용하면 되긴 하지만 퇴사한 경우에 이 키를 어떻게 교체할 수 있는지까지는 아직 찾아보지 못했다.

2017/01/18 02:50 2017/01/18 02:50