Cloud

KeyCloak SSO 실습

Hanhorang31 2025. 11. 23. 03:04

KeyClock SSO 학습 내용을 정리 합니다.

학습 내용은 CloudNet@ 가시다님이 진행하는 CI/CD스터디를 참고하였습니다.

 

기본 환경 구성

클러스터에 Jenkins와 ArgoCD를 배포하고 다음장에서 KeyCloak를 통해 연동하겠습니다.

  • Kind 클러스터 구성 + Ingree Nginx 컨트롤러 배포
# kind k8s 배포
kind create cluster --name myk8s --image kindest/node:v1.32.8 --config - <https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml

kubectl edit -n ingress-nginx deployments/ingress-nginx-controller
...
  - --enable-ssl-passthrough
  
  
# ingress 배포 확인
kubectl get deploy,svc,ep ingress-nginx-controller -n ingress-nginx
  • Jenkins 배포 + Ingress 설정
kubectl create ns jenkins

cat <http://jenkins.example.com"
  • 웹 접속 후 초기 설정 진행
  • ArgoCD 배포 + Ingress 설정

 

# 인증서 생성 
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout argocd.example.com.key \
  -out argocd.example.com.crt \
  -subj "/CN=argocd.example.com/O=argocd"
  

# argocd 네임스페이스 생성
kubectl create ns argocd

# tls 시크릿 생성
kubectl -n argocd create secret tls argocd-server-tls \
  --cert=argocd.example.com.crt \
  --key=argocd.example.com.key
  

# Argocd 구성 
cat < argocd-values.yaml
global:
  domain: argocd.example.com

server:
  ingress:
    enabled: true
    ingressClassName: nginx
    annotations:
      nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
      nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    tls: true
EOF


# 설치 : Argo CD v3.1.9
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd --version 9.0.5 -f argocd-values.yaml --namespace argocd

# 도메인 추가
echo "127.0.0.1 argocd.example.com" | sudo tee -a /etc/hosts

# 최초 접속 암호 확인
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d ;echo

ARGOPW=ghOYe-BIOkbPBRkx

# CLI 로그인 및 암호 변경
argocd login argocd.example.com --insecure --username admin --password $ARGOPW

argocd account update-password --current-password $ARGOPW --new-password qwe12345

 

 

KeyClock

SSO(싱글 사인온)란 사용자 한 번 로그인하면 여러 애플리케이션/서비스에 다시 로그인하지 않아도 되는 환경을 말합니다.

Keycloak은 오픈소스 IAM(Identity & Access Management) 솔루션으로, SSO를 포함해 OAuth2, OpenID Connect, SAML 프로토콜을 지원합니다.

또한, 유저 페더레이션 기능을 제공하여 존재하는 LDAP 또는 Active Directory 와 연계하여 사용이 가능합니다.

 

Keycloak 배포

kubectl create ns keycloak

cat <http://keycloak.example.com/admin"
  • realms 생성 : myrealm
  • users 생성 : miri - 암호 miri123

클러스터 내부에서 keyclock / Jenkins / argoCD 도메인 호출 가능하게 설정

  • 클러스터 내부에서 도메인 호출 시 무조건 해당 IP로 연결하게 설정
  • 하단 옵션 중 reload에 따라 파드 재시작 없이 바로 적용
# 서비스 IP 확인 
kubectl get svc -n jenkins 
kubectl get svc -n argocd argocd-server
kubectl get svc -n keycloak

NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)              AGE
jenkins-svc   ClusterIP   10.96.244.163   <none>        8080/TCP,50000/TCP   22m
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
argocd-server   ClusterIP   10.96.247.238   <none>        80/TCP,443/TCP   16m
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
keycloak   ClusterIP   10.96.76.255   <none>        80/TCP    11m

# coredns 변경
kubectl edit cm -n kube-system coredns
...
        hosts {
           10.96.244.163 jenkins.example.com
           10.96.247.238 argocd.example.com
           10.96.76.255  keycloak.example.com
           fallthrough
        } 
        prometheus :9153
...
loop # 무한 루프 감지 및 차단 
reload # 설정 자동 갱신
loadblanace # 부하 분산

SSO(OIDC) 설정 & RBAC 설정

위 배포한 KeyCloak를 기반으로 ArgoCD, Jenkins SSO 설정을 진행하겠습니다.

