CNAP 클라우드 플랫폼 GitOps 대시보드를 직접 체험해보세요  —  실시간 CI/CD, 모니터링, 보안 관제를 한 곳에서     CNAP 클라우드 플랫폼 GitOps 대시보드를 직접 체험해보세요  —  실시간 CI/CD, 모니터링, 보안 관제를 한 곳에서     CNAP 클라우드 플랫폼 GitOps 대시보드를 직접 체험해보세요  —  실시간 CI/CD, 모니터링, 보안 관제를 한 곳에서     CNAP 클라우드 플랫폼 GitOps 대시보드를 직접 체험해보세요  —  실시간 CI/CD, 모니터링, 보안 관제를 한 곳에서    

Keycloak

Kubernetes에서 OpenID Connect / OAuth 2.0 기반 SSO 서버를 운영합니다.

  • 버전: Keycloak 26.5.2, Helm Chart codecentric/keycloakx 7.1.9
  • 네임스페이스: security
  • 접속 URL: https://keycloak.cnapcloud.com (사용자), https://keycloak-admin.cnapcloud.com (관리자)
  • 의존성: CNPG Cluster (2-04-cnpg-cluster.md), cert-manager (1-03-cert-manager.md), ingress-nginx (1-04-ingress-nginx.md)

1. 개요

Keycloak은 OpenID Connect / OAuth 2.0 / SAML 프로토콜을 지원하는 오픈소스 SSO 서버입니다. 이 환경에서는 Grafana, RabbitMQ 등 모든 서비스의 인증·인가를 단일 cnap Realm에서 관리합니다.

공식 Keycloak 이미지(quay.io/keycloak/keycloak)와 codecentric/keycloakx Helm Chart를 사용하며, 스토리지는 외부 CNPG PostgreSQL 클러스터에 위임합니다. SPI 확장(keycloak-extensions-spi, keycloak-metrics-spi)은 init container를 통해 공식 이미지의 providers 경로에 주입합니다.

Realm 구성은 keycloak-config-cli Job이 담당하며, ArgoCD 환경에서는 PostSync Hook으로 자동 실행됩니다.


2. 사전 요구사항

  • CNPG Cluster: database 네임스페이스에 keycloak 데이터베이스 및 keycloak 사용자 생성 완료 (2-04-cnpg-cluster.md)
  • TLS Secret: security 네임스페이스에 cnapcloud.com-tls Secret 존재
  • DNS 등록:
    <INGRESS_LB_IP>  keycloak.cnapcloud.com
    <INGRESS_LB_IP>  keycloak-admin.cnapcloud.com
    

3. 디렉터리 구조

keycloak-x/
├── Makefile
├── config/
│   └── cnap-realm-config.json       # cnap Realm 전체 설정 내보내기 (참고용)
└── kustomize/
    ├── base/
    │   └── helm/
    │       └── keycloakx/           # codecentric/keycloakx Helm Chart (make pull로 다운로드)
    └── overlays/
        └── dev/
            ├── kustomization.yaml   # Kustomize 진입점, namespace: security
            ├── helm/
            │   ├── helm-chart.yaml  # HelmChartInflationGenerator 설정
            │   └── values.yaml      # Keycloak Helm Chart 커스터마이징
            ├── secrets/
            │   └── sops/
            │       └── keycloak.enc.env     # SOPS 암호화된 시크릿 (db-password, admin-password)
            └── resources/
                ├── keycloak-sops-secrets.yaml   # SopsSecretGenerator — keycloak-secrets Secret 생성
                ├── keycloak-config-cli/
                │   ├── configmap.yaml   # Realm JSON 설정 (master, cnap)
                │   └── job.yaml         # keycloak-config-cli Job (ArgoCD PostSync Hook)
                ├── user-storage/
                │   ├── deployment.yaml  # 사용자 스토리지 SPI 서비스
                │   └── service.yaml
                └── inicis-mock/
                    ├── deployment.yaml  # Inicis 결제 Mock 서버 (dev 전용)
                    ├── service.yaml
                    └── ingress.yaml     # inicis.cnapcloud.com

