실습 환경 배포
kind k8s 배포
# 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
EOF
# 확인
kubectl get node
kubectl get pod -A

Ingress-Nginx 배포 - Github
# 노드 라벨 확인
kubectl get nodes 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 활성화 설정 https://kubernetes.github.io/ingress-nginx/user-guide/tls/#ssl-passthrough
KUBE_EDITOR="nano" kubectl edit -n ingress-nginx deployments/ingress-nginx-controller
...
- --enable-ssl-passthrough
혹은
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

kubectl get deployment ingress-nginx-controller -n ingress-nginx -o yaml \
| sed '/- --publish-status-address=localhost/a\
- --enable-ssl-passthrough' | kubectl apply -f -
위 단계에서 다음과같이 json 파싱에러가 난다. (가시다님 실습에서는 안나던데...)

kubectl patch deployment ingress-nginx-controller -n ingress-nginx —type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "—enable-ssl-passthrough"}]'
그래서 위 명령어로 패치했다.

Jenkins 배포 + Ingress
#
kubectl create ns jenkins
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-pvc
namespace: jenkins
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins
namespace: jenkins
spec:
replicas: 1
selector:
matchLabels:
app: jenkins
template:
metadata:
labels:
app: jenkins
spec:
securityContext:
fsGroup: 1000
containers:
- name: jenkins
image: jenkins/jenkins:lts
ports:
- name: http
containerPort: 8080
- name: agent
containerPort: 50000
livenessProbe:
httpGet:
path: "/login"
port: 8080
initialDelaySeconds: 90
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 5
readinessProbe:
httpGet:
path: "/login"
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
volumeMounts:
- name: jenkins-home
mountPath: /var/jenkins_home
volumes:
- name: jenkins-home
persistentVolumeClaim:
claimName: jenkins-pvc
---
apiVersion: v1
kind: Service
metadata:
name: jenkins-svc
namespace: jenkins
spec:
type: ClusterIP
selector:
app: jenkins
ports:
- port: 8080
targetPort: http
protocol: TCP
name: http
- port: 50000
targetPort: agent
protocol: TCP
name: agent
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jenkins-ingress
namespace: jenkins
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
spec:
ingressClassName: nginx
rules:
- host: jenkins.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: jenkins-svc
port:
number: 8080
EOF
# 확인
kubectl get deploy,svc,ep,pvc -n jenkins
kubectl get ingress -n jenkins jenkins-ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
jenkins-ingress nginx jenkins.example.com localhost 80 60s
# 도메인 설정
## macOS의 /etc/hosts 파일 수정
echo "127.0.0.1 jenkins.example.com" | sudo tee -a /etc/hosts
cat /etc/hosts
## C:\Windows\System32\drivers\etc\hosts 관리자모드에서 메모장에 내용 추가
127.0.0.1 jenkins.example.com
# 초기 암호 확인
kubectl exec -it -n jenkins deploy/jenkins -- cat /var/jenkins_home/secrets/initialAdminPassword
7521bc306a4049a6a0fd75391ae4b235
# 웹 접속 : 기본 설정 진행
curl -s http://jenkins.example.com -I
open "http://jenkins.example.com"

웹 접속 후 초기 설정 진행
- 초기 암호 입력 : 초기 암호 확인후 입력
- Customize Jenkins : Install suggested plugins 클릭 후 설치
- 관리자 계정 설정 : admin / qwe123

Argo CD 배포 by Helm + Ingress - Ingress , Docs
[Argo CD TLS 동작 요약]
- OpenSSL로 self-signed 인증서 생성 및 Secrets 생성
- Argo CD 서버는 TLS 인증서를 argocd-server-tls Secret 에서 직접 로드
- server.ingress.tls=true + nginx.ingress.kubernetes.io/ssl-passthrough=true
- 설정으로 Ingress는 TLS를 종료하지 않고 그대로 Pod에게 전달
- 따라서 브라우저 → Ingress → Argo CD Server 까지 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)

