OpenBao 내부 들여다보기 - 포크 이후 2년, 진짜로 Vault를 대체할 수 있나

작년부터 우리 팀에서도 신규 서비스에는 Vault 대신 OpenBao를 깔기 시작했다. 처음 도입할 때만 해도 "그냥 Vault 1.14 그대로 박제된 거 아니냐"는 의심이 컸는데, 막상 1년 가까이 운영해보니 그 평가는 좀 수정해야겠다는 생각이 든다. 특히 올해 2월에 나온 2.5에서 namespace와 horizontal read scaling이 들어가면서, 단순히 Vault의 OSS 미러가 아니라 자기 방향성을 갖기 시작했다.
이 글은 "OpenBao 도입 가이드"가 아니다. 그건 이미 충분히 많다. 대신 내부 구조가 Vault와 어디서 갈라지기 시작했는지, 운영자 입장에서 어느 지점을 신경 써서 봐야 하는지를 정리한다.
포크 시점이 결정한 것들
OpenBao는 Vault 1.14.0에서 갈라져 나왔다. 이건 단순한 버전 번호 이상의 의미가 있다. 1.14 시점에서 Vault는 storage backend 추상화(physical.Backend), seal abstraction, mount table 직렬화 포맷, 그리고 token store 구조가 어느 정도 굳어진 상태였다. 그래서 OpenBao 2.x는 이 인터페이스들을 그대로 끌어안고 시작했다.
그런데 사실 내부적으로 보면 라이선스 변경 시점에 HashiCorp가 작업 중이던 몇 가지가 1.14 직후에 코드로 들어가지 못했다. 대표적으로 secrets sync 기능, transit BYOK 워크플로 일부, 그리고 ent 전용으로만 있던 일부 audit 백엔드들. OpenBao는 이걸 처음부터 직접 새로 쓰거나, 아예 다른 길로 갔다. 예컨대 horizontal read scaling은 Vault Enterprise의 Performance Standby와 비슷한 발상이지만, 구현은 Raft 기반에서 read-only follower가 일관성 토큰(consistency token)을 검증하고 자체 응답하는 방식으로 갈렸다. 결과적으로 ent 라이선스 없이도 read traffic을 분산할 수 있다.
근데 이게 공짜는 아니다. read scaling을 켜면 follower가 mount 변경, policy 업데이트 같은 이벤트를 lazy하게 인지하기 때문에, 한순간이라도 stale한 응답이 나갈 수 있다. 대부분의 secret read는 이걸 신경 쓸 필요가 없지만, dynamic credential을 발급하면서 동시에 policy를 바꾸는 운영 시나리오라면 명시적으로 leader pinning을 해야 한다. 이건 문서에는 작게 적혀 있는데, 실제 트러블슈팅 들어가면 한참 헤맨다.
storage 레이어와 Raft 통합
OpenBao 운영에서 가장 많이 받는 질문이 "스토리지 백엔드 뭘 쓰냐"다. 외부 의존성을 줄이려는 흐름이 강해지면서, Consul backend는 사실상 deprecated 취급이고 integrated storage(Raft)가 디폴트가 됐다. 이 부분은 Vault와 동일하지만, OpenBao는 한 단계 더 들어가서 Raft의 snapshot 처리에 손을 댔다.
기본 동작은 이렇다. 모든 KV write는 Raft log에 append되고, 일정 임계(기본 8MB log size 또는 16,384 entries)에 도달하면 snapshot이 생성된다. 그런데 Vault에서 한참 운영하다 보면 snapshot 생성 중에 latency spike가 튀는 패턴을 본 사람들이 있을 거다. 특히 transit 엔진에 키가 많거나, PKI에 인증서가 누적된 경우. OpenBao 2.4부터는 snapshot streaming 시 부분 직렬화(partial serialization)를 도입해서, 큰 mount 하나가 전체를 막는 현상이 줄었다.
운영 관점에서 확인해야 할 메트릭은 단순하다.
bao_raft_snapshot_persist_duration_seconds (히스토그램)
bao_raft_log_size_bytes (게이지)
bao_raft_apply_duration_seconds (히스토그램)
persist_duration이 30초를 자주 넘기면 storage I/O를 먼저 의심해야 한다. 우리 팀에서는 EBS gp3에 baseline 3000 IOPS로 돌렸더니 PKI mount에 인증서 5만 개 쌓이고 나서 snapshot이 1분 가까이 걸리는 걸 본 적이 있다. io2로 바꾸는 대신 PKI 정리 job을 따로 돌리는 쪽으로 갔는데, 이건 정답이 없는 영역이라 환경별로 다르다.
seal/unseal 흐름과 auto-unseal 호환성
Vault에서 OpenBao로 넘어올 때 가장 신경 쓰이는 부분이 seal 호환성이다. 결론부터 말하면 KMS 기반 auto-unseal은 거의 그대로 동작한다. AWS KMS, GCP Cloud KMS, Azure Key Vault, Transit, HSM(PKCS#11) 모두 인터페이스가 보존됐다. Shamir 시드도 동일하다.
내부적으로 seal 흐름은 root key → key ring → master key 계층을 거쳐서 actual data encryption key를 풀어내는 구조인데, 여기서 데이터 포맷은 1.14 시점 그대로다. 그래서 Vault 1.14에서 snapshot을 떠서 OpenBao로 restore하면 동작한다. 실제로 우리도 staging 환경 한 군데를 이렇게 마이그레이션했다. 다만 ent 전용이었던 일부 audit device 설정이나 namespace 메타데이터는 빠지므로, 이걸 미리 그어두고 시작해야 한다.
여기서 좀 더 깊이 들어가면 흥미로운 게 있다. OpenBao 2.5의 namespace 구현은 Vault Enterprise와 와이어 포맷은 호환되지만, 내부 path 트리 자료구조가 다르다. Vault Ent는 namespace를 mount table 위에 얇은 라우팅 레이어로 얹은 반면, OpenBao는 namespace를 mount table의 first-class key로 끌어올렸다. 그래서 namespace 수가 수백 개 이상 늘어나도 라우팅 lookup이 O(log n)으로 유지된다. 다만 namespace 간 ACL 격리 의미론(특히 token wrapping이 cross-namespace로 일어날 때)은 아직 Vault Ent와 미묘하게 다르다. 문서만 보고 들어가지 말고, 실제 token capabilities를 dry-run으로 확인해보는 게 안전하다.
auth method와 plugin 격리
plugin 시스템도 짚고 갈 만하다. OpenBao는 Vault와 동일한 go-plugin 기반인데, plugin sandboxing 옵션이 추가됐다. plugin_runtime 설정으로 runc, containerd, 또는 systemd-nspawn 안에서 plugin을 띄울 수 있다. 우리는 PKI plugin과 KMIP plugin을 분리해서 띄우는 데 이걸 쓰고 있다.
plugin_runtime "secure" {
type = "container"
oci_runtime = "runc"
rootless = true
cgroup_parent = "/bao-plugins"
}
plugin_registry {
pki {
runtime = "secure"
memory_limit = "256MB"
cpu_quota = "50%"
}
}
장점은 명확하다. plugin이 panic나도 코어가 안 죽고, 메모리 누수 있는 plugin이 호스트를 잠식하지도 않는다. 다만 cold start latency가 늘어난다. 첫 요청에 800ms-1.2초가 추가로 붙는 걸 봤다. 그래서 prewarm 설정을 켜두거나, 자주 안 쓰는 plugin에만 격리를 적용하는 게 현실적이다.
그래서 갈아탈 만한가
이게 이 글의 핵심인데, 솔직히 케이스 바이 케이스다. ent 라이선스에 의존하는 기능(DR replication, control groups, MFA의 일부 옵션)을 쓰고 있었다면 아직 OpenBao로 1:1 매핑이 안 된다. 반면 OSS Vault만 써왔거나, Performance Standby 정도가 아쉬워서 ent를 고민하던 팀이라면 OpenBao 2.5는 진지하게 평가할 만하다. 특히 GitLab이 작년 자사 CI 내부 secret manager를 OpenBao로 정리한 사례, Adfinis가 2월에 commercial subscription을 내놓은 흐름을 보면 생태계가 비어 있던 ent 자리를 빠르게 채우고 있다.
내가 더 보고 있는 건 IBM이 4월에 발표한 Vault 2.0 방향이다. IBM 인수 후 첫 메이저 릴리스인데, identity federation 강화 쪽으로 가닥이 잡혔다. OpenBao가 이 격차를 어떻게 따라잡을지가 향후 1-2년의 분기점이 될 거 같다.
운영 입장에서 한 가지만 강조하자면, OpenBao로 갈 거면 storage backend 메트릭 수집 체계부터 단단히 깔아두자. Raft 계열 시스템의 운영 난이도는 결국 snapshot과 commit latency 가시성에서 갈린다.