4. 배포

4.1 Namespace 생성

cd keycloak-x
make namespace

4.2 패키지 준비

codecentric/keycloakx Helm Chart를 로컬에 다운로드합니다.

make pull

kustomize/base/helm/keycloakx/에 Chart v7.1.9가 저장됩니다.

4.3 배포 설정

kustomize/overlays/dev/helm/values.yaml의 주요 설정입니다.

데이터베이스 연결

외부 CNPG PostgreSQL 클러스터에 연결합니다. 패스워드는 keycloak-secrets Secret을 참조합니다.

database:
  vendor: postgres
  hostname: pg-cluster-rw.database.svc
  port: 5432
  database: keycloak
  username: keycloak
  existingSecret: keycloak-secrets
  existingSecretKey: db-password

관리자 계정

Keycloak 26.x 이상에서는 bootstrap 환경 변수로 초기 관리자를 설정합니다. 이 계정은 최초 기동 시에만 생성되며, 이후에는 DB에서 직접 관리됩니다. 패스워드는 keycloak-secrets Secret을 참조합니다.

extraEnv: |
  - name: KC_BOOTSTRAP_ADMIN_USERNAME
    value: "admin"
  - name: KC_BOOTSTRAP_ADMIN_PASSWORD
    valueFrom:
      secretKeyRef:
        name: keycloak-secrets
        key: admin-password
  - name: KC_HOSTNAME
    value: "https://keycloak.cnapcloud.com"
  - name: KC_HOSTNAME_ADMIN
    value: "https://keycloak-admin.cnapcloud.com"

KC_OPTIMIZED 설정

KC_OPTIMIZED=false는 기동 시 빌드 단계를 실행합니다. 환경 변수나 proxy 설정을 변경할 때는 false로 유지하고, 설정이 안정화된 후 true로 변경하면 기동 시간이 단축됩니다.

SPI 확장 주입

init container가 공식 이미지의 /opt/keycloak/providers/에 JAR을 복사합니다.

extraInitContainers: |
  - name: keycloak-extensions-spi
    image: cnapcloud/keycloak-extensions-spi:26.5.2-1
    command:
      - sh
      - -c
      - |
        cp /resources/spi/keycloak-extensions-spi-1.0.0-SNAPSHOT.jar /opt/keycloak/providers/
        cp /resources/spi/keycloak-metrics-spi-7.0.0.jar /opt/keycloak/providers/
    volumeMounts:
      - name: providers
        mountPath: /opt/keycloak/providers

Realm 구성 (keycloak-config-cli)

resources/keycloak-config-cli/configmap.yaml에 Realm JSON을 정의합니다.

// master.json — frontendUrl은 관리자 콘솔 도메인
{ "realm": "master", "enabled": true,
  "attributes": { "frontendUrl": "https://keycloak-admin.cnapcloud.com" } }

// cnap.json — frontendUrl은 사용자 도메인
{ "realm": "cnap", "enabled": true,
  "attributes": { "frontendUrl": "https://keycloak.cnapcloud.com" } }

resources/keycloak-config-cli/job.yaml의 Job이 위 ConfigMap을 Keycloak에 적용합니다. ArgoCD 환경에서는 PostSync Hook으로 자동 실행되며, 그 외 환경에서는 make apply 후 Job을 수동으로 재실행합니다.

4.4 시크릿 암호화

secrets/sops/keycloak.enc.env에 평문으로 작성 후 SOPS로 암호화합니다.

db-password=<DB 패스워드>
admin-password=<Keycloak 관리자 패스워드>
cd kustomize/overlays/dev/secrets/sops
sops --encrypt --pgp <GPG_FINGERPRINT> \
  --unencrypted-suffix _unencrypted \
  --in-place keycloak.enc.env

참고: GPG Fingerprint는 gpg --list-secret-keys로 확인합니다. ArgoCD가 사용하는 키와 동일한 키를 사용해야 합니다.

