Skip to content

공통 계약 (Canonical Contract)

이 문서는 모든 스택 요구사항이 공통으로 지켜야 하는 계약을 정의한다. 각 스택 문서는 여기서 정의한 canonical 어휘/필드를 자기 스택의 실제 contract 값으로 구체화한다.

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.pyPomeriumTrustAdapter.
  • 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).
  • 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로 정규화한다.
  • 권한 원천은 /org/... group path + realm role 조합이다(canonical_group_root: /org).
  • 정책 엔진 입력은 canonical 필드만 사용:
    • principalType, groupPath, operation, resourceRef
  • operationcreate | 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.pyis_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 / SVIDSPIRE + Kubernetes ServiceAccount selector신원 발급자가 아님
Pod의 Kubernetes API 신원Kubernetes ServiceAccountAPI 신원 발급자가 아님
단기 SA token / RoleBinding leaseOpenBao 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 allow
  • SpiceDB relation == Secret materialization
  • Armory loadout == human traffic feature flag
  • SPIRE SVID == secret value

Deployment evidence 관찰도 같은 규칙을 따른다. Kubernetes Roledeployments, pods, pods/log, events 같은 최소 evidence read verb만 부여한다. 실제 사용자/에이전트가 해당 관찰 표면을 볼 자격은 SpiceDB deployment_evidence#view로 판정한다.

브릿지는 명시적이어야 한다. 앱 내부 CheckPermission, Pomerium policy, admission controller, controller/operator, CI/CD preflight 중 하나가 SpiceDB 판정을 실제 생성/발급/적용 전에 호출해야 한다.

각 스택의 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: 프로젝트 단위 권한/리소스 동기화
  • 모든 런타임 시크릿은 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 요구사항)
  1. operation payload / explicit input
  2. OpenBao scope secret
  3. env file
  4. default value (개발모드 한정)
  • 모든 동기화/실패는 audit event로 남긴다.
  • unresolved 상태(예: githubUserId 누락)는 pending으로 분류하고 reason code를 강제한다.

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_adapteredge 세션 신뢰 방식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에 존재