[Korean] Armon Dadgar와 함께하는 HashiCorp Vault 소개
이 화이트 보드 비디오에서는 HashiCorp의 공동 설립자이자 CTO인 Armon Dadgar를 통해 Vault가 무엇이며 어떻게 작동하는지에 대한 설명을 듣습니다.
Transcript
오늘은 HashiCorp Vault에 대해 이야기해 보겠습니다.
Vault에 대해 이야기할 때, 우리가 해결하고자 하는 것은 시크릿 관리 문제입니다. 시크릿 관리에 대해 이야기하면, 가장 먼저 자연스럽게 나오는 질문은 “시크릿이 무엇인가?”하는 것입니다.
시크릿 관리에 대해 우리가 이야기하고자 하는 것은 다양한 크리덴셜의 관리입니다. 우리가 크리덴셜이라고 말하는 것은 시스템 접에 필요한 인증과 인가 정보를 부여하는 모든 것을 의미합니다. 대표적인 몇 가지 예로 사용자 이름과 비밀 번호, 데이터베이스 인증정보, API 토큰 또는 TLS 인증서 등을 들 수 있습니다.
요점은 이들 중 어느 것이든, 예를 들어 사용자 이름 및 비밀번호를 이용해 시스템에 로그인하거나 인증할 수 있으며, TLS 인증서 등을 이용해 신원을 입증하고 접근 권한을 부여받을 수도 있습니다.
이들 모두 시크릿의 영역에 속하며, 매우 신중하게 관리하고자 합니다. 우리는 누가 시스템에 접근했는지 누가 이용하는지 알고자 합니다. 대부분의 상황에서, 이를 주기적으로 변경하는 방법에 대한 몇 가지 사례를 소개하고자 합니다.
실제로 이를 관리하는 상태를 살펴보면, 시크릿이 무분별하게 퍼져 있는 것을 알 수 있습니다. 시크릿이 과도하게 확산되어 있다는 것은 어디에나 있다는 것을 의미합니다.
소스 코드 내부에 플레인 텍스트로 작성되어 있으며, 때로는 사용자이름과 비밀번호가 헤더에 하드코딩되어 있을 수 있습니다. 이것은 결국 형상 관리와 같은 내부적인 과제로 귀결됩니다.
마찬가지로 Chef나 Puppet, Ansible 등에 플레인 텍스트로 사용되기 때문에 누구나 로그인해서 이들 인증정보를 확인할 수 있습니다. 결국, 이런 정보는 일반적으로 GitHub, GitLab 또는 Bitbucket 등과 같은 버전 제어 시스템에 저장하는 것으로 귀결됩니다. 이런 현상들은 인프라 전반에 널리 산재하고 있습니다.
여기에 어떤 과제가 있습니까? 우리는 누가 이런 정보에 접근하는지 알지 못합니다. 조직 내에서 누가 GitHub 접근 권한을 가지고 있는지, 접속하여 소스 코드를 확인하고, 데이터 베이스 접속 정보를 볼 수 있는 지 알지 못합니다.
심지어 이를 할 수 있다고 하더라도, 실제로 그와 같은 일을 했는지는 알 수 없습니다. 저, 아몬이 보안 정보를 알 수 있었기 때문에 감사 추적 정보가 없습니다. 여기서 접근했는지 알 수 있을까요? 따라서, 누가 접근 권한이 있는지를 세부적으로 관리할 수 없으며, 누가 이를 이용해 무엇을 했는지를 감사할 수도 없습니다.
더욱 심각한 것은 시크릿 정보를 어떻게 주기적으로 변경하느냐 하는 것입니다. 보안 침해가 있었거나, 정기적인 변경을 실행을 위해, 데이터베이스 인증정보를 변경해야 하는 경우, 소스 코드에 하드코딩되어 있거나, 다수의 시스템에 분산되어 있다면, 매우 어려워집니다. 시크릿 정보를 효과적으로 변경하는 방법을 알기 어렵습니다.
이러한 상황을 우리는 시크릿 확산이라고 부릅니다. Vault를 개발하기 시작했을 때 첫 번째 목표는 이러한 문제를 직시하고 "어떻게 개선할 수 있는가?"하는 것이었습니다.
이것이 바로 Vault의 출발점입니다. Vault는 시크릿 확산 문제를 인식하는 데서 시작합니다. 그리고 중앙 집중화를 통해서만 이를 해결할 수 있습니다. 모든 곳에 흩어져 있던 모든 시크릿 정보들을 중앙화하고 .Vault는 Vault에 저장된 데이터와 Vault와 client간에 오고 가는 데이터에 대한 암호화를 보장합니다.
이는 몇 가지 속성들을 제공합니다. 하나는 최소한 현재 플레인 텍스트로 저장하는 여타 시스템들과 달리, 보안 정보를 저장한 곳을 볼 수 있다면, 암호화되어 있는 것을 확인할 수 있습니다. 시크릿 정보를 확인할 수 있는 절대적인 권한을 얻을 수 없습니다.
그 다음은 Vault로서, 이러한 정보에 대해 세부적인 접근 제어 기능을 제공합니다. 조직 내 누군가가 GitHub에 대한 접근 권한이 있으며 소스코드를 볼 수 있는 것이 아니라, 이제 훨씬 세분화되어서 웹 서버는 데이터베이스 인증정보에 대한 접근 권한이 필요하고 API 서버는 API 토큰이 필요하게 정의할 수 있습니다. 하지만, 모든 사람들이 모든 것에 접근해야 할 필요는 없습니다.
그리고 이런 기능에 감사 추적 기능을 제공합니다. 이제 어떤 인증정보가 웹 서버 접근를 했는지, 저같은 사람이 해당 시스템에서 어떤 인증정보를 접근했는지 알 수 있습니다. 이제 이들을 관리하는 방법에 대한 향상된 가시성과 통제력을 확보하게 됐습니다.
이는 Vault의 가장 기본적인 과제로서, 최소한 무분별하게 확산되어 어디에나 있는 환경에서 탈피해 중앙 집중화되고, 철저하게 암호화하며, 누가 접근했는지 이를 확인할 수 있음과 암호화를 확실히 보장하는 향상된 가시성을 가지게 됩니다.
이것이 1단계입니다. 그 다음 단계 과제는 이 크리덴셜을 누구에게 부여할 것인지 파악하는 것입니다. 이제 모든 인증정보는 Vault에 안전하게 저장되며 이를 찾아내어 애플리케이션에 제공합니다.
또 다른 문제는, 애플리케이션이 보안을 유지하는 데는 형편없다는 것입니다. 불가피하게 애플리케이션은 크리덴셜을 로깅 시스템으로 로그를 보내 표준 출력에 기록됩니다. 그러고는 Splunk로 전달되어 이제 누구든지 볼 수 있는 중앙 로그에 있게 됩니다.
이는 모든 유형의 진단 출력에 드러납니다. 예외가 있을 수 있지만 애플리케이션이 사용자 이름과 비밀번호, 오류 보고서의 트레이스백(traceback)이나 내부를 보여줄 수 있습니다. 이건 오류가 발생하면 외부 모니터링 시스템으로 전달될 수 있습니다.
일반적으로, 우리가 파악하기로 애플리케이션의 보안 유지 기능은 매우 취약합니다. 따라서, 중앙 집중화가 훌륭히 이루어지고, 강력하게 제어하여 애플리케이션으로 전달되는 중에 이를 암호화하더라도, 애플리케이션을 신뢰할 수 없습니다.
Vault에서 선보이는 2단계 기능 중 하나는 일명 동적 시크릿(dynamic secrets)입니다. 동적 시크릿은 취약할 수 밖에 없는 장기 인증정보 대신, 단기적으로 사용하는 임시 인증정보를 애플리케이션에 제공한다는 개념입니다. 동적으로 생성되지만 수명이 짧습니다. 예를 들어 30일만 유효한 인증정보를 애플리케이션에 제공할 수 있습니다.
여기에 따른 이점은 몇 가지가 있습니다. 이제 애플리케이션이 이 인증정보를 유출하더라도 지정한 기간 동안만 유효합니다. 로깅 시스템에 이를 작성할 수 있으며 보여질 수도 있지만, 인증서를 폐기하고 계속해서 새로운 인증서를 발행함으로써 공격이 성공하기 어려운 대상으로 만듭니다.
또 다른 중요한 이점은 이제 각 인증정보가 각 클라이언트에 고유하다는 것입니다. 이전에는 만약 50대의 웹서버가 있다면, 이들 모두가 접근해서 고정된 데이터베이스 인증정보를 읽을 수 있었습니다. 만약에 보안사고가 발생하고 데이터베이스 인증정보가 유출되는 경우, 보안 취약 지점을 정확하게 찾아내는 것이 매우 어렵다는 것을 의미합니다. 동적 시크릿 환경에서는 50대의 서버가 모두 동일한 인증정보를 공유하는 것이 아니라, 이들 50대 웹 서버 각각이 고유의 인증정보를 갖습니다. 따라서, 웹 머신 42가 침해가 발생한 지점이라는 것을 매우 정확하게 알게 됩니다.
마지막으로 인증 정보 해지관리 측면을 크게 개선할 수 있다는 것입니다. 이제 웹 머신 42가 침해 지점이라는 사실을 알았다면, 웹 머신 42의 사용자 이름과 비밀번호만 폐기함으로써 보안유출을 격리시킬 수 있습니다. 하지만, 만약 50대의 머신이 동일한 사용자 이름과 비밀번호를 공유한다면, 이를 폐기하려는 순간, 전체 서비스를 중단시켜야 하는 상황을 초래할 것입니다. 따라서, 폐기로 인한 영향도가 기존 공유 시크릿 방식이 동적 시크릿보다 훨씬 큽니다.
우리가 발견한 세 번째 과제는 애플리케이션이 종종 데이터를 저장한다는 것입니다. 문제는 애플리케이션이 저장된 데이터를 어떻게 보호하는가 입니다. Vault에 모든 정보를 저장할 수는 없습니다. Vault는 시크릿을 관리하기 위한 것이지 자체가 보안이 아닙니다.
우리가 자주 보는 것은 Vault가 중앙 집중식 시크릿 관리 저장소로 사용되고 사람들이 암호화 키를 저장하고 있다는 것입니다. Vault에 암호화 키를 넣은 다음 해당 키를 다시 응용 프로그램에 배포할 수 있습니다. 애플리케이션이 저장된 데이터를 보호하기 위해 암호화를 수행하고 있습니다.
그러나 우리가 찾은 것은 일반적으로 애플리케이션이 암호화를 올바르게 구현하지 않는다는 것입니다. 미묘한 뉘앙스가 많고 잘못되기 쉽습니다. 이러한 종류의 실수로 인해, 실수가 발생할 때 종종 전체 암호화를 손상시킵니다.
우리가 자주 겪는 문제 중 하나는 Vault에서 어떻게 암호화 키를 저장하고 이를 애플리케이션에 전달하고 앱이 암호화를 올바르게 수행한다고 가정하는 것입니다.
이는 Vault에서 "서비스로서의 암호화(encrypt as a service)"라고 부르는 기능으로 진화했습니다. 여기서 아이디어는 개발자에게 키를 제공하고 개발자가 암호화를 올바르게 구현한다고 기대하는 대신 Vault가 몇 가지 작업을 수행한다는 것입니다. 그 중 하나는 이름이 지정된 키 세트를 생성할 수 있다는 것입니다. "신용카드 정보(credit card information)"라는 키를 생성하고 "주민등록번호(social security number)"라는 두 번째 키를, 그리고 "PII"라는 키를 생성합니다.
이는 단지 이름입니다. 이 키에 이름을 지정할 것이며 값을 부여하지는 않을 것입니다. 그러나 우리가 공개하는 것은 암호화를 수행하는 하이레벨 API 세트입니다. 이들 API는 암호화, 복호화 또는 서명 또는 유효성 확인 등 여러분이 기대하는 고전적인 작업을 수행합니다.
이제 저는 개발자로서 API로 Vault를 호출하고 "신용카드 키(key)와 몇 가지 데이터를 이용해 HMAC을 실행"하려고 합니다. Vault가 막는 것은 Vault가 구현을 제공하기 때문에 개발자가 이러한 하이 레벨 작업을 올바르게 구현하였는지 고민할 필요가 없다는 것입니다. Vault는 키 관리도 제공하고 있습니다. 개발자들은 실제로 관리되고 있는 키를 결코 보지 못합니다.
이는 몇 가지 이점을 제공합니다. 하나는 Vault를 통해 검증된 구현을 사용하기 때문에 암호화가 올바르게 구현되도록 보장한다는 것입니다. 이 구현은 우리, 오픈소스 커뮤니티, 그리고 우리가 사용하는 외부 감사자에 의해 검증되었습니다.
또한 키 관리를 오프로드 할 수 있습니다. 암호화가 어렵다고 생각하면, 키 관리는 더욱 어렵습니다. 실제로 얼마나 많은 애플리케이션들이 키 버전 관리, 키 순환, 키, 해제, 그리고 키 관리 전체 라이프사이클을 적절하게 구현하고 있는지를 묻는다면, 그 대답은 매우 적을 것입니다. 왜냐하면 이는 매우 까다롭기 때문입니다. 그러나 이를 Vault로 오프로드하면 하이레벨API를 사용하여 이 모든 작업을 수행할 수 있습니다. 또한, Vault를 통해 전체 키 라이프사이클을 수행할 수 있습니다.
실제로 지금까지 말씀드린 것들은 우리가 개발자들을 돕기 위해 노력하고 있는 3가지 주요 과제들입니다. 인증정보가 일반 텍스트로 작성되고 여러 많은 시스템 전반에 무분별하게 확산되어 있는 상황에서 탈피해 중앙에서 관리되고 엄격한 접근 제어와 분명한 가시성을 제공할 수 있는 시나리오로 어떻게 전환할 수 있을까요? 그리고 어떻게 한 걸음 더 나아가 보안적으로 안전하다고 확신하기 어려운 애플리케이션을 보호할 수 있을까요?
단기/임시 접근을 통해 이를 해결할 수 있습니다. 웹 서버가 데이터베이스에 접근하기 위해서는 정적 인증정보가 아닌 단기간 내에 변경되는 동적 인증정보를 이용하기 때문에 보안적 공격이 성공하기 어려운 타깃을 만듭니다. 그리고 마지막으로 애플리케이션이 어떻게 저장된 데이터를 보호하도록 할 수 있을까요? 이는 일련의 키 관리와 하이레벨 암호화 오프로드를 통해 이루어집니다. 이들 3가지가 Vault의 핵심 원리입니다.
이제 하이레벨 아키텍처를 중점적으로 살펴보도록 하겠습니다. 어떻게 이를 구현할 수 있을까요? Vault의 아키텍처와 관련해 몇 가지 중요한 사항들을 이해해야 합니다. 하나는 Vault의 플러그 인이 매우 용이하다는 것입니다. 이는 많은 다양한 플러그인 방식을 지원합니다.
Vault는 라이프사이클 관리를 포함한 많은 책임을 담당하는 중앙 코어가 있어, 요청이 올바르게 처리되도록 보장하며 많은 여러 확장 포인트가 있기 때문에 우리 환경에 맞춰 적용할 수 있습니다.
매우 중요한 첫번째 특징인 인증 백엔드입니다. 이를 통해 Vault는 클라이언트가 여러 시스템에서 인증을 받을 수 있도록 합니다. 예를 들어, EC2VM에서 부팅하면, 이 EC2VM은 AWS 인증 플러그인을 사용해 인증합니다. 이 플러그인으로 Amazon의 자격 증명(Identity) 개념에 연동함으로써 caller, 예를 들어 웹 서버를 증명할 수 있습니다.
하지만, 실제 사람인 사용자가 있는 경우, LDAP 또는 Active Directory 등을 사용하여 아이덴티티(identity)를 증명할 수 있습니다. 쿠버네티스(Kubernetes) 등 하이레벨 플랫폼을 사용하는 경우, 쿠버네티스 인증 제공자를 활용할 수도 있습니다.
이들 인증 제공자의 목표는 쿠버네티스, LDAP 또는 AWS 등 우리가 신뢰하는 시스템을 이용해 애플리케이션 또는 사용자의 아이덴티티(identity)를 제공하는 것입니다. 여기에서 주목해야 할 것은 caller의 아이덴티티 개념입니다.
이를 이용해 감사 백엔드로 연결하면, 요청/응답 감사를 외부 시스템으로 연결하고 스트리밍할 수 있기 때문에 누가 무엇을 했는지에 대한 추적 정보를 받을 수 있습니다. 이는 예를 들어, Splunk로 모든 다양한 감사 로그를 전송할 것입니다. Vault를 통해 여러 다양한 감사 로그를 확보하게 될 것입니다. 또한, Splunk는 물론, 예를 들어 Syslog와 같은 시스템으로 전송할 수 있습니다.
다음 단계 과제는 Vault가 저장 데이터를 실제로 어디에 저장하느냐 하는 것입니다. Vault에 시크릿 정보를 읽고 작성하려고 한다면, 어딘가에 이를 저장할 수 있어야 합니다. 이를 스토리지 백엔드라고 부릅니다.
스토리지 백엔드는 데이터 저장을 담당합니다. 이는 여러 다른 요소들과 결합될 수 있습니다. 이는 MySQL Postgres의 표준 RDBMS나, Consul과 같은 시스템 그리고 Google Spanner와 같은 클라우드 관리 데이터베이스가 될 수도 있습니다. 그러나 이러한 백엔드 시스템의 목표는 가용성이 높은 방식으로 내구성있는 스토리지를 제공하여 이러한 백엔드 시스템 중 하나의 손실을 견딜 수 있도록 하는 것입니다.
그리고 마지막으로, Vault가 여러 다른 시크릿 정보에 대한 접근을 어떻게 제공하느냐 하는 것입니다. 이들은 그 자체가 보안 백엔드(secret backend)입니다. 이는 몇 가지 다른 형태로 나타납니다. 가장 중요한 용도는 앞서 이야기한 동적 시크릿 기능을 실행하는 것입니다.
첫 번째 보안 백엔드의 형태는 단순합니다. 이는 단순한 key-value의 형태입니다. 단순히 정적 사용자 이름과 비밀번호를 저장할 수 있습니다. 사용자 이름과 비밀번호를 부여하며 이들은 정적입니다. 이는 단순한 key-value 저장소로서 암호화되어 저장됩니다. 하지만, 보다 수준을 높여 앞서 설명한 동적 시크릿 기능을 사용할 수도 있습니다. 여기서부터 여러 다양한 플러그인이 활용되기 시작합니다.
여러 다양한 데이터베이스 플러그인이 있으며, 데이터베이스 플러그인을 통해 MySQL, Postgres, Oracle 등의 인증정보를 동적으로 관리할 수 있을 것입니다. RabbitMQ와 같은 것들로 메시지 큐에 대한 동적 자격 증명을 실행할 수 있습니다.
그 이외에도, 심지어 AWS에도 동일한 원리를 적용할 수 있습니다. S3에서 읽기/쓰기를 해야 하는 애플리케이션이 있지만, 계정 관리에 대한 장기적인 접근 권한을 부여하는 것을 원하지 않을 수 있습니다. AWS 백엔드에서 역할을 정의하고 실행하는 대신, 필요에 따라 동적으로 단기 인증정보를 생성할 것입니다. 이는 동적 시크릿의 패러다임을 확장합니다.
이는 Vault가 동일한 원리를 많은 다양한 요소들에 적용할 수 있도록 하는 확장 포인트입니다. 일반적인 용도 중 하나가 PKI입니다. 실제로 인증서 관리는 악몽이 될 가능성이 있습니다. 흔히 볼 수 있는 인증서는 길게 설정된 5~10년 기간의 인증서입니다. 그 이유는 이것을 생성하는 프로세스를 거치기 싫기 때문입니다. 이에 반해 Vault는 프로그래밍 방식으로 이를 생성합니다. 사람들은 매우 단기간의 인증서를 사용하게 될 것입니다. 아마도 72시간, 24시간 단위로 짧을 것입니다. 이러한 방식으로 방어할 대상을 지속적으로 변경시킵니다.
가능한 목록은 계속 늘어나고 있으며 예를 들어, SSH와 같은 것들을 포함합니다. SSH에 대한 접근도 중개가능하기 때문에 대규모의 서버를 제어하는 단일 PIN은 없습니다.
본질적으로 이런 기능이 Vault를 매우 유연하게 합니다. 이런 동작을 통해 Vault는 여러 다른 인증 제공자들을 대신하여 인증하는 클라이언트를 관리할 수 있습니다. 신뢰할 수 있는 다양한 로그 관리 소스에 대해 감사를 실행하고, 내구성이 뛰어난 거의 모든 시스템에 데이터를 저장하며, 새로운 보안 백엔드를 추가하여 정적 또는 동적으로 관리할 수 있는 보안 유형의 범위를 확장할 수 있습니다.
이는 단일 인스턴스로, Vault가 됩니다. Vault 인스턴스를 실행하는 경우, 각 인스턴스는 이들 중 하나입니다. 보다 광범위한 배포 환경에서 이는 고가용성을 제공하기 위해 다수의 Vault 인스턴스를 실행합니다. 일반적으로 공유 백엔드를 구축합니다. 예를 들어, 이는 내부적으로 다른 서버 3개 로 HA를 제공하는 Consul일 수 있습니다. 그런 다음, 프론트에서 다수의 Vault를 실행할 것입니다.
Vault는 공유 백엔드와 조율해 리더로서 동작할 것입니다. 이들 중 하나는 현재의 리더로 선택되고, 마찬가지로 클라이언트도 있고, 요청(request)은 리더에게 전달됩니다. 리더가 아닌 노드에 요청하더라도 리더 노드에 포워딩됩니다.
이러한 방식으로 어떤 특정 노드가 정지하거나, 전원이 꺼지거나, 프로세스가 충돌하거나, 네트워크 연결에 문제가 생기면, 이를 감지하고 새로운 노드를 자동으로 리더로 승격됩니다. 이 인스턴스는 유효한 작업을 인계받고 다른 보조 노드들을 승격시키기 시작합니다.
이는 개략적으로 Vault를 살펴본 것입니다. 해당 구성은 공유 네트워크 서비스로서 작동하며, 네트워크를 통해 API 클라이언트로서 통신을 주고받습니다. Vault가 일반적으로 노출하는 것은 RESTful JSON API입니다. HTTP를 통해 JSON형태로 전달되어 비교적 쉽게 애플리케이션을 통합할 수 있습니다.
Vault에 대한 개략적인 소개가 도움이 되었기를 바랍니다. 자세한 내용은 다른 자료들을 참조해 주십시오. 감사합니다.