많은 분들이 다양한 이유들로 AWS 환경에서 SSM Parameter Store와 Secret Manager를 사용해서 다양한 환경변수를 관리하고 있습니다. 하지만 AWS 외에 GCP, Azure, NCP, OnPremise까지 다양한 환경에서는 어떻게 환경 변수를 관리할 수 있을까요?
그리고 각종 환경 변수들의 다양한 암호화를 어떻게 보장할 수 있을까요?
HashiCorp 사의 Key Management Solution인 Vault라는 도구를 자세하게 공부하며, 동시에 Vault Certificate를 취득하기 위해서 작성된 시리즈 문서입니디.
Vault 설치하기
Vault는 특정한 플랫폼에 구애받지 않고 다양한 플랫폼에서 실행이 가능합니다.
또한 Vault는 다양한 운영체제에서 실행이 가능합니다.
Vault is platform agnotic… meaning it can be run on many different underlying platforms
Kubernetes
Cloud-based Machines (AWS EC2 Instances, Azure Virtual Machiens)
VMWare Virtual Machines
Physical Servers
A Laptop
Vault is also available for many operating systems…
MacOS
Windows
Linux
FreeBSD
NetBSD
OpenBSD
Solaris
이러한 Vault를 사용하기 위해서는 기본적인 4단계가 존재합니다.
Vault 설치하기
Configuration File 생성하기
Vault 초기화하기
Vault Unseal하기
Vault 설치하기
다음의 2 사이트 중 한 곳에서 Vault를 다운로드 받을 수 있습니다.
vaultproject (docs) — Installing Vault (MacOS)를 참고하여 설치를 진행하였습니다.
brew tap hashicorp/tap
brew install hashicorp/tap/vault
혹은 다음과 같이 packer를 이용해서 vault를 설치하고 셋팅할 수 있습니다.
Vault Dev Server
별도의 복잡한 환경설정 없이 Vault를 실행할 수 있는 모드를 Dev Server라고 합니다.
Dev Server는 간단하게 킬 수 있는 만큼 다음과 같은 Pros/Cons가 존재합니다.
Pros | Cons |
---|---|
Quickly run vault without configuration | Non-Persistent — Runs in memory |
Automatically initialized and unsealed | Insecure — doesn’t use TLS |
Enables the UI — available at localhost | Sets the listener to 127.0.0.1:8200 |
Provides as Unseal Key | Mounts a K/V v2 Secret Engine |
Automatically logs in as root | Provide a root token |
이런 Dev Server는 주로 어떤 상황에서 사용할 수 있을까요?
PoC
신규 개발 통합
신규 기능 테스트
기능 실험
Proof of Concepts
New Development Integrations
Testing New Features of Vault
Experimenting with Features
아래와 같은 명령어로 Vault Dev Server를 실행할 수 있습니다.
vault server -dev
이후 별도의 터미널을 열어서 아래 명령어를 입력해서 상태를 확인할 수 있습니다.
vault status
하지만 아래와 같은 프로토콜 에러가 발생합니다.
WARNING! VAULT_ADDR and -address unset. Defaulting to https://127.0.0.1:8200.
Error checking seal status: Get "https://127.0.0.1:8200/v1/sys/seal-status": http: server gave HTTP response to HTTPS client
이는 Vault Dev Server가 http에서 실행되기 때문에 발생합니다.
따라서 아래 명령어를 통해서 VAULT_ADDR을 변경하여, 에러를 해결할 수 있습니다.
export VAULT_ADDR='http://127.0.0.1:8200'
vault status
아래와 같은 출력값을 볼 수 있습니다.
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 1
Threshold 1
Version 1.17.1
Build Date 2024-06-25T16:33:25Z
Storage Type inmem
Cluster Name vault-cluster-c217abcd
Cluster ID abcdabcd-2a0a-6274-f601-d25c1633508a
HA Enabled false
그리고 아래와 같은 명령어들을 추가로 입력할 수 있습니다.
vault secrets list
vault kv put secret/vaultcourses/bryan bryan=bryan
특히 vault kv put 부분을 반복적으로 입력하면 version이 점진적으로 증가되는 것을 알 수 있습니다.
USERNAME@USERNAME-DEVICE ~ % vault kv put secret/vaultcourses/bryan bryan=bryan
========= Secret Path =========
secret/data/vaultcourses/bryan
======= Metadata =======
Key Value
--- -----
created_time 2024-06-30T15:43:15.167994Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 4
USERNAME@USERNAME-DEVICE ~ % vault kv put secret/vaultcourses/bryan bryan=bryan
========= Secret Path =========
secret/data/vaultcourses/bryan
======= Metadata =======
Key Value
--- -----
created_time 2024-06-30T15:43:15.849787Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 5
Vault 지식 점검 #1
Vault Configuration File은 HCL / Json으로 쓸 수 있습니다.
HCL은 HashiCorp Configuration Language의 약자입니다.
/etc/vault.d/vault.hcl
의 Configuration File에listener
stanza를 추가하는 코드 예제입니다.listener "tcp" { address = "0.0.0.0:8200" cluster_address = "0.0.0.0:8201" tls_disable = true }
Vault에서
seal/unseal
작업을 처리하는 방법을 생각해봅시다.
기본으로 존재하는 Root Key를 여러 개의 Sharing Key로 분할하도록 구성하려면, 그 어떤 설정도 하지 않아도 됩니다.no configuration
다음과 같이 UI 활성화를 비롯한 일부 설정을 포함한
/etc/vault.d/vault.hcl
구성을 가져갈 수도 있습니다.# UI를 활성화합니다. ui = true # 로그 레벨을 ERROR로 설정합니다. log_level = "ERROR" # 클러스터 이름을 설정합니다. cluster_name = "my-vault-cluster" # 주요 API 주소를 설정합니다. api_addr = "https://vault.gswhv.com:8200" # 클러스터 주소를 설정합니다. cluster_addr = "https://vault.gswhv.com:8201"
이 모든 구성 변경을 다음과 같은 명령어로 테스트할 수 있습니다.
vault operator diagnose -config=/etc/vault.d/vault.hcl
Vault Production Server
Vault Production Server를 운영하기 위해서 다음의 조건들을 충족하게 서버를 생성합니다.
구성 파일을 통해 하나 이상의 퍼시스턴트 노드를 배포합니다.
요구 사항을 충족하는 스토리지 백엔드 사용
여러 Vault 노드가 클러스터로 구성됩니다.
애플리케이션에 가까운 곳에 배포
대부분의 경우 Vault 프로비저닝 자동화
Deploy one or more persistent nodes via configuration file
Use a storage backend that meets the requirements
Multiple Vault nodes will be configured as a cluster
Deploy close to your applicatiosn
Most likely, you’ll automate the provisioning of Vault
Vault를 생성하기 위해서는 아래 구문을 입력할 수 있습니다.
vault server -config=<file>
실제로 Production 환경에서는 Vault를 실행하는 서비스 관리자를 따로 가지고 있습니다. (e.g. systemctl, window service manager)
또한 Linux에서는 Vault(or Consul)을 실행하는 경우 systemd 파일도 필요합니다.
In a production environment, you’ll have a service manager executing
and managing the Vault service (systemctl, Windows Service Manager, etc.)For Linux, you also need a systemd file to manage the service for Vault (and Consul if you’re running Consul)
Systemd for a vault service
https://github.com/btkrausen/hashicorp/tree/master/vault/config_filesSystemd file for a consul service
https://github.com/btkrausen/hashicorp/blob/master/consul/consul.serviceSystemd for a consul client (that would run on the vault node)
https://github.com/btkrausen/hashicorp/blob/master/vault/config_files/consul-client.json
Single Node Vault Cluster
Vault를 Single Node로 운영하는 구성은 추천하지 않습니다.
Not a recommended architecture
No redundancy
No sacalability
Multi Node Vault Cluster
연결된 네트워크를 통해서 스토리지 복제 환경에서 Multi Node로 Vault로 구성하는 것을 추천합니다.
Vault Node A, B, C
Integrated storage replication (with network)
Manual Instll to run vault server
아래 과정을 통해서 vault serve를 실행할 수 있습니다.
Download vault from HashiCorp
Unpackage vault to a Directory
Set path to executable
Add configuration file & customize
Create systemd service file
Download consul from HashiCorp
Configure and join consul cluster
Launch vault service
Vault 지식 점검 #2
/etc/vault.d/vault.hcl
에 storage 작성하기storage "raft" { path = "/opt/vault/data" node_id = "vault-node-a" }
/etc/vault.d/vault.hcl
에 listener 작성하기listener "tcp" { address = "0.0.0.0:8200" cluster_address = "0.0.0.0:8201" tls_disable = 1 }
/etc/vault.d/vault.hcl
에 seal 작성하기ui = true log_level = "ERROR" cluster_name = "my-vault-cluster" api_addr = "https://vault.gswhv.com:8200" cluster_addr = "https://vault.gswhv.com:8201"
Vault / Consul Storage Backend 설정하기
Vault + Consul Storage Backend 는 다음과 같은 특징이 있습니다.
Provides Durable K/V Storage For Vault | Supports High Availability |
---|---|
Can Independently Scale Backend | Distributed System |
Easy to Automate | Built-in Snapshots for Data Retention |
Built-in Integration Between Consul/Vault | HashiCorp Supported |
세부적으로 다음과 같은 아키텍쳐 측면의 특징을 가지게 됩니다.
Consul은 여러 노드를 사용하여 클러스터 형식으로 배포됩니다.
Consul Cluster는 홀수로 배포됩니다.
모든 데이터는 클러스터의 모든 노드 간에 복제됩니다.
리더 선거는 단일 Consul Node가 리더로 승격됩니다.
리더는 새 로그 항목을 수락하고 다른 모든 노드에 복제합니다.
Vault Storage Backend 용 Consul Cluster는 Production 환경의 Consul 기능에 사용해서는 안됩니다.
Consul is deployed using multiple nodes and configured as a cluster
Clusters are deployed in odd numbers (for voting members)
All data is replicated among all nodes in the cluster
A leader election promotes a single Consul node as the ledaer
The leader accepts new logs entries and replicates to all other nodes
Consul cluster for Vault storage backend shouldn’t be used for Consul functions in a production setting.
AWS 환경에서 Consul Storage Backend를 선택해서 배포하면 다음과 같습니다.
Region
VPC
Availability Zone 1, 2, 3
Private Subnet 1, 2, 3
Vault Server 1, 2, 3
Consul Backend 1a/1b, 2a/2b, 3a/3b
실제 Vault ←→ Consul, Consul ←→ Consul 간의 작업은 다음과 같이 이루어집니다.
Vault ← → Consul
Vault communicates with local consul agentLocal Consul Agent joinss the Consul cluster as client
아래 코드를 통해서 그 구조를 살펴볼 수 있습니다.
storage "consul" {
address = "127.0.0.1:8500"
path = "vault/"
token = "..."
}
listener "tcp" {
address = "0.0.0.0:8200"
cluster_address = "0.0.0.0:8201"
tls_disable = 0
tls_cert_file = "/etc/vault.d/client.pem"
tls_key_file = "/etc/vault.d/cert.key"
tls_disable_client_certs = "true"
}
seal "awskms" {
region = "us-east-1"
kms_key_id = "12345678-abcd-1234-abcd-1234567891-1"
endpoint = "example.kms.us-east-1.vpce.example.com"
}
api_addr = "https://vault-us-east-1.example.com:8200"
cluster_addr = "https://vault-us-east-1.example.com:8201"
cluster_name = "vault-prod-us-east-1"
ui = true
Vault / Integrated Storage Backend 설정하기
Vault + Integrated Storage Backend 는 다음과 같은 특징이 있습니다.
Vault Internal Storage Option | Supports high availability |
---|---|
Leverages Raft Consensus Protocol | Only need to troubleshoot vault |
All Vault nodes have copy of vault’s data | Built-in Snapshots for data retenstion |
Eliminates network hop to consul | HashiCorp Supported |
세부적으로 다음과 같은 아키텍쳐 측면의 특징을 가지게 됩니다.
통합 스토리지를 사용하여, Vault가 Cluster 하위의 Vault Node 전체에 자체 복제된 스토리지를 제공할 수 있습니다.
복제된 데이터를 저장할 Local Path를 정의합니다.
모든 데이터는 Cluster 하위의 Vault Node 간에 복제됩니다.
Consul Cluster를 실행하고 관리할 필요가 없습니다.
Integrated Storage (e.g. Raft) allows Vault nodes to provide its own replicated storage across the Vault nodes within a cluster.
Define a local path to store replicated data.
All data is replicated among all nodes in the cluster.
Eliminates the need to also run a consul cluster and manage it.
AWS 환경에서 Integrated Storage Backend를 선택해서 배포하면 다음과 같습니다.
Region
VPC
Availability Zone 1, 2, 3
Private Subnet 1, 2, 3
Vault Node 1, 2, 3
실제로 각 Vault Node 1, 2, 3은 서로 tcp/8201 포트를 통해서 스토리지 복제 동기화를 진행합니다.
아래 코드를 통해서 그 구조를 살펴볼 수 있습니다.
storage "raft" {
path = "/opt/vault/data"
node_id = "node-a-us-east-1.example.com"
retry_join {
auto_join = "provider=aws region=us-east-1 tag_key=vault tag_value=us-east-1"
}
}
listener "tcp" {
address = "0.0.0.0:8200"
cluster_address = "0.0.0.0:8201"
tls_disable = 0
tls_cert_file = "/etc/vault.d/client.pem"
tls_key_file = "/etc/vault.d/cert.key"
tls_disable_client_certs = "true"
}
seal "awskms" {
region = "us-east-1"
kms_key_id = "12345678-abcd-1234-abcd-1234567891-1"
endpoint = "example.kms.us-east-1.vpce.example.com"
}
api_addr = "https://vault-us-east-1.example.com:8200"
cluster_addr = "https://vault-us-east-1.example.com:8201"
cluster_name = "vault-prod-us-east-1"
ui = true
CLI를 사용하여 Standby Node를 Cluster에 수동으로 결합하기
vault operator raft join https://active_node.example.com:8200
Manually join standby nodes to the cluster using the CLI
vault operator raft join https://active_node.example.com:8200
HA Cluster를 통해서 Vault 1, 2, 3을 구성하면 그 중 1개가 Leader가 됩니다.
나머지 2개가 Follower가 되어서 작동합니다. 이후 아래 코드로 Cluster Member를 조회할 수 있습니다.
vault operator raft list-peers
Node Address State Voter
-------- ---------------- ----- ------
vault_1 10.0.101.22:8201 leader true
vault_2 10.0.101.23:8201 follower true
vault_3 10.0.101.24:8201 follower true
vault_4 10.0.101.25:8201 follower true
vault_5 10.0.101.26:8201 follower true