본문으로 건너뛰기

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
DBPostgreSQL (jdbc:postgresql://postgresql:5432/keycloak)
HTTP Path/auth
Hostnamehttps://gend.local:8443/auth
NodePort31800

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)

설정
PKCES256
Direct Access Grants활성화
Redirect URIshttp://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-clipublic 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 하게 다음을 수행합니다:

  1. publicClient=false + serviceAccountsEnabled=true 전환
  2. 서비스 계정 (service-account-admin-cli) 에 master realm-level admin role 부여 (super-admin — sub-realm 관리 권한 포함)
  3. client_secret 을 K8s Secret gend-api-keycloak-admin (키: GEND_KEYCLOAK_ADMIN_CLIENT_SECRET) 으로 주입 → gend-api envFrom 으로 자동 로드
master/admin-cli 회복 데드락

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 = OffVERIFY_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 연동이 있거나 아직 한 번도 로그인하지 않은 계정은 건수만 참고로 표시한다. :::

관련 문서