인프라 접근 권한도 코드로: Teleport로 구현하는 Access as Code

IaC(Infrastructure as Code) 이전의 인프라 관리는 이랬습니다. 서버마다 SSH로 들어가 패키지를 설치하고, nginx 설정을 손으로 고치고, 그 내용을 팀 위키에 표로 정리했죠. 서버가 늘어날수록 표는 현실을 따라가지 못했습니다. Puppet, Chef, Ansible이 서버 구성을 코드로 옮기고, Terraform과 Pulumi가 인프라 자체를 선언하면서 이 문제는 풀렸습니다.
그런데 인프라 접근 권한은 어떻게 관리하고 계신가요?
물론 접근 권한도 코드로 관리할 수 있습니다. AWS IAM 정책은 Terraform의 aws_iam_policy로 선언하고, Kubernetes RBAC는 매니페스트를 GitOps로 배포하죠. 다만 이때도 권한은 프로바이더별, 프로토콜별로 나뉜 자리에 그대로 남습니다. 그래서 "이 사람이 프로덕션의 어디까지 접근할 수 있나"를 확인하려면 이런 절차를 밟게 됩니다.
- IAM에서 사용자에게 직접 연결된 정책, 소속 그룹의 정책, 위임받을 수 있는 role을 모으고, 여기에 권한 경계와 SCP가 씌우는 상한까지 교차 계산
- 서버에 들어가
authorized_keys확인, 또는 구성 관리 코드에서 역추적 - DB마다 접속해 계정 목록 확인
- 클러스터와 네임스페이스마다 유효 권한을 계산하고, 결과를 사람 단위로 취합
- 이 넷을 모아 사람 이름으로 다시 이어붙이기
퇴사자 정리 과정에서는 이 중 하나가 빠지고, 반년 뒤 감사에서 문제가 발견되기도 합니다.
인프라는 코드로 선언하는데, 그 인프라에 누가 접근할 수 있는지 관리하는 일은 흩어진 채 남아 있습니다.
이 간극을 가리키는 표현으로 Access as Code(AaC)가 있습니다. 인프라를 코드로 관리하듯 접근 권한도 같은 방식으로 관리한다는 뜻으로, 인프라 접근 관리 벤더가 써 온 말입니다. 다만 "무엇을 충족해야 AaC인가"에 대한 합의된 기준은 찾기 어렵습니다.
이 글은 그 기준을 세우는 데서 시작합니다. AaC를 만족하려면 무엇이 필요한지 네 가지 조건으로 정리하고, 그 기준으로 기존 도구들을 평가한 뒤 Teleport를 자세히 살펴봅니다. 인포그랩은 Teleport 공식 파트너라서 후반부는 Teleport를 중심으로 다뤘습니다. 그러나 네 가지 조건은 도구와 분리해 제시하니 다른 솔루션을 검토하실 때도 그대로 쓰실 수 있습니다.
AaC란 무엇인가
인프라 접근 권한을 코드로 선언하고, git으로 버전을 관리하며, 여러 프로토콜을 하나의 정의로 묶고, 접근에 필요한 크리덴셜을 그 코드가 직접 만들어내는 방식.
이 글은 AaC를 이렇게 정의합니다. IaC와 나란히 놓으면 둘의 차이는 분명해집니다.
| 코드가 정의하는 것 | 핵심 질문 | |
|---|---|---|
| IaC | 만들어지는 대상과 그 상태 | 무엇을 어떻게 만드나 |
| AaC | 역할이 닿을 수 있는 대상과 조건 | 이 사람이 어디까지 접근할 수 있나 |
"무엇을 충족해야 AaC인가"는 네 가지 조건으로 구분할 수 있습니다. 각 조건은 두 곳에서 도출했습니다. 하나는 인프라 관리와 접근 제어 양쪽에서 이미 검증된 원칙이고, 다른 하나는 도입부에서 본 접근 권한 고유의 문제입니다. 사람 한 명의 권한을 확인하는 데 다섯 단계가 필요하다는 것, 그리고 퇴사자 정리 과정에서는 한 단계가 빠진다는 것이죠.
앞의 두 조건은 접근 권한을 코드로 다루기 위한 최소 요건이고, 뒤의 두 조건은 그 코드가 무엇까지 포괄하고 무엇을 만들어내야 하는지를 묻습니다.

