
가시다님께서 진행하신 CI/CD 스터디 추가 실습 내용을 기반으로 작성했다.
이번 글에서는 단일 kind 클러스터 위에 Jenkins, Argo CD, Keycloak, OpenLDAP를 올리고, OIDC와 LDAP을 활용해 SSO와 RBAC를 통합하는 과정을 단계별로 정리한다.
1. 기본 실습 환경 구성
1-1. kind 클러스터 생성 (myk8s)
# kind k8s 배포
kind create cluster --name myk8s --image kindest/node:v1.32.8 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
labels:
ingress-ready: "true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
- containerPort: 30000
hostPort: 30000
protocol: TCP
EOF
# 확인
kubectl get node
kubectl get pod -A
1-2. Ingress-Nginx 배포
# 노드 라벨 확인
kubectl get node myk8s-control-plane -o jsonpath={.metadata.labels} | jq
# ... "ingress-ready": "true" ...
# NGINX ingress 배포
kubectl apply -f \
https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
# SSL Passthrough flag 활성화
KUBE_EDITOR="nano" kubectl edit -n ingress-nginx deployments/ingress-nginx-controller
# ...
# - --enable-ssl-passthrough
# 또는 one-liner
kubectl get deployment ingress-nginx-controller -n ingress-nginx -o yaml \
| sed '/- --publish-status-address=localhost/a\
- --enable-ssl-passthrough' \
| kubectl apply -f -
# ingress 배포 확인
kubectl get deploy,svc,ep ingress-nginx-controller -n ingress-nginx
kubectl describe -n ingress-nginx deployments/ingress-nginx-controller
1-3. Jenkins 배포 + Ingress
kubectl create ns jenkins
cat <http://jenkins.example.com -I
open "http://jenkins.example.com"
웹에서:
- 초기 암호 입력
- Install suggested plugins
- 관리자 계정: admin / qwe123
1-4. Argo CD 배포 (Helm + Ingress + TLS)
Argo CD TLS 동작 요약
- openssl로 self-signed 인증서 생성
- argocd 네임스페이스에 argocd-server-tls secret 생성
- Argo CD 서버는 이 secret에서 TLS를 직접 로드
- ingress는 ssl-passthrough로 TLS 종료 없이 그대로 포워딩 → end-to-end HTTPS 유지
sequenceDiagram
participant C as Client (Browser)
participant I as Ingress (Nginx)
participant S as Argo CD Server
participant T as Secret(argocd-server-tls)
C->>I: HTTPS Request (argocd.example.com)
Note over I: ssl-passthrough=true<br/>TLS 그대로 전달
I->>S: Forward HTTPS (Passthrough)
S->>T: Load TLS certificate + key
S->>C: HTTPS Response (Self-Signed Cert)
1) TLS 인증서 및 secret
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"
ls -l argocd.example.com.*
# crt, key 생성 확인
kubectl create ns argocd
kubectl -n argocd create secret tls argocd-server-tls \
--cert=argocd.example.com.crt \
--key=argocd.example.com.key
2) Helm values + 설치
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 (chart 9.1.0)
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd \
--version 9.1.0 \
-f argocd-values.yaml \
--namespace argocd
# 리소스 확인
kubectl get pod,ingress,svc,ep,secret,cm -n argocd
kubectl describe ingress -n argocd argocd-server
kubectl get ingress -n argocd argocd-server
Ingress spec 확인:
kubectl get ingress -n argocd argocd-server -o yaml | kubectl neat | yq
# ingressClassName: nginx
# hosts: argocd.example.com
# tls:
# - secretName: argocd-server-tls
# annotations:
# force-ssl-redirect: "true"
# ssl-passthrough: "true"
hosts 설정 및 접속:
# macOS
echo "127.0.0.1 argocd.example.com" | sudo tee -a /etc/hosts
cat /etc/hosts
# Windows: hosts 파일에 추가
# 127.0.0.1 argocd.example.com
curl -vk https://argocd.example.com/
초기 비밀번호, 로그인:
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d ; echo
ARGOPW=<위 값>
argocd login argocd.example.com --insecure \
--username admin \
--password "$ARGOPW"
argocd cluster list
argocd proj list
argocd account list
argocd account update-password \
--current-password "$ARGOPW" \
--new-password qwe12345
1-5. Keycloak 배포 + Ingress
Keycloak은 SSO / IAM 서버로 사용한다.
kubectl create ns keycloak
cat <http://keycloak.example.com -I
open "http://keycloak.example.com/admin"
# admin / admin
1-6. 클러스터 내부 도메인 해석 (CoreDNS hosts 플러그인)
Jenkins / Argo CD / Keycloak를 ClusterIP + 도메인 이름으로 내부에서 호출하기 위해 CoreDNS 설정을 수정한다.
서비스 IP 확인:
kubectl get svc -n jenkins
kubectl get svc -n argocd argocd-server
kubectl get svc -n keycloak
# 각 ClusterIP 메모
CoreDNS ConfigMap 수정:
KUBE_EDITOR="nano" kubectl edit cm -n kube-system coredns
kubernetes 블록 아래에 hosts 블록 추가:
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
hosts {
<JENKINS_CLUSTER_IP> jenkins.example.com
<ARGOCD_CLUSTER_IP> argocd.example.com
<KEYCLOAK_CLUSTER_IP> keycloak.example.com
fallthrough
}
reload
적용 로그 확인:
kubectl logs -n kube-system -l k8s-app=kube-dns --timestamps
2. SSO (OIDC) – Jenkins 연동
2-1. Keycloak Client 설정 (Jenkins)
Realm: myrealm 기준.
- Client ID: jenkins
- Name: jenkins client
- Client authentication: ON
- Authentication flow: Standard flow
- Root URL: http://jenkins.example.com/
- Home URL: http://jenkins.example.com/
- Valid redirect URIs:
- Valid post logout redirect URIs:
- Web origins: + (all)
생성 후 Credentials에서 Client Secret 값 메모.
2-2. Jenkins OpenID Connect 설정
Jenkins 플러그인 설치:
- OpenID Connect Authentication 플러그인 설치
Manage Jenkins → Security에서 Security Realm을 OIDC로 변경:
- Login with OpenID Connect
- Client id: jenkins
- Client secret: <Keycloak client secret>
- Configuration mode: Discovery
- Well-known endpoint:
http://keycloak.example.com/realms/myrealm/.well-known/openid-configuration
- Well-known endpoint:
- Advanced → Override scopes: openid email profile
- User fields → User name field name: preferred_username
- Security configuration → Disable ssl verification: 체크
이제 Jenkins에서 로그아웃 후, 로그인 시 Keycloak 로그인 화면으로 리다이렉트되는 것을 확인한다.
※ Jenkins에서 miri / miri123 로그인 → Keycloak 세션에서 jenkins client 세션 생성 확인 가능.
3. SSO (OIDC) – Argo CD 연동
3-1. Keycloak Client 설정 (Argo CD)
- Client ID: argocd
- Name: argocd client
- Client authentication: ON
- 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 (CLI용)
- Valid post logout redirect URIs:
- Web origins: +
Client Secret 값 메모.
3-2. Argo CD OIDC 설정
1) clientSecret을 secret에 넣기
kubectl -n argocd patch secret argocd-secret \
--patch='{"stringData": { "oidc.keycloak.clientSecret": "<REPLACE_WITH_CLIENT_SECRET>" }}'
확인:
kubectl get secret -n argocd argocd-secret -o jsonpath='{.data}' | jq
# "oidc.keycloak.clientSecret": "..."
2) argocd-cm에 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:
requestedScopes: ["openid", "profile", "email"]
'
ConfigMap 확인:
kubectl get cm -n argocd argocd-cm -o yaml | grep oidc.config: -A5
3) Argo CD 서버 재시작
kubectl rollout restart deploy argocd-server -n argocd
이제 Argo CD에서 로그아웃 후 다시 로그인하면 Keycloak 화면으로 리다이렉트되며, miri / miri123로 로그인 가능하다.
4. OpenLDAP + Keycloak 연동
4-1. OpenLDAP 서버 + phpLDAPadmin 배포
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
value: "Example Org"
- name: LDAP_DOMAIN
value: "example.org"
- name: LDAP_ADMIN_PASSWORD
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"
---
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
# phpLDAPadmin 접속
open http://127.0.0.1:30000
# Base DN: dc=example,dc=org
# Bind DN: cn=admin,dc=example,dc=org / Password: admin
4-2. OpenLDAP 조직도 구성 (alice / bob, devs / admins)
컨테이너 내부에서 ldapadd 실행:
kubectl -n openldap exec -it deploy/openldap -c openldap -- bash
OU 생성:
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
사용자 추가:
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
그룹 추가:
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
alice 인증 테스트:
ldapwhoami -x -D "uid=alice,ou=people,dc=example,dc=org" -w alice123
# dn:uid=alice,ou=people,dc=example,dc=org
exit
4-3. Keycloak User Federation – LDAP 연결
Realm: myrealm
- User Federation → Add LDAP provider
필수 값 설정:
- UI display name: ldap
- Vendor: Other
- Connection URL: ldap://openldap.openldap.svc:389
- Test connection
- Bind DN: cn=admin,dc=example,dc=org
- Bind Credential: admin
- Test authentication
LDAP 검색 설정:
- 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
Sync 설정:
- Import Users: ON
- 나머지는 기본값 유지
- 저장 후 Action → Sync all users 실행
- Users 메뉴에서 * 검색하면 alice, bob가 Keycloak으로 동기화된 것을 확인할 수 있다.
5. Argo CD + LDAP 사용자 로그인
샘플 Application 배포:
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
kubectl get applications -n argocd
kubectl get pod,svc,ep -n guestbook
이 상태에서 Keycloak bob / bob123로 Argo CD에 로그인하면, 아직 RBAC이 없어서 아무 것도 안 보이는 상태다.
이걸 그룹 기반 RBAC으로 풀어간다.
6. RBAC with Groups (LDAP → Keycloak → Argo CD)
6-1. LDAP 그룹 → Keycloak 그룹 동기화
Keycloak에서:
- User Federation → LDAP Provider → Mappers → Add mapper
- Name: ldap-groups
- Mapper type: group-ldap-mapper
- LDAP Groups DN: ou=groups,dc=example,dc=org
- Group Name LDAP Attribute: cn
- Group Object Classes: groupOfNames
- Membership LDAP attribute: member
- Membership attribute type: DN
- Mode: READ_ONLY
저장 후:
- Mappers → ldap-groups → Action → Sync LDAP groups to Keycloak
Groups 메뉴에서 devs, admins 그룹이 생성되고, 각각 멤버에 bob, alice가 매핑된 것을 확인한다.
6-2. Keycloak 토큰에 groups claim 추가
- Client Scopes → Create
- Name: groups
- 나머지 기본값 → Save
- 방금 만든 groups scope에서 Mappers → Configure a new mapper → Group Membership 선택
- Name: groups
- Token Claim Name: groups
- Full group path: Off
- Clients → argocd → Client scopes → Add client scope → groups → Default 로 추가
Argo CD OIDC 설정에 scope 추가:
KUBE_EDITOR="nano" kubectl edit cm -n argocd argocd-cm
# requestedScopes: ["openid", "profile", "email", "groups"]
잠시 후 Argo CD 로그인 시도 → auth?...scope=openid+profile+email+groups 포함되는지 확인.
6-3. Argo CD RBAC 설정
argocd-rbac-cm 수정:
KUBE_EDITOR="nano" kubectl edit cm argocd-rbac-cm -n argocd
policy.csv에 devs 그룹을 admin 역할로 매핑:
data:
policy.csv: |
g, devs, role:admin
이제 Keycloak에서 bob은 LDAP 그룹 devs에 속해 있고,
Keycloak 토큰의 groups claim을 통해 Argo CD에 devs로 전달된다.
Argo CD는 devs → role:admin으로 RBAC을 적용해, bob 계정으로 로그인해도 admin 권한을 가진다.
6-4. Jenkins에도 groups scope 추가 (연동)
Keycloak:
- Clients → jenkins → Client scopes → Add client scope → groups → Default
Jenkins Security 설정:
- 추가 scopes에 groups 포함
- groups field name에 groups 지정
다시 bob으로 Jenkins 로그인 시, 그룹 정보가 Jenkins에 전달되고, 롤 기반 권한 부여 플러그인과 연계해서 RBAC 구성 가능.
7. Jenkins RBAC (Role-based Authorization Strategy) 참고
※ Jenkins RBAC 부분은 보너스 성격이라, 요약만 적어두고 자세한 스크린샷은 그대로 활용하면 된다.
- 플러그인: Role-based Authorization Strategy
- Global roles:
- 예: view 역할 생성 → readonly 사용자에게 view 할당
- Project roles:
- 예: TestOnlyRole을 만들어 My.*Project 패턴에만 권한 부여
- 특정 사용자(puser)에게만 해당 역할을 부여해 “특정 Job만 관리 가능한 사용자” 만들기
이 구조를 Keycloak 그룹과 결합하면:
※ Jenkins에서 “LDAP devs 그룹 = 특정 프로젝트 관리자”, “admins 그룹 = Jenkins 관리자” 같은 구성이 가능하고, 나중에 사용자를 추가할 때 LDAP 그룹에만 넣어도 전체 권한이 연쇄적으로 적용되어 관리 비용을 줄일 수 있다.
8. 실습 정리 및 클린업
실습 종료 후 정리:
kind delete cluster --name myk8s
/etc/hosts (또는 Windows hosts)에 추가했던 아래 항목 삭제:
- jenkins.example.com
- argocd.example.com
- keycloak.example.com
'스터디(Study) > CI·CD Study' 카테고리의 다른 글
| HashiCorp Vault로 시작하는 현대 시크릿 관리 (0) | 2025.11.29 |
|---|---|
| 예제로 배우는 Argo CD 5장: Argo CD로 쿠버네티스 클러스터 부트스트랩 (0) | 2025.11.22 |
| 예제로 배우는 Argo CD 4장: 접근 제어 (1) | 2025.11.15 |
| 예제로 배우는 Argo CD 3장: Argo CD 운영 (1) | 2025.11.08 |
| 예제로 배우는 Argo CD 2장: Argo CD 시작하기 (0) | 2025.11.08 |
댓글