OpenID Connect의 권한 부여 코드 흐름

  1. 애플리케이션(ArgoCD or Jenkins)은 인증 요청을 준비하고 사용자 브라우저를 Keycloak으로 리디렉션하도록 요청합니다.
  2. 사용자 브라우저는 사용자를 권한 부여 엔드포인트라는 엔드포인트에서 Keycloak으로 리디렉션합니다.
  3. 사용자가 아직 Keycloak에 인증되지 않은 경우, Keycloak은 사용자를 인증합니다.
  4. 애플리케이션(ArgoCD or Jenkins) 은 Keycloak으로부터 인증 응답의 형태로 권한 부여 코드를 수신합니다.
  5. 애플리케이션은 Keycloak의 토큰 엔드포인트로 토큰 요청을 통해 권한 부여 코드를 ID 토큰과 액세스 토큰으로 교환합니다.
  6. 이제 애플리케이션은 사용자의 신원을 확인하는 데 사용할 수 있는 ID 토큰을 확보하고, 사용자에 대한 인증된 세션을 설정할 수 있습니다.

ArgoCD OIDC SSO 구성

Keycloak Client 생성

더보기
  • Valid post logout redirect URIs : 리디렉션 공격 차단
  • Web origins : CORS 문제 처리 해결

Client →Credentials 내 Client Secret 발급

# 생성된 client 에서 → Credentials 값(메모)를 붙여넣어서 실행
kubectl -n argocd patch secret argocd-secret --patch='{"stringData": { "oidc.keycloak.clientSecret": "EYbycVXpArQRWX3AJ5yEVbJBdoRA8aEp" }}'

# 설정 확인 
kubectl get secret -n argocd argocd-secret -o jsonpath='{.data}' | jq
oidc.keycloak.clientSecret":"V2JiQkVSME1pR1NBS3ZsSGJ1bG83ODE0QnZzVFkzbU0=

# argoCD OIDC 정보 등록 
kubectl patch cm argocd-cm -n argocd --type merge -p '
data:
  oidc.config: |
    name: Keycloak
    issuer: http://keycloak.example.com/realms/myrealm
    clientID: argocd
    clientSecret: EYbycVXpArQRWX3AJ5yEVbJBdoRA8aEp
    requestedScopes: ["openid", "profile", "email"]
'

# 확인
kubectl get cm -n argocd argocd-cm -o yaml | grep oidc.config: -A5

# ArgoCD 파드 재시작
kubectl rollout restart deploy argocd-server -n argocd

Keycloak User를 통해 접근 가능

로그인 후 세션 정보 확인

Keycloak에서 동작

KeyCloak은 HTTP의 stateless 특성을 보완하기 위해, 서버 측 세션 + 클라이언트 측 쿠키를 결합한 구조로 동작

1. 사용자가 로그인

2. Keycloak 서버는 서버 내부에 세션(User Session)을 생성

3. 브라우저에 KEYCLOAK_IDENTITY 쿠키를 발급 (세션 ID 포함)

4. 브라우저가 요청을 보낼 때마다 쿠키를 전달하고, Keycloak은 이를 통해 세션을 식별

5. Keycloak은 세션 ID와 일치 여부를 확인해 인증을 유지

 

 

Jenkins OIDC SSO 구성

[Keycloak] Client 설정

Keycloak Client 생성

더보기

Credentail 정보 저장 → It0UZdAmCmbdWWnWYOtvm8S8txMdYBjF

[Jenkins] 설정

  1. OpenID Connect Authentication 플러그인 설치
  2. Manage JenkinsSecurity : Security Realm 설정

Jenkins 로그인 확인

  • clientId 확인 및 로그인 세션 확인

 

 

 

LDAP 연동 및 KeyClock 연동

LDAP(Lightweight Directory Access Protocol) : 사용자·그룹·권한 정보를 계층적으로 보관하는 “주소록/조직도”

  • 디렉터리 서비스에 접근하기 위한 표준 프로토콜
  • 쉽게 말해, “조직 내 사용자, 그룹, 권한 등의 정보를 트리 구조로 저장하고 조회하는 시스템”
# 예시
dc=example,dc=org     # Base DN(Root DN)
 ├── ou=people
 │    ├── uid=alice
 │    └── uid=bob
 └── ou=groups
      ├── cn=devs
      └── cn=admins

 

 