# TLS 키·인증서 생성 : 공통 이름이 argocd.example.com 이어야 합니다
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.*
-rw-r--r--@ 1 gasida staff 1184 Nov 8 23:10 argocd.example.com.crt
-rw-------@ 1 gasida staff 1704 Nov 8 23:10 argocd.example.com.key
# Argo CD가 자신의 TLS를 쓰도록 argocd 네임스페이스에 tls 타입 시크릿 생성
kubectl create ns argocd
# tls 시크릿 생성 : key/crt 파일이 로컬에 있어야 함
kubectl -n argocd create secret tls argocd-server-tls \
--cert=argocd.example.com.crt \
--key=argocd.example.com.key
#
cat <<EOF > 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.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 # 아래 address 에 localhost 는 배포 후 sync 까지 다소 시간 소요 될 수 있음
NAME CLASS HOSTS ADDRESS PORTS AGE
argocd-server nginx argocd.example.com localhost 80, 443 6m42s
kubectl get ingress -n argocd argocd-server -o yaml | kubectl neat |yq
...
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
...
name: argocd-server
namespace: argocd
spec:
ingressClassName: nginx
rules:
- host: argocd.example.com
http:
paths:
- backend:
service:
name: argocd-server
port:
number: 443
path: /
pathType: Prefix
tls:
- hosts:
- argocd.example.com
secretName: argocd-server-tls
# 도메인 설정
## macOS의 /etc/hosts 파일 수정
echo "127.0.0.1 argocd.example.com" | sudo tee -a /etc/hosts
cat /etc/hosts
## C:\Windows\System32\drivers\etc\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
38L2ZmXne7jIaRSZ
ARGOPW=<최초 접속 암호>
ARGOPW=YYpjg453yyKi4HdK
# argocd 서버 cli 로그인 : argocd cli 설치 필요
argocd login argocd.example.com --insecure --username admin --password $ARGOPW
# 확인
argocd cluster list
argocd proj list
argocd account list
# admin 계정 암호 변경 : qwe12345
argocd account update-password --current-password $ARGOPW --new-password qwe12345
# Argo CD 웹 접속 주소 확인 : admin 계정 / qwe12345
open "http://argocd.example.com"
open "https://argocd.example.com"





Keycloak 배포 + Ingress : miri 유저 (암호 : miri123) - Docs
- Keycloak은 사용자의 인증, 암호 복구, 약관 동의 등 다양한 로그인 기능을 별도 개발 없이 손쉽게 제공합니다.
- 다양한 인증 프로토콜(OAuth2, OpenID Connect, SAML 2.0)을 지원하여 표준 기반의 통합 인증 체계를 구축할 수 있습니다.
- 자체 사용자 DB 및 Active Directory, LDAP와의 연동이 모두 가능해 기존 인프라와 쉽게 통합됩니다.
- 소셜 로그인 및 외부 ID 공급자(구글, 페이스북 등)와 연동할 수 있는 Identity Brokering 기능을 내장합니다.
- SSO(싱글 사인온) 기능을 제공해 여러 애플리케이션에 한 번 인증으로 접근이 가능합니다.
- 애플리케이션이 직접 암호를 다루지 않고 보안 토큰 기반 인증을 사용하여 보안 수준을 높입니다.
- 세션 관리, 인증 위치 추적, 원격 세션 종료 등 관리 및 보안 유틸리티도 제공합니다.
- 사용자, 클라이언트, 역할, 정책 관리 등 관리자 도구와 편리한 UI를 제공합니다.
- 높은 확장성과 커스터마이징: 플러그인, 커스텀 인증 메커니즘, 자체 로그인 프로토콜 확장이 가능합니다.
- 클러스터링 및 고가용성 구성이 가능하여 대규모 서비스에서도 안정적으로 운영할 수 있습니다.
#
kubectl create ns keycloak
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
namespace: keycloak
labels:
app: keycloak
spec:
replicas: 1
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:26.4.0
args: ["start-dev"] # dev mode 실행
env:
- name: KEYCLOAK_ADMIN
value: admin
- name: KEYCLOAK_ADMIN_PASSWORD
value: admin
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: keycloak
namespace: keycloak
spec:
selector:
app: keycloak
ports:
- name: http
port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: keycloak
namespace: keycloak
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
ingressClassName: nginx
rules:
- host: keycloak.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: keycloak
port:
number: 8080
EOF
# 확인
kubectl get deploy,svc,ep keycloak -n keycloak
kubectl get ingress keycloak -n keycloak
NAME CLASS HOSTS ADDRESS PORTS AGE
keycloak nginx keycloak.example.com localhost 80 91s
# 도메인 설정
## macOS의 /etc/hosts 파일 수정
echo "127.0.0.1 keycloak.example.com" | sudo tee -a /etc/hosts
cat /etc/hosts
## C:\Windows\System32\drivers\etc\hosts 관리자모드에서 메모장에 내용 추가
127.0.0.1 keycloak.example.com
# keycloak 웹 접속 : admin / admin
curl -s http://keycloak.example.com -I
open "http://keycloak.example.com/admin"

realms 생성 : myrealm

users 생성 : miri - 암호 miri123

