접근 제어

adk-auth를 사용하는 AI Agents를 위한 엔터프라이즈급 접근 제어.

개요

adk-auth는 ADK Agents를 위한 감사 로깅 및 SSO 지원을 포함하는 역할 기반 접근 제어(RBAC)를 제공합니다. 이를 통해 어떤 사용자가 어떤 Tool에 접근할 수 있는지에 대한 보안이 강화된 세부적인 제어가 가능합니다.

아키텍처

┌─────────────────────────────────────────────────────────────────┐
│                        에이전트 요청                             │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                     SSO 토큰 유효성 검사                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────────┐  │
│  │ Google      │  │ Azure AD    │  │ OIDC 검색               │  │
│  │ 공급자      │  │ 공급자      │  │ (Okta, Auth0 등)         │  │
│  └─────────────┘  └─────────────┘  └─────────────────────────┘  │
│                          │                                       │
│                   ┌──────┴──────┐                                │
│                   │ JWKS 캐시   │  ← 키 자동 새로고침            │
│                   └─────────────┘                                │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼ TokenClaims
┌─────────────────────────────────────────────────────────────────┐
│                       클레임 매퍼                                │
│                                                                  │
│    IdP 그룹            →        adk-auth Roles                  │
│    ─────────────────────────────────────────                    │
│    "AdminGroup"        →        "admin"                         │
│    "DataAnalysts"      →        "analyst"                       │
│    (기본값)              →        "viewer"                         │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼ Roles
┌─────────────────────────────────────────────────────────────────┐
│                      접근 제어                                   │
│                                                                  │
│    Role: admin                                                   │
│    ├── 허용: AllTools                                          │
│    └── 허용: AllAgents                                          │
│                                                                  │
│    Role: analyst                                                 │
│    ├── 허용: Tool("search")                                    │
│    ├── 허용: Tool("summarize")                                 │
│    └── 거부:  Tool("code_exec")  ← 거부가 우선함                │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼ Check Result
┌─────────────────────────────────────────────────────────────────┐
│                      감사 로깅                                   │
│                                                                  │
│    {"user":"alice","resource":"search","outcome":"allowed"}     │
│    {"user":"bob","resource":"exec","outcome":"denied"}          │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                     Tool 실행                                    │
│               (접근이 허용된 경우에만)                          │
└─────────────────────────────────────────────────────────────────┘

설계 원칙

1. 거부 우선 (Deny Precedence)

역할에 허용(allow) 및 거부(deny) 규칙이 모두 있는 경우, 거부가 항상 우선합니다:

let role = Role::new("limited")
    .allow(Permission::AllTools)      // Allow everything...
    .deny(Permission::Tool("admin")); // ...except admin

// Result: Can access any tool EXCEPT "admin"

2. 다중 역할 합집합 (Multi-Role Union)

여러 역할을 가진 사용자는 권한의 합집합을 얻지만, 어떤 역할에서든 거부 규칙이 적용됩니다:

let ac = AccessControl::builder()
    .role(reader)    // allow: search
    .role(writer)    // allow: write
    .assign("alice", "reader")
    .assign("alice", "writer")
    .build()?;

// Alice can access both "search" AND "write"

3. 명시적 규칙 우선 (Explicit Over Implicit)

권한은 명시적입니다. 기본적으로는 어떤 접근 권한도 부여되지 않습니다:

let role = Role::new("empty");
// This role grants NO permissions

ac.check("user", &Permission::Tool("anything")); // → Denied

4. 인증(Authentication)과 인가(Authorization)의 분리

  • 인증 (SSO): 사용자가 누구인지 확인합니다 (JWT를 통해)
  • 인가 (RBAC): 사용자가 무엇에 접근할 수 있는지 결정합니다
// Authentication: validate JWT, extract claims
let claims = provider.validate(token).await?;

// Authorization: check specific permission
ac.check(&claims.sub, &Permission::Tool("search"))?;

// Combined: SsoAccessControl does both
sso.check_token(token, &permission).await?;

설치

[dependencies]
adk-auth = "0.2.0"

# For SSO/OAuth support
adk-auth = { version = "0.2.0", features = ["sso"] }

핵심 구성 요소

Permission

pub enum Permission {
    Tool(String),     // Specific tool by name
    AllTools,         // Wildcard: all tools
    Agent(String),    // Specific agent by name  
    AllAgents,        // Wildcard: all agents
}

Role

let analyst = Role::new("analyst")
    .allow(Permission::Tool("search".into()))
    .allow(Permission::Tool("summarize".into()))
    .deny(Permission::Tool("code_exec".into()));

AccessControl

let ac = AccessControl::builder()
    .role(admin)
    .role(analyst)
    .assign("alice@company.com", "admin")
    .assign("bob@company.com", "analyst")
    .build()?;

// Check permission
ac.check("bob@company.com", &Permission::Tool("search".into()))?;

ProtectedTool

자동 권한 확인으로 Tool을 감쌉니다:

