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