OpenLDAP 서버 배포

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  name: openldap
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: openldap
  namespace: openldap
spec:
  replicas: 1
  selector:
    matchLabels:
      app: openldap
  template:
    metadata:
      labels:
        app: openldap
    spec:
      containers:
        - name: openldap
          image: osixia/openldap:1.5.0
          ports:
            - containerPort: 389
              name: ldap
            - containerPort: 636
              name: ldaps
          env:
            - name: LDAP_ORGANISATION    # 기관명, LDAP 기본 정보 생성 시 사용
              value: "Example Org"
            - name: LDAP_DOMAIN          # LDAP 기본 Base DN 을 자동 생성
              value: "example.org"
            - name: LDAP_ADMIN_PASSWORD  # LDAP 관리자 패스워드
              value: "admin"
            - name: LDAP_CONFIG_PASSWORD
              value: "admin"
        - name: phpldapadmin
          image: osixia/phpldapadmin:0.9.0
          ports:
            - containerPort: 80
              name: phpldapadmin
          env:
            - name: PHPLDAPADMIN_HTTPS
              value: "false"
            - name: PHPLDAPADMIN_LDAP_HOSTS
              value: "openldap"   # LDAP hostname inside cluster
---
apiVersion: v1
kind: Service
metadata:
  name: openldap
  namespace: openldap
spec:
  selector:
    app: openldap
  ports:
    - name: phpldapadmin
      port: 80
      targetPort: 80
      nodePort: 30000
    - name: ldap
      port: 389
      targetPort: 389
    - name: ldaps
      port: 636
      targetPort: 636
  type: NodePort
EOF

# 확인
kubectl get deploy,pod,svc,ep -n openldap


# 기본 LDAP 정보 : 아래 Bind DN과 PW로 로그인
## Base DN: dc=example,dc=org
## Bind DN: cn=admin,dc=example,dc=org
## Password: admin

open http://127.0.0.1:30000

조직도 구성


# 파드 접근
kubectl -n openldap exec -it deploy/openldap -c openldap -- bash

pstree -aplpst
run,1 -u /container/tool/run
  └─slapd,433 -h ldap://openldap-54857b746c-n8xm7:389 ldaps://openldap-54857b746c-n8xm7:636 ldapi:/// -u openldap -g openldap -d 256
      ├─{slapd},436
      ├─{slapd},437
      └─{slapd},438
      
# LDAP 관리자 인증 테스트 : 정상일 경우 LDAP 기본 엔트리 출력
ldapsearch -x -H ldap://localhost:389 -b dc=example,dc=org -D "cn=admin,dc=example,dc=org" -w admin

# 실습 사용 최종 트리 구조 추가 
dc=example,dc=org
├── ou=people
│   ├── uid=alice
│   │   ├── cn: Alice
│   │   ├── sn: Kim
│   │   ├── uid: alice
│   │   └── mail: alice@example.org
│   └── uid=bob
│       ├── cn: Bob
│       ├── sn: Lee
│       ├── uid: bob
│       └── mail: bob@example.org
└── ou=groups
    ├── cn=devs
    │   └── member: uid=bob,ou=people,dc=example,dc=org
    └── cn=admins
        └── member: uid=alice,ou=people,dc=example,dc=org
      
      
# ldapadd로 ou 추가 (organizationalUnit)
cat <<EOF | ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin
dn: ou=people,dc=example,dc=org
objectClass: organizationalUnit
ou: people

dn: ou=groups,dc=example,dc=org
objectClass: organizationalUnit
ou: groups
EOF

# ldapadd로 users 추가 (inetOrgPerson) : alice , bob
cat <<EOF | ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin
dn: uid=alice,ou=people,dc=example,dc=org
objectClass: inetOrgPerson
cn: Alice
sn: Kim
uid: alice
mail: alice@example.org
userPassword: alice123

dn: uid=bob,ou=people,dc=example,dc=org
objectClass: inetOrgPerson
cn: Bob
sn: Lee
uid: bob
mail: bob@example.org
userPassword: bob123
EOF


# ldapadd로 groups 추가 (groupOfNames) : devs, admins
cat <<EOF | ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin
dn: cn=devs,ou=groups,dc=example,dc=org
objectClass: groupOfNames
cn: devs
member: uid=bob,ou=people,dc=example,dc=org

