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의 권한 부여 코드 흐름

- 애플리케이션(ArgoCD or Jenkins)은 인증 요청을 준비하고 사용자 브라우저를 Keycloak으로 리디렉션하도록 요청합니다.
- 사용자 브라우저는 사용자를 권한 부여 엔드포인트라는 엔드포인트에서 Keycloak으로 리디렉션합니다.
- 사용자가 아직 Keycloak에 인증되지 않은 경우, Keycloak은 사용자를 인증합니다.
- 애플리케이션(ArgoCD or Jenkins) 은 Keycloak으로부터 인증 응답의 형태로 권한 부여 코드를 수신합니다.
- 애플리케이션은 Keycloak의 토큰 엔드포인트로 토큰 요청을 통해 권한 부여 코드를 ID 토큰과 액세스 토큰으로 교환합니다.
- 이제 애플리케이션은 사용자의 신원을 확인하는 데 사용할 수 있는 ID 토큰을 확보하고, 사용자에 대한 인증된 세션을 설정할 수 있습니다.
ArgoCD OIDC SSO 구성
Keycloak Client 생성
- client id : argocd
- name : argocd client
- Client authentication : Check
- Authentication flow : Standard flow
- Root URL : https://argocd.example.com/
- Home URL : /applications
- Valid redirect URIs : https://argocd.example.com/auth/callback
- http://localhost:8085/auth/callback (needed for argo-cd cli, depends on value from --sso-port)
- Valid post logout redirect URIs : https://argocd.example.com/applications
- Web origins : +

- 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 생성
- Keycloak 옵션 설정
- client id : jenkins
- name : jenkins client
- Client authentication : Check
- Authentication flow : Standard flow
- Root URL : http://jenkins.example.com/
- Home URL : http://jenkins.example.com/
- Valid redirect URIs : http://jenkins.example.com/securityRealm/finishLogin
- Valid post logout redirect URIs : http://jenkins.example.com/OicLogout
- Web origins : +
Credentail 정보 저장 → It0UZdAmCmbdWWnWYOtvm8S8txMdYBjF
[Jenkins] 설정
- OpenID Connect Authentication 플러그인 설치
- Manage Jenkins → Security : 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 |