스터디(Study)/CI·CD Study

Jenkins · Argo CD · Keycloak · OpenLDAP로 Kubernetes SSO + RBAC 구성하기 [부록 실습]

Chann._.y 2025. 11. 22.
728x90

가시다께서 진행하신 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 기준.

생성 후 Credentials에서 Client Secret 값 메모.


2-2. Jenkins OpenID Connect 설정

Jenkins 플러그인 설치:

  • OpenID Connect Authentication 플러그인 설치

Manage Jenkins → Security에서 Security Realm을 OIDC로 변경:

  • Login with OpenID Connect

이제 Jenkins에서 로그아웃 후, 로그인 시 Keycloak 로그인 화면으로 리다이렉트되는 것을 확인한다.

※ Jenkins에서 miri / miri123 로그인 → Keycloak 세션에서 jenkins client 세션 생성 확인 가능.


3. SSO (OIDC) – Argo CD 연동

3-1. Keycloak Client 설정 (Argo CD)

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

  1. 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
  • 나머지는 기본값 유지
  1. 저장 후 Action → Sync all users 실행
  2. 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 추가

  1. Client Scopes → Create
    • Name: groups
    • 나머지 기본값 → Save
  2. 방금 만든 groups scope에서 Mappers → Configure a new mapper → Group Membership 선택
    • Name: groups
    • Token Claim Name: groups
    • Full group path: Off
  3. 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

 

728x90

댓글