실습 환경 배포

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

" ingress-ready ": " true "

 

 

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

  1. Keycloak은 사용자의 인증, 암호 복구, 약관 동의 등 다양한 로그인 기능을 별도 개발 없이 손쉽게 제공합니다.
  2. 다양한 인증 프로토콜(OAuth2, OpenID Connect, SAML 2.0)을 지원하여 표준 기반의 통합 인증 체계를 구축할 수 있습니다.
  3. 자체 사용자 DB 및 Active Directory, LDAP와의 연동이 모두 가능해 기존 인프라와 쉽게 통합됩니다.
  4. 소셜 로그인 및 외부 ID 공급자(구글, 페이스북 등)와 연동할 수 있는 Identity Brokering 기능을 내장합니다.
  5. SSO(싱글 사인온) 기능을 제공해 여러 애플리케이션에 한 번 인증으로 접근이 가능합니다.
  6. 애플리케이션이 직접 암호를 다루지 않고 보안 토큰 기반 인증을 사용하여 보안 수준을 높입니다.
  7. 세션 관리, 인증 위치 추적, 원격 세션 종료 등 관리 및 보안 유틸리티도 제공합니다.
  8. 사용자, 클라이언트, 역할, 정책 관리 등 관리자 도구와 편리한 UI를 제공합니다.
  9. 높은 확장성과 커스터마이징: 플러그인, 커스텀 인증 메커니즘, 자체 로그인 프로토콜 확장이 가능합니다.
  10. 클러스터링 및 고가용성 구성이 가능하여 대규모 서비스에서도 안정적으로 운영할 수 있습니다.

 

#
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

 

Jenkins OIDC 설정 - Plugin

 

플러그인 설치 : OpenID Connect Authentication

 

Manage JenkinsSecurity : Security Realm 설정 → 하단 Save

 

Login with Openid Connect

  • Client id : jenkins
  • Client secret : <keycloak 에서 jenkins client 에서 credentials>
  • Configuration mode : Discovery…
  • 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

생성된 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 연동

  1. Keycloak은 ‘User Federation’ 메뉴를 통해 LDAP 서버와 사용자 정보를 연동하고 동기화합니다.
  2. General Options에서 Active Directory 같은 벤더 유형과 관리 콘솔에 표시될 이름을 설정합니다.
  3. Connection 설정 시 ldap:// URL과 StartTLS 보안 여부, 그리고 접속용 계정(Bind DN) 정보를 입력합니다.
  4. 제공되는 Test connection 및 Test authentication 버튼으로 연결 상태와 계정 권한을 즉시 검증할 수 있습니다.
  5. Searching 설정에서 사용자 검색의 루트(Users DN), 로그인 ID 속성, 검색 범위(Subtree 등)를 정의합니다.
  6. Edit mode를 통해 데이터를 읽기 전용(READ_ONLY)으로 할지, Keycloak 변경 사항을 LDAP에 쓸지(WRITABLE) 정합니다.
  7. Synchronization 설정으로 사용자 데이터 임포트 여부와 전체/변경 데이터의 자동 동기화 주기를 지정합니다.
  8. Kerberos 통합 옵션과 조회 성능 최적화를 위한 캐싱 정책(Cache settings)을 구성할 수 있습니다.
  9. Advanced 설정에서는 LDAPv3 암호 변경 기능 활성화 및 Keycloak의 비밀번호 정책 적용 여부를 다룹니다.
  10. 마지막으로 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초 정도 후에 아래 로그인 진행

argocd 로그인 시 scope 에 groups 추가 확인

 

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
...

 

bob계정에서도 애플리케이션확인

Jenkins 설정

Jenkins 에 scope 에 groups 추가 설정

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

 

+ Recent posts