암호화된 파일은 Git에 커밋하고, 평문 패스워드는 커밋하지 않습니다.

4.5 배포 실행

cd keycloak-x
make preview   # 생성될 매니페스트 확인
make apply     # 배포

5. 설치 후 검증

5.1 Realm 생성 확인

keycloak-config-cli Job 완료 여부를 확인합니다.

kubectl get job keycloak-config-cli -n security

예상 결과: COMPLETIONS: 1/1

https://keycloak-admin.cnapcloud.com에 접속하여 좌측 드롭다운에서 mastercnap Realm이 생성되었는지 확인합니다.

5.2 OIDC Well-Known 엔드포인트 확인

curl -s https://keycloak.cnapcloud.com/realms/cnap/.well-known/openid-configuration \
  | jq '{issuer, authorization_endpoint, token_endpoint}'

issuer, authorization_endpoint, token_endpoint가 반환되면 정상입니다.

5.3 액세스 토큰 발급 테스트

admin-cli (public client)로 cnap realm 사용자 인증을 확인합니다.

curl -s -X POST \
  -d "client_id=admin-cli" \
  -d "grant_type=password" \
  -d "username=<username>" \
  -d "password=<password>" \
  https://keycloak.cnapcloud.com/realms/cnap/protocol/openid-connect/token \
  | jq '{expires_in, token_type}'

예상 결과: "expires_in": 300, "token_type": "Bearer"

5.4 Metrics 엔드포인트 확인

curl -s https://keycloak.cnapcloud.com/realms/master/metrics | head -5

jvm_memory_objects_pending_finalization, keycloak_user_event_* 등 Prometheus 형식 메트릭이 반환되면 정상입니다.


6. 운영

6.1 Client 등록 (Grafana 연동 예시)

Keycloak Admin → cnap realm → ClientsCreate client

Settings 탭

항목
Client IDgrafana
Client typeOpenID Connect
Client authenticationOn
Home URLhttps://grafana.cnapcloud.com
Valid redirect URIs*
Valid post logout redirect URIs+
Web originshttps://grafana.cnapcloud.com

Credentials 탭에서 Client Secret을 복사합니다.

6.2 Groups Mapper 설정

Client Scope 생성

Keycloak Admin → cnap realm → Client scopesCreate client scope

항목
Namegroups
TypeDefault
ProtocolOpenID Connect
Include in token scopeOn

Mapper 추가

groups scope → MappersAdd mapperBy configurationGroup Membership

항목
Namegroups
Token Claim Namegroups
Full group pathOff
Add to ID tokenOn
Add to access tokenOn
Add to userinfoOn

Client에 Scope 추가

groups scope를 Default로 설정하면 이후 생성하는 Client에는 자동으로 포함됩니다. Scope 생성 이전에 만든 Client(예: grafana)에는 수동으로 추가합니다.

ClientsgrafanaClient scopesAdd client scopegroupsDefault

6.3 그룹 생성

GroupsCreate group에서 아래 그룹을 생성합니다.

그룹용도
admin관리자 권한
editor편집 권한
viewer읽기 권한

신규 사용자의 기본 그룹을 지정하려면: Realm settingsUser registrationDefault groupsviewer 추가

6.4 사용자 생성

UsersCreate new user

항목
Usernamealice
Emailalice@cnapcloud.com
Email verifiedOn

사용자 생성 후:

  • Groups 탭 → Join Group → 원하는 그룹 선택
  • Credentials 탭 → Set password → Temporary: Off

6.5 Metrics Event Listener 설정

keycloak-metrics-spi를 통해 이벤트 기반 메트릭을 수집하려면 Realm별로 설정합니다.

Keycloak Admin → cnap realm → Realm settingsEventsEvent listenersmetrics-listener 추가

이 설정은 cnapcloud/keycloak-extensions-spi SPI가 설치된 경우에만 적용됩니다.


7. Troubleshooting

