스터디(Study)/CI·CD Study

예제로 배우는 Argo CD 4장: 접근 제어

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

 

접근 제어

1·2장에서 GitOps와 Argo CD의 기본 원리를 살펴보고,
3장에서는 고가용성과 실전 운영 전략을 다뤘다.

이제부터는 “누가 Argo CD에 접근해서 무엇을 할 수 있는가?”를 다루는 접근 제어 챕터다.
특히 다음 네 가지 축을 중심으로 정리해본다.

  • 선언적 사용자 관리 (admin, 로컬 계정)
  • RBAC 정책
  • 서비스 어카운트 (CI/CD용 계정)
  • SSO 및 Keycloak 연동

접근 제어는 “보안”만의 이야기가 아니라, 운영상 안정성과 책임의 경계(블레임 라인)를 분리하는 핵심 요소다.


1. 접근 제어 개요

Argo CD의 접근 제어는 크게 이렇게 구성된다.

구분 역할
로컬 사용자 / admin 설치 직후 접근 가능한 계정, 최소 권한 운영용 로컬 계정
RBAC 정책 (argocd-rbac-cm) 사용자·그룹이 어떤 리소스에 어떤 동작을 할 수 있는지 정의
AppProject 역할 특정 프로젝트에 속한 애플리케이션에 대한 세밀한 권한 제어
서비스 어카운트 CI/CD 파이프라인 등 자동화에서 사용하는 계정/토큰
SSO (Dex, Keycloak 등) 외부 IdP와 연동하여 조직 계정 기반으로 접근 제어

3장에서 인프라 레벨의 HA를 다뤘다면, 4장은 그 위에서 “사람과 시스템이 어떻게 안전하게 Argo CD를 사용할 것인지”를 설계하는 이야기라고 보면 된다.


2. 선언적 사용자 – admin과 로컬 계정

2.1 기본 admin 계정과 초기 비밀번호

Argo CD를 설치하면 기본적으로 admin 계정이 생성된다.

  • 시스템 전체에 대한 슈퍼유저 권한 보유
  • 초기 비밀번호는 argocd-initial-admin-secret 시크릿에 저장
kubectl get secret -n argocd argocd-initial-admin-secret \
  --context kind-myk8s \
  -o jsonpath='{.data.password}' | base64 -d
# 출력 값이 초기 admin 비밀번호

이 비밀번호로 UI 또는 CLI에 로그인한 뒤, 반드시 비밀번호를 변경해야 한다.

argocd account update-password
# Enter password of currently logged in user (admin): <기존 비번>
# Enter new password for user admin: <새 비번>
# Confirm new password for user admin: <새 비번>
# Password updated

비밀번호를 분실했을 때는 FAQ에 따라 시크릿의 bcrypt 해시를 초기화하거나 수정해서 복구 가능하다.

운영 환경에서는 admin 계정을 장기간 쓰지 않고, 초기 구성 후 비활성화하는 것이 일반적인 패턴이다.


2.2 admin 계정 비활성화

argocd-cm ConfigMap에서 admin 계정을 비활성화할 수 있다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
data:
  admin.enabled: "false"
  • 설치 직후: admin 계정으로 Argo CD 초기 설정 및 로컬 계정/SSO 구성
  • 이후: admin 계정 비활성화, 실제 운영은 로컬 사용자 또는 SSO 기반 계정 사용

관리자를 “비밀번호를 아는 소수”에서 “조직 계정/그룹”으로 옮겨가는 과정이기 때문에, 장기적으로는 반드시 거쳐야 할 단계라고 보는 게 맞다.


2.3 최소 권한 로컬 사용자 – alice 생성

일상적인 작업(애플리케이션 조회, 동기화 등)을 위한 로컬 사용자 alice를 선언적으로 생성해보자.

  1. argocd-cm에 사용자 추가
KUBE_EDITOR="nano" kubectl edit cm -n argocd argocd-cm