1. 선언적이어야 한다
접근 권한을 "이 사람에게 이 버튼을 눌러 권한을 준다"가 아니라 "이 역할은 이런 속성의 리소스에 이런 작업을 할 수 있다"고 선언해야 합니다. 명령형(imperative)이 아닌 선언형(declarative)으로, IaC가 인프라에 적용한 것과 같은 원칙입니다.
새로 만든 기준은 아닙니다. CNCF Sandbox 프로젝트 OpenGitOps는 벤더 중립적인 GitOps 정의를 목표로 네 가지 원칙을 세웠는데, 첫 번째가 시스템의 desired state를 선언적으로 표현하는 것입니다. OpenGitOps는 이를 인프라뿐 아니라 애플리케이션 계층까지 아우르는 표준으로 제시합니다. 그 인프라에 누가 접근하는지에도 같은 기준을 적용하지 않을 이유가 없습니다.
없으면: 적용하기 전에 무엇이 바뀌는지 검토할 수 없습니다. 권한이 선언된 상태가 아니라 절차의 결과로만 남기 때문에 실제 상태가 의도와 어긋나도 알아챌 방법이 없습니다. 도입부에서 구성 관리 코드를 역추적해야 했던 것이 이 경우입니다. 그 코드가 무엇을 했는지 따라가야 지금 누가 접속할 수 있는지 알 수 있으니 코드를 읽는 일과 상태를 아는 일이 분리됩니다.
2. 모든 변경이 버전 관리를 거쳐야 한다
권한 변경이 커밋으로 남아야 합니다. 누가, 언제, 왜 이 권한을 추가했는지가 변경 이력에 있어야 합니다.
OpenGitOps의 두 번째 원칙이 요구하는 것이 이것입니다. desired state를 불변성과 버전 관리가 강제되는 방식으로 저장하고, 전체 변경 이력을 유지하라는 것이죠. 다만 OpenGitOps는 저장소로 git을 특정하지 않습니다. 불변성과 버전 관리라는 요건만 충족한다면 다른 시스템도 가능하다고 봅니다. 그럼에도 이 글이 git을 전제하는 것은, 인프라 팀이 이미 git 위에서 일하고 있어 접근 권한만 다른 도구에 두면 리뷰 흐름이 갈라지기 때문입니다.
여기서 한 가지가 따라옵니다. 이력이 남는 것만으로는 부족하고, 승인을 거치지 않은 변경은 시스템에 반영되지 않아야 합니다. 저장소가 유일한 입력 경로일 때만 그 이력이 실제 상태를 설명하니까요.
없으면: "이 권한이 왜 여기 있지"에 답할 방법이 없습니다. 감사가 요구하는 것은 현재 상태만이 아니라 그 상태에 이른 경위입니다. 선언적이기만 하고 이력이 없으면 팀 위키의 표를 YAML로 바꾼 것에 그칩니다.