k8s 내부에서 실습을 위한 도메인 호출 설정
# 기본 통신 및 정보 확인 : ClusterIP 메모!
kubectl get svc -n jenkins
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
jenkins-svc ClusterIP 10.96.56.98 <none> 8080/TCP,50000/TCP 19m
kubectl get svc -n argocd argocd-server
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
argocd-server ClusterIP 10.96.249.176 <none> 80/TCP,443/TCP 27m
kubectl get svc -n keycloak
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
keycloak ClusterIP 10.96.199.16 <none> 80/TCP 5m2s
kubectl get svc -n jenkins
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
jenkins-svc ClusterIP 10.96.230.164 <none> 8080/TCP,50000/TCP 30m
(⎈|kind-myk8s:N/A) gasida ~/Downloads/cicd-study kubectl get svc -n argocd argocd-server
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
argocd-server ClusterIP 10.96.127.179 <none> 80/TCP,443/TCP 9m35s
(⎈|kind-myk8s:N/A) gasida ~/Downloads/cicd-study kubectl get svc -n keycloak
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
keycloak ClusterIP 10.96.144.94 <none> 80/TCP 3m51s
# 해결해보자 : coredns 에 hosts 플러그인 활용
KUBE_EDITOR="nano" kubectl edit cm -n kube-system coredns
.:53 {
...
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
hosts {
<CLUSTER IP> jenkins.example.com
<CLUSTER IP> argocd.example.com
<CLUSTER IP> keycloak.example.com
fallthrough
}
...
reload # cm 설정 변경 시 자동으로 reload 적용됨
...
# cm 설정 변경 시 자동으로 reload 적용 로그 확인
kubectl logs -n kube-system -l k8s-app=kube-dns --timestamps
✘ minji ~/Study/cicd kubectl get svc -n jenkins
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
jenkins-svc ClusterIP 10.96.210.95 <none> 8080/TCP,50000/TCP 18m
minji ~/Study/cicd kubectl get svc -n argocd argocd-server
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
argocd-server ClusterIP 10.96.98.11 <none> 80/TCP,443/TCP 10m
minji ~/Study/cicd kubectl get svc -n keycloak
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
keycloak ClusterIP 10.96.143.205 <none> 80/TCP 6m31s
SSO (OIDC) 설정
Keycloak Client 설정 : Jenkins - Plugin
- 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 : +
- 생성된 client 에서 → Credentials : 메모 해두기

Jenkins OIDC 설정 - Plugin
플러그인 설치 : OpenID Connect Authentication


Manage Jenkins → Security : Security Realm 설정 → 하단 Save
Login with Openid Connect

- Client id : jenkins
- Client secret : <keycloak 에서 jenkins client 에서 credentials>
- Configuration mode : Discovery…
- Well-know configuration endpoint http://keycloak.example.com/realms/**myrealm**/.well-known/openid-configuration
- [고급] 클릭 → Override scopes : openid email profile
- Well-know configuration endpoint http://keycloak.example.com/realms/**myrealm**/.well-known/openid-configuration
- Advanced configuration : [User fileds] 클릭 → User name field name : preferred_username
- Security configuration 클릭
- Disable ssl verification : Check
Jenkins 로그인 with SSO : jenkins 에서 admin logout 이후 로그인 - miri 유저 (암호 : miri123)
jenkins 로그인 시, 아래 처럼 302 Redirect 로 keycloak 으로 향하는 것 확인


키클락의 miri 계정으로 로그인 성공
Argo CD
- Keycloak Client 설정 : Argo CD - Docs
- 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 : +
생성된 client 에서 → Credentials : 메모 해두기

ArgoCD OIDC 설정 - Docs
- 클라이언트 시크릿 설정
```bash
# 생성된 client 에서 → Credentials 값(메모)를 붙여넣어서 실행
kubectl -n argocd patch secret argocd-secret --patch='{"stringData": { "oidc.keycloak.clientSecret": "<REPLACE_WITH_CLIENT_SECRET>" }}'
kubectl -n argocd patch secret argocd-secret --patch='{"stringData": { "oidc.keycloak.clientSecret": "mV3IZO3nmHoZr3BBC37UpdrMSMkF9Umt" }}'
# 확인
kubectl get secret -n argocd argocd-secret -o jsonpath='{.data}' | jq
...
"oidc.keycloak.clientSecret": "bVYzSVpPM25tSG9acjNCQkMzN1VwZHJNU01rRjlVbXQ=",
```
- Now we can configure the config map and add the oidc configuration to enable our keycloak authentication.
```bash
# 설정 추가
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: mV3IZO3nmHoZr3BBC37UpdrMSMkF9Umt
requestedScopes: ["openid", "profile", "email"]
'
# 확인
kubectl get cm -n argocd argocd-cm -o yaml | grep oidc.config: -A5
```
- argocd-server 파드 재시작
```bash
**kubectl rollout restart deploy argocd-server -n argocd**
```



OpenLDAP 설정
LDAP
네트워크상에서 조직 내 사용자, 그룹, 권한 정보를 관리하고 찾는 경량화된 표준 통신 규약임.
디지털 회사 주소록 + 출입증 발급 시스템이라고 보면 됨. 회사 인사팀이 직원 명부 관리하고 보안팀이 출입 권한 주는 역할을 동시에 하는 셈.
데이터 구조는 조직도랑 똑같음. 데이터가 마구잡이로 있는 게 아니라, 트리(Tree) 형태의 계층 구조로 저장됨. 회사 조직도(본사-부서-팀-직원) 생각하면 이해가 빠름.
식별자(DN)는 각 항목(Entry)을 구분하는 고유 이름표를 DN(Distinguished Name)이라고 함. 파일 경로처럼 전체 위치를 나타냄.
DN은 dc=example(회사), ou=people(부서), uid=alice(직원) 같이 작은 단위인 RDN들이 모여서 만들어짐. 주소 체계랑 비슷함.
저장 단위 (Entry & Attribute)
정보 저장의 기본 단위는 Entry이고, 그 안에 이름, 이메일, 직급 같은 세부 정보들이 Attribute(속성)라는 이름으로 담겨 있음.
인증 (Authentication): 사용자가 로그인할 때 아이디/비번을 LDAP 서버에 보내면, "어, 우리 직원 맞네" 하고 신분을 확인해줌.
인가 (Authorization): "이 사람은 개발팀이니까 서버 접근 가능", "이 사람은 인사팀이니까 급여 정보 열람 가능" 처럼 그룹/직급 정보를 줘서 권한 설정의 근거를 마련해줌.
왜 만들었나?
옛날에 쓰던 X.500이라는 프로토콜이 너무 무겁고 복잡해서, 네트워크 부담 줄이고 가볍게 쓰려고 만든 다이어트 버전임.
OpenLDAP 서버 배포 - 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 logs -n openldap -l app=openldap -c phpldapadmin -f # phpLDAPadmin
kubectl logs -n openldap -l app=openldap -c openldap -f # openldap


OpenLDAP 조직도 구성 : 사용자(alice, bob), 그룹(devs, admins)
#
kubectl -n openldap exec -it deploy/openldap -c openldap -- bash
----------------------------------------------------
# 프로세스 확인
pstree -aplpst
run,1 -u /container/tool/run
└─slapd,433 -h ldap://openldap-54857b746c-ch9g4:389 ldaps://openldap-54857b746c-ch9g4:636 ldapi:/// -u openldap -g openldap -d 256
├─{slapd},436
└─{slapd},437
# 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
adding new entry "ou=people,dc=example,dc=org"
adding new entry "ou=groups,dc=example,dc=org"
# 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
adding new entry "uid=alice,ou=people,dc=example,dc=org"
adding new entry "uid=bob,ou=people,dc=example,dc=org"
# 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
adding new entry "cn=devs,ou=groups,dc=example,dc=org"
adding new entry "cn=admins,ou=groups,dc=example,dc=org"
# ldapsearch 검색 : ou
ldapsearch -x -D "cn=admin,dc=example,dc=org" -w admin \
-b "dc=example,dc=org" "(objectClass=organizationalUnit)" ou
# ldapsearch 검색 : 사용자
ldapsearch -x -D "cn=admin,dc=example,dc=org" -w admin \
-b "ou=people,dc=example,dc=org" "(uid=*)" uid cn mail
# ldapsearch 검색 : 그룹/멤버 확인
ldapsearch -x -D "cn=admin,dc=example,dc=org" -w admin \
-b "ou=groups,dc=example,dc=org" "(objectClass=groupOfNames)" cn member
# LDAP 사용자 인증 테스트 : 정상일 경우 LDAP 기본 엔트리 출력
ldapwhoami -x -D "uid=alice,ou=people,dc=example,dc=org" -w alice123
dn:uid=alice,ou=people,dc=example,dc=org
# 빠져나오기
exit
----------------------------------------------------





KeyClock 연동
- Keycloak은 ‘User Federation’ 메뉴를 통해 LDAP 서버와 사용자 정보를 연동하고 동기화합니다.
- General Options에서 Active Directory 같은 벤더 유형과 관리 콘솔에 표시될 이름을 설정합니다.
- Connection 설정 시 ldap:// URL과 StartTLS 보안 여부, 그리고 접속용 계정(Bind DN) 정보를 입력합니다.
- 제공되는 Test connection 및 Test authentication 버튼으로 연결 상태와 계정 권한을 즉시 검증할 수 있습니다.
- Searching 설정에서 사용자 검색의 루트(Users DN), 로그인 ID 속성, 검색 범위(Subtree 등)를 정의합니다.
- Edit mode를 통해 데이터를 읽기 전용(READ_ONLY)으로 할지, Keycloak 변경 사항을 LDAP에 쓸지(WRITABLE) 정합니다.
- Synchronization 설정으로 사용자 데이터 임포트 여부와 전체/변경 데이터의 자동 동기화 주기를 지정합니다.
- Kerberos 통합 옵션과 조회 성능 최적화를 위한 캐싱 정책(Cache settings)을 구성할 수 있습니다.
- Advanced 설정에서는 LDAPv3 암호 변경 기능 활성화 및 Keycloak의 비밀번호 정책 적용 여부를 다룹니다.
- 마지막으로 Trust email 옵션을 통해 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
- `Import Users`: **On** (LDAP → KeyCloak : Sync OK)
- `Sync Registrations`: **Off** (KeyCloak → LDAP : Sync OK)
- `Periodic full sync`: **On**
- `Periodic changed users sync`: **On**
- `Save`
**`[KeyCloak] LDAP Mapper 확인`**
**admin Console**
User Federation → LDAP Provider 선택 → **Mappers**: user에 대한 Mappers가 기본 존재
**`[KeyCloak] LDAP -> KeyCloak 동기화`**
**admin Console**
User Federation → LDAP Provider 선택 → Settings → Action : **Sync all users** ⇒ **실패 시, 다시 한번 더 시도!**
- 처음 LDAP 연동 후 사용자 전부 불러올 때 : LDAP의 **전체 사용자**를 Keycloak DB에 동기화(기존 데이터 덮어씀)







Argo CD 사용 시도 : LDAP devs 그룹에 bob 유저
#
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
helm:
valueFiles:
- values.yaml
path: helm-guestbook
repoURL: 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 applications -n argocd guestbook -o yaml
kubectl get pod,svc,ep -n guestbook

RBAC with Groups
OpenLDAP 그룹 정보 → Keycloak 동기화 설정. keycloak 에 group 에 현재 아무 그룹도 없음

- **admin Console**
- User Federation → LDAP Provider 선택 → **Mappers → Add mapper → 아래 설정 후 Save**
- name : **ldap-groups**
- Mapper type: **group-ldap-mapper**
- LDAP Groups DN : **ou=groups,dc=example,dc=org**
- Group Name LDAP Attribute: **cn**
- Group Object Classed: **groupOfNames**
- Membership LDAP attribute: **member**
- Membership attribute type: **DN**
- Mode: **READ_ONLY**

- admin Console
… → Mappers → ldap-groups 선택 → Action → Sync LDAP groups to Keycloak


Keycloak 에서 토큰에 Group 전달을 위한 설정 : ArgoCD Client 설정 - Docs
Client Scopes 생성 : Name (groups) , 나머지는 기본값 → 하단 Save
해당 Client scopes 메뉴 진입 상태에서 mappers 클릭 → [Configure a new mapper] 클릭
- mapper 리스트가 나타나면 'Group Membership' 선택 후 Name, Token Claim Name 에 groups 입력 , Full group path 는 Off

argocd client 에서 groups 전달을 위한 설정 : Client 에서 argocd 클릭
- [Client scopes] 탭 이동 후, Add client scope 클릭 후 생성한 groups를 선택합니다. 이때, [Add] 선택 후 드롭다운의 Default를 선택.
Argo CD에 scope 에 groups 추가 설정 → 로그인 시도 , 로그인 실패 시 반복 시도 해보자!
# 아래 추가
KUBE_EDITOR="vi" kubectl edit cm -n argocd argocd-cm
...
requestedScopes: ["openid", "profile", "email" , "groups"]
-> 적용을 위해서 15초 정도 후에 아래 로그인 진행


Argo CD RBAC 할당
Keycloak 그룹 ArgoCDAdmins에 ArgoCD 권한을 매핑하기 위해 argocd-rbac-cm 컨피그맵을 업데이트
#
KUBE_EDITOR="vi" kubectl edit cm argocd-rbac-cm -n argocd
...
data:
policy.csv: |
g, devs, role:admin
...

Jenkins 설정
Jenkins 에 scope 에 groups 추가 설정
- Keycloak 설정 : jenkins client 에서 add client scope 클릭 후 groups 를 default 로 추가
- Jenkins 설정 : Security → 아래 처럼 scops 에 groups 와 groups fileld name 에 groups 추가
