Keycloak SSO
Keycloak을 사용한 GenD SSO 인증 서버 구성 및 관리 방법입니다.
개요
GenD는 Keycloak 26.2를 IdP로 사용합니다. gend Realm에 사용자, 역할, 클라이언트를 정의하며, PostgreSQL을 백엔드 DB로 사용합니다.
배포
Keycloak은 infra/keycloak/deployment.yaml로 배포됩니다.
kubectl apply -f infra/keycloak/deployment.yaml
kubectl apply -f infra/keycloak/openldap.yaml # LDAP 연동 (선택)
주요 구성
| 설정 | 값 |
|---|---|
| 이미지 | quay.io/keycloak/keycloak:26.2 |
| DB | PostgreSQL (jdbc:postgresql://postgresql:5432/keycloak) |
| HTTP Path | /auth |
| Hostname | https://gend.local:8443/auth |
| NodePort | 31800 |
Realm 설정
infra/keycloak/realm-export.json에 사전 정의된 Realm 구성:
| 설정 | 값 |
|---|---|
| Access Token 수명 | 1800초 (30분) |
| SSO Session 유휴 | 1800초 |
| SSO Session 최대 | 36000초 (10시간) |
| Brute Force 보호 | 활성화 (5회 실패 후 잠금) |
클라이언트 구성
gend-ui (Public Client)
| 설정 | 값 |
|---|---|
| PKCE | S256 |
| Direct Access Grants | 활성화 |
| Redirect URIs | http://localhost:5173/*, https://gend.genon.ai/* 등 |
gend-api (Confidential Client)
서비스 계정 활성화, audience 검증에 사용됩니다.
master/admin-cli (Admin REST 클라이언트, #900)
gend-api 의 KeycloakAdminService 가 /api/v1/admin/users 등 관리 API 처리를 위해 master realm 의 client_credentials grant 로 토큰을 획득합니다. 빌트인 admin-cli 가 public client 인 채로 두면 unauthorized_client: Public client not allowed to retrieve service account 401 으로 /admin/users 502 회귀.
배포 스크립트(infra/scripts/12-deploy-auth.sh, infra/azure-deploy/scripts/02-deploy-k8s.sh) 가 매 배포마다 idempotent 하게 다음을 수행합니다:
publicClient=false+serviceAccountsEnabled=true전환- 서비스 계정 (
service-account-admin-cli) 에 master realm-leveladminrole 부여 (super-admin — sub-realm 관리 권한 포함) client_secret을 K8s Secretgend-api-keycloak-admin(키:GEND_KEYCLOAK_ADMIN_CLIENT_SECRET) 으로 주입 → gend-apienvFrom으로 자동 로드
admin-cli 를 confidential 로 전환한 뒤 gend-api-keycloak-admin Secret 을 잃으면 kcadm 의 password grant 도 차단되어 인증 회복 불가. 회복은 PostgreSQL keycloak DB 의 client 테이블 직접 UPDATE 만 가능:
UPDATE client SET public_client=true, service_accounts_enabled=false
WHERE client_id='admin-cli'
AND realm_id=(SELECT id FROM realm WHERE name='master');
-- Keycloak Pod restart 후 재배포 스크립트 실행
따라서 client_secret 은 reuse-by-default — 매 배포마다 회전하지 않고, ROTATE_ADMIN_CLI_SECRET=1 환경변수 명시 시에만 회전합니다.
사용자 역할
| 역할 | 설명 |
|---|---|
admin | 전체 기능 접근 |
analyst | 읽기 + 쿼리 실행 |
viewer | 읽기 전용 |
gend-llm-internal | 내부 LLM 전체 모델 접근 |
gend-llm-confidential | 기밀 LLM 접근 |
gend-llm-external | 외부 LLM 제한 접근 |
관리자 접근
# Keycloak Admin Console
open http://localhost:31800/auth/admin
# 기본 계정: admin / <keycloak-admin-secret>
관리자가 만든 계정은 프로필을 채워야 로그인된다
gend realm 의 user profile 은 email · 이름(firstName) · 성(lastName) 을
user 역할에 필수로 걸어 둔다. 셀프 가입은 가입 폼이 이 값을 받으므로 문제가 없지만,
Admin Console·Admin REST API 로 계정을 만들면서 비워 두면 VERIFY_PROFILE 이
미완료 상태로 남는다.
GenD UI 는 로그인에 direct grant(grant_type=password)를 쓴다 — 브라우저 리다이렉트
플로우가 아니라서 미완료 필수 작업을 사용자가 스스로 해소할 화면이 없다. 그 결과
해당 계정은 아래 오류로 로그인 자체가 막힌다.
error="resolve_required_actions", reason="Account is not fully set up"
같은 이유로, 관리자가 특정 사용자에게 필수 작업(예: UPDATE_PASSWORD)을 걸어 두면
그 사용자도 GenD UI 로 로그인할 수 없게 된다. 현재 realm 은
resetPasswordAllowed=false 라 사용자 자력 복구도 불가하다.
:::tip 계정 생성 시 체크
email · 이름 · 성을 모두 채우고, Required user actions 를 비운 채
비밀번호를 Temporary = Off 로 설정한다. 이메일 확인됨(Email verified) 도 On
으로 둔다 — 현재 gend realm 은 Verify Email = Off 라 VERIFY_EMAIL 이 자동으로
붙지는 않지만, 그 설정을 켜는 순간 같은 증상이 재발한다.
이미 막힌 계정은 Admin Console 에서 프로필을 채우고 Required user actions 를 비우면 즉시 정상화된다. :::
자동 탐지 (v1.3+): 이 상태는 Admin Console 목록만 봐서는 드러나지 않는다 —
VERIFY_PROFILE 은 동적으로 평가돼 requiredActions 필드에 저장되지 않기
때문이다. 그래서 베타 로그 모니터가 6시간마다 계정을 전수 점검하고, 막힌 계정이
있으면 beta-log-alert 라벨로 이슈를 열어 알린다(해소되면 자동으로 닫는다).
수동 실행:
python3 scripts/check_keycloak_login_blocked.py
# → "✅ 로그인 막힌 계정 없음 (검사 88건)" 또는 계정별 사유 표
:::note SSO 사용자는 스스로 풀 수 있다
프로필이 비어 있어도 Google SSO 로 들어오면 막히지 않는다. 브라우저
리다이렉트 플로우는 VERIFY_PROFILE 을 대화형 화면으로 띄우므로 사용자가 그
자리에서 채우고 통과한다. 자력 복구가 불가능한 건 비밀번호로만 로그인하는
계정뿐이다(GenD UI 가 direct grant 를 쓰기 때문).
그래서 위 점검은 비밀번호 전용 계정만 알린다. SSO 연동이 있거나 아직 한 번도 로그인하지 않은 계정은 건수만 참고로 표시한다. :::