개발관련/삽질

hashicorp사의 Vault(볼트) - 개요 - 1

동팡 2021. 6. 29. 00:22

목차

  • 들어가며
  • 개요 
  • 용어 정의
  • 아키텍처 개요

 

들어가며

본 게시물은 Vault 공식 문서의 내용과 필자의 생각을 정리하였다. Vault는 무엇인지, 어디에 사용하는지, 왜 사용하는지, 어떻게 사용하는지 서술한다. Vault를 처음 접하는 인원은 Vault의 흐름과 골격을 이해할 수 있는 시간을 갖는다. 또한 Vault를 실질적으로 사용해보는 시간을 갖는다. Vault는 Dev 서버 모드를 지원한다. Dev 서버 모드는 사전 설정이 되어있는 데모 서버라고 생각하면 좋다. 사용자는 해당 데모 서버에서 Vault를 학습할 수 있다. 시간이 괜찮으면 Vault HA를 구성한다(시간이 있으면...). 

 

본 게시물의 시리즈는 다음과 같다.

 

개요

HashiCorp의 Vault는 민감한 데이터를 안전하게 저장하는 저장소이다. 민감한 데이터의 종류는 다음과 같다. 1)Secret 2)Credential 3)Password 4)Enctyption-Key 등이다. 이와 같이 기밀성이 요구되는 정보는 DBMS에 단순 저장하면 안 된다. Vault는 위의 개체들을 암호화하여 안전하게 저장한다. 

 

용어 정의

Storage Backend

  • Vault의 암호화된 Secret을 보관하는 곳이다. 

Barrier

  • Barrier는 Vault의 일부 구성요소를 감싸고 있다. 때문에 Vault와 Storage Backend 간의 통신은 Barrier를 거쳐야 하며, Barrier가 프록시 역할(대문 역할)을 한다. Barrier의 구성요소들은 서로 Trust 한 관계를 유지/성립한다.
  • Vault는 암호화된 데이터만 밖으로 나오게 한다.
  • Vault는 Barrier의 상태가 “unsealed”가 되어야 접근할 수 있다.

Secret Engine

  • Secret의 관리를 책임진다.
  • Secret 관련 작업은 Secret Engine으로 전달하고 Engine의 구현체마다 상이한 방식으로 저장한다. 
  • Secret Engine의 인터페이스를 활용하여 DB, File System 또는 유저가 정의한 방식으로 저장한다.

Audit Device

  • 모든 Vault의 Request/Respones는 Audit Device에 의해 감사 로깅이 된다. 

Auth Method 

  • Vault에 접근하는 클라이언트를 인증한다.
  • 인증된 클라이언트의 토큰을 반환한다.

Client Token

  • HTTP에서의 세션 ID와 같은 토큰을 반환한다.
  • Vault의 REST API를 사용하는 경우 HTTP 헤더에 토큰을 적재한다.

Secret

  • Vault에서 관리하는 비밀 객체이다.
  • Secret은 일정 주기를 가지며, 해당 주기가 만료되면 폐기해야 한다.

 

 

아키텍처 개요

High level overview of vault

[전체 흐름]

 

Vault는 기밀성이 요구되는 데이터(이하 Secret)를 안전하게 보관하는 저장소이다. Vault를 사용하기 위해서는 “unsealing” 초기화 작업이 필요하다. Vault 서버 초기화 시 Vault“sealed” 상태이다. “Unsealed-Key”는 “sealed” 상태를 “unsealed” 상태로 전이할 수 있다. “unsealed” 상태가 되면 Barrier안에 있는 Vault의 모든 기능 및 구성요소에 원활하게 접근할 수 있다.

 

Vault에서 실질적으로 Secret을 암/복호화하는 키는 "Encryption-Key"이다.

1) "Master-Key"는 “Encryption-Key”를 암호화한다.

2) "Unsealed-Key"는 “Master-Key”를 암호화한다.

3) “Unsealed-key”SSS(Shamir Secret Sharing) 알고리즘으로분할 보관한다.

 

