공통 계약 (Canonical Contract)
공통 계약 (Canonical Contract)
Section titled “공통 계약 (Canonical Contract)”이 문서는 모든 스택 요구사항이 공통으로 지켜야 하는 계약을 정의한다. 각 스택 문서는 여기서 정의한 canonical 어휘/필드를 자기 스택의 실제 contract 값으로 구체화한다.
0) 신뢰 체인 한눈에
Section titled “0) 신뢰 체인 한눈에”sequenceDiagram
autonumber
participant U as 직원 / AI account
participant E as Pomerium edge<br/>(auth.yamon.io)
participant KC as Keycloak realm yamon
participant EN as Microsoft Entra
participant A as 대상 솔루션
participant API as FractalOps API<br/>(portal session)
U->>E: 보호 호스트 접근 (예: penpot.yamon.io)
alt 세션 없음
E->>U: 302 -> /.pomerium/sign_in
E->>KC: OIDC authorize
KC->>EN: broker (primary_idp_alias: microsoft)
EN-->>KC: 인증 완료
KC-->>E: id_token + claims
end
E->>A: 요청 전달 + x-pomerium-jwt-assertion / x-pomerium-claim-*
A->>API: portal session 해석 (subject_key / subject_id)
API-->>A: principal 식별 + 권한 투영
- edge는 항상 Pomerium 하나다. 인증 권한이 어디든(
external_idp/stack_local/portal) 바깥 문은 동일하다. - API는 Pomerium이 검증한 JWT(
x-pomerium-jwt-assertion)와 claim 헤더 (x-pomerium-claim-email,x-pomerium-claim-groups)로 principal을 해석한다. 검증 로직은contexts/identity/application/session_trust.py의PomeriumTrustAdapter.
1) 인증/세션
Section titled “1) 인증/세션”- Edge 인증은
Pomerium단일 경로를 사용한다. - 앱 직접 로그인은 관리자 콘솔(예: Keycloak Admin) 외에는 금지한다.
- 로그인 실패 시 각 솔루션은 직접 에러를 띄우지 않고 포털 로그인 경로로 리다이렉트한다.
- 세션 신뢰 어댑터는
session_trust_adapter필드로 명시된다.pomerium: edge JWT/claim을 앱이 직접 신뢰(OIDC 스택).pomerium_edge_gate: edge에서 먼저 인증을 강제하고, 앱 안에서는 로컬 세션을 쓴다.portal: 포털 세션으로 신뢰(Armory/PlaywrightGrid 등 내부 plane).local_loopback: 데스크톱 loopback OIDC(예: protected-registry-desktop).
- Pomerium sign-in/sign-out URL은
auth_base_for_redirect->.pomerium/sign_in/.pomerium/sign_out형태로 생성된다(코드:session_trust.py).
2) 주체 식별자
Section titled “2) 주체 식별자”- canonical principal key:
subject_key(예:entra:<object-id>)subject_id(FractalOps 내부 안정 ID)
- principal type:
human | ai만 허용
- 비정규 별칭(
bot,agent,user)은 허용하지 않는다. - identity-contract의
supported_principal_kinds는 더 세분화된 표현 (human,guest,ai_account,service_account)을 쓰며, 이는 투영 정책 입력값이다. product 어휘는human | ai로 정규화한다.
3) 권한 표현
Section titled “3) 권한 표현”- 권한 원천은
/org/...group path + realm role 조합이다(canonical_group_root: /org). - 정책 엔진 입력은 canonical 필드만 사용:
principalType,groupPath,operation,resourceRef
operation은create | delete만 허용- 권한 판정 엔진은 SpiceDB(Zanzibar ReBAC)다. 단일 application-resource 권한 SSOT로서
실제
CheckPermission을 수행하고, autodream 자율운영 규칙은 SpiceDB CEL caveat로 표현된다. SpiceDB는 Keycloak(identity SSOT)에서 안정 키로 principal/group을 참조할 뿐 identity를 복제하지 않는다. (OPA + OpenFGA는 제거됨 — 별도 rego/decision-path 플레인 없음.) - SCIM group membership 변경은 canonical group path만 받아들인다
(코드:
scim_sync_bridge.py의is_canonical_group_path검증).
4) Secret lifecycle 과 authorization lifecycle
Section titled “4) Secret lifecycle 과 authorization lifecycle”Secret lifecycle과 authorization lifecycle은 분리한다. SpiceDB는 OpenBao, External Secrets Operator, Kubernetes ServiceAccount를 대체하지 않는다. SpiceDB는 secret을 만들거나 회전하거나 주입하지 않고, 어떤 principal이 어떤 secret template, service account, namespace, project resource, deployment evidence surface를 받을 자격이 있는지만 판정한다.
| 영역 | 권위자 | SpiceDB 보완 |
|---|---|---|
| 키/토큰 생성·회전·만료 | OpenBao | 생성자가 아님 |
Kubernetes Secret 동기화 | External Secrets Operator | 주입자가 아님 |
| Workload identity / SVID | SPIRE + Kubernetes ServiceAccount selector | 신원 발급자가 아님 |
| Pod의 Kubernetes API 신원 | Kubernetes ServiceAccount | API 신원 발급자가 아님 |
| 단기 SA token / RoleBinding lease | OpenBao Kubernetes secrets engine 또는 GitOps/controller | 요청 전 자격 판정 가능 |
| “누가 이 secret/template/resource를 받을 수 있나” | SpiceDB CheckPermission | 핵심 책임 |
| 유저/에이전트/고객/프로젝트별 임시 위임 | SpiceDB relationship + caveat | 핵심 책임 |
| “누가 deployment rollout/log/event evidence를 볼 수 있나” | SpiceDB deployment_evidence#view | 핵심 책임 |
권장 흐름은 다음과 같다.
Pomerium / Keycloak principal -> SpiceDB CheckPermission -> workload는 SPIRE JWT-SVID로 OpenBao JWT auth -> 허용된 경우에만 OpenBao 단기 secret/token 또는 stack seed material 발급 -> ESO 또는 controller가 Kubernetes Secret/ServiceAccount/RoleBinding 투영 -> TTL 만료 또는 revoke 시 OpenBao/ESO/controller가 회수금지할 등식:
SpiceDB allow == Kubernetes API allowSpiceDB relation == Secret materializationArmory loadout == human traffic feature flagSPIRE SVID == secret value
Deployment evidence 관찰도 같은 규칙을 따른다. Kubernetes Role은 deployments, pods,
pods/log, events 같은 최소 evidence read verb만 부여한다. 실제 사용자/에이전트가
해당 관찰 표면을 볼 자격은 SpiceDB deployment_evidence#view로 판정한다.
브릿지는 명시적이어야 한다. 앱 내부 CheckPermission, Pomerium policy,
admission controller, controller/operator, CI/CD preflight 중 하나가
SpiceDB 판정을 실제 생성/발급/적용 전에 호출해야 한다.
5) 프로비저닝 모드
Section titled “5) 프로비저닝 모드”각 스택의 directory_projection_protocol + account_materialization_strategy 조합으로 결정된다.
flowchart TD
start["접근 발생"] --> q1{directory_projection_protocol}
q1 -- scim --> scim["SCIM 표준 push<br/>(datahub / nexus / windmill / langboard)"]
q1 -- api --> api["native API 투영<br/>(penpot / daytona)"]
q1 -- groups --> grp["OIDC groups claim<br/>(argocd / headlamp / k3s / keycloak)"]
q1 -- api_reference_only --> ref["참조만, 로컬 권한<br/>(dokploy / novu)"]
scim --> mat{account_materialization_strategy}
api --> mat
grp --> mat
ref --> mat
mat -- preprovisioned --> pre["사전 생성"]
mat -- jit --> jit["접근 시점 생성"]
mat -- federated_session --> fed["세션만 연합"]
mat -- local_invite_or_api_key --> loc["로컬 초대 / API key"]
scim: 표준 SCIM 지원 솔루션jit: 로그인/접근 시점 API 기반 즉시 생성poll: 비표준 솔루션 증분 동기화(meta.lastModified커서)project: 프로젝트 단위 권한/리소스 동기화
5) 시크릿/토큰
Section titled “5) 시크릿/토큰”- 모든 런타임 시크릿은 OpenBao 우선 조회
- env 평문은 bootstrap/운영 전환용 입력으로만 사용
- 참조 포맷은
ref:<scope>:<key>이며, catalog 안의${FRACTALOPS_*}플레이스홀더는 런타임에resolve_env_or_secret로 해석된다(코드:stack_identity_contract_catalog.py). - 토큰 회전은 OpenBao 원문 값을 노출하지 않는 rotation envelope 계약으로 추적한다.
- 회전 envelope은 owner agent, EOS/controller, active generation, consumer, activation step, fingerprint를 기록한다.
- connector audit는 민감 토큰이 raw secret만 갖고 envelope 없이 운용되는 상태를 실패로 처리한다. (자세한 내용: OpenBao 요구사항)
6) SSOT 우선순위
Section titled “6) SSOT 우선순위”- operation payload / explicit input
- OpenBao scope secret
- env file
- default value (개발모드 한정)
7) 관측/감사
Section titled “7) 관측/감사”- 모든 동기화/실패는 audit event로 남긴다.
- unresolved 상태(예: githubUserId 누락)는
pending으로 분류하고 reason code를 강제한다.
8) 스택 계약 필드 사전
Section titled “8) 스택 계약 필드 사전”identity-contract catalog가 각 스택에 부여하는 표준 필드다. 모든 스택 문서는 이 필드를
자기 값으로 구체화한다(StackIdentityContract 도메인 모델과 1:1).
| 필드 | 의미 | 대표 값 |
|---|---|---|
authentication_authority | 로그인 책임 주체 | external_idp, stack_local, portal, keycloak |
authentication_protocol | 인증 프로토콜 | oidc, oidc_client_credentials, email_password, admin_secret_console, portal_session |
session_trust_adapter | edge 세션 신뢰 방식 | pomerium, pomerium_edge_gate, portal, local_loopback |
identity_assertion_mode | 주체 주장 전달 방식 | oidc_claims, forwarded_jwt_assertion, edge_authenticated_local_session, portal_claims |
directory_projection_protocol | 디렉터리 투영 방식 | scim, api, groups, oidc_claims, api_reference_only |
entitlement_projection_mechanism | 권한 투영 메커니즘 | project_team_api, scim_group_projection, oidc_group_rbac, … |
account_materialization_strategy | 계정 실체화 시점 | preprovisioned, jit, federated_session, local_invite_or_api_key, service_account |
projection_scope | 투영 범위 | project, workspace, cluster, realm, registry, service |
verified_limitations | 실측된 제약(증거 URL 포함) | dokploy/novu/redpanda에 존재 |