data:
  # alice 사용자 추가
  #  - apiKey : API 키 발급 가능
  #  - login  : UI/CLI 로그인 가능
  accounts.alice: apiKey, login
  # 계정 비활성화 예시
  # accounts.alice.enabled: "false"
  1. 계정 생성 확인
argocd account list
# NAME   ENABLED  CAPABILITIES
# admin  true     login
# alice  true     apiKey, login
  1. 비밀번호 설정
argocd account update-password \
  --account alice \
  --current-password qwe12345 \
  --new-password alice12345
  1. 시크릿에 저장된 계정 정보 확인
kubectl get secret -n argocd argocd-secret \
  -o jsonpath='{.data}' | jq

여기에 accounts.alice.password, accounts.alice.passwordMtime, accounts.alice.tokens 등이 bcrypt 및 base64 형태로 저장된다.


2.4 권한이 없을 때의 모습 – Guestbook 예제

관리자가 guestbook Helm 예제 애플리케이션을 생성했다고 하자.

cat <https://github.com/argoproj/argocd-example-apps
    targetRevision: HEAD
  syncPolicy:
    automated:
      enabled: true
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true
  destination:
    namespace: guestbook
    server: https://kubernetes.default.svc
EOF

이 상태에서 alice로 로그인하면, RBAC 기본 정책에 따라 애플리케이션이나 클러스터가 보이지 않을 수 있다.

즉:

  • “사용자를 만들었다고 해서 자동으로 권한이 생기는 것은 아니다”
  • “사용자 관리”와 “권한 관리(RBAC)”는 별개의 단계다

라는 것을 직관적으로 보여주는 예제다.


3. RBAC: 정책으로 권한 부여하기

Argo CD의 RBAC는 크게 두 레벨에서 구성된다.

  1. 전역 RBAC: argocd-rbac-cm ConfigMap
  2. AppProject 단위 역할: AppProject.spec.roles

3.1 전역 RBAC 설정 – argocd-rbac-cm

argocd-rbac-cm 기본 값은 다음과 같이 비어 있다.

kubectl get cm -n argocd argocd-rbac-cm -o jsonpath='{.data}' | jq
# {
#   "policy.csv": "",
#   "policy.default": "",
#   "policy.matchMode": "glob",
#   "scopes": "[groups]"
# }

여기서 중요한 필드:

  • policy.csv : 역할 정의(p) + 사용자/그룹 매핑(g)
  • policy.default : 별도 매핑이 없는 사용자가 부여받는 기본 역할

Argo CD 내장 역할 두 가지:

역할 설명
role:readonly 모든 리소스 읽기 전용
role:admin 모든 리소스에 대한 무제한 접근

3.2 기본 정책을 읽기 전용으로 변경

운영에서 가장 안전한 기본값은 “읽기 전용”이다.

KUBE_EDITOR="nano" kubectl edit cm -n argocd argocd-rbac-cm

data:
  policy.csv: ""
  policy.default: role:readonly

이렇게 설정하면:

  • 모든 사용자는 최소한 “읽기 전용”으로 Argo CD를 사용할 수 있다.
  • 쓰기/동기화/삭제 등은 추가 역할 매핑을 통해서만 허용된다.

alice 계정으로 다시 로그인해서:

argocd login argocd.example.com --insecure --username alice
# Password: alice12345

argocd app list
# guestbook 앱이 보이고 상태 조회 가능 (Synced/Healthy 등)

UI에서도 Application / Cluster 목록이 보이지만, 삭제나 수동 sync 같은 쓰기 동작은 제한된다.

운영 관점에서 보면, **“기본은 다 읽을 수 있지만, 바꾸려면 명시적인 권한이 필요하다”**라는 정책이 장애 대응과 감사(감사 로그 추적)에 모두 유리하다.


3.3 AppProject 단위 역할

전역 RBAC 외에도, 프로젝트 단위로 역할을 정의할 수 있다.

대표적인 필드:

  • spec.sourceRepos : 어떤 Git repo에서만 가져올 수 있는지
  • spec.destinations : 어느 클러스터/네임스페이스로 배포 가능한지
  • spec.roles : 해당 프로젝트에 속한 애플리케이션에 대한 역할과 정책

