9.8 KiB
통합 테넌트 및 권한 아키텍처 정책 (Integrated Tenant & ReBAC Policy)
이 문서는 Baron SSO 시스템 내 B2C(개인)부터 B2B(단일기업), B2B2B(그룹사) 및 사내 조직도(UserGroup)에 이르는 모든 격리 공간과 권한 상속을 다루는 **다형성 테넌트(Polymorphic Tenant)**의 공식 정책을 정의합니다.
연구 결과에 기반하여, 단순한 B2C 수준을 넘어 복잡한 테넌트 생태계를 안정적이고 고성능으로 지원하기 위해 외부 DB 의무 채택, 트랜잭셔널 아웃박스 동기화, Keto 기반 인가 백본, 논리적 다중 테넌트 OIDC 관리라는 4대 핵심 아키텍처 원칙을 준수합니다.
1. 기본 원칙 (Core Axioms)
1.1 "모든 격리 공간은 테넌트이다" (Everything is a Tenant)
- 개인 워크스페이스, 기업 고객, 지주사, 그리고 사내의 특정 팀이나 본부(
UserGroup) 등 모든 종류의 격리 공간은 최상위 범용 단위인Tenant로 취급됩니다. - 과거에 혼용되던
UserGroup네임스페이스는 폐기되며, 격리 공간을 나타내는 Keto(ReBAC) 네임스페이스는Tenant하나로 단일화됩니다. 권한 상속 로직은 테넌트의 성격과 무관하게 동일하게 동작합니다.
1.2 "권한 주체와 자원의 분리" (Namespaces)
시스템의 완벽한 권한 통제를 위해, Keto에는 Tenant 외에도 다음과 같은 필수 네임스페이스가 존재합니다.
Tenant: 격리된 공간 (회사, 부서, 개인)RelyingParty: 테넌트가 소유하는 자원/앱 (OIDC 클라이언트)System: 테넌트에 종속되지 않는 전역 권한 (Super Admin 등)
1.3 소유(Ownership)와 가시성(Visibility)의 분리
- 시스템의 모든 자원(예: RelyingParty, 앱)은 반드시 특정
Tenant가 소유(manage)합니다. - 그러나 자원의 소유권과 누가 접근할 수 있는가(가시성,
access)는 별개입니다. 내부망용 앱(Private)과 대국민 서비스(Public)를 동일한 기업(Tenant)이 동시에 소유하고 제어할 수 있습니다.
1.4 Ory SSOT와 Backend read model 분리
사용자 데이터를 Kratos의 내부 트레이트(Traits)에 무분별하게 저장하는 것은 안티 패턴입니다. 이는 토큰 비대화와 쿼리 성능 저하를 초래합니다.
- Kratos (Identity): "누구인가?" (인증, 이메일, 패스워드 등 순수 식별 정보). 테넌트, 직급 등 관계형 데이터는 절대 보관하지 않습니다.
- Backend DB read model: Ory에 저장되지 않거나 Ory API로 필요한 조회가 불가능한 조직 표시/검색 metadata, 테넌트 설정, 외부 연동 상태만 보관합니다.
- Keto (ReBAC Authorization Backbone): "무엇을 할 수 있는가?" (권한 및 상속).
2. 하이브리드(다형성) 테넌트 아키텍처
데이터베이스의 tenants 테이블은 가장 가벼운 신분증(Identity) 역할을 수행하며, 세부 비즈니스 설정은 타입별로 별도의 테이블에 1:1 조인(Join)하여 관리합니다.
2.1 테넌트 유형 (Tenant Types)
| 타입 (Enum) | 설명 | 특징 및 1:1 조인 테이블 |
|---|---|---|
PERSONAL |
B2C 개인 워크스페이스 | 일반 사용자 가입 시 1:1로 생성. 조직도(UserGroup) 기능 비활성화. |
COMPANY |
B2B 일반 기업/법인 | 독립된 비즈니스. 사내 조직도를 가짐. 무거운 설정은 company_settings에 저장. |
COMPANY_GROUP |
B2B2B 지주사/그룹사 | 여러 COMPANY를 하위로 거느리며 최고 관리자 권한을 통합. company_settings 조인. |
USER_GROUP |
사내 조직 (본부/팀 등) | COMPANY 내부에 속하는 조직. 사내 조직도 메타데이터는 user_groups 테이블에 저장. |
3. 중앙집중형 인가 백본 (Keto ReBAC Tuples)
복잡한 다단계 B2B2B 조직도 내에서의 상속 권한을 외부 RDBMS 논리만으로 실시간 검증하는 것은 과부하를 초래합니다. 따라서 조직도 변경 시 해당 데이터를 즉시 Keto의 관계 튜플로 변환하여 전송하고, 인가 검증(Check)은 초고속 병렬 처리가 가능한 Keto 엔진으로 오프로딩합니다.
3.1 조직장(Leader)과 어드민(Admin) 상속
특정 부서(테넌트)의 그룹장(owners)으로 임명되면, 해당 부서 및 그 하위 부서의 최고 관리자(admins) 권한을 자동으로 상속받습니다.
- 조직장 임명:
Tenant:<조직ID>#owners@User:<유저ID> - 자동 상속 룰:
Tenant:<조직ID>#admins@Tenant:<조직ID>#owners
3.2 계층 간 권한 상속 (Hierarchy)
지주사 \rightarrow 법인 \rightarrow 사내조직 간의 상속은 모두 parents 튜플을 사용합니다.
- 계층 설정:
Tenant:<하위ID>#parents@Tenant:<상위ID> - 자동 상속 룰: 상위 테넌트의
admins는 하위 테넌트의manage및view권한을 모두 가집니다.
3.3 리소스 제어 및 RP Admin (Relying Party)
RP(앱)는 별도의 가상 테넌트를 만들지 않고 자원(Object) 자체의 다중 상속을 통해 권한을 제어합니다.
- 앱 관리 (Manage):
RelyingParty:<앱ID>#parents@Tenant:<소유테넌트ID>(앱 소유권 지정. 해당 테넌트의 최고 관리자가 앱을 관리할 수 있음)RelyingParty:<앱ID>#admins@User:<유저ID>(특정 유저를 RP Admin으로 직접 지정)
- Private 앱 접근 (Access):
RelyingParty:<앱ID>#access@Tenant:<소유테넌트ID>#members(소유한 회사의 멤버만 접근) - Public 앱 접근 (Access):
RelyingParty:<앱ID>#access@System:authenticated_users#members(전역 인증 유저 누구나 접근)
4. 권한 흐름도 (Mermaid)
graph TD
%% Types
subgraph COMPANY_GROUP [지주사 테넌트]
CG[Tenant: 한맥 그룹]
end
subgraph COMPANY [법인 테넌트]
C1[Tenant: 한맥 IT]
end
subgraph USER_GROUP [사내조직 테넌트]
UG1[Tenant: 개발본부]
UG2[Tenant: 클라우드팀]
end
subgraph USERS [사용자]
CEO[User: 그룹 최고경영자]
DEV_L[User: 팀장]
DEV[User: 팀원]
RP_ADM[User: RP 전담 관리자]
end
subgraph RESOURCES [자원]
RP1[RelyingParty: 사내 인트라넷]
end
%% Hierarchy Tuples
C1 -- "parents" --> CG
UG1 -- "parents" --> C1
UG2 -- "parents" --> UG1
RP1 -- "parents" --> C1
%% Memberships
CEO -- "owners" --> CG
DEV_L -- "owners" --> UG2
DEV -- "members" --> UG2
%% RP Admin
RP_ADM -- "admins" --> RP1
%% Effective Admin Control (Inherited)
CEO -. "Inherits Admin Control" .-> C1
CEO -. "Inherits Admin Control" .-> UG1
CEO -. "Inherits Admin Control" .-> UG2
CEO -. "Inherits Manage" .-> RP1
DEV_L -. "Inherits Admin Control" .-> UG2
%% Styles
style CG fill:#dfd,stroke:#333
style C1 fill:#dfd,stroke:#333
style UG1 fill:#f9f,stroke:#333
style UG2 fill:#f9f,stroke:#333
style CEO fill:#ff9,stroke:#333
style DEV_L fill:#ff9,stroke:#333
style RP_ADM fill:#ff9,stroke:#333
style RP1 fill:#bbf,stroke:#333
5. 지능형 프록시를 통한 논리적 다중 테넌트 OIDC 관리
인프라 비용 팽창을 막기 위해 테넌트별로 Hydra(OAuth2/OIDC) 클러스터를 물리적으로 복제하는 것은 지양합니다.
5.1 Logical Pooling for OAuth2
- 모든 테넌트(
COMPANY,PERSONAL)는 소수의 공유된 Hydra 클러스터를 사용합니다. - Hydra 클러스터 앞단에 도메인 및 헤더를 재작성하는 지능형 프록시(API Gateway)를 배치하여, 테넌트별로 물리적으로 분리된 것과 같은 라우팅 효과를 제공합니다.
5.2 동적 클레임 조립 (Dynamic Claim Assembly)
- 로그인 및 동의(Consent) 흐름의 프로토콜 원장은 Ory Hydra입니다.
- 백엔드는 Ory에서 확인한 identity/relationship과 허용된 read model을 조합해 요청된 클라이언트(RP)의 테넌트 맥락(Context)을 계산하고, Hydra에 전달할 claim을 조립합니다.
6. 분산 시스템 동기화 무결성 확보 (Data Consistency)
Kratos 웹훅 통신 지연이나 이중 쓰기(Dual-Write) 오류로 인한 '고아 레코드(Ghost Identity)'를 원천 차단해야 합니다.
6.1 빠른 제어권 반환 및 비동기 처리 혼합 (Kratos \rightarrow DB)
- 가입 웹훅(after_registration): Kratos에서 웹훅 요청이 오면, 백엔드는 국소 DB 트랜잭션을 열어 최소한의 필수 식별자 데이터만
users테이블에 커밋하고 즉각적으로 200 OK를 응답하여 Kratos에 제어권을 반환합니다. (가입 중단 방지) - 웰컴 이메일 발송, 초기 조직도 프로비저닝 등 무거운 작업은 즉시 처리하지 않고 메시지 큐(Message Queue)나 비동기 워커로 넘겨 후속 처리합니다.
6.2 트랜잭셔널 아웃박스 패턴 (DB \rightarrow Keto)
- 백엔드 로직에 의해 테넌트나 권한이 변경될 때, Keto API를 직접 호출하면 네트워크 오류 시 부분 실패(Partial Failure)가 발생할 수 있습니다.
- 이를 방지하기 위해 DB 커밋 시 동일 트랜잭션 내에서
keto_outbox테이블에 튜플 변경 이벤트를 기록합니다. 이후 CDC(Change Data Capture) 로직이나 릴레이 워커를 통해 비동기로 시스템 간 동기화를 진행하여 응답 속도와 정합성을 동시에 달성합니다.
6.3 삭제 정책 (Cascade) 및 정기 대사
- 즉시 회수: 백엔드 DB에서 Soft Delete(
deleted_at)가 발생하면, Outbox 워커는 지연 없이 즉각적으로 Keto의 튜플을 Hard Delete 합니다. - 정기 대사 (Reconciliation): Kratos(Identity), PostgreSQL(DB), Keto(ReBAC) 3자 간의 불일치(고아 튜플, 누락된 멤버십 등)를 매일 1회 이상 배치 크론 잡을 통해 능동적으로 색출하고 자동 복구/삭제합니다.