3. 프로토콜을 가로질러 하나의 코드로 정의해야 한다
하나의 역할 정의가 SSH, Kubernetes, DB, 애플리케이션 접근을 함께 담아야 합니다. 프로토콜마다 다른 포맷의 파일로 흩어져 있으면 이 조건을 충족하지 못합니다.
이 요구가 특별한 것은 아닙니다. 이미 여러 곳에서 같은 일이 일어났습니다. AWS IAM은 S3, EC2, Lambda처럼 성격이 전혀 다른 서비스를 하나의 JSON 정책 언어로 다룹니다. 서비스마다 고유한 액션을 정의하되, 표현 방식은 s3:ListBucket, ec2:StartInstances처럼 하나로 통일했죠. 도입부에서 본 것처럼 평가 구조까지 단순한 건 아니지만, 적어도 서비스마다 다른 권한 언어를 쓰지는 않습니다. Kubernetes RBAC도 Pod, Secret, Deployment를 리소스 유형별 권한 체계로 나누지 않고 하나의 Role 오브젝트로 묶습니다. Terraform 역시 프로바이더마다 API가 다른데도 하나의 언어로 선언합니다.
각 경계 안에서는 이 통합이 이미 상식입니다. 아무도 "S3용 권한 언어와 EC2용 권한 언어를 따로 쓰자"고 하지 않습니다. 그런데 SSH와 Kubernetes와 DB 사이에서는 여전히 따로 쓰고 있습니다.
경계 자체도 생각보다 좁습니다. AWS의 통합은 인가 계층까지 닿지 못했습니다. IAM 데이터베이스 인증을 켜면 비밀번호 없이 IAM 토큰으로 RDS에 접속할 수 있지만 거기까지입니다. AWS 문서 자신이 이 토큰은 인증에만 쓰이고 세션이 수립된 뒤에는 영향을 주지 않는다고 밝힙니다. 그 사용자가 어떤 테이블을 읽을 수 있는지는 여전히 DB 안의 GRANT가 정합니다. 통합은 저마다의 경계에서 멈춰 섰습니다.
물론 차이가 있습니다. AWS는 자기가 만든 API 계층에서는 통합을 결정할 수 있었지만, SSH와 Kubernetes와 MySQL은 주인이 다릅니다. 그래서 이 통합은 프로토콜 자체를 바꾸는 방식으로는 일어나지 않습니다. Terraform이 프로바이더 API를 바꾸지 않고 그 위에 하나의 언어를 얹었듯, 접근 계층도 각 프로토콜을 그대로 두고 그 위에서 하나의 역할로 표현하는 방식이어야 합니다. 조건 3은 새로운 원칙을 요구하는 게 아니라 이미 받아들여진 원칙을 적용하는 경계를 한 단계 넓히자는 것입니다.
없으면: 도입부의 다섯 단계가 그대로 남습니다. 조회 대상이 시스템에서 파일로 바뀔 뿐, 포맷이 다른 정의를 사람이 읽어 합치는 작업은 사라지지 않습니다. 프로토콜별로 코드화하는 것과 프로토콜을 가로질러 한 벌로 정의하는 것은 다릅니다.
4. 크리덴셜을 코드가 직접 발급해야 한다
코드로 선언된 역할이 접근할 때마다 단명 크리덴셜을 발급해야 합니다. 인증서든 토큰이든 동적으로 생성된 계정이든 형태는 상관없습니다. 크리덴셜을 코드로 관리하는 게 아니라, 코드가 크리덴셜을 그때그때 만들어내는 방식입니다.
새로운 발상은 아닙니다. AWS STS는 역할을 위임받아 임시 크리덴셜을 발급하는데, 기본 유효 시간은 1시간이고 필요하면 900초까지 좁힐 수 있습니다. 발급된 임시 크리덴셜은 만료되면 그것으로 끝이라 따로 보관하고 교체할 대상이 되지 않습니다. HashiCorp Vault의 동적 시크릿도 접근 시점에 자격 증명을 만들어냅니다. DB 시크릿 엔진의 기본 TTL은 1시간, 최대 24시간이고 역할 정의에서 더 좁힐 수 있으며, 만료되면 Vault가 생성했던 DB 사용자를 삭제합니다. SPIFFE는 CNCF 졸업 프로젝트로, 워크로드가 인증 시크릿을 미리 갖고 있지 않아도 실행 환경 검증을 통해 신원을 얻고 단명 인증서나 토큰을 받는 방식을 표준으로 정의했습니다. 정적인 장기 크리덴셜을 발급 시점에 만들어지는 단명 크리덴셜로 대체하는 흐름은 이미 여러 층위에서 진행되고 있고, 조건 4는 그 흐름을 인프라 접근에 적용하자는 것입니다.
여기서 갈리는 지점이 있습니다. 크리덴셜을 안전하게 보관하는 것과 크리덴셜이 애초에 존재하지 않는 것은 다릅니다. 비밀번호나 키를 볼트에 넣고 코드로 등록하는 방식은 크리덴셜을 코드로 관리하는 것이지 코드가 크리덴셜을 만들어내는 것이 아닙니다. 보관된 크리덴셜은 계속 존재하므로 로테이션 대상이 되고, 로테이션은 그 자체로 운영 부담과 실패 지점을 만듭니다. 발급 시점에만 생기고 만료되는 크리덴셜에는 보관할 것도 교체할 것도 없습니다.
없으면: 퇴사자 정리에서 한 단계가 빠지는 문제가 남습니다. 정적 크리덴셜이 존재하는 한 회수 대상 목록이 필요하고, 목록에서 빠진 항목은 계속 유효합니다. 발급 시점에만 생기고 TTL이 지나면 사라지는 크리덴셜에는 회수 목록을 관리할 일이 없습니다. 즉시 차단이 필요한 경우는 남지만, 그건 목록에서 빠뜨릴 수 있는 종류의 일이 아닙니다.
기존 도구는 네 조건을 어디까지 충족하나
앞서 세운 네 조건으로 기존 도구를 살펴봅니다. CyberArk, Cloudflare Access, HashiCorp Boundary 세 가지입니다. 비교를 위해 Teleport도 표에 함께 넣었고, 근거는 다음 섹션에서 다룹니다. 각 제품의 전체 기능을 평가하는 것이 아니라, 이 글이 정의한 AaC 조건에 대해서만 판정합니다.
| 조건 | CyberArk | Cloudflare Access | Boundary | Teleport |
|---|---|---|---|---|
| 1. 선언적 | 충족 | 충족 | 충족 | 충족 |
| 2. 모든 변경이 버전 관리 | 유보 | 유보 | 유보 | 유보 |
| 3. 프로토콜 가로지르는 단일 정의 | 미달 | 미달 | 미달 | 충족 |
| 4. 크리덴셜을 코드가 발급 | 충족 | 충족 | 조건부 | 충족 |
조건부는 외부 제품과 연동해야 충족한다는 뜻입니다. 유보는 제품 간 차이가 아니라 판정 자체를 보류했다는 뜻이고, 그 이유를 먼저 밝힙니다.
네 제품 다 Terraform 프로바이더를 제공하므로 변경을 코드로 선언하고 이력을 남길 수 있습니다. 그러나 조건 2가 요구하는 것은 그것만이 아니라 저장소 밖 변경이 반영되지 않는 것입니다. 네 제품 모두 웹 콘솔과 CLI로 직접 변경이 가능하고, Terraform은 그 변경을 다음 plan 실행 전까지 알지 못합니다. 지속적 조정을 수행하는 GitOps 방식을 얹으면 콘솔 변경이 되돌려지지만, 조정 주기 동안은 반영된 상태로 남습니다. 접근 권한에서는 그 몇 분이 의미를 갖습니다. 이는 특정 제품의 한계가 아니라 도구가 자기 관리 경로를 독점하지 못하는 데서 오는 문제입니다. 저장소를 유일한 입력 경로로 만드는 일은 제품 선택이 아니라 운영 규율과 권한 설계에 달려 있습니다. 뒤에서 다룰 Teleport도 예외가 아닙니다.
나머지 세 조건을 도구별로 살펴보겠습니다.
CyberArk: 단명 크리덴셜은 발급하되, 정책이 대상 유형별로 갈린다
Palo Alto Networks가 CyberArk를 인수했고, 2026년 5월 포트폴리오를 Idira로 리브랜딩했습니다. 전환은 단계적이라 제품 URL과 컴포넌트 이름은 아직 그대로이고, 이 글은 인지도를 고려해 CyberArk로 표기합니다.
CyberArk는 제품군마다 Terraform 프로바이더가 다릅니다. Secrets Hub 프로바이더(cyberark/cyberark)는 safe와 계정, secret store, 동기화 정책을 다루고, Secure Infrastructure Access(SIA)는 cyberark/idsec 계열입니다. 아래 판정은 SIA를 기준으로 합니다.
조건 4는 충족합니다. SIA는 두 가지 접근 방식을 제공하는데, ZSP 모드에서는 세션 시작 시 임시 사용자를 만들고 종료 시 삭제합니다. Linux 대상은 단명 SSH 인증서를 씁니다. 인증된 연결마다 CA 개인키로 인증서를 생성해 서명하고, 이 인증서는 수 분 내에 무효화됩니다. 같은 사용자가 같은 대상에 다시 접속해도 매번 새 인증서가 발급됩니다. 다만 ZSP가 적용되는 대상은 Windows, Linux, 데이터베이스 셋이고, Kubernetes는 볼팅 크리덴셜 경로로 접근합니다.
조건 3에서 갈립니다. SIA는 Windows, Linux, DB, Kubernetes를 커버하지만 정책을 대상 유형별로 만듭니다. 가상 머신 접근 정책과 DB 접근 정책이 별도 생성 절차를 갖습니다. 하나의 정의가 여러 프로토콜을 함께 담지 않습니다. 그리고 이 넷 밖의 대상은 SIA가 아니라 Privileged Session Manager, Secure Web Sessions, Secure Cloud Access 같은 별도 제품이 담당합니다.
Cloudflare Access: 단명 인증서를 쓰지만 대상이 SSH와 RDP에 머문다
조건 4는 충족합니다. Access for Infrastructure는 전통적 SSH 키를 Access 로그인 토큰 기반의 단명 인증서로 대체합니다. 관리자가 서버마다 공개키를 배포하고 그 키가 몇 년씩 남는 구조를 없앤 것입니다.
조건 3에서 갈립니다. 2024년 10월 발표 당시 Cloudflare는 SSH를 첫 기능으로 내놓으며 RDP, Kubernetes, DB 지원을 예고했습니다. 2026년 7월 현재 인프라 대상으로 지원되는 프로토콜은 SSH와 RDP입니다. 다만 RDP는 브라우저 기반 경로로 제공되고, 인프라 애플리케이션 문서는 여전히 SSH를 기준으로 쓰여 있습니다. 대상 모델 자체는 프로토콜 불가지론적으로 설계돼 Kubernetes 클러스터나 DB도 대상으로 등록할 수 있지만, 그 프로토콜로 접근을 제어하고 단명 크리덴셜을 발급하는 기능은 아직 SSH와 RDP에 한정됩니다.
HashiCorp Boundary: 역할이 프로토콜 내부 권한을 표현하지 못한다
조건 1은 충족합니다. HashiCorp Boundary 팀이 직접 유지 관리하는 공식 Terraform 프로바이더가 있고, boundary_scope, boundary_target, boundary_role 같은 리소스로 접근 구성을 선언할 수 있습니다.
조건 4는 조건부 충족입니다. Boundary는 0.12부터 Vault SSH Secrets Engine을 연동해 단명 SSH 인증서를 세션에 주입합니다. 주입된 크리덴셜은 사용자에게 노출되지 않고 세션 종료와 함께 소멸합니다. 다만 이 기능은 HCP Boundary 또는 Boundary Enterprise에서만 쓸 수 있고, Vault를 별도로 운영해야 합니다. Boundary 단독으로는 이 조건을 충족하지 못합니다. Vault 데이터베이스 시크릿 엔진을 연동하면 단명 데이터베이스 크리덴셜도 발급할 수 있습니다. 다만 TCP 타깃은 주입을 지원하지 않아 브로커링 방식이 되고, 크리덴셜이 사용자에게 노출됩니다.
조건 3에서 갈립니다. Boundary의 타깃 유형은 SSH, RDP, TCP 셋이고, 이 중 SSH와 RDP는 HCP Boundary 또는 Enterprise 전용입니다. 나머지는 모두 TCP 타깃으로 처리되는데, TCP 타깃은 연결 대상 서버의 세부 사항을 알지 못하고 정의된 주소와 포트만 인식합니다. DB든 Kubernetes 클러스터든 Windows 서버든 마찬가지입니다.
그래서 Boundary의 role이 표현하는 것은 "이 타깃에 세션을 열 수 있다"까지입니다. 이 DB에 readonly 사용자로, 이 클러스터에 이 그룹으로 같은 프로토콜 내부의 권한은 role에 담기지 않습니다. 조건 3이 요구하는 것은 프로토콜별 권한을 하나의 역할 정의에 담는 것이므로, Boundary는 여기서 갈립니다.
세션 녹화도 조건과 무관하지만 짚어둡니다. Plus 또는 Enterprise 에디션이 필요하고 현재 SSH 대상을 중심으로 지원됩니다.
정리
세 도구를 보고 나면 한 가지가 분명해집니다. 조건 4는 이미 이 분야의 기본값입니다. 단명 크리덴셜을 발급하는 것은 더 이상 차별점이 아닙니다. 다만 같은 ‘충족’ 판정이라도 폭이 다릅니다. CyberArk는 Windows와 Linux, DB에 걸쳐 제공하고, Cloudflare는 SSH에, Boundary는 Vault를 붙인 SSH에 머뭅니다.
갈리는 지점은 조건 3 하나입니다. 여러 프로토콜을 하나의 정의로 묶느냐. 세 도구는 대상 유형별로 정책이 갈리거나, 지원 프로토콜 범위가 좁거나, 역할이 프로토콜 내부 권한을 표현하지 못합니다.
앞서 조건 3이 이 글에서 유일하게 표준 없이 선례로부터 유추한 조건이라고 밝혔습니다. 그 조건이 실제로 도구를 갈라내는 유일한 축이라는 것은 조건 3을 세운 판단을 지지하는 결과이기도 하고, 동시에 이 기준이 특정 방향에 유리하게 작동한다는 뜻이기도 합니다. 판단은 읽는 분께 맡깁니다.
Teleport는 네 조건을 어떻게 충족하나
앞 표에서 Teleport는 조건 1, 3, 4를 충족하고 조건 2는 유보였습니다. 각각의 근거를 살펴봅니다.
조건 1과 3: 하나의 role 정의가 네 프로토콜을 담는다
kind: role
version: v7
metadata:
name: platform-engineer
spec:
allow:
# SSH: env=production + team=platform 레이블이 붙은 서버에만
node_labels:
env: production
team: platform
logins: [devops]
# Kubernetes: cluster-admins 그룹으로
kubernetes_groups: [cluster-admins]
kubernetes_labels:
env: production
# Database: readonly 사용자로, appdb·auditdb만
# db_names는 PostgreSQL, MongoDB, Cloud Spanner에서만 강제됩니다
db_users: [readonly]
db_names: [appdb, auditdb]
db_labels:
env: production
# App
app_labels:
env: production
options:
max_session_ttl: 8h # 인증서 유효 시간
require_session_mfa: true
role 버전은 v7을 씁니다. v8은 Kubernetes CRD 지원이 추가됐지만 모든 에이전트가 v18 이상이어야 해서 여기서는 널리 쓰이는 v7을 기준으로 합니다.
이 YAML이 조건 1과 3을 동시에 보여줍니다. 권한을 명령이 아니라 상태로 선언하고, 네 프로토콜의 권한을 한 파일에 담습니다.
주목할 것은 각 프로토콜의 권한이 그 프로토콜의 언어로 표현된다는 점입니다. SSH는 접속 계정(logins)으로, Kubernetes는 그룹(kubernetes_groups)으로, DB는 DB 사용자와 접속 가능한 DB 이름(db_users, db_names)으로 씁니다. 조건 3이 요구한 것이 이것입니다. 여러 대상에 세션을 열 수 있다는 선언이 아니라, 각 프로토콜 안에서 무엇을 할 수 있는지가 하나의 정의에 들어가는 것입니다.
포맷이 하나이고, 관리하는 곳이 하나입니다.