3장의 “App of Apps” 패턴과 결합하면, 플랫폼 팀이 “프로젝트 단위 샌드박스”를 만들어 각 팀에 할당하는 구조를 만들 수 있다.


4. 서비스 어카운트 – 자동화를 위한 계정

4.1 서비스 어카운트 개념

여기서 말하는 서비스 어카운트는 Argo CD 내부 계정 기준이다.

  • CI/CD 파이프라인, 배치 작업, 외부 시스템에서 Argo CD API를 호출할 때 사용하는 계정
  • 실제 사용자에게 연결된 계정을 그대로 쓰면:
    • 계정 비활성화/퇴사/팀 변경 시 파이프라인 장애
    • 감사 관점에서 “사람이 한 것인지, 자동화가 한 것인지” 경계가 흐려짐
  • 따라서 “사람 계정”과 “서비스 계정”은 명확히 분리하는 것이 좋다.

Argo CD에서 서비스를 위한 계정을 만드는 방법은 두 가지다.

  1. 로컬 사용자 + apiKey만 활성화
  2. AppProject 역할에 토큰을 발급해서 사용

4.2 로컬 서비스 계정 – gitops-ci

1) apiKey만 허용된 계정 생성

KUBE_EDITOR="nano" kubectl edit cm -n argocd argocd-cm

data:
  accounts.gitops-ci: apiKey

확인:

argocd account list
# NAME       ENABLED  CAPABILITIES
# admin      false    login
# alice      true     apiKey, login
# gitops-ci  true     apiKey
  • gitops-ci는 UI/CLI 로그인 기능이 없고, API 키만 사용할 수 있는 계정이다.

2) 토큰 생성 권한 부여 – role:user-update

기본적으로 다른 계정의 토큰을 생성하려면 권한이 필요하다.
실습에서는 alice에게 계정 업데이트 권한을 주기 위해 role:user-update를 추가한다.

KUBE_EDITOR="nano" kubectl edit cm -n argocd argocd-rbac-cm

data:
  policy.csv: |
    # p : 정책 정의
    p, role:user-update, accounts, update, *, allow
    p, role:user-update, accounts, get, *, allow
    # g : 사용자 → 역할 매핑
    g, alice, role:user-update

이제 alice로 로그인해서 서비스 계정 토큰을 생성할 수 있다.

argocd account generate-token -a gitops-ci
# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.... 형태의 JWT 토큰 출력

argocd account get-user-info --auth-token <위 토큰>
# Logged In: true
# Username: gitops-ci

이 토큰을 CI/CD 파이프라인에서 Authorization: Bearer <TOKEN> 형태로 사용하면 된다.

현재는 policy.default = role:readonly이기 때문에, 이 토큰은 기본적으로 “읽기 전용 권한을 가진 서비스 계정 토큰”이다.

  • 상태 조회, 앱 리스트, 클러스터 목록 조회 등은 가능
  • 동기화, 삭제, 프로젝트 변경 등은 불가

실무에서는 이 토큰에 필요한 최소 권한만 추가 부여해서 사용하는 것이 이상적이다.


4.3 프로젝트 역할과 토큰 – proj:sample-apps:read-sync

두 번째 방식은 프로젝트 역할(Role)에 토큰을 부여하는 것이다.

1) 샘플 프로젝트 생성 – sample-apps

cat <https://kubernetes.default.svc
  sourceRepos:
    - https://github.com/argoproj/argocd-example-apps.git
EOF

이 프로젝트의 특징:

  • destinations : test 네임스페이스, 해당 클러스터로만 배포 가능
  • sourceRepos : 지정된 Git repo에서만 가져올 수 있음
  • roles.read-sync : sample-apps 프로젝트에 속한 앱에 대해서만 get/sync 허용

2) 프로젝트에 애플리케이션 연결 – pre-post-sync

