보안
보안 (Security)
Argo CD는 PCI 컴플라이언스 요구사항을 충족하기 위해 엄격한 내부 보안 리뷰와 침투 테스트를 거쳤어요. 여기서는 Argo CD의 보안 주제와 구현 세부사항 몇 가지를 다뤄볼게요.
출처: 문서
본문
인증 (Authentication)
Argo CD API 서버에 대한 인증은 오직 JSON Web Tokens(JWT)로만 수행돼요. 사용자 이름/비밀번호 bearer 토큰은 인증에 사용되지 않아요. JWT는 다음 중 한 방식으로 획득·관리돼요:
- 로컬
admin사용자의 경우 사용자 이름/비밀번호가/api/v1/session엔드포인트를 통해 JWT로 교환돼요. 이 토큰은 Argo CD API 서버 자체가 서명·발급하며 24시간 후 만료돼요(이 토큰은 한때 만료되지 않았어요, CVE-2021-26921 참고). admin 비밀번호가 업데이트되면 기존 admin JWT 토큰이 모두 즉시 무효화돼요. 비밀번호는argocd-secretSecret에 bcrypt 해시로 저장돼요. - Single Sign-On 사용자의 경우 구성된 OIDC identity provider로 OAuth2 로그인 흐름을 완료해요(번들 Dex provider를 통해 위임되거나, 자체 관리 OIDC provider에 직접). 이 JWT는 IDP가 서명·발급하며 만료·무효화는 provider가 처리해요. Dex 토큰은 24시간 후 만료돼요.
- 자동화 토큰은
/api/v1/projects/{project}/roles/{role}/token엔드포인트를 사용해 프로젝트용으로 생성되며 Argo CD가 서명·발급해요. 이 토큰은 범위와 권한이 제한적이며, 속해 있는 프로젝트의 애플리케이션 리소스만 관리하는 데 사용돼요. 프로젝트 JWT는 만료를 설정할 수 있고, 프로젝트 역할에서 JWT 참조 ID를 삭제하면 즉시 무효화할 수 있어요.
인가 (Authorization)
인가는 사용자 JWT 그룹 claims의 그룹 멤버십 목록을 반복하며 각 그룹을 RBAC 정책의 역할/규칙과 비교해 수행해요. 일치하는 규칙이 있으면 API 요청에 접근이 허용돼요.
TLS
모든 네트워크 통신은 TLS로 수행돼요 — 세 컴포넌트(argocd-server, argocd-repo-server, argocd-application-controller) 사이의 서비스 간 통신도 포함해서요. Argo CD API 서버는 --tlsminversion 1.2 플래그로 TLS 1.2 사용을 강제할 수 있어요. Redis와의 통신은 기본적으로 평문 HTTP로 수행돼요. TLS는 커맨드라인 인자로 설정할 수 있어요.
Git & Helm 리포지토리 (Git & Helm Repositories)
Git과 helm 리포지토리는 repo-server라는 독립 서비스가 관리해요. repo-server는 어떤 Kubernetes 권한도 갖지 않으며 어떤 서비스(git 포함)의 자격 증명도 저장하지 않아요. repo-server는 Argo CD 운영자가 허용·신뢰한 리포지토리를 클론하고, 리포지토리의 주어진 경로에서 Kubernetes 매니페스트를 생성하는 책임을 가져요. 성능과 대역폭 효율을 위해 repo-server는 이 리포지토리의 로컬 클론을 유지해, 이후 리포지토리에 대한 커밋을 효율적으로 다운로드해요.
Argo CD가 배포하도록 허용된 git 리포지토리를 구성할 때는 보안 고려사항이 있어요. 요컨대, Argo CD가 신뢰하는 git 리포지토리에 무단 쓰기 접근을 얻으면 아래에 설명된 심각한 보안 영향이 있어요.
무단 배포 (Unauthorized Deployments)
Argo CD는 git에 정의된 Kubernetes 리소스를 배포하므로, 신뢰하는 git 리포지토리에 접근할 수 있는 공격자는 배포되는 Kubernetes 리소스에 영향을 줄 수 있어요. 예를 들어 공격자는 배포 매니페스트를 업데이트해 환경에 악성 컨테이너 이미지를 배포하거나, git에서 리소스를 삭제해 라이브 환경에서 prune되게 할 수 있어요.
도구 명령 호출 (Tool command invocation)
Argo CD는 raw YAML 외에도 helm과 kustomize라는 두 가지 인기 있는 Kubernetes 설정 관리 도구를 기본 지원해요. 매니페스트를 렌더링할 때 Argo CD는 이 설정 관리 도구를 실행해(즉 helm template, kustomize build) 매니페스트를 생성해요. 신뢰하는 git 리포지토리에 쓰기 접근이 있는 공격자는 트리 밖(out-of-tree) 파일을 읽으려는 악성 helm 차트나 kustomization을 만들 수 있어요. 여기에는 인접 git 리포지토리와 repo-server 자체의 파일이 포함돼요. 이것이 조직에 위험인지 여부는 git 리포지토리 내용이 민감한 성격인지에 달려 있어요. 기본적으로 repo-server 자체는 민감 정보를 담지 않지만, 민감 정보(예: 복호화 키)를 담는 Config Management Plugins로 구성될 수 있어요. 그런 플러그인을 사용하면 리포지토리 내용이 항상 신뢰될 수 있도록 극도의 주의를 기울여야 해요.
선택적으로 내장 설정 관리 도구를 개별적으로 비활성화할 수 있어요. 사용자가 특정 설정 관리 도구를 필요로 하지 않을 거라는 걸 안다면 그 도구를 비활성화하는 것이 좋아요. 자세한 내용은 Tool Detection을 참고하세요.
원격 base와 helm 차트 의존성 (Remote bases and helm chart dependencies)
Argo CD의 리포지토리 허용 목록은 초기 클론되는 리포지토리만 제한해요. 하지만 kustomize와 helm 모두 추가 리포지토리를 참조·따르는 기능(kustomize remote bases, helm chart dependencies 등)이 있는데, 이는 리포지토리 허용 목록에 없을 수 있어요. Argo CD 운영자는 신뢰하는 git 리포지토리에 쓰기 접근이 있는 사용자가 구성된 git 리포지토리에서 쉽게 검색·감사되지 않는 Kubernetes 리소스를 담은 다른 원격 git 리포지토리를 참조할 수 있음을 이해해야 해요.
민감 정보 (Sensitive Information)
시크릿 (Secrets)
Argo CD는 API에서 민감 데이터를 절대 반환하지 않으며, API 페이로드와 로그의 모든 민감 데이터를 redact해요. 여기에는 다음이 포함돼요:
- 클러스터 자격 증명
- Git 자격 증명
- OAuth2 클라이언트 시크릿
- Kubernetes Secret 값
외부 클러스터 자격 증명 (External Cluster Credentials)
외부 클러스터를 관리하기 위해 Argo CD는 외부 클러스터의 자격 증명을 argocd 네임스페이스의 Kubernetes Secret으로 저장해요. 이 secret은 argocd cluster add 중 생성된 argocd-manager ServiceAccount와 연결된 K8s API bearer 토큰과, 그 API 서버에 대한 연결 옵션(TLS 구성/인증서, AWS role-arn 등)을 담아요. 이 정보는 Argo CD 서비스가 사용하는 클러스터의 REST config와 kubeconfig를 재구성하는 데 사용돼요.
Argo CD가 사용하는 bearer 토큰을 교체하려면 토큰을 삭제하면(kubectl 사용) Kubernetes가 새 bearer 토큰으로 새 secret을 생성해요. 새 토큰은 argocd cluster add를 다시 실행해 Argo CD에 다시 입력할 수 있어요. 관리되는 클러스터에 대해 다음 명령을 실행하세요:
# run using a kubeconfig for the externally managed cluster
kubectl delete secret argocd-manager-token-XXXXXX -n kube-system
argocd cluster add CONTEXTNAME
[!NOTE] Kubernetes 1.24는 Service Account 토큰 자동 생성을 중단했어요. Argo CD 2.4부터
argocd cluster add는 1.24 클러스터를 추가할 때 ServiceAccount 와 만료되지 않는 Service Account 토큰 Secret을 생성해요. 향후 Argo CD는 장기 토큰 사용을 피하기 위해 Kubernetes TokenRequest API 지원을 추가할 거예요.
관리되는 클러스터에 대한 Argo CD의 접근을 폐기하려면 관리되는 클러스터에 대해 RBAC 아티팩트를 삭제하고, Argo CD에서 클러스터 항목을 제거하세요:
# run using a kubeconfig for the externally managed cluster
kubectl delete sa argocd-manager -n kube-system
kubectl delete clusterrole argocd-manager-role
kubectl delete clusterrolebinding argocd-manager-role-binding
argocd cluster rm https://your-kubernetes-cluster-addr
[!NOTE] AWS EKS 클러스터의 경우 get-token 명령으로 외부 클러스터에 인증하며, 로컬에 저장된 토큰 대신 IAM 역할을 사용하므로 토큰 교체가 필요 없고 폐기도 IAM으로 처리돼요.
클러스터 RBAC (Cluster RBAC)
기본적으로 Argo CD는 clusteradmin 수준 역할을 사용해 다음을 수행해요:
- 클러스터 상태 감시·조작
- 클러스터에 리소스 배포
Argo CD는 제대로 동작하기 위해 관리 클러스터의 리소스에 대한 클러스터 전체 읽기(read) 권한을 요구하지만, 클러스터에 대한 전체 쓰기(write) 권한은 반드시 필요하지 않아요. argocd-server와 argocd-application-controller가 사용하는 ClusterRole은 쓰기 권한을 Argo CD가 관리하길 원하는 네임스페이스와 리소스로만 제한하도록 수정할 수 있어요.
외부 관리 클러스터의 권한을 세밀하게 조정하려면 argocd-manager-role의 ClusterRole을 편집하세요:
# run using a kubeconfig for the externally managed cluster
kubectl edit clusterrole argocd-manager-role
Argo CD가 자체 클러스터(즉 https://kubernetes.default.svc)에 대해 갖는 권한을 세밀하게 조정하려면 Argo CD가 실행 중인 곳의 다음 클러스터 역할을 편집하세요:
# run using a kubeconfig to the cluster Argo CD is running in
kubectl edit clusterrole argocd-server
kubectl edit clusterrole argocd-application-controller
[!TIP] Argo CD가 어떤 종류의 리소스에 접근하는 것을 거부하고 싶다면 제외 리소스로 추가하세요.
감사 (Auditing)
GitOps 배포 도구로서 Git 커밋 히스토리는 애플리케이션 구성에 어떤 변경이 언제, 누구에 의해 이뤄졌는지에 대한 자연스러운 감사 로그를 제공해요. 하지만 이 감사 로그는 Git에서 일어난 일에만 적용되며 클러스터에서 일어나는 이벤트와 반드시 일대일로 일치하지는 않아요. 예를 들어 사용자 A가 애플리케이션 매니페스트에 여러 커밋을 했지만, 사용자 B가 그 변경을 나중에 어떤 시점에야 클러스터에 동기화했을 수 있어요.
Git 리비전 히스토리를 보완하기 위해 Argo CD는 해당되는 경우 책임 행위자를 나타내며 애플리케이션 활동의 Kubernetes Events를 방출해요. 예를 들어:
$ kubectl get events
LAST SEEN FIRST SEEN COUNT NAME KIND SUBOBJECT TYPE REASON SOURCE MESSAGE
1m 1m 1 guestbook.157f7c5edd33aeac Application Normal ResourceCreated argocd-server admin created application
1m 1m 1 guestbook.157f7c5f0f747acf Application Normal ResourceUpdated argocd-application-controller Updated sync status: -> OutOfSync
1m 1m 1 guestbook.157f7c5f0fbebbff Application Normal ResourceUpdated argocd-application-controller Updated health status: -> Missing
1m 1m 1 guestbook.157f7c6069e14f4d Application Normal OperationStarted argocd-server admin initiated sync to HEAD (8a1cb4a02d3538e54907c827352f66f20c3d7b0d)
1m 1m 1 guestbook.157f7c60a55a81a8 Application Normal OperationCompleted argocd-application-controller Sync operation to 8a1cb4a02d3538e54907c827352f66f20c3d7b0d succeeded
1m 1m 1 guestbook.157f7c60af1ccae2 Application Normal ResourceUpdated argocd-application-controller Updated sync status: OutOfSync -> Synced
1m 1m 1 guestbook.157f7c60af5bc4f0 Application Normal ResourceUpdated argocd-application-controller Updated health status: Missing -> Progressing
1m 1m 1 guestbook.157f7c651990e848 Application Normal ResourceUpdated argocd-application-controller Updated health status: Progressing -> Healthy
이 이벤트들은 Event Exporter나 Event Router 같은 다른 도구로 더 오랜 기간 영속화할 수 있어요.
WebHook 페이로드 (WebHook Payloads)
webhook 이벤트의 페이로드는 신뢰할 수 없는 것으로 간주돼요. Argo CD는 페이로드에서 webhook 이벤트에 관련된 애플리케이션(어떤 리포지토리가 수정됐는지 등)을 추론하기 위해서만 페이로드를 검사한 다음, 조정을 위해 관련 애플리케이션을 새로고침해요. 이 새로고침은 3분 간격으로 정기적으로 일어나는 새로고침과 같지만 webhook 이벤트로 덜 기다리고 앞당겨지는 것뿐이에요.
로깅 (Logging)
보안 필드 (Security field)
보안 관련 로그는 찾고, 분석하고, 보고하기 쉽도록 security 필드로 태그돼요.
| Level | Friendly Level | Description | Example |
|---|---|---|---|
| 1 | Low | 비정상이 아닌, 악성이 아닌 이벤트 | 성공적인 접근 |
| 2 | Medium | 악성 이벤트일 수 있지만 사용자/시스템 오류일 가능성이 높은 이벤트 | 접근 거부 |
| 3 | High | 악성일 가능성이 있지만 부작용이 없거나 차단된 이벤트 | 리포지토리의 경계 밖 symlink |
| 4 | Critical | 부작용이 있었던 악성 또는 악용 가능 이벤트 | 파일시스템에 남겨진 시크릿 |
| 5 | Emergency | 명백히 악성이며 우연히 절대 발생해선 안 되고 활성 공격을 나타내는 이벤트 | 계정 무차별 대입 |
해당되는 경우 Common Weakness Enumeration 번호를 지정하는 CWE 필드도 추가돼요.
[!WARNING] 모든 보안 로그가 아직 포괄적으로 태그된 것은 아니며, 이 예시들이 반드시 구현된 것은 아닌 점을 유의하세요.
API 로그 (API Logs)
Argo CD는 /cluster.ClusterService/Create, /session.SessionService/Create 등 민감한 것으로 간주되는 요청을 제외한 대부분의 API 요청 페이로드를 로깅해요. 전체 메서드 목록은 server/server.go에서 찾을 수 있어요.
API 서버는 보통 프록시 뒤에 있으므로 Argo CD는 API 엔드포인트를 요청하는 클라이언트의 IP 주소를 로깅하지 않아요. 대신 API 서버 앞에 있는 프록시 서버에서 IP 주소 로깅을 구성하는 것이 권장돼요.
표준 애플리케이션 로그 필드 (Standard Application log fields)
애플리케이션 관련 로그에 대해 Argo CD는 다음 표준 필드를 로깅해요:
- application: 네임스페이스가 없는 Application 이름
- app-namespace: Application의 네임스페이스
- project: Application의 프로젝트
ApplicationSets
Argo CD의 ApplicationSets 기능에는 자체 보안 고려사항이 있어요. ApplicationSets를 사용하기 전에 그 문제를 인지하세요.
디렉토리 App 메모리 사용 제한 (Limiting Directory App Memory Usage)
2.2.10, 2.1.16, >2.3.5
디렉토리형 Application(소스가 raw JSON 또는 YAML 파일인 것)은 YAML 파일의 크기와 구조에 따라 상당한 repo-server 메모리를 소비할 수 있어요.
repo-server의 메모리 과다 사용(잠재적으로 크래시와 서비스 거부를 유발)을 피하려면 argocd-cmd-params-cm에 reposerver.max.combined.directory.manifests.size 설정 옵션을 설정하세요.
이 옵션은 개별 앱의 모든 JSON 또는 YAML 파일의 결합 크기를 제한해요. 매니페스트의 메모리 내 표현은 디스크의 매니페스트 크기보다 최대 300배 클 수 있음에 유의하세요. 또한 이 제한은 Application당 기준이에요. 여러 애플리케이션에 대한 매니페스트가 한 번에 생성되면 메모리 사용이 더 높아져요.
예시:
repo-server에 10G 메모리 제한이 있고, raw JSON 또는 YAML 파일을 사용하는 Application이 열 개 있다고 가정해 봐요. Application당 안전한 최대 결합 파일 크기를 계산하려면 10G를 300 * 10 Apps(300은 매니페스트의 최악의 경우 메모리 성장 계수)로 나누세요.
10G / 300 * 10 = 3M
따라서 이 설정에 대해 합리적으로 안전한 구성은 앱당 3M 제한이에요.
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cmd-params-cm
data:
reposerver.max.combined.directory.manifests.size: '3M'
300x 비율은 악의적으로 제작된 매니페스트 파일을 가정해요. 우발적인 과도한 메모리 사용으로부터만 보호하려 한다면 더 작은 비율을 사용해도 안전할 거예요.
악의적인 사용자가 추가 Application을 만들 수 있다면 총 메모리 사용을 늘릴 수 있다는 점을 기억하세요. App 생성 권한을 신중하게 부여하세요.