조건 2: git으로 관리하지만, 유일한 경로는 아니다
role YAML을 git 리포지터리에 넣고 tctl create -f로 적용합니다(tctl은 Teleport 관리자 CLI이고, -f는 기존 리소스를 덮어쓰는 force 플래그입니다). kubectl apply -f와 같은 패턴입니다.
# role 변경
$ git diff
- db_users: [readonly]
+ db_users: [readonly, readwrite]
# PR 리뷰 후 머지, CI에서 자동 적용
$ tctl create -f roles/platform-engineer.yaml
권한 변경이 PR로 남습니다. "이 DB 쓰기 권한이 왜 추가됐지"를 git log로 추적할 수 있습니다.
다만 앞서 밝힌 대로 조건 2는 유보입니다. Teleport는 tctl과 웹 UI로도 role을 직접 바꿀 수 있어서 저장소가 유일한 입력 경로가 되지는 않습니다. 이 구조를 만드는 것은 제품이 아니라 운영의 몫입니다. 무엇이 더 필요한지는 뒤에서 도입 단계를 다룰 때 짚겠습니다.
조건 4: role이 인증서를 직접 발급한다
tsh login을 실행하면(tsh는 Teleport 사용자 CLI입니다) 이렇게 진행됩니다.
- IdP(Keycloak, Google, Okta 등)로 사용자 인증. OIDC와 SAML 커넥터는 Enterprise 기능이고, 커뮤니티 에디션은 GitHub 커넥터와 로컬 사용자를 지원합니다
- 사용자의 role 확인
- Teleport 내장 CA가 해당 role 기반의 단명 인증서 발급
- 인증서 만료 시 접근 차단
$ tsh login --proxy=teleport.example.com
# IdP 인증 완료
$ tsh status
Logged in as: kyle@infograb.net
Roles: platform-engineer, base-viewer
Valid until: 2026-07-22 18:00:00 +0900 KST ← role의 max_session_ttl
비밀번호나 SSH 키가 서버에 존재하지 않습니다. authorized_keys가 없습니다. role 코드가 곧 접근의 생명주기를 정의합니다.
인증서 만료만으로 부족한 경우도 있습니다. 사고 대응처럼 즉시 차단이 필요하면 사용자나 세션에 lock을 적용합니다. lock을 걸면 해당 대상의 진행 중인 세션이 종료되고, lock이 유효한 동안 새 세션도 거부됩니다.
레이블이 인프라 코드와 접근 코드를 잇는다
조건 3과 4를 충족해도 남는 일이 있습니다. 새 서버가 생길 때마다 누가 접근할지 정하는 일입니다. Teleport는 이것을 레이블로 처리합니다.
레이블이 붙는 경로는 여럿입니다. 에이전트 설정에 직접 선언할 수도 있고, 클라우드 태그에서 자동으로 가져올 수도 있습니다. 아래는 AWS EC2 태그를 자동으로 가져오는 경우입니다.
# Terraform으로 서버 생성 시
resource "aws_instance" "api_server" {
tags = {
env = "production"
team = "platform"
sensitivity = "internal"
}
# 인스턴스 메타데이터에서 태그를 읽을 수 있게 활성화
metadata_options {
instance_metadata_tags = "enabled"
}
}
Teleport 에이전트는 시작할 때 인스턴스 메타데이터에서 태그를 읽어 노드 레이블로 등록하고, 이후 한 시간마다 갱신합니다. 이때 레이블에는 aws/ 접두사가 붙습니다. 위 태그는 aws/env, aws/team, aws/sensitivity가 됩니다.
role은 이 이름으로 매칭합니다.
spec:
allow:
node_labels:
aws/env: production
aws/team: platform
서버가 생기는 순간, role에 정의된 사람들에게 접근 권한이 생깁니다. 권한을 따로 추가할 필요가 없습니다.
인프라 코드와 접근 코드가 레이블을 매개로 연결됩니다. 서버 목록과 권한 목록을 사람이 맞춰볼 일이 사라집니다.
Teleport로 실제로 달라지는 것들
앞서 네 조건을 세우고 Teleport가 어떻게 충족하는지 봤습니다. 이 조건들이 실무에서 무엇을 바꾸는지, 그리고 조건과 별개로 무엇이 더 얹히는지 나눠서 봅니다. 이하 내용은 Teleport 기준입니다.
조건 4가 만드는 변화: 온보딩과 오프보딩
도입부에서 퇴사자 정리 과정에 한 단계가 빠지는 문제를 봤습니다. 정적 크리덴셜이 존재하는 한 회수 대상 목록이 필요하고, 목록에서 빠진 항목은 계속 유효하기 때문입니다.
Teleport에서는 회수할 크리덴셜 목록이 없습니다. 신규 입사자는 IdP에 계정을 만들고 role 할당 PR을 머지하면 끝입니다. 퇴사 시 IdP 계정을 비활성화하면 새 인증서를 받을 수 없고, 이미 발급된 인증서는 설정된 TTL 후 만료됩니다.
다만 TTL이 남아 있는 구간은 존재합니다. 즉시 차단이 필요하면 사용자 lock을 함께 적용해 진행 중인 세션까지 끊습니다.
호스트 사용자 자동 생성을 쓰는 경우는 예외입니다. Teleport 문서가 권장하는 keep 모드는 세션이 끝나도 대상 서버에 사용자와 홈 디렉터리를 남깁니다. 접근 경로는 Teleport가 막지만 계정 자체는 남으므로, 이 부분은 별도 정리 절차가 필요합니다.
조건 1, 2, 3이 만드는 변화: 권한 감사
도입부의 다섯 단계는 권한이 프로토콜별로 흩어져 있어서 생겼습니다. 조건 3이 그것을 하나의 role 정의로 묶고, 조건 1이 그 정의를 읽을 수 있는 상태로 만들고, 조건 2가 변경 이력을 남깁니다.
그래서 "이 사람이 프로덕션의 어디까지 접근할 수 있나"에 답하는 일이 리포지터리 안에서 끝납니다. 사용자에게 할당된 role을 확인하고 그 role의 정의를 읽으면 됩니다. 사용자가 role을 여럿 가지면 권한은 합쳐지고, deny 규칙이 있으면 그쪽이 우선합니다. 그 권한이 언제 왜 생겼는지는 git blame으로 이어집니다.
여러 시스템을 오가던 다섯 단계 중 네 단계가 한 저장소 안의 조회로 바뀝니다. AWS IAM처럼 클라우드 제공자가 자체 권한 체계를 갖는 영역은 남습니다. Teleport의 aws_role_arns는 어떤 IAM role을 assume할 수 있는지를 정할 뿐, 그 role이 무엇을 할 수 있는지는 여전히 AWS IAM이 정합니다.
조건 밖에서 얹히는 것: 긴급 접근
여기서부터는 네 조건이 요구하는 범위가 아닙니다. 조건을 충족한 도구가 그 위에서 무엇을 더 할 수 있는지 보여주는 예입니다.
Teleport의 Access Request는 필요한 순간에만 임시로 권한을 부여하는 Just-in-Time 접근을 role에 정의하게 해줍니다. Just-in-Time Access Request는 Enterprise 기능이고, 커뮤니티 에디션에서는 CLI로 role을 요청하는 형태만 쓸 수 있습니다.
# 새벽 2시 온콜 상황에서 production DB 긴급 접근이 필요한 예시
$ tsh request create \
--roles=db-admin \
--reason="production DB OOM 대응, 온콜 티켓 #2847" \
--max-duration=2h
reviewer에게 알림이 가고(Slack·Jira 등 외부 채널 알림은 별도 플러그인 설정이 필요합니다), 승인되면 제한된 시간 동안 고권한 role을 사용할 수 있습니다. 실제 지속 시간은 요청 값과 role에 설정된 상한 가운데 짧은 쪽을 따르고, 요청한 role의 max_session_ttl이 그보다 먼저 만료되면 Teleport가 만료 시각을 거기에 맞춥니다. max_duration 자체의 상한은 14일이지만, 운영 환경에서는 필요한 최소 시간으로 제한하는 것이 안전합니다.
만료 후 재접근이 필요하면 새 Access Request를 만들어야 하며, 요청과 검토 기록은 Audit Log에 남습니다.
이 워크플로 자체도 role에 선언됩니다.

