Teleport를 활용한 제로트러스트 아키텍처 설계

프로젝트 개요

Teleport를 활용하는 OSS 공모전에 참가했었다. 아쉽게 결과는 내지 못했지만, 사용했던 오픈소스 프로그램인 Teleport가 공부할만한 가치가 있었기 때문에 활용해 보았다.

이 프로젝트에서는 pearl-invest라는 가상의 MSA기반 핀테크 앱을 기반으로 Teleport를 도입해 제로트러스트 원칙을 준수하고 RBAC를 구현할 수 있도록 했다.

v1.0의 문제점

현 버전으로 개선하기 전의 빌드에서는 문제가 있었다.

1 Pod = 1 Container
├── Application (Node.js API)
├── NGINX (Reverse Proxy)
└── Teleport Agent (Remote Access)

supervisor로 여러 프로세스를 관리했지만, 이 접근 방식은 여러 문제를 야기했다:

  1. 단일 책임 원칙 위배: 하나의 컨테이너가 너무 많은 역할을 담당
  2. 리소스 낭비: 각 백엔드 서비스마다 NGINX와 Teleport Agent가 중복 실행

이 빌드에서 원하는 방식대로 실행은 가능했으나, Teleport가 권장하는 방식이 아니었다.

v2.0 아키텍처 설계

핵심 설계 원칙

  1. 물리적 분리: 프론트엔드는 고성능 미니PC, 백엔드는 라즈베리파이
  2. 역할 분리: 각 컴포넌트는 단일 책임만 가짐
  3. 제로 트러스트 보안: Teleport를 통한 중앙화된 접근 제어

프론트엔드

프론트엔드는 next.js를 사용하여 빌드했다.

version: '3.8'
services:
  frontend:
    build:
      context: ./frontend
    container_name: pearl_frontend
    restart: always
    
  nginx:
    image: nginx:alpine
    container_name: pearl_nginx_proxy
    restart: always
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf

위와 같은 docker compose를 사용했고, nginx컨테이너를 리버스 프록시로 배치했다.

백엔드

https://github.com/grademe12/Pearl-Invest/tree/main

위 깃 레포지토리의 main 브랜치와 같이 구성했다. 백엔드는 k8s를 사용해 실제 서비스처럼 구현해 보려고 했다. k8s의 다양한 기능들을 사용해보지는 못해 아쉬움이 남는다. 팀 단위로 프로젝트를 진행하게 된다면 반드시 사용해보고 싶은 부분이다.

Teleport 클러스터

Teleport 클러스터는 AWS EC2에 배치하였다. Elastic IP를 적용하고 Route 53에서 저렴한 도메인을 하나 구매해, 전체 인터넷에서 접근이 가능하도록 구성했다.

curl https://cdn.teleport.dev/install.sh | bash -s ${TELEPORT_VERSION?} ${TELEPORT_EDITION?}

EC2에서 위 명령어로 Teleport commuity edition을 설치해주면 된다. Teleport가 웹 UI를 모두 제공할 것이다. 생각보다 무거운 프로그램이기 때문에 free tier인 t2 micro 로는 부족함이 있었다.

Teleport agent

Teleport agent는 백엔드 k8s 클러스터에 별개의 포드로 설치되었다. 프로젝트 초반에는 커스텀 Dockerfile을 작성하기 위해 많은 노력을 했으나 쉽지 않았다. 과정에서 Helm을 알게됐고, Teleport에서 지원하는 것도 알게 됐다. 웹 UI에서도 k8s agent로 쉽게 설치 할 수 있도록 안내를 하고 있어, 이를 활용하였다.

RBAC 통합하기

이번 프로젝트에서 가장 핵심적인 부분이다. 이전 버전 빌드에서는 각 포드별로 1개씩 Teleport agent가 배치됐기 때문에, 각 리소스마다 할당된 태그를 기반으로 Teleport 계정이 접근할 수 있는 리소스를 관리했다.

이번 빌드에서는 단일 Teleport agent를 활용해, 접속한 Teleport 계정이 할당된 Role에 따라 접근 가능한 포드를 미리 배정받는 형태로 빌드하였다.

# Teleport Agent용 ServiceAccount 권한
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: teleport-impersonator
rules:
- apiGroups: [""]
  resources: ["users", "groups", "serviceaccounts"]
  verbs: ["impersonate"]
- apiGroups: [""]
  resources: ["pods", "pods/exec", "pods/log"]
  verbs: ["*"]
# 실제 사용자 그룹 권한
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: auth-api-permissions
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch", "create"]
- apiGroups: [""]
  resources: ["pods/exec"]  # subresource 별도 정의 필수!
  verbs: ["create", "get"]

1. Kubernetes Subresource 권한 설정

pods/exec와 같은 subresource는 반드시 별도로 정의해야 한다:

# ❌ 잘못된 예시
resources: ["pods", "pods/exec"]
verbs: ["*"]

# ✅ 올바른 예시
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["pods/exec"]  # 별도 rule로 분리
  verbs: ["create", "get"]

2. Impersonation 개념 이해

Teleport Agent는 ServiceAccount로 실행되지만, 실제 사용자 권한으로 “impersonate”하여 요청을 전달한다. 이 때문에 Agent SA에는 impersonate 권한이 필수다.

3. 라벨 기반 접근 제어 한계

Pod 라벨 기반 접근 제어가 예상대로 작동하지 않았다. 대신 Pod 이름 패턴 매칭을 사용했다:

# 작동하지 않음
resourceNames: ["app=auth-api"]

# 작동함
resourceNames: ["auth-api-*"]

마무리

Teleport라는 oss를 처음 사용해봤고, 참고할만한 자료도 많지 않아서 쉽지 않았다. 특히 RBAC구현을 구체적으로 어떻게 해야 하는지도 막막했고, k8s도 잘 몰랐기에 권한관리를 어떻게 해야 하는지도 잘 몰랐다. 처음에 구상했던 대로 구현을 성공한 지금도 제한적인 기능만을 사용했기 때문에 여전히 어려운 점은 있지만 그럼에도 불구하고 배운게 많은 프로젝트였다. 앞으로 k8s학습에 더 힘을 써야 할 것 같다.

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다