Security

보안 계정을 처음부터 다시 설계했습니다.

Okta·VPN·TGW로 세운 Zero Trust 기반

김용현
·
# AWS# Zero Trust# Okta
보안 계정을 처음부터 다시 설계했습니다.
date
slug
author
status
tags(최대 3개)
summary
type
thumbnail
category
updatedAt
TL;DR
  • AWS 계정이 늘어나는 동안 인증·네트워크·서버 접근의 기준이 계정마다 따로 자랐습니다.
  • 보안 전용 AWS 계정을 새로 만들어 네트워크·인증·배포의 기준면으로 삼았습니다. Transit Gateway로 계정을 잇고, Okta로 사람을, QueryPie로 데이터를 하나의 관문 뒤에 두었습니다.
  • 서버에 사람이 들어가는 일을 예외로 만들었습니다. SSH는 없앴고, 남은 접근은 인프라팀만 SSM으로 감사로그를 남깁니다.
 
notion image

1. 이 권한, 누가 열 수 있어요?

한동안 같은 장면이 반복됐습니다. 누군가 특정 리소스에 접근해야 한다고 요청하면, 담당자는 그 권한이 어느 계정의 어떤 경로에 걸려 있는지부터 확인했습니다. IAM 정책과 네트워크 경로를 보고, 서버라면 접속 방법을 따로 챙겼습니다. 필요하면 임시로 열었다가 나중에 닫았습니다.
문제는 이 확인이 매번 필요했다는 점입니다. 계정이 늘어나는 동안 사람을 인증하는 축, 네트워크로 들여보내는 축, 서버에 직접 붙는 축이 서로 다르게 움직였습니다. 세 축이 정리되지 않으니 누가 무엇에 닿을 수 있는가라는 질문의 답이 한곳에 모이지 않았습니다. 그 지도를 머릿속에 들고 있는 소수의 사람들에게 요청이 몰렸습니다.
느린 건 권한 발급만이 아닙니다. 사고가 났을 때 요청자가 닿는 범위를 답하는 데도 시간이 걸렸습니다. 추적성이 흐리다는 건 감사와 차단이 항상 늦는다는 뜻입니다. 서비스는 잘 돌아갔지만, 흔들린 건 서비스가 아니라 권한을 다루는 방식이었습니다.
ISMS 심사도 같은 지점을 짚었습니다. 개별 항목을 하나씩 땜질하는 대신, 인프라팀은 세 축을 한 계정으로 모으는 기반부터 다시 세우기로 했습니다.
notion image

2. 전용 계정을 택한 이유

방법은 두 갈래였습니다. 운영 계정에서 보안 통제까지 얹는 길과 보안을 위한 전용 계정을 새로 만드는 길입니다.
버린 대안부터 말씀드리면 당장은 운영 계정에 얹는 쪽이 빨랐습니다. 그러나 통제와 감사를 한곳에 모으려면 다른 워크로드와 섞이지 않아야 했습니다. 보안 로그와 권한이 서비스 트래픽·리소스와 뒤엉키면, 지금 문제를 형태만 바꿔 물려받는 셈이었습니다. 계정 경계 자체가 통제의 단위가 되지 못하면, 그 위에 무엇을 세워도 경계가 흐려집니다.
그래서 전용 보안 계정을 새로 만들었습니다. 판단 기준은 세 가지입니다.
  • 감사 경계: 보안 관련 리소스와 로그가 한 계정에 모이면 누가 무엇에 접근했는지를 한곳에서 봅니다.
  • 영향 범위: 운영 워크로드와 분리해 보안 계정의 변경이 서비스로 번지지 않게 합니다.
  • 기준면: 이 계정을 네트워크·인증·배포의 중심 기준면으로 삼아 정책을 한 번만 정의하고 연결된 계정에 닿게 합니다.
핵심은 이 계정이 단순히 보안 기능을 모아두는 창고가 아니라는 점입니다. 인증·네트워크·권한 위임의 기준을 한 계정에서 통제할 수 있어야 다음 확장이 가능했습니다.

3. 네트워크: 하나의 허브로 통합

가장 먼저 네트워크를 모았습니다. 중심은 보안 계정에 둔 Transit Gateway 허브입니다. 허브를 가운데 두고 운영, 개발, 테스트 계정의 VPC를 스포크로 붙였습니다. 허브와 스포크는 서로의 대역을 라우팅하도록 연결하고, 이름 해석을 위한 리졸버와 트래픽을 거르는 보안 그룹을 함께 둡니다.
계정을 잇는 방법은 여러 가지입니다. VPC를 서로 직접 잇는 방식(Peering)이나 계정마다 별도 경유 서버(Bastion)를 두는 방식도 있었습니다. 둘 다 연결이 적을 때는 단순하지만, 계정과 망이 늘어날수록 연결의 수와 규칙이 곱셈으로 불어납니다. 저희에게 필요했던 건 연결이 늘어도 운영 규칙이 무너지지 않는 구조였습니다. 이 때 허브-스포크는 라우팅을 한곳에서 일관되게 잡고 중앙에서 정책을 집행할 수 있습니다.
notion image
망은 엔지니어 접근면, 일반 업무면, 외부 협력사면, 게스트면으로 역할에 따라 분리했습니다. 각 면은 무엇을 허용하고 무엇을 차단하는지가 다릅니다. 엔지니어링 리소스(AWS 등)는 엔지니어 접근면에서, 그것도 인증과 그룹별 권한을 받은 사람만 특정 서비스에 닿습니다. 업무면은 엔지니어링 리소스로의 경로 자체를 갖지 않습니다. 협력사와 게스트면은 필요한 접점만 열고 나머지는 닫습니다. 망을 나눈 목적은 여러 개로 쪼개는 데 있지 않고, 실수나 침해가 한 면을 넘어 번지지 못하게 하는 데 있습니다.
사내외 진입은 하나의 관문으로 모았습니다. 내부망은 사내 VPN을 거쳐 허브로 들어오고, 이 VPN 인증은 Okta와 접근 제어(NAC)에 묶입니다. 사무실 밖에서 들어올 때도 예외는 없습니다. VPN 클라이언트와 Okta를 함께 통과해야 접근이 열리고, 그 이력은 남습니다.

4. 접근: 예외로 전환

네트워크를 모아 사람과 서버 쪽에서 같은 원리를 적용했습니다. 목표는 접근을 어렵게 만드는 게 아니라 접속 행위를 예외로 두고 표준 경로를 기본값으로 바꾸는 것입니다.
표준 경로를 기본값으로, 사람이 서버에 직접 들어가는 일은 예외
다섯 축에 같은 원리를 적용했습니다.
  • 사람 → Okta 하나로 인증
  • 데이터 → QueryPie 한 경로
  • 서버 → SSH 폐지, SSM만 예외
  • 노드 → Bottlerocket 불변 OS
  • 권한 → 소유권 기반 위임 + 감사로그

사람: 하나의 인증으로 통합

모든 엔지니어가 각자의 접근 키와 비밀번호를 따로 관리하던 구조에서는 일관된 권한 관리가 어려웠습니다. 개인마다 발급하던 IAM 사용자와 개별 VPN 계정을 걷어내고, 로그인을 Okta 하나로 모았습니다. AWS 계정 접근, 배포 도구, 관측 도구, DB 접근 도구까지 여러 앱을 SSO로 묶었습니다. 인증은 비밀번호와 2차 인증을 함께 요구합니다. 계정을 발급해 달라거나 비밀번호를 모른다는 요청이 사라졌고, 사람이 인증받는 문은 하나가 됐습니다.

데이터: 하나의 통제 경로

모든 DB 접근은 QueryPie를 지납니다. 서버에 직접 붙거나 우회하는 경로는 막았고, 권한은 필요한 사람에게 필요한 범위만 내줍니다. 익숙한 지름길을 닫으면 처음엔 불편합니다. 그래서 표준 경로를 먼저 매끄럽게 만든 뒤에 옛길을 닫았습니다.

서버: 사람이 들어가지 않는 구조

SSH 포트는 운영 환경에서 걷어냈습니다. 남은 접근은 인프라팀만 SSM을 거쳐야만 가능하고, 모든 명령은 언제 누가 무엇을 했는지가 감사로그에 남습니다.
예전에 Shell로 하던 일은 표준 경로로 옮겼습니다. 관측 도구(Datadog)로 로그를 관찰하고, 배포는 GitOps(ArgoCD)와 점진 배포(Argo Rollouts)로 일원화했습니다. 개발자는 서버를 만질 일이 없습니다.
GitHub CI로 컨테이너 이미지를 만들면 배포는 알림으로 경과만 확인하면 됩니다. 서버로 밀어 넣던(push) 방식에서 끌어가는(pull) 방식으로 배포를 바꾼 결과입니다. 레거시 EC2가 남아 있는 구간은 인프라팀만 SSM으로 접근하고 이력을 남깁니다.

노드: Bottlerocket 불변 OS

그래서 안전하다는 선언보다 왜 안전에 유리한지가 중요합니다. Bottlerocket은 루트 파일시스템이 읽기 전용인 불변 이미지라 노드에 들어가 손대는 관리면 자체가 작습니다. 매 순간 패키지를 깔고 고치는 과정에서 생기는 드리프트가 줄고 노드는 Karpenter가 필요에 따라 세우고 걷어냅니다.
오랜 기간 운영해 보니 골든 이미지를 만드는 경우가 아니라면 사람이 노드에 직접 들어갈 일이 없었습니다. 보안 관점에서도 살아 있는 서버를 사람이 드나드는 것보다 운영체제를 정적으로 분석하는 편이 낫습니다. 들어갈 문이 없으면 그 문으로 생기는 위험도 없습니다.

권한: 소유권 기반 위임

인프라팀을 제외한 모든 사람은 운영 환경에서 조회 권한만 갖습니다. 대신 각 조직이 소유한 리소스와 서비스에는 운영 권한을 통째로 위임했습니다. 통제를 위해 권한을 조이는 대신 소유한 범위 안에서는 마음껏 움직이고 그만큼 책임을 갖습니다.
넓게 위임할 수 있었던 건 추적성이 뒷받침하기 때문입니다. 사람은 Okta로 인증하고 AWS에서의 행위는 CloudTrail에, DB 접근은 QueryPie에, 망 접근은 VPN에 남습니다. 인증부터 행위까지의 경로가 로그로 이어지니 넓게 열어도 사후에 언제든 되짚습니다.

5. 함정과 교정

기반 공사에는 함정이 있었습니다. 그 함정이 서비스로 번지지 않게 하는 것이 이 설계의 목적입니다.
허브에 계정들을 붙이는 과정에서 한 테스트 계정의 사내망 접근이 어긋난 구간이 있었습니다. 새 경로를 잡는 도중, 그 계정으로 들어가는 내부 연결이 의도대로 서지 않았습니다. 중요한 건 이 문제가 테스트 계정 안에 갇혔고, 서비스에는 영향이 없었습니다. 운영 워크로드와 계정을 분리해 둔 설계가 기반 공사 중의 시행착오를 프로덕션 밖에서 소화했습니다.
교정은 화려하지 않았습니다. 공유 자원일수록 한 번에 바꾸지 않는 것이 가장 빠른 길이었습니다. 영향 범위를 좁게 잡고 순차로 적용하며, 매 단계 연결과 이름 해석을 확인했습니다. 계정 경계가 곧 실험의 안전지대였습니다.

6. 달라진 점

가장 큰 변화는 특정 지표가 아니라 일하는 방식입니다. 진입점을 축마다 하나로 모으자 통제가 한 화면 안으로 들어옵니다.
영역
이전
이후
사람 인증
엔지니어 50여 명이 개인 IAM 사용자·개별 VPN 계정, 권한 부여·회수가 제각각
Okta SSO 11개 앱 + 2차 인증, 개인 IAM 사용자 폐지
서버 접근
필요하면 서버에 직접 접속
SSH 폐지, 남은 접근은 인프라팀만 SSM + 전 과정 감사로그
배포
서버로 밀어 넣는(push) 방식
GitHub CI + GitOps로 끌어가는(pull) 방식, 알림으로 경과 확인
데이터 접근
경로가 여럿
QueryPie 단일 경로
노드 운영
사람이 들어가 손봄
Bottlerocket 불변 OS + Karpenter 위임, 접근 문 최소화
권한
필요할 때 직접 부여, 회수 지연
소유권 기반 위임 + 인증→행위 감사로그로 추적
숫자로도 드러납니다. 개인 엔지니어 IAM 사용자는 개발·운영 계정에서 합쳐 54개를 폐지했고 잔존은 없습니다(롤백용 스냅샷만 보관). 사람 인증은 11개 앱을 하나의 SSO와 2차 인증 뒤로 모았고, DB 접근은 QueryPie 한 경로에서 111개의 연결과 52명의 사용자를 통제합니다. 서버로 직접 붙는 관행은 감사로그가 남는 예외로 바뀌었습니다.
작은 사례 하나가 이 변화를 잘 보여줍니다. 예전이라면 잠깐 부여됐다가 조용히 남았을 개인 권한이, 지금은 점검에 걸려 표준 권한으로 되돌아옵니다. 권한이 한곳에 모여 있으니, 남으면 안 되는 것이 잘 보입니다.

7. 마치며

보안은 무언가를 막는 일처럼 보이지만, 실제로는 기반을 다지는 일에 가깝습니다. 전용 계정과 허브는 그 자체로 자랑이라기보다 그 위에 무엇을 안전하게 올릴 수 있는지로 값이 매겨집니다.
물론 다 끝난 건 아닙니다. 아직 모든 내부 도구가 하나의 관문으로 들어온 것도 아니고, 권한 위임은 더 잘게 나눌 여지가 있습니다. 다만 이제는 그런 남은 일이 한곳에서 보입니다. 그 자체가 이전과 가장 크게 달라진 점입니다.
이 기반 위에서 운영 중인 레거시 클러스터를 단 하나의 애플리케이션도 흔들지 않고 이 허브로 옮기고 있습니다. 그 이야기는 다음 편에서 이어가겠습니다.
 
김용현 Infra Lead/CISO 추측 대신 실측으로 계정 경계부터 클러스터까지 안전하게 인프라를 운영합니다.

댓글