dn: cn=admins,ou=groups,dc=example,dc=org
objectClass: groupOfNames
cn: admins
member: uid=alice,ou=people,dc=example,dc=org
EOF

# ldapsearch 검색 : ou
ldapsearch -x -D "cn=admin,dc=example,dc=org" -w admin \
  -b "dc=example,dc=org" "(objectClass=organizationalUnit)" ou
  

# LDAP 사용자 인증 테스트 : 정상일 경우 LDAP 기본 엔트리 출력
ldapwhoami -x -D "uid=alice,ou=people,dc=example,dc=org" -w alice123

      
exit

DN 예시

만약 example.com 도메인 아래의 people 조직 단위에 Kim이라는 사용자가 있다면,

그의 DN은 다음과 같이 구성됩니다.

 

 

Keycloak에서 LDAP 구성 방법

User Federation을 통해 LDAP와 연동

[Keycloak] LDAP 설정

더보기

admin Console

[Realm] myrealm → User Federation → Add LDAP providers

필드값 최소 설정

  • General
    • UI display name: ldap
    • Vendor: Other
  • Connection and authentication
    • Connection URL: ldap://openldap.openldap.svc:389 ⇒ Test connection
    • Bind DN: (= Login DN) cn=admin,dc=example,dc=org
    • Bind Credential: admin ⇒ Test authentication
  • LDAP searching and updating : 참고로 LDAP에 uid 로 alice, bob 존재
    • Edit mode: READ_ONLY
    • Users DN: ou=people,dc=example,dc=org
    • Username LDAP attribute: uid
    • RDN LDAP attribute: uid
    • User Object Classes: inetOrgPerson
    • Search scope: Subtree (OU 하위 모두 탐색)
  • Synchronization settings(LDAP 사용자 데이터를 Keycloak으로 동기화하는 주기를 정의)
    • Import Users(LDAP 사용자 정보를 Keycloak DB에 임포트할지 여부): On (LDAP → KeyCloak : Sync OK)
    • Sync Registrations(Keycloak에서 새로 생성한 사용자를 LDAP에 동기화할지 여부) : Off (KeyCloak → LDAP : Sync OK)
    • Periodic full sync(자동 동기화 주기 설정): On
    • Periodic changed users sync(자동 동기화 주기 설정) : On
  • Save

[Keycloak] LDAP 동기화

동기화 이후, User 조회

bob 유저로 argoCD 로그인

 

 

 

RBAC with Group

ArgoCD 샘플 애플리케이션 배포 후 bob 유저로 로그인 시 자원 확인 불가능 → 권한 부여 필요

LDAP 그룹 단위로 K8S RBAC를 매핑하여 권한 부여 테스트를 진행하겠습니다.

 

ArgoCD 샘플 애플리케이션 배포

cat <https://github.com/gasida/cicd-study
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
    syncOptions:
    - CreateNamespace=true
  destination:
    namespace: guestbook
    server: https://kubernetes.default.svc
EOF

[Keycloak] Group 동기화 설정

설정 옵션

[KeyCloak] Client Scope 설정

  • Client Scopes 생성 : Name (groups) , 나머지는 기본값
  • Client scopes 메뉴 진입 상태에서 mappers 클릭 → [Configure a new mapper] → 'Group Membership' 선택 후 Name, Token Claim Name 에 groups 입력 , Full group path 는 Off
  • argocd client 에서 groups 전달을 위한 설정 : Client 에서 argocd 클릭

[ArgoCD] Client Scope 추가

kubectl edit cm -n argocd argocd-cm
...
    requestedScopes: ["openid", "profile", "email" , "groups"]

[ArgoCD] RBAC 할당

Keycloak 그룹 ArgoCDAdmins에 ArgoCD 권한을 매핑하기 위해 argocd-rbac-cm 컨피그맵을 업데이트

kubectl edit cm argocd-rbac-cm -n argocd
...
data:
  policy.csv: |
    g, devs, role:admin
...



'Cloud' 카테고리의 다른 글

Vault VSO 맛보기  (0) 2025.12.07
HashiCorp Vault 맛보기  (0) 2025.11.29
ArgoCD로 멀티클러스터 관리하기  (0) 2025.11.23
ArgoCD 접근제어 설정 방법  (0) 2025.11.16
ArgoCD 정리 (1)  (0) 2025.11.08