7.1 keycloak-config-cli Job 실패

증상: Job이 BackoffLimitExceeded 또는 Error 상태

원인 1: Keycloak 기동 대기 시간 초과. Job의 가용성 체크 타임아웃은 120s이며 backoffLimit: 3이므로, Keycloak 기동이 늦어지면 모든 재시도가 소진될 수 있음

해결 1: Job을 삭제 후 재적용합니다.

kubectl delete job keycloak-config-cli -n security
kubectl apply -f kustomize/overlays/dev/resources/keycloak-config-cli/job.yaml -n security

원인 2: 관리자 계정 불일치. Job의 KEYCLOAK_USER / KEYCLOAK_PASSWORDvalues.yamlKC_BOOTSTRAP_ADMIN_USERNAME / KC_BOOTSTRAP_ADMIN_PASSWORD가 다름

7.2 Provider JAR 미로드

증상: Realm settings → Events → Event listeners에 metrics-listener가 목록에 없음

원인: KC_OPTIMIZED=true 상태에서 providers가 변경된 경우 재빌드 없이 변경사항이 반영되지 않음

해결: KC_OPTIMIZED=false로 변경 후 재배포합니다.

extraEnv: |
  - name: KC_OPTIMIZED
    value: "false"

Keycloak 재기동 후 providers를 다시 스캔하면 metrics-listener가 목록에 나타납니다.

7.3 관리자 계정 로그인 실패

증상: https://keycloak-admin.cnapcloud.com에서 admin 로그인 실패

원인: bootstrap 관리자 계정은 최초 기동 시에만 생성되며 이후 values.yaml 변경이 반영되지 않음. DB에 저장된 비밀번호와 불일치

해결: kcadm.sh로 비밀번호를 재설정합니다.

kubectl exec -it keycloak-0 -n security -- /opt/keycloak/bin/kcadm.sh \
  config credentials \
  --server http://localhost:8080 \
  --realm master \
  --user admin \
  --password <현재_비밀번호>

kubectl exec -it keycloak-0 -n security -- /opt/keycloak/bin/kcadm.sh \
  set-password \
  --username admin \
  --new-password <새_비밀번호>

7.4 사용자 ingress 루트 접근 시 403

증상: https://keycloak.cnapcloud.com/에 접속 시 403 응답

원인: 정상 동작. values.yaml의 nginx configuration-snippet에서 루트(/) 접근을 의도적으로 차단. 루트 접근 시 /admin으로 리다이렉트되는 것을 방지하기 위한 설정

사용자는 연동된 애플리케이션에서 /realms/cnap/... 경로로 리다이렉트되어 접근합니다.


8. 제거

주의: Keycloak 데이터는 외부 CNPG 클러스터에 저장됩니다. Keycloak을 삭제해도 DB 데이터는 유지됩니다. DB까지 완전히 삭제하려면 CNPG에서 별도로 처리합니다.

cd keycloak-x
make delete

security 네임스페이스의 다른 서비스가 없을 경우에만 네임스페이스를 삭제합니다.

kubectl delete namespace security

부록. 체크리스트

배포 전:

  • CNPG keycloak 데이터베이스 및 사용자 생성 완료
  • security 네임스페이스에 cnapcloud.com-tls Secret 존재
  • DNS 등록 완료 (keycloak.cnapcloud.com, keycloak-admin.cnapcloud.com)

검증:

  • kubectl get job keycloak-config-cli -n securityCOMPLETIONS: 1/1 확인
  • https://keycloak-admin.cnapcloud.com 접속 — master, cnap Realm 생성 확인
  • OIDC Well-Known 엔드포인트 응답 확인
  • Metrics 엔드포인트 응답 확인

Keycloak 설정:

  • Client 등록 (각 서비스별)
  • groups Client Scope 설정 및 Client에 할당
  • 그룹 생성 (admin, editor, viewer)
  • 사용자 생성 및 그룹 할당
  • Metrics Event Listener (metrics-listener) 활성화