cat <https://github.com/argoproj/argocd-example-apps
    targetRevision: master
  destination:
    namespace: test
    server: https://kubernetes.default.svc
  syncPolicy:
    automated:
      enabled: false
    syncOptions:
    - CreateNamespace=true
EOF

alice 계정에서 다음을 실행하면:

argocd app sync argocd/pre-post-sync
# permission denied: applications, sync, sample-apps/pre-post-sync, sub: alice

동기화 권한이 없으므로 실패한다.

3) 프로젝트 역할 토큰 생성

먼저 alice에게 프로젝트 업데이트 권한을 추가로 부여한다.

KUBE_EDITOR="nano" kubectl edit cm -n argocd argocd-rbac-cm

data:
  policy.csv: |
    p, role:user-update, accounts, update, *, allow
    p, role:user-update, accounts, get, *, allow
    p, role:user-update, projects, update, sample-apps, allow
    g, alice, role:user-update

그 다음 alice가 프로젝트 역할 토큰을 생성한다.

argocd proj role create-token sample-apps read-sync
# Create token succeeded for proj:sample-apps:read-sync.
# Token: <JWT 토큰 출력>

이제 이 토큰을 사용해서 동기화를 수행하면 된다.

TOKEN=<위에서 발급된 토큰>

argocd app sync argocd/pre-post-sync --auth-token $TOKEN

이 토큰의 특성:

  • 주체(subject)는 proj:sample-apps:read-sync
  • sample-apps 프로젝트에 속한 앱에 대해서만 get/sync 가능
  • 다른 프로젝트/앱에는 권한 없음

즉, “이 파이프라인은 sample-apps 프로젝트 안에서만 동작한다”는 강력한 보안 경계를 토큰 하나로 표현할 수 있다.


5. SSO – Dex와 Keycloak

5.1 SSO 개념

SSO(Single Sign-On)는 다음과 같은 역할을 한다.

  • 애플리케이션(Argo CD)은 사용자 인증을 직접 처리하지 않는다.
  • 대신 외부 IdP(예: Keycloak, Google, 회사 SSO 등)에 인증을 위임한다.
  • 사용자는 한 번만 로그인하면 여러 애플리케이션에 접근 가능하다.
  • 권한(그룹, 역할)은 중앙에서 관리하고, 각 애플리케이션에서는 토큰의 클레임(roles, groups 등)에 따라 권한을 부여한다.

Argo CD는 SSO를 두 가지 방법으로 제공한다.

  1. 내장 Dex OIDC 공급자 사용
  2. Dex 없이, 외부 OIDC 공급자(예: Keycloak)를 직접 사용

SSO가 활성화되고 로컬 계정/관리자가 비활성화되면, Argo CD UI에서 ID/PW 입력창은 사라지고 “SSO 로그인 버튼”만 남는다.


5.2 Keycloak 개요

Keycloak은 애플리케이션에 초점을 맞춘 오픈 소스 ID 및 접근 관리 도구다.

주요 특징:

  • 로그인 UI, 비밀번호 재설정, 2FA, 약관 동의 등 인증 관련 기능 제공
  • OAuth 2.0, OpenID Connect, SAML 2.0 지원
  • 자체 사용자 DB + 소셜 로그인 + 기업 디렉터리(AD/LDAP) 연동
  • 세션/SSO/로그아웃 관리
  • 확장 가능한 구조 (커스텀 인증 플로우, 사용자 스토어, 토큰 변환 등)

Argo CD 같은 애플리케이션 입장에서는 “Keycloak이 인증/인가의 허브 역할을 해 주고, 자신은 토큰만 검증하면 되는 구조”라서 구현 부담이 크게 줄어든다.


5.3 Keycloak Dev 모드 실행 (Docker)

docker run -d \
  -e KEYCLOAK_ADMIN=admin \
  -e KEYCLOAK_ADMIN_PASSWORD=admin \
  --net host \
  --name dev-keycloak \
  quay.io/keycloak/keycloak:22.0.0 start-dev
  • 관리자 콘솔: http://localhost:8080/admin
  • 초기 관리자 계정: admin / admin