options:
max_session_ttl: 1h
request_access: reason
request_prompt: "긴급 접근 사유를 구체적으로 작성하세요"
allow:
request:
roles: [db-admin]
max_duration: 2h
thresholds:
- approve: 1
deny: 1
request_access와 thresholds는 Enterprise 전용 필드입니다.
조건 밖에서 얹히는 것: 세션 감사
Teleport는 SSH와 Kubernetes 세션을 녹화하고, 지원되는 데이터베이스 프로토콜의 쿼리 이벤트를 Audit Log에 남깁니다. 특정 기간의 프로덕션 접근 주체와 활동을 중앙에서 조회할 수 있습니다.
조건 2가 "누가 이 권한을 언제 왜 부여했는가"를 답한다면, 세션 감사는 "그 권한으로 실제 무엇을 했는가"를 답합니다. 둘이 붙어야 감사 요구가 온전히 충족됩니다.


Teleport로 AaC를 시작하는 법
전부 한 번에 바꿀 필요는 없습니다. 오히려 한 번에 바꾸려다 멈추는 경우가 많습니다. 접근 경로를 바꾸는 일은 사람들의 일상 작업 흐름을 건드리기 때문에, 되돌릴 수 있는 단위로 쪼개는 편이 안전합니다.
아래 순서는 의존 관계를 따릅니다. 레이블이 없으면 role을 선언할 수 없고, 접근 경로가 Teleport를 거치지 않으면 role이 적용될 대상이 없습니다. 그래서 레이블, 경로, 코드, 통합 순입니다.

