Keycloak 네임스페이스 이관 운영 런북
GenD 접근권한 정본 모델(access-control-canonical-model.md)의 P1.2 단계입니다. 기존 flat 그룹(예: /sales, /CONFIDENTIAL)을 워크스페이스 네임스페이스 그룹으로 이관합니다.
- 부서:
/tenants/<slug>/<dept>(L1 부서 칸막이) - clearance:
/tenants/<slug>/clearance/<level>(LLM 처리 가능 데이터 등급)
선행: 워크스페이스 격리(
workspace-isolation.md)와 JWT 그룹 경로 파서(P1.1)가 이미 배포되어 있어야 합니다. 파서는 신규 경로가 없으면 기존/tenants/<slug>멤버십만 인식하므로, 본 이관은 순수 additive 입니다.
(a) 왜 additive 인가 — 인증이 깨지지 않는 이유
- 기존
/tenants/<slug>멤버십은 그대로 둡니다. sync 는 그 아래에clearanceintermediate +PUBLIC/CONFIDENTIAL/TOP_SECRET서브그룹 구조만 생성합니다. - 멤버십·role mapping 은 절대 건드리지 않습니다. 그룹을 만들어도 사용자는 자동으로 들어가지 않으므로, sync 단독으로는 누구의 권한도 변하지 않습니다.
- 멱등(idempotent) — 서브그룹이 이미 있으면
POST0건. 동시 sync 의409 Conflict는 재조회로 무시합니다. - clearance 레벨은
SecurityLevelEnum(PUBLIC/CONFIDENTIAL/TOP_SECRET) 단일 진실 공급원을 그대로 따릅니다 — 런북에서 레벨 문자열을 손으로 바꾸지 마세요.
(b) 구조 생성 — sync 실행
구조 생성은 코드가 합니다. admin 권한으로 sync 엔드포인트를 호출하면 워크스페이스마다 /tenants/<slug> + clearance intermediate + 3 레벨 서브그룹이 보장됩니다.
# gend-api 로 port-forward (외부 ingress 는 /api 만 라우팅)
kubectl -n gend port-forward svc/gend-api 18000:8000 &
# 전체 active 워크스페이스 일괄 sync (drift 복구용)
curl -sS -X POST http://localhost:18000/api/v1/workspaces/sync-keycloak/all \
-H "Authorization: Bearer $ADMIN_JWT" | jq .
# 단일 워크스페이스만
curl -sS -X POST http://localhost:18000/api/v1/workspaces/$WORKSPACE_ID/sync-keycloak \
-H "Authorization: Bearer $ADMIN_JWT" | jq .
부서 그룹(/tenants/<slug>/<dept>)은 열거 불가하므로 sync 시점에 만들지 않습니다. P1.3 멤버 다이얼로그가 부서 추가 시점에 ensure_dept_group(slug, dept) 를 통해 on-demand 로 생성합니다. 수동으로 미리 만들려면 아래 (c) 의 kcadm create 를 사용하세요.
admin 엔드포인트 응답(WorkspaceSyncReport)은 tenants_group_created·tenant_group_created 와 함께 clearance_groups_created: list[str] 를 반환합니다 — 이번 sync 에서 신규 생성된 clearance 레벨 이름 목록(모두 이미 존재하면 빈 배열)입니다. 즉 위 curl ... | jq . 출력만으로 clearance 구조가 새로 만들어졌는지 바로 확인할 수 있습니다. 단, 권위 있는 교차 확인은 여전히 아래 (d) 의 kcadm get groups 로 실제 그룹 트리를 보는 것입니다.
(c) 멤버십 수동 이관 — kcadm
kcadm.sh 자격증명은 keycloak-setup.md 절차를 따릅니다(service-account-admin-cli, master realm admin role). 아래는 기존 flat 그룹의 사용자를 네임스페이스 그룹으로 추가 join 하는 절차입니다. flat 그룹은 아직 그대로 둡니다((f) 참조).
# 0) kcadm 로그인 (예시 — 환경에 맞게 조정)
kcadm.sh config credentials --server $KC_URL --realm master \
--client admin-cli --secret $GEND_KEYCLOAK_ADMIN_CLIENT_SECRET
REALM=gend
# 1) 대상 네임스페이스 그룹 id 확인 (이미 sync 로 생성됨)
# /tenants/<slug>/clearance/CONFIDENTIAL 의 id 를 얻는다.
kcadm.sh get groups -r $REALM --query search=tenants
# → tenants → <slug> → clearance → CONFIDENTIAL 트리에서 id 추출
TARGET_GID=<위에서 확인한 CONFIDENTIAL 서브그룹 id>
# 2) 기존 flat 그룹(/CONFIDENTIAL)의 멤버 나열
FLAT_GID=$(kcadm.sh get groups -r $REALM --query search=CONFIDENTIAL \
--fields id,name | jq -r '.[] | select(.name=="CONFIDENTIAL") | .id')
kcadm.sh get groups/$FLAT_GID/members -r $REALM --fields id,username
# 3) 각 사용자를 네임스페이스 그룹에 join (PUT — 멱등, 추가만)
# NOTE: 루프 변수에 bash readonly builtin 인 `UID` 를 쓰지 말 것 → `MEMBER_UID`.
for MEMBER_UID in <member uuid 목록>; do
kcadm.sh update users/$MEMBER_UID/groups/$TARGET_GID -r $REALM \
-s realm=$REALM -s userId=$MEMBER_UID -s groupId=$TARGET_GID -n
done
# 4) 부서 그룹도 동일하게 (예: /sales → /tenants/<slug>/sales)
# 부서 그룹이 없으면 먼저 생성:
PARENT_GID=<`/tenants/<slug>` 그룹 id>
kcadm.sh create groups/$PARENT_GID/children -r $REALM -s name=sales
이관은 워크스페이스 단위로 끊어서 진행하고, 각 단계 후 (d) 검증을 돌려 실제 멤버십을 확인하세요. 한 번에 전 사용자 이관하지 말 것 — 문제 발생 시 영향 범위를 좁게 유지합니다.
(d) 검증
# 네임스페이스 트리 구조 확인 — clearance intermediate + 3 레벨, 부서 서브그룹
kcadm.sh get groups -r gend --query search=tenants \
| jq '.[] | select(.name=="tenants") | .subGroups'
# 특정 사용자의 그룹 멤버십 확인 (네임스페이스 경로가 보여야 함)
kcadm.sh get users/$MEMBER_UID/groups -r gend --fields path
# 토큰 검증 — 로그인 후 JWT 의 groups claim 에
# /tenants/<slug>/clearance/<level>, /tenants/<slug>/<dept> 가 포함되는지 확인
JWT 파서(P1.1)가 새 경로를 인식하면, workspace_clearances / workspace_dept_groups 가 토큰 페이로드에 채워집니다.
(e) 롤백 노트
- 네임스페이스 그룹을 삭제하면 그 그룹에 직접 join 한 사용자의 해당 멤버십도 즉시 사라집니다 (Keycloak group 삭제는 멤버십 cascade).
- 그러나 (c) 단계에서 flat 그룹의 멤버십은 그대로 남겨두므로(추가 join 만 함), 네임스페이스 그룹을 지워도 flat 그룹이 여전히 권한의 정본(authoritative) 입니다. 즉 이관을 되돌려도 사용자는 원래 권한을 유지합니다.
- 롤백 = 네임스페이스 서브그룹 삭제 후, 시스템이 다시 flat 그룹만 바라보도록 두면 됩니다(P5 폐기 전까지는 둘 다 유효).
# 롤백: 특정 워크스페이스의 clearance 네임스페이스 제거 (예시)
CLEARANCE_GID=<`/tenants/<slug>/clearance` 그룹 id>
kcadm.sh delete groups/$CLEARANCE_GID -r gend
(f) flat 그룹은 아직 지우지 마세요 (P5)
기존 flat 그룹(/sales, /CONFIDENTIAL 등)을 지금 삭제하지 마세요. P5(폐기 단계) 전까지 flat 그룹은 권한의 정본입니다. 네임스페이스 이관은 additive 병행 단계이며, flat → 네임스페이스 전환이 모든 사용자·모든 라우터에서 검증되기 전에 flat 그룹을 지우면 권한이 사라집니다. 폐기는 별도 단계(P5)에서 일괄 수행합니다.
관련 문서
- 접근권한 정본 모델:
access-control-canonical-model.md - 워크스페이스 격리:
workspace-isolation.md - Keycloak admin-cli 설정:
../auth/keycloak-setup.md