5.4 Realm / User / Group / Role 구성

  1. Realm 생성
  • 이름: myrealm
  • Realm Settings → Display name 설정
  1. User 생성
  • Users → Add user
    • Username: keycloak
    • Email: keycloak@keycloak.org
    • First name: Ola
    • Last name: Nordmann
  • Credentials 탭에서 패스워드 설정 (Temporary: Off)
  1. Group 생성 및 사용자 그룹 조인
  • Groups → Create group → mygroup
  • Users → 해당 user → Groups → mygroup Join
  1. Realm Role 생성 및 사용자 매핑
  • Realm roles → Create role → myrole
  • Users → Role mapping → myrole Assign

이 구조는 이후 Argo CD RBAC에서 groups 클레임을 이용해 권한을 부여하는 기반이 된다.


5.5 Argo CD용 Keycloak 클라이언트 생성

Keycloak 관리자 콘솔에서:

생성 후 Credentials 탭에서 Client Secret을 복사해 둔다.
예: mV3IZO3nmHoZr3BBC37UpdrMSMkF9Umt


5.6 Argo CD OIDC 설정

1) clientSecret을 Argo CD 시크릿에 저장

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

2) argocd-cm에 OIDC 설정 추가

KUBE_EDITOR="nano" kubectl edit cm -n argocd argocd-cm

data:
  url: https://argocd.example.com

  oidc.config: |
    name: Keycloak
    issuer: http://192.168.254.110:8080/realms/master
    clientID: argocd
    clientSecret: mV3IZO3nmHoZr3BBC37UpdrMSMkF9Umt
    requestedScopes: ["openid", "profile", "email"]
  • issuer: Keycloak Realm의 Issuer URL
  • requestedScopes: 토큰에 포함하고 싶은 기본 정보 범위

그룹 정보를 RBAC에 활용하고 싶다면, groups 스코프를 추가한다.

requestedScopes: ["openid", "profile", "email", "groups"]

3) Argo CD 서버 재시작

kubectl rollout restart deploy argocd-server -n argocd

이제 Argo CD 로그인 화면에 “SSO 버튼”이 보이고, 해당 버튼을 누르면 Keycloak 로그인 페이지로 리다이렉트된다.


5.7 그룹 기반 RBAC – Keycloak groups → Argo CD roles

Keycloak 그룹과 Argo CD 역할을 매핑하면, 조직의 그룹 구조를 그대로 가져와서 쓸 수 있다.

예)

KUBE_EDITOR="nano" kubectl edit cm -n argocd argocd-rbac-cm

data:
  policy.default: role:readonly
  policy.csv: |
    g, keycloak-admin, role:admin
    g, keycloak-dev,   role:developer
  • Keycloak에서 keycloak-admin, keycloak-dev 그룹을 만들고
  • 각 그룹에 사용자를 할당
  • OIDC 토큰에 groups 클레임이 포함되도록 설정

→ 이렇게 하면 Argo CD에서는 “사용자가 어떤 그룹에 속해 있느냐”에 따라 역할이 자동 할당된다.

플랫폼 팀 입장에서는 그룹만 관리해도 권한 구조가 따라오므로 운영 비용이 크게 줄어든다.


6. OAuth 2.0 / OpenID Connect / JWT 빠른 정리

4장에서는 Keycloak을 OIDC 공급자로 썼기 때문에, 배경에 깔린 프로토콜을 간단히 짚고 넘어가면 전체 구조가 더 잘 보인다.

6.1 OAuth 2.0 – 인가 프레임워크

OAuth 2.0은 “권한 위임”을 위한 프레임워크다.

역할 설명
Resource Owner 보통 사용자 (본인 데이터의 주인)
Client 사용자를 대신해 리소스에 접근하려는 애플리케이션
Authorization Server 인증/인가를 담당, Access Token 발급 (Keycloak)
Resource Server 실제 보호된 리소스를 제공하는 API 서버