1단계: 레이블 체계 설계
Teleport를 도입하기 전에 인프라의 레이블 체계를 먼저 설계합니다. env, team, cloud, sensitivity 같은 키 체계를 지금 운영 중인 서버, 클러스터, DB에 붙이는 것부터 시작합니다. 이 이름이 role YAML의 매칭 기준이 됩니다.
먼저 정할 것이 하나 있습니다. 레이블을 에이전트 설정에 직접 선언할지, 클라우드 태그에서 자동으로 가져올지입니다. 자동 임포트를 쓰면 레이블 키에 접두사가 붙어 role의 매칭 이름이 달라지므로, role을 쓰기 전에 정해야 합니다.
2단계: SSH부터 Teleport로 전환
서버에 Teleport 에이전트를 설치하고 SSH 접근을 Teleport를 통하도록 전환합니다. 기존 authorized_keys는 전환 기간 유지하다가 점진적으로 제거합니다.
이 기간에는 두 경로가 공존합니다. 즉 감사 로그도 두 곳에 남고, 아직 제거하지 않은 키로 우회할 수 있습니다. 전환 완료 기준을 미리 정해두는 편이 좋습니다.
3단계: role을 git으로 관리
role YAML을 git 리포지터리에 넣고, PR 승인을 거친 변경만 CI/CD에서 tctl create -f로 적용합니다. 저장소를 실제 상태의 기준으로 삼으려면 삭제와 저장소 밖 변경을 감지·조정하는 절차도 필요합니다.
저장소를 유일한 일반 변경 경로로 만들려면 웹 UI나 tctl을 막는 것이 아니라, CI 계정 외의 사용자와 봇에서 role의 생성·수정·삭제 권한을 제거해야 합니다. 저장소와 CI 설정의 변경 권한도 함께 통제해야 합니다.
그래도 자체 호스팅 클러스터에는 예외가 남습니다. Auth Service 호스트에서 로컬 관리자 권한으로 tctl을 실행할 수 있는 사람은 전체 권한을 얻을 수 있으므로, 해당 호스트와 설정·백엔드에 대한 접근도 제한해야 합니다. 조건 2를 제품이 아닌 운영의 몫이라고 한 이유가 여기 있습니다.
4단계: 프로토콜을 하나의 role로 묶기
SSH 접근이 안정되면 Kubernetes, DB, 애플리케이션 접근을 role 정의에 추가합니다. 프로토콜별로 role을 나누기보다, 같은 직무와 책임에 속한 여러 프로토콜의 권한을 하나의 role에 담는 것이 요점입니다. 이 단계에서 이 글의 조건 3이 완성됩니다.
max_session_ttl을 짧게 설정하면 발급된 인증서의 노출 가능 시간을 줄일 수 있지만 재인증 빈도는 높아집니다. 인증서 만료 시 진행 중인 세션도 종료하려면 disconnect_expired_cert를 함께 설정해야 합니다.
맺음말
코드로 만든 인프라에, 코드로 관리하는 접근. 이제 자연스러운 다음 단계입니다.
접근 권한을 코드로 선언하고 PR로 검토하며, 하나의 역할로 여러 프로토콜의 권한을 표현하고, 그 역할에 따라 단명 크리덴셜을 발급하는 것. 이 글에서 세운 네 가지 조건으로 보면 Teleport는 이 구조를 폭넓게 구현한 도구입니다. 다만 저장소를 유일한 변경 경로로 만드는 일까지 제품이 대신해 주지는 않습니다. git과 CI/CD의 권한, 우회 접근 경로, Auth Service 운영까지 함께 통제해야 AaC가 실제 운영 방식이 됩니다.
“접근 권한도 git blame으로 추적할 수 있어야 한다.” 여기에 “실제 상태도 그 저장소와 일치해야 한다”는 조건까지 자연스럽게 받아들여진다면, AaC를 시작할 준비가 된 것입니다.
흩어진 접근 권한을, 검토 가능한 운영 체계로
시스템마다 따로 관리하던 권한을 Teleport와 Git 기반 흐름으로 하나의 역할 체계에 연결하세요.
참고 자료
- "Configure Teleport", Teleport Documentation, https://goteleport.com/docs/admin-guides/infrastructure-as-code/[](https://goteleport.com/docs/admin-guides/infrastructure-as-code/%5C)
- "Teleport Role Templates", Teleport Documentation, https://goteleport.com/docs/zero-trust-access/rbac-get-started/role-templates/[](https://goteleport.com/docs/zero-trust-access/rbac-get-started/role-templates/%5C)
- "Teleport Role Reference", Teleport Documentation, https://goteleport.com/docs/reference/access-controls/roles/[](https://goteleport.com/docs/reference/access-controls/roles/%5C)
- "Configure Access Requests", Teleport Documentation, https://goteleport.com/docs/identity-governance/access-requests/access-request-configuration/[](https://goteleport.com/docs/identity-governance/access-requests/access-request-configuration/%5C)
- "Database Access with Self-Hosted MySQL or MariaDB", Teleport Documentation, https://goteleport.com/docs/enroll-resources/database-access/enrollment/self-hosted/mysql-self-hosted/[](https://goteleport.com/docs/enroll-resources/database-access/enrollment/self-hosted/mysql-self-hosted/%5C)
- "Non-HTTP Applications", Cloudflare One Docs, 2026-04-21, https://developers.cloudflare.com/cloudflare-one/access-controls/applications/non-http/[](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/non-http/%5C)
- "Session Recording", HashiCorp Boundary Documentation, [https://developer.hashicorp.com/boundary/docs/session-recording](https://developer.hashicorp.com/boundary/docs/session-recording%5C)
- "Terraform Patterns for Boundary", HashiCorp Boundary Documentation, https://developer.hashicorp.com/boundary/docs/install-boundary/terraform-patterns[](https://developer.hashicorp.com/boundary/docs/install-boundary/terraform-patterns%5C)
- "CyberArk Identity Security Terraform Provider", Terraform Registry, https://registry.terraform.io/providers/cyberark/idsec/latest/docs[](https://registry.terraform.io/providers/cyberark/idsec/latest/docs%5C)
Kyle
AI Platform Engineer
InfoGrab의 AI Platform Engineer로서, 클라우드와 Kubernetes 인프라 위에 AI를 얹어 운영 방식 자체를 바꾸는 일을 합니다. 사람이 반복하던 운영·관리 업무를 다양한 LLM과 에이전트가 대신 처리하도록 구조를 설계하고, 인프라 운영을 자동화해 도구화하는 데 강점이 있습니다. 흩어진 작업과 정보를 에이전트가 추적·실행할 수 있는 형태로 체계화하며, 인프라를 단단한 토대로 두고 그 위에서 AI가 실제 운영 문제를 푸는 환경을 만드는 방식으로 일합니다.
이 저자의 글 모두 보기 →이 글이 도움이 되셨나요?
인포그랩 전문가가 맞춤 상담을 도와드립니다.
관련 글

Teleport 기능 톺아보기 1탄(SSO, Access Requests편)
Teleport는 인프라 접근 관리 도구로, 조직이 멤버 관리, 인프라 자산 관리, 권한 관리, 보안 준수의 복잡성을 해소하고, 시간을 아끼도록 지원합니다. 특히 SSO와 Access Requests 기능은 편리한 로그인, 유연한 권한 권리에 도움이 됩니다. 이 글은 Teleport의 Enterprise Edition과 Community Edition 주요 기능, SSO와 Access Requests 이용 방법을 다뤘습니다.

인프라 접근 관리 도구 Teleport 알아보기
Teleport는 인프라 접근 관리 도구로 SSH, Windows 접속, Kubernetes 클러스터, DBMS 접속, 웹 애플리케이션에 더 쉽게 접근하도록 설정하는 기능을 지원합니다. 조직에서 멤버 관리, 인프라 자산 관리, 권한 관리, 보안 준수 등의 복잡성을 해소하고, 시간을 아끼고 싶다면 이 도구를 사용해볼만 합니다. 인포그랩에서는 사내에 Teleport를 도입해 인프라 접근 관리의 안전성과 효율성을 높였는데요. 이 글에서는 Teleport 기능과 동작 방식, 활용 방법, 인포그랩의 도입 사례를 살펴봤습니다.

Claude Code Routines로 DevOps PR 리뷰·의존성 점검 자동화하기
Routines는 Claude Code가 정해진 시점이나 이벤트에 따라 Anthropic 클라우드에서 자동으로 작업을 시작하고 결과를 전달하는 기능입니다. 추론이 필요한 반복 작업을 사람 개입 없이 자동으로 처리합니다. 이 글은 Routines의 개념과 동작 방식, n8n과 차이점을 다뤘습니다. 또 DevOps 워크플로용 3가지 실습으로 Routines의 실무 활용법과 운영 시 유의 사항을 살펴봤습니다.