use adk_auth::ToolExt;

let protected = my_tool.with_access_control(Arc::new(ac));

// When executed, checks permission before running
protected.execute(ctx, args).await?;

AuthMiddleware

여러 Tool을 일괄적으로 보호합니다:

let middleware = AuthMiddleware::new(ac);
let protected_tools = middleware.protect_all(tools);

SSO 통합

지원되는 공급자

공급자생성자발급자
GoogleGoogleProvider::new(client_id)accounts.google.com
Azure ADAzureADProvider::new(tenant, client)login.microsoftonline.com
OktaOktaProvider::new(domain, client){domain}/oauth2/default
Auth0Auth0Provider::new(domain, audience){domain}/
일반OidcProvider::from_discovery(issuer, client)모든 OIDC 공급자

TokenClaims

검증된 JWT에서 추출된 클레임:

pub struct TokenClaims {
    pub sub: String,              // 주체 (사용자 ID)
    pub email: Option<String>,    // 이메일
    pub name: Option<String>,     // 표시 이름
    pub groups: Vec<String>,      // IdP 그룹
    pub roles: Vec<String>,       // IdP 역할
    pub hd: Option<String>,       // Google 호스팅 도메인
    pub tid: Option<String>,      // Azure 테넌트 ID
    // ... 더 많은 표준 OIDC 클레임
}

ClaimsMapper

IdP 그룹을 adk-auth 역할에 매핑합니다:

let mapper = ClaimsMapper::builder()
    .map_group("AdminGroup", "admin")
    .map_group("Users", "viewer")
    .default_role("guest")
    .user_id_from_email()
    .build();

SsoAccessControl

SSO 유효성 검사와 RBAC를 한 번의 호출로 결합합니다:

let sso = SsoAccessControl::builder()
    .validator(GoogleProvider::new("client-id"))
    .mapper(mapper)
    .access_control(ac)
    .audit_sink(audit)
    .build()?;

// 토큰 유효성 검사 + 권한 확인 + 감사 로그
let claims = sso.check_token(token, &Permission::Tool("search".into())).await?;

감사 로깅

FileAuditSink

let audit = FileAuditSink::new("/var/log/adk/audit.jsonl")?;
let middleware = AuthMiddleware::with_audit(ac, audit);

출력 형식 (JSONL)

{"timestamp":"2025-01-01T10:30:00Z","user":"bob","session_id":"sess-123","event_type":"tool_access","resource":"search","outcome":"allowed"}
{"timestamp":"2025-01-01T10:30:01Z","user":"bob","session_id":"sess-123","event_type":"tool_access","resource":"code_exec","outcome":"denied"}

사용자 정의 감사 싱크

use adk_auth::{AuditSink, AuditEvent, AuthError};
use async_trait::async_trait;

pub struct DatabaseAuditSink { /* ... */ }

#[async_trait]
impl AuditSink for DatabaseAuditSink {
    async fn log(&self, event: AuditEvent) -> Result<(), AuthError> {
        // 데이터베이스에 삽입
        sqlx::query("INSERT INTO audit_log ...")
            .bind(event.user)
            .bind(event.resource)
            .execute(&self.pool)
            .await?;
        Ok(())
    }
}

예시

# 핵심 RBAC
cargo run --example auth_basic          # 역할 기반 접근 제어
cargo run --example auth_audit          # 감사 로깅

# SSO (--features sso 필요)
cargo run --example auth_sso --features sso     # 완전한 SSO 흐름
cargo run --example auth_jwt --features sso     # JWT 유효성 검사
cargo run --example auth_oidc --features sso    # OIDC 디스커버리
cargo run --example auth_google --features sso  # Google Identity

보안 모범 사례

관행설명
기본적으로 거부명시적으로 필요한 권한만 부여
명시적 거부위험한 작업에 대한 거부 규칙 추가
모든 것 감사규정 준수를 위해 로깅 활성화
서버 측 유효성 검사항상 서버에서 JWT 유효성 검사
HTTPS 사용JWKS 엔드포인트에는 보안 연결 필요
키 순환JWKS 캐시는 매시간 자동 새로 고침
토큰 수명 제한단기 액세스 토큰 사용

오류 처리

use adk_auth::{AccessDenied, AuthError};
use adk_auth::sso::TokenError;

// RBAC errors
match ac.check("user", &Permission::Tool("admin".into())) {
    Ok(()) => { /* access granted */ }
    Err(AccessDenied { user, permission }) => {
        eprintln!("Denied: {} cannot access {}", user, permission);
    }
}

// SSO errors
match provider.validate(token).await {
    Ok(claims) => { /* token valid */ }
    Err(TokenError::Expired) => { /* token expired */ }
    Err(TokenError::InvalidSignature) => { /* signature invalid */ }
    Err(TokenError::InvalidIssuer { expected, actual }) => { /* wrong issuer */ }
    Err(e) => { /* other error */ }
}

이전: ← 평가 | 다음: 개발 가이드라인 →