RBAC bootstrap / lockout 운영 매뉴얼 (D-hive-32)
hive 의 권한 모델은 verb-orthogonal RBAC (hive 서비스 페이지). 본 런북은 그 bootstrap / lockout 안전망의 운영 절차 — owner 가 모자라거나(quorum) 전부 사라졌을 때(lockout) 어떻게 권한을 회복하는가.
AI 요청 프롬프트
https://docs.axelabs.ai/ops/runbook/rbac-bootstrap 따라 hive RBAC lockout 복구해줘.
진행:
1. 상황 식별 — 단일 owner 위험(quorum) vs owner 0명(full lockout) vs 일반 role 부여 차단
2. 1차 = CLI break-glass (`hive role-assign`) — owner 1명 이상 남아있으면 이걸로 충분
3. owner 0명이면 DB break-glass (Layer 2) — 직접 SQL INSERT, 그 후 access_audit_log 수동 보강
4. 모든 step 결과를 받고 다음 — 특히 owner 신원 확인 후 grant
5. 복구 후 owner ≥2 재확인 (`hive role-list`) + (선택) /ops/updates Ship Log 한 줄본인 AI session = Claude Code / Cursor / Claude.app / 기타. 본문은 사람이 직접 read 도 가능.
전제 — 실행 위치: 프로덕션 CLI 는 컨테이너 안에서 돈다. 모든
hive <cmd>는 호스트에서docker exec hive-mcp-blue hive <cmd>로 감싼다 (active blue 기준 — green 이 active 면 컨테이너명만 교체).hive는 bare console-script entrypoint (pyproject [project.scripts] hive = "hive.cli:main").
1. 모델 요약 — 무엇을 보호하는가
| 구성 | 값 | 소스 |
|---|---|---|
| owner role 이름 | owner (구 admin noun 대체) | rbac.OWNER_ROLE |
| owner 최소 인원 | 2 (quorum) | rbac.OWNER_MIN_COUNT = 2 |
| quorum 강제 시점 | revoke 시에만 — 마지막에서 2번째 owner 해지 차단 | rbac.revoke_role → OwnerQuorumError |
| assign 시점 | 강제 없음 — idempotent, 무제한 grant 가능 | rbac.assign_role |
| 역할 데이터 | shared.user_roles (human) / shared.system_actor_permissions (system) | alembic 0006_rbac_shared |
핵심: quorum 은 해지에서만 막힌다. owner 를 추가하는 길(assign)은 절대 막히지 않으므로, owner 가 1명이라도 남아 있으면 lockout 이 아니다 — CLI 로 2번째 owner 를 즉시 채워 quorum 을 복구하면 된다. lockout 은 owner 가 0명일 때만 발생하고, 그때만 Layer 2(DB break-glass)가 필요하다.
2. Layer 1 — owner quorum (≥2) 자동 안전망
revoke_role(role_name="owner") 은 해지 후 active owner 가 OWNER_MIN_COUNT 미만이 되면
OwnerQuorumError 를 던지고 트랜잭션을 롤백한다 (rbac.py revoke_role, active - 1 < OWNER_MIN_COUNT). 즉 마지막 1~2번째 owner 는 시스템이 스스로 보호한다.
확인:
docker exec hive-mcp-blue hive role-list --entity axec
# members 배열에서 role=owner 의 active 인원 수 확인 — 2명 미만이면 즉시 보강(아래)3. Break-glass — owner 보강 / lockout 복구
Layer 2a — CLI break-glass (hive role-assign) — owner 가 1명 이상 남아있을 때 1차 경로
docker exec hive-mcp-blue hive role-assign \
--entity axec --email [email protected] --role ownerassign_role 은 idempotent (이미 active 면 그대로 반환). 이 CLI 경로는 quorum 검사를 받지 않으므로 owner 1명만 남은 위험 상태에서도 즉시 2번째 owner 를 채울 수 있다.
⚠️ audit provenance — 정확히 이해할 것 (자주 오해되는 지점) CLI
role-assign의 grant 는shared.access_audit_log에 기록되지 않는다.assign_role(rbac.py) 은 오직shared.user_roles에 한 행을 INSERT 하고, 그 행의granted_by = 'cli:role-assign'+granted_at = now()가 유일한 (row-level) audit provenance 다.access_audit_log는 MCP 도구 래퍼rbac.audited_session만 쓴다 (ok/denied/errorINSERT) — CLI 는 이 래퍼를 거치지 않고 DB trigger 도 없다 (alembic0006에 trigger 없음). 따라서 CLI grant 는access_audit_log행을 남기지 않는다. 통합 break-glass audit trail 을 원하면 Layer 2b 와 동일하게 아래access_audit_logINSERT 를 수동으로 추가하라 (권장).
확인 — grant 가 user_roles 에 남았는지 (row-level provenance):
docker exec hive-postgres psql -U hive -d hive -c \
"SELECT user_email, role_id, granted_by, granted_at
FROM shared.user_roles
WHERE entity_id='axec' AND revoked_at IS NULL
ORDER BY granted_at DESC LIMIT 5;"Layer 2b — DB break-glass (owner 0명 = full lockout)
owner 가 한 명도 남아 있지 않으면 grant hive.role 권한을 가진 주체가 없어 CLI/MCP grant 자체가 거부될 수 있다. 이때만 DB 에 직접 INSERT 한다.
# 1) owner role 의 id 확인 (entity 별로 seed 됨)
docker exec hive-postgres psql -U hive -d hive -c \
"SELECT id, name FROM shared.roles WHERE entity_id='axec' AND name='owner';"
# 2) owner 직접 부여 — partial unique index uq_user_roles_active
# (entity_id, user_email, role_id) WHERE revoked_at IS NULL 가 중복 active 를 막음
docker exec hive-postgres psql -U hive -d hive -c \
"INSERT INTO shared.user_roles (entity_id, user_email, role_id, granted_by)
SELECT 'axec', '[email protected]', r.id, 'break-glass:db'
FROM shared.roles r
WHERE r.entity_id='axec' AND r.name='owner'
ON CONFLICT DO NOTHING;"복구는 항상 owner ≥2 로 끝내라 (단일 owner 면 다시 같은 lockout 위험). 2명 채운 뒤 hive role-list 로 재확인.
DB break-glass 의 audit trail — 수동 보강 (필수 규율)
DB INSERT 도, CLI grant 와 마찬가지로 access_audit_log 에 자동 기록되지 않는다. row-level provenance 는 user_roles.granted_by = 'break-glass:db' 가 담당하지만, 통합 audit trail 을 위해 같은 트랜잭션 맥락에서 access_audit_log 에 수동 행을 남긴다:
docker exec hive-postgres psql -U hive -d hive -c \
"INSERT INTO shared.access_audit_log
(entity_id, actor_kind, actor_id, verb, resource, tool_name, status, detail)
VALUES
('axec','human','break-glass','grant','hive.role','break-glass:db','ok',
'{\"reason\":\"owner lockout recovery\",\"granted_to\":\"[email protected]\"}'::jsonb);"status 는 ok|error|denied 만 허용 (CHECK 제약 ck_access_audit_status), verb 는 6 verb CHECK 를 따른다.
Layer 3 — Legal escalation
owner 신원·권한 회복을 DB 레벨로도 정당화할 수 없는 경우 (예: 회사 지배구조 분쟁, 운영자 전원 부재) → 회사 법무 절차. break-glass 는 어디까지나 신뢰된 운영자가 신원 확인된 owner 를 회복시키는 길이지, 권한 분쟁의 판정 수단이 아니다.
4. 복구 후 체크리스트
hive role-list --entity <entity>→ owner active 인원 ≥2 확인.- break-glass 를 썼다면
granted_by(cli:role-assign/break-glass:db) 가user_roles에 남았는지 확인 + (DB break-glass 면)access_audit_log수동 행 확인. - quorum 이 정상화됐으면 임시 owner 는 정상 거버넌스로 revoke (단, 항상 ≥2 유지하며 단계적으로).
- (선택)
/ops/updatesShip Log 또는 known-gaps 에 lockout 사건 한 줄 기록.
5. seed (참고) — 신규 entity 의 owner ≥2 보장
신규 entity onboarding 시 처음부터 owner 2명을 강제하면 lockout 자체가 잘 안 생긴다. seed_roles 는 OWNER_MIN_COUNT 미만의 distinct owner email 을 받으면 거부한다.
docker exec hive-mcp-blue hive seed-roles \
--entity axec --owner [email protected] --owner [email protected]
# idempotent. customers.yaml user_entity_map 사용자에 member 자동 부여 (--no-members 로 끄기).