키들의 관계는 아래 도식화 자료와 같다.

 

Vault Crypto-Key mechanism

“Encryption-Key”는 “Secret”을 안전하게 암/복호화한다. “Secret”들은 “Storage backend”에 보관하며, “Secret”에 접근하기 위해서는 REST API를 사용한다. REST API는 Create, Register, Rotate, Destroy 등의 명령을 제공하며, "Secret"을 제어할 수 있다. REST API를 사용하기 위해서는 "토큰 인증"과 "토큰에 상응하는 정책 인가" 작업이 필요하다. 

 

[고가용성]

Vault는 HA를 구성할 수 있다. HA의 구조는 Active-Standby 구조이다. 오픈 소스 버전의 경우 Standby는 Read-Only 기능을 제공하지 않는다. Standby에 요청이 들어오면 요청을 Active에 포워딩한다. Read-Only 기능은 엔터프라이즈에서 제공한다. 그래도 오픈 소스 버전에서 Hot-Standby를 제공하기 때문에 Failover는 문제없다. 제일 큰 단점은 Standby의 read-only의 미지원이다. Product 레벨에서 Vault를 사용하기 위해서는 해당 기능이 제일 필요할 것 같다.

 

Replication 방식은 PostgreSQL의 방식과 같이 Active-Standby 구성이며, Active의 변경/추가/삭제된 것에 대한 로그(WAL, Write Ahead Log) Standby에 전송한다. 데이터 처리에 있어 보통의 DBMS과 같이 WAL 기법을 사용하는 것 같다.

 

[보안 특성]

  • Vault와 클라이언트 상호 인증은 제공하지 않는다. 다만 서버에서 제공하는 네트워크 계층 보안, TLS를 제공한다.
  • 정상 토큰을 보유한 주체는 리소스("Secret")에 접근할 수 있다.
  • 토큰 인가 정책은 리소스에 접근하는 주체를 제어한다.
  • Secret에 접근하는 모든 행위를 감사 로깅한다.
  • "Secret"을 암호화하여 Storage Backend에 저장한다.
  • Vault의 Barrier를 통과하는 모든 요청과 반환은 AES-256(GCM)으로 암호화한다. IV의 경우, 자동으로 임의로 생성한다.
  • SSS(Shamir Secret Sharing) 알고리즘으로 MasterKey를 분리 보관한다.

 

[Key 회전(갱신)]

rekey 명령은 Unsealed-KeyMaster-Key를 갱신한다. 갱신된 Master-KeyEncryption-Key를 재암호화하며,갱신된 Unsealed-Key SSS에 의해 다시 분리하여 보관하여야 한다. 

rotate 명령은 Encryption-Key를 갱신한다. 기존 Encryption-Key는 별도의 keyring에 보관한다. 후의 요청들은 새로운 Encryption-Key로 암호화를 한다. Keyring에 있는 Encryption-Key는 복호화 용도에만 사용한다. 이와 같이 사용하면 re-encryption을 수행하지 않아도 괜찮다(keyring에 예전 Encryption-Key를 다 보관하고 있다. 갱신에 의미가 있나?).

 

[정책]

사용자는 Identifier/Authentication값을 요청한다. 요청 성공 시 토큰을 반환받는다. 해당 토큰은 다음과 같이 화이트 리스트 정책을 갖고 있다

# This section grants all access on "secret/*". Further restrictions can be
# applied to this broad policy, as shown below.
path "secret/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}
 
# Permit reading secret/foo/bar/teamb, secret/bar/foo/teamb, etc.
path "secret/+/+/teamb" {
  capabilities = ["read"]
}
 
# Policies can also specify allowed, disallowed, and required parameters. Here
# the key "secret/restricted" can only contain "foo" (any value) and "bar" (one
# of "zip" or "zap").
path "secret/restricted" {
  capabilities = ["create"]
  allowed_parameters = {
    "foo" = []
    "bar" = ["zip", "zap"]
  }
}