Authorization Code Flow의 핵심 흐름:

  1. Client가 사용자에게 “Authorization Server로 로그인해 달라”고 리다이렉트
  2. 사용자가 Authorization Server에서 로그인 및 동의
  3. Authorization Server가 Client에게 Authorization Code 전달
  4. Client가 Authorization Code로 Access Token(필요시 Refresh Token 포함)을 발급받음
  5. Client는 Access Token을 붙여 Resource Server에 접근

Argo CD + Keycloak 시나리오에서는:

  • 브라우저(사용자)가 Keycloak에 로그인
  • Keycloak이 Argo CD에게 ID/Access Token을 발급
  • Argo CD는 이 토큰을 기반으로 사용자 세션/권한을 관리한다.

6.2 OpenID Connect – OAuth 위에 얹은 “인증” 계층

  • OAuth 2.0은 “인가(Authorization)” 프로토콜이지, 인증(Authentication)을 직접 정의하지 않는다.
  • OpenID Connect는 OAuth 2.0 위에 “사용자 인증” 계층을 추가한 표준.
  • scope=openid를 포함하면 “인증 요청”이 되며, ID Token(JWT 형식)을 반환한다.
  • ID Token에는 sub, email, name, groups 등의 클레임이 포함될 수 있다.

Argo CD 입장에서는:

  • Access Token: API 호출 권한
  • ID Token: 사용자 정보 / 그룹 정보 기반으로 RBAC 역할 부여

6.3 JWT – 토큰 형식

Keycloak이 발급하는 토큰은 대부분 JWT(JSON Web Token)이다.

  • 헤더(header).페이로드(payload).서명(signature) 형식
  • Base64URL 인코딩
  • 페이로드에 클레임(roles, groups, email 등)을 담을 수 있음
  • 서명은 Authorization Server의 비밀/키를 이용해 생성되며, Resource Server에서 검증 가능

리소스 서버(예: Argo CD API 서버)는:

  1. issuer/키 정보를 Discovery/JWKS로 확인
  2. JWT 서명 검증
  3. 만료 시간/발행자/대상(clientID) 등을 검증
  4. 페이로드에 있는 roles/groups를 읽어 권한을 판단

결국 4장의 접근 제어는 “누가 어떤 JWT를 들고 왔고, 그 JWT 안에 어떤 클레임이 들어 있는가”를 기준으로 동작한다고 볼 수 있다.


7. 정리

항목 내용
선언적 사용자 admin 계정은 초기 구성에만 사용하고, 이후에는 로컬 사용자/SSO 계정으로 전환
RBAC 전역 정책 argocd-rbac-cm의 policy.csv, policy.default로 기본 역할 및 사용자/그룹 매핑
기본 권장 정책 policy.default = role:readonly로 두고, 쓰기 권한은 명시적으로 할당
서비스 어카운트 로컬 계정에 apiKey만 부여하거나, 프로젝트 역할에 토큰을 생성해 CI/CD에서 사용
프로젝트 역할 특정 프로젝트 내부 애플리케이션에 대한 read/sync 등의 권한을 세밀하게 제어
SSO Dex 또는 Keycloak 같은 외부 OIDC 공급자와 연동해 조직 계정 기반으로 접근 제어
Keycloak 연동 Realm/User/Group/Role/Client → ArgoCD OIDC 설정 → groups 기반 RBAC

개인적인 인사이트

  • 접근 제어는 “나중에 보안팀에서 하라고 하는 것”이 아니라, 처음부터 구조적으로 설계해야 하는 운영 기능에 가깝다.
  • admin 계정을 언제, 어떤 기준으로 비활성화할지 명확히 정해두는 것만으로도 사고 위험이 크게 줄어든다.
  • 프로젝트 역할 + 토큰 조합은 “파이프라인의 공격 범위를 프로젝트 단위로 잘라내는 칼” 같은 역할을 한다.
  • SSO와 그룹 기반 RBAC를 붙이면, “사람이 소속된 팀”이 곧 “권한 경계”가 되기 때문에, 조직이 커질수록 효과가 기하급수적으로 커진다.
728x90

댓글