
우리는 지금까지 시스템을 운영하면서 많은 것을 “신뢰”해왔다.
내부 네트워크에 있는 서버는 외부보다 안전하다고 생각했고, 같은 Kubernetes Cluster에 있는 Workload는 어느 정도 신뢰할 수 있다고 생각했으며, 운영자에게 높은 권한을 부여하고 “이 사람은 필요한 작업만 할 것”이라고 생각했다.
하지만 시스템이 복잡해지고 공격 경로가 다양해지면서 이러한 가정은 점점 위험해졌다. 그리고 AI Agent가 Infrastructure를 직접 변경하기 시작하면서 이 문제는 더욱 중요해지고 있다.
AI에게 모든 권한을 줄 수 있을까? 그렇다면 사람에게는?
어떤 주체도 기본적으로 신뢰하지 않는 것, 이것이 Zero Trust의 출발점이다.
1. Zero Trust란 무엇인가
Zero Trust를 가장 단순하게 표현하면 다음과 같다.
Never Trust, Always Verify
하지만 이것을 단순히 “아무도 믿지 않는다”라고 이해하면 부족하다. Zero Trust의 핵심은 신뢰를 권한의 근거로 사용하지 않는 것이다.
예를 들어 다음과 같은 구조가 있다고 하자.
flowchart LR
Internet["Internet"]
Firewall["Firewall"]
Internal["Internal Network"]
Application["Application"]
Database["Database"]
Kubernetes["Kubernetes"]
Internet --> Firewall
Firewall --> Internal
Internal --> Application
Internal --> Database
Internal --> Kubernetes
전통적인 네트워크 보안에서는 다음과 같은 사고방식이 존재했다.
flowchart LR
External["External Network"]
Firewall["Firewall"]
Internal["Internal Network"]
External -->|"Untrusted"| Firewall
Firewall -->|"Trusted"| Internal
즉, 외부는 위험하고 내부는 상대적으로 신뢰할 수 있다라는 경계가 존재했다. Zero Trust에서는 이 가정을 제거한다.
flowchart LR
User["User"]
Workload["Workload"]
Agent["AI Agent"]
Policy["Policy"]
Resource["Resource"]
Denied["DENY"]
User --> Policy
Workload --> Policy
Agent --> Policy
Policy -->|"ALLOW"| Resource
Policy -->|"DENY"| Denied
네트워크 위치만으로 접근을 허용하지 않고 요청하는 주체가 누구인지, 무엇을 하려고 하는지, 어떤 Resource에 접근하는지, 현재 Context가 무엇인지 등을 확인한 후 결정한다.
2. Perimeter Security의 한계
전통적인 보안 모델은 Network Perimeter를 중심으로 발전했다.
Network Perimeter: 조직의 보안 내부 네트워크와 인터넷 또는 기타 통제되지 않는 외부 네트워크 사이의 경계
출처: https://www.cloudflare.com/ko-kr/learning/access-management/what-is-the-network-perimeter/
flowchart LR
Internet["Internet"]
Firewall["Firewall"]
Internal["Internal Network"]
Internet --> Firewall
Firewall --> Internal
하지만 현대의 Infrastructure에서는 이 경계가 점점 흐려지고 있다. 예를 들어 Cloud, Kubernetes, SaaS, Microservices, CI/CD, Multi Cloud 등 모든 Resource가 하나의 Network 안에 존재하지 않는다. 특히 Microservice Architecture에서는 다음과 같은 요청이 끊임없이 발생한다.
flowchart LR
ServiceA["Service A"]
ServiceB["Service B"]
ServiceC["Service C"]
Database["Database"]
ServiceA --> ServiceB
ServiceB --> ServiceC
ServiceC --> Database
Service A가 내부 네트워크에 있다는 이유만으로 Service B에 접근할 수 있어야 할까? Zero Trust에서는 다음 질문을 한다.
Service A가 Service B의 특정 API를 호출할 권한이 있는가?
3. Zero Trust의 핵심 원칙
Zero Trust를 구현하는 방법은 여러 가지가 있지만 핵심 원칙은 크게 다음과 같이 정리할 수 있다.
1. Verify Explicitly
항상 주체와 요청을 확인한다.
2. Least Privilege
필요한 권한만 부여한다.
3. Assume Breach
이미 침해되었을 가능성을 전제로 설계한다. 이 세 가지를 Architecture로 표현하면 다음과 같다.
flowchart TD
Request["Request"]
Identity["Identity"]
Context["Context"]
Policy["Policy Evaluation"]
Resource["Resource"]
Denied["DENY"]
Request --> Identity
Request --> Context
Identity --> Policy
Context --> Policy
Policy -->|"ALLOW"| Resource
Policy -->|"DENY"| Denied
중요한 것은 “인증했는가?”만 보는 것이 아니라는 점이다. 인증(Authentication)은 질문의 일부일 뿐이다.
누구인가와 무엇을 할 수 있는가는 다른 문제다. 이 차이를 이해하는 데 OAuth와 OIDC가 좋은 예가 된다.
4. Authentication과 Authorization
두 개념은 반드시 분리해서 이해해야 한다.
Authentication
“당신은 누구인가?”
Authorization
“당신이 이것을 해도 되는가?”
예를 들어 사용자가 로그인했다고 하자.
flowchart LR
Subject["Subject"]
Authentication["Authentication"]
Identity["Identity"]
Subject --> Authentication
Authentication --> Identity
이것만으로 다음 작업이 허용되는 것은 아니다.
flowchart LR
Identity["Identity"]
Authorization["Authorization"]
Action["Delete Production Database"]
Identity --> Authorization
Authorization --> Action
따라서 다음을 함께 고려되어야 한다.
flowchart LR
Subject["Subject"]
Auth["Authentication"]
Identity["Identity"]
Authorization["Authorization"]
Resource["Resource"]
Subject --> Auth
Auth --> Identity
Identity --> Authorization
Authorization --> Resource
Zero Trust는 Authentication만 강화하는 시스템이 아니다. Authentication과 Authorization을 지속적으로 결합하는 시스템이다.
OAuth와 OIDC
둘은 함께 사용되는 경우가 많기 때문에 같은 기술처럼 느껴지지만, 목적이 다르다.
OAuth 2.0
OAuth 2.0은 기본적으로 Authorization Framework로 이 Client가 특정 Resource에 접근할 수 있는가? 를 다루기 위한 프레임워크다.
예를 들어 애플리케이션이 사용자의 Google Drive에 접근한다고 하자.
flowchart LR
Client["Application"]
AS["Authorization Server"]
RS["Resource Server"]
Client -->|"Authorization Request"| AS
AS -->|"Access Token<br/>scope: drive.read"| Client
Client -->|"Access Token"| RS
RS -->|"Protected Resource"| Client
여기서 Access Token은 Resource Server에 접근하기 위한 권한을 나타내지만 여기서 중요한 질문이 하나 남는다.
이 Token을 발급받은 사용자가 정확히 누구인가?
OAuth 2.0 자체의 핵심 목적은 사용자의 Identity를 인증하는 것이 아니다.
OIDC
OpenID Connect(OIDC)는 OAuth 2.0 위에서 동작하는 Authentication Protocol이다.
즉 “이 사용자가 누구인가?”를 확인하기 위한 표준이다.
OIDC에서는 ID Token을 통해 사용자의 Identity에 대한 정보를 전달한다.
flowchart LR
User["User"]
OP["OpenID Provider"]
Client["Client"]
Identity["Identity"]
User -->|"Authenticate"| OP
OP -->|"ID Token"| Client
Client --> Identity
매우 단순하게 표현하면 다음과 같다.
flowchart LR
OIDC["OIDC"]
OAuth["OAuth 2.0"]
OIDC -->|"Authentication"| Identity["Who are you?"]
OAuth -->|"Authorization"| Access["What can you access?"]
둘은 함께 사용된다
실제 시스템에서는 OAuth와 OIDC가 함께 사용되는 경우가 많다.
sequenceDiagram
participant User
participant Client as Application
participant OP as Identity Provider
participant API as Resource Server
User->>Client: Login
Client->>OP: Authorization Request
OP->>User: Authenticate
User->>OP: Authentication
OP-->>Client: ID Token + Access Token
Client->>API: Request + Access Token
API->>API: Validate Access Token
API-->>Client: Resource
여기서 각각의 역할은 다르다.
flowchart LR
IDToken["ID Token"]
AccessToken["Access Token"]
IDToken --> AuthN["Authentication"]
AuthN --> Identity["Who is this?"]
AccessToken --> AuthZ["Authorization"]
AuthZ --> Resource["What can this Client access?"]
다음과 같이 이해하면 된다.
OIDC는 Identity를 인증하고 전달하며, OAuth는 Resource에 대한 접근 권한을 위임한다.
Authentication만으로는 부족하다
사용자가 OIDC를 통해 성공적으로 인증되었다고 하자.
flowchart LR
User["User"]
OIDC["OIDC"]
Identity["Authenticated Identity"]
User --> OIDC
OIDC --> Identity
그렇다고 이 사용자가 모든 작업을 수행할 수 있는 것은 아니다.
flowchart TD
Identity["Authenticated Identity"]
Get["Get Pod"]
Create["Create Pod"]
Delete["Delete Pod"]
IAM["Modify IAM"]
DB["Delete Database"]
Decision["Authorization Decision"]
Allow["ALLOW"]
Deny["DENY"]
Identity --> Get
Identity --> Create
Identity --> Delete
Identity --> IAM
Identity --> DB
Get --> Decision
Create --> Decision
Delete --> Decision
IAM --> Decision
DB --> Decision
Decision --> Allow
Decision --> Deny
Identity는 누구인가를 설명할 뿐이다. Authorization은 별도의 질문이다.
이 Identity가 이 Resource에 이 Action을 수행할 수 있는가?
Zero Trust에서는 Authentication과 Authorization을 분리해서 생각해야 한다.
flowchart LR
User["Human"]
OIDC["OIDC<br/>Authentication"]
Identity["Identity"]
Policy["Authorization Policy"]
Resource["Resource"]
Deny["Request Rejected"]
User --> OIDC
OIDC --> Identity
Identity --> Policy
Policy -->|"ALLOW"| Resource
Policy -->|"DENY"| Deny
여기서 OAuth의 scope는 Authorization을 표현하는 하나의 수단이지, 전체 Authorization Policy 그 자체는 아니다. 실제 시스템에서는 다음과 같은 요소까지 함께 고려할 수 있다.
flowchart LR
Identity["Identity"]
Role["Role"]
Resource["Resource"]
Action["Action"]
Context["Context"]
Risk["Risk"]
Policy["Policy Evaluation"]
Allow["ALLOW"]
Deny["DENY"]
Identity --> Policy
Role --> Policy
Resource --> Policy
Action --> Policy
Context --> Policy
Risk --> Policy
Policy --> Allow
Policy --> Deny
OAuth Scope만으로 충분하지 않은 이유
예를 들어 Access Token에 다음과 같은 Scope가 있다고 하자.
scope = kubernetes.write
이것만으로 모든 Kubernetes Write 작업을 허용하는 것은 위험하다.
flowchart TD
Scope["kubernetes.write"]
Deploy["Deploy payment-api"]
Restart["Restart payment-api"]
Delete["Delete payment-api"]
IAM["Modify cluster permissions"]
Namespace["Delete production namespace"]
Policy["Fine-grained Policy"]
Scope --> Deploy
Scope --> Restart
Scope --> Delete
Scope --> IAM
Scope --> Namespace
Deploy --> Policy
Restart --> Policy
Delete --> Policy
IAM --> Policy
Namespace --> Policy
Scope는 접근 권한을 표현하는 중요한 메커니즘이지만, 실제 시스템에서는 더 세밀한 Authorization Policy가 필요할 수 있다.
예를 들어 다음과 같이 판단할 수 있다.
flowchart LR
Identity["Identity<br/>platform-engineer"]
Resource["Resource<br/>production/payment-api"]
Action["Action<br/>deploy"]
Context["Context<br/>managed-device"]
Policy["Policy Engine"]
Deploy["Deploy"]
Reject["Reject"]
Identity --> Policy
Resource --> Policy
Action --> Policy
Context --> Policy
Policy -->|"ALLOW"| Deploy
Policy -->|"DENY"| Reject
따라서 OAuth를 사용한다고 해서 Zero Trust Authorization이 완성되는 것은 아니다.
OAuth와 OIDC는 Zero Trust Architecture를 구성하는 Identity와 Delegated Authorization의 중요한 기반이고, 그 위에 RBAC, ABAC, Policy Engine, JIT, Risk Evaluation 등을 결합할 수 있다.
5. Identity가 중심이 된다
Zero Trust에서는 Network보다 Identity가 중요해진다. 네트워크 기반의 전통적인 접근 제어가 아래와 같다고 가정하자.
flowchart LR
SourceIP["Source IP"]
Firewall["Firewall Rule"]
SourceIP --> Firewall
Zero Trust 접근은 아래와 같다.
flowchart LR
Identity["Identity"]
Resource["Resource"]
Action["Action"]
Context["Context"]
Policy["Policy"]
Identity --> Policy
Resource --> Policy
Action --> Policy
Context --> Policy
다음과 같은 요청이 있다고 하면, 이 정보를 기반으로 Policy를 평가한다.
Identity:
payment-service
Resource:
order-api
Action:
POST /orders
Context:
production
flowchart TD
Identity["payment-service"]
Allow["POST /orders<br/>ALLOW"]
DenyDelete["DELETE /orders<br/>DENY"]
DenyUsers["GET /users<br/>DENY"]
Identity --> Allow
Identity --> DenyDelete
Identity --> DenyUsers
따라서 Zero Trust Architecture의 중심에는 Identity가 존재한다.
6. Least Privilege
Zero Trust에서 가장 중요한 원칙 중 하나다. 권한은 가능한 한 작게 부여해야 한다. 다음과 같이 전체 Administrator 권한을 부여하는 것보다 필요한 작업만 명시적으로 허용하는 것이 안전하다.
flowchart LR
Application["Application"]
Administrator["Administrator"]
Application --> Administrator
flowchart LR
Application["Application"]
Read["Read Orders"]
Create["Create Orders"]
Application --> Read
Application --> Create
이를 Blast Radius 관점에서 보면 더욱 명확하다.
flowchart TD
Compromised["Compromised Workload"]
Broad["Broad Permission"]
Narrow["Least Privilege"]
Large["Large Blast Radius"]
Small["Small Blast Radius"]
Compromised --> Broad
Compromised --> Narrow
Broad --> Large
Narrow --> Small
Least Privilege의 목적은 공격 자체를 완전히 막는 것이 아니다. 침해되었을 때 피해 범위를 줄이는 것이다.
7. Assume Breach
Zero Trust는 “침해되지 않을 것”이라는 가정에서 출발하지 않는다. 오히려 다음을 가정한다.
flowchart LR
Workload["Workload A"]
Compromised["Compromised"]
Workload -->|"Breach"| Compromised
따라서 공격자가 Workload A를 장악했더라도 아래와 같이 피해 범위를 제한할 수 있어야 한다.
flowchart TD
Workload["Compromised Workload A"]
ServiceB["Service B"]
Database["Database"]
IAM["IAM"]
Kubernetes["Kubernetes"]
Workload -->|"ALLOW"| ServiceB
Workload -->|"DENY"| Database
Workload -->|"DENY"| IAM
Workload -->|"DENY"| Kubernetes
이것이 Zero Trust와 Network Segmentation, Microsegmentation이 연결되는 이유다.
8. Policy Decision
결국 Zero Trust 시스템의 핵심 질문은 하나로 수렴한다.
이 요청을 허용할 것인가?
이를 결정하는 컴포넌트를 일반적으로 Policy Decision Point(PDP)라고 표현할 수 있다.
flowchart LR
Subject["Subject"]
Request["Request"]
PDP["Policy Decision Point"]
Resource["Resource"]
Block["DENY"]
Subject --> Request
Request --> PDP
PDP -->|"ALLOW"| Resource
PDP -->|"DENY"| Block
실제 구현에서는 Policy Decision과 Policy Enforcement를 분리할 수 있다.
flowchart LR
Request["Request"]
PEP["Policy Enforcement Point"]
PDP["Policy Decision Point"]
Resource["Resource"]
Request --> PEP
PEP --> PDP
PDP -->|"ALLOW"| PEP
PDP -->|"DENY"| PEP
PEP --> Resource
PDP는 Policy를 평가하고, PEP는 그 결과에 따라 실제 요청을 허용하거나 차단한다. 이 분리는 이후 사람과 AI Agent의 Authorization Architecture에서도 계속 등장한다.
9. Policy를 어떻게 구현하는가
Policy Engine을 직접 구현할 수도 있지만 일반적으로 검증된 Policy Engine을 활용한다. 대표적인 선택지는 다음과 같다.
Open Policy Agent
OPA는 Policy Decision을 애플리케이션과 분리하고 Rego를 이용하여 정책을 관리할 수 있다. Kubernetes, API Gateway, Service-to-Service Authorization 등 다양한 영역에서 활용할 수 있다.
AWS Cedar
Cedar는 Authorization Policy를 명시적으로 정의하기 위한 Policy Language다. 특히 애플리케이션 Authorization을 Policy 중심으로 설계할 때 고려할 수 있다.
Cerbos
Cerbos 역시 애플리케이션에서 Authorization을 분리하여 중앙화된 Policy Decision 구조를 구성할 수 있다. 중요한 것은 특정 제품이 아니다. 아래와 같은 구조를 만드는 것이다.
flowchart LR
Application["Application"]
PolicyDecision["Policy Decision"]
Decision["ALLOW / DENY"]
Application --> PolicyDecision
PolicyDecision --> Decision
10. Identity는 어떻게 만드는가
Human Identity는 일반적으로 Identity Provider(IdP)를 통해 관리한다.
flowchart LR
User["User"]
IdP["Identity Provider"]
Token["Identity Token"]
Resource["Resource"]
User --> IdP
IdP --> Token
Token --> Resource
Workload 역시 Identity를 가져야 한다.
flowchart TD
WorkloadA["Workload A"]
IdentityA["Identity A"]
WorkloadB["Workload B"]
IdentityB["Identity B"]
WorkloadA --> IdentityA
WorkloadB --> IdentityB
이때 장기 Credential을 직접 주는 대신 Workload Identity를 사용하는 방식이 선호된다. 대표적인 기술은 아래와 같다.
- OIDC Federation
- SPIFFE / SPIRE
- Kubernetes Workload Identity
- AWS IAM Roles for Service Accounts 계열
- AWS STS AssumeRole
11. Short-lived Credential
다음과 같은 구조는 위험하다.
flowchart LR
Application["Application"]
Credential["Long-lived Access Key"]
Lifetime["Valid for Months"]
Application --> Credential
Credential --> Lifetime
Credential이 유출되면 공격자가 장기간 사용할 수 있기 때문이다. 이러한 구조 대신 우리는 임시 자격 증명을 사용해야 한다.
sequenceDiagram
participant Workload
participant IdP as Identity Provider
participant STS
participant AWS
Workload->>IdP: Authenticate
IdP-->>Workload: Identity Token
Workload->>STS: AssumeRole
STS-->>Workload: Temporary Credential
Workload->>AWS: API Request
AWS STS의 Temporary Security Credentials는 이런 목적에 활용할 수 있다. 하지만 Short-lived Credential도 만능은 아니다. Credential이 유효한 시간 동안에는 악용될 수 있다. 따라서 아래와 같은 구조를 설계해야 한다.
flowchart LR
ShortLived["Short-lived Credential"]
LeastPrivilege["Least Privilege"]
Policy["Policy Enforcement"]
Audit["Audit"]
Detection["Detection"]
ShortLived --> Security["Reduced Blast Radius"]
LeastPrivilege --> Security
Policy --> Security
Audit --> Security
Detection --> Security
12. Zero Trust와 Observability
접근을 통제하는 것만으로 끝나지 않는다. 어떤 Identity가 어떤 Resource에 어떤 Action을 했는지 추적할 수 있어야 한다.
flowchart LR
Identity["Identity"]
Action["Action"]
Resource["Resource"]
Audit["Audit"]
Identity --> Action
Action --> Resource
Identity --> Audit
Action --> Audit
Resource --> Audit
Audit에는 최소한 다음 정보를 추적할 수 있어야 한다.
Who
What
When
Where
Why
Decision
즉 특정 Event에 대해 흐름을 추적할 수 있어야 한다.
flowchart LR
Identity["Identity"]
Resource["Resource"]
Action["Action"]
Context["Context"]
Decision["Policy Decision"]
Audit["Audit Trail"]
Identity --> Audit
Resource --> Audit
Action --> Audit
Context --> Audit
Decision --> Audit
13. Zero Trust는 네트워크 보안이 아니다
Zero Trust를 Firewall이나 VPN과 동일시하면 안 된다. Network Security는 Zero Trust를 구현하는 하나의 수단일 뿐이다. Zero Trust의 범위는 아래와 같이 확장될 수 있다.
Human
Workload
Service
Device
Application
AI Agent
이를 바탕으로 다음과 같은 구조가 가능하다.
flowchart TD
Human["Human"]
Workload["Workload"]
Agent["AI Agent"]
Identity["Identity"]
Policy["Policy"]
Resource["Resource"]
Human --> Identity
Workload --> Identity
Agent --> Identity
Identity --> Policy
Policy --> Resource
중심은 Network가 아니라 Identity와 Policy다.
14. Zero Trust의 실제 비용
Zero Trust는 공짜가 아니다. 모든 요청에 Identity와 Policy를 적용하면 시스템 복잡성이 증가한다.
flowchart LR
Request["Request"]
Authentication["Authentication"]
Authorization["Authorization"]
Policy["Policy"]
Resource["Resource"]
Request --> Authentication
Authentication --> Authorization
Authorization --> Policy
Policy --> Resource
추가적인 Latency가 발생할 수 있고, Identity Infrastructure와 Policy를 관리해야 한다. 또한 Policy가 너무 세분화되면 개발 생산성이 떨어질 수 있다.
따라서 Zero Trust의 목표를 모든 것을 막는다로 설정하면 안된다. 정확한 목표는 위험에 비례하여 통제한다.
15. Zero Trust Architecture
지금까지의 내용을 하나로 합치면 다음과 같다.
flowchart TD
Human["Human"]
Workload["Workload"]
Agent["AI Agent"]
IdP["Identity Provider"]
Auth["Authentication"]
Policy["Policy Engine"]
Risk["Context / Risk"]
Resource["Resource"]
Audit["Audit"]
Denied["DENY"]
Human --> IdP
Workload --> IdP
Agent --> IdP
IdP --> Auth
Auth --> Policy
Risk --> Policy
Human --> Policy
Workload --> Policy
Agent --> Policy
Policy -->|"ALLOW"| Resource
Policy -->|"DENY"| Denied
Human --> Audit
Workload --> Audit
Agent --> Audit
Policy --> Audit
Resource --> Audit
이것이 Zero Trust의 핵심 구조다. 신뢰할 수 있는 Network를 만드는 것이 아니라, 신뢰하지 않아도 안전하게 접근할 수 있는 시스템을 만드는 것이다.
마치며
Zero Trust는 새로운 보안 제품 하나를 도입하는 것이 아니다. 그것은 시스템을 바라보는 방식의 변화다.
기존에는 “이 사용자는 내부 사용자니까 믿을 수 있다.” 였다면 Zero Trust에서는 이 사용자가 누구인지 확인하고, 지금 요청한 작업이 허용되는지 판단한다.
그리고 이 관점은 앞으로 AI Agent가 시스템의 중요한 실행 주체가 될수록 더욱 중요해진다. 다음 편에서는 이 원칙이 실제로 사람(Human)에게 어떻게 적용되어 왔는지 살펴본다.
MFA, RBAC, ABAC, JIT Access, Workload Identity, Device Trust와 같은 기술들이 실제로 어떤 문제를 해결하고 있으며, 이들이 어떻게 하나의 Zero Trust Architecture로 연결되는지 살펴보자.