0. Contents


1. Overview

고객 클러스터: 눌러서 설명 보기고객 클러스터Application · 서버 버전차트 저장소: 눌러서 설명 보기차트 저장소https://… index.yaml · tgz바깥 주소: 눌러서 설명 보기바깥 주소1.1.1.1:443 · 메타데이터 주소클러스터 kukie-devnamespace kukie-devagent: 눌러서 설명 보기agentreplicas 1 · 메모리 768Mi대화 잠금 · 승인 대기: 눌러서 설명 보기대화 잠금 · 승인 대기프로세스 메모리. 재시작하면 전부 끊긴다고객 클러스터 접속 정보: 눌러서 설명 보기고객 클러스터 접속 정보이 파드를 지나간다. 차트를 여기서 돌리지 않는다chart_fetch: 눌러서 설명 보기chart_fetchhttps · 공인 주소만리다이렉트도 재검사받은 차트는 풀지 않음helm.render(): 눌러서 설명 보기helm.render()실패도 결과로 받는다예외를 내지 않는다나가는 길이 열려 있어 차트는 이 파드가 받는다reloader: 눌러서 설명 보기reloaderredis-0: 눌러서 설명 보기redis-0:6379member: 눌러서 설명 보기member:8080web: 눌러서 설명 보기web:80NetworkPolicy helm-render: 눌러서 설명 보기NetworkPolicy helm-renderingress agent:8000 · egress []helm-render: 눌러서 설명 보기helm-renderreplicas 1 · 메모리 512Mi · CPU 1FastAPI :8000: 눌러서 설명 보기FastAPI :8000동시 2개, 넘으면 429 busy자리를 먼저 잡고 본문을 읽는다. 저장하는 것은 없다chart.py: 눌러서 설명 보기4chart.py 실행 전 검사링크 · 스키마 $ref 거절, 푸는 크기 64MiB 에서 중단/work (emptyDir): 눌러서 설명 보기/work (emptyDir)chart/ · values/000.yaml쓸 수 있는 유일한 곳. 요청마다 만들고 끝나면 지운다helm template: 눌러서 설명 보기5helm template 프로세스 v4.2.15초 · 200MiB · 출력 20MB 를 넘으면 프로세스 그룹 종료토큰 없음: 눌러서 설명 보기토큰 없음읽기 전용: 눌러서 설명 보기읽기 전용uid 1000: 눌러서 설명 보기uid 1000caps 제거: 눌러서 설명 보기caps 제거123POST /rendertgz · values · 조건6YAML 또는 reasonX-Helm-VersionAPI 서버 · 클러스터 DNS 도 시간 초과시간 초과namespace argocdNetworkPolicy 4개, 단속을 켜자 함께 적용됐다argocd server: 눌러서 설명 보기serverargocd repo-server: 눌러서 설명 보기repo-serverargocd application-controller: 눌러서 설명 보기application-controllerargocd redis: 눌러서 설명 보기redisagent 에서 redis:6379 · repo-server:8081 은 막히고 server:80 은 열린다. 앱 12개 Synced · Healthyaws-eks-nodeagent: 눌러서 설명 보기7aws-eks-nodeagent --enable-network-policy=true정책을 실제로 막는 것은 노드의 이 프로그램이다. 옵션이 꺼져 있으면 NetworkPolicy 객체는 아무것도 막지 않는다① ② 에이전트가 조건과 차트를 모아 ③ 렌더링 파드로만 넘기고, ④ ⑤ 검사와 제한을 거친 helm 의 결과만 ⑥ 으로 돌아온다값 찾기와 PR 경로에는 아직 이어지지 않았다. 시험은 에이전트 파드에서 직접 호출했다파랑 접속 정보가 지나가는 파드 · 보라 고객 차트가 도는 파드 · 회색 관련 없는 파드 · 초록 열린 길 · 빨강 막힌 길
나쁜 차트가 다치게 할 수 있는 범위를 렌더링 파드 하나로 줄인 전체 구조
칸을 누르면 설명이 열린다

배경

  • 02편의 PR 경로는 plain 과 Kustomize 만 지원한다
  • Helm 앱을 지원하려면 고객 차트를 helm template 으로 렌더링해야 한다. 04편의 값 찾기는 후보마다 렌더링을 돌린다
  • 고객 차트는 우리가 쓰지 않은 입력이다. 템플릿은 프로그램처럼 돌고, 스키마와 링크는 파일을 읽는다

이 글의 질문

  • 렌더링을 왜 에이전트와 다른 파드에서 하는가
  • helm 이 고객 차트 때문에 무엇을 읽고 어디에 닿는가
  • 결과를 Argo CD 와 어떻게 맞추는가
  • 네트워크를 막았을 때 차트는 누가 받는가

2. Pod Separation — 파드 분리

2.1 에이전트 파드의 제약

  • 에이전트는 모든 고객이 함께 쓰는 파드 한 대이고, 메모리 제한은 768Mi 다
  • 방별 대화 잠금과 승인 대기가 프로세스 메모리에 있다. 파드가 재시작되면 다른 고객의 대화까지 끊기고 승인 대기도 사라진다
  • 이 파드에는 고객 클러스터 접속 정보가 지나간다. 고객 차트를 같은 공간에서 돌리면 렌더링 사고가 접속 정보까지 닿는다

2.2 렌더링 앱의 언어

렌더링 앱은 helm 을 실행하는 접수창구다. 실제 렌더링과 그 메모리는 helm 프로세스의 몫이라, 앱 언어를 바꿔도 렌더링 시간과 메모리는 그대로다.

helm 을 Go 라이브러리로 앱 안에 넣으면 프로세스를 띄우는 시간은 줄어든다. 대신 도는 템플릿을 도중에 끊을 수 없고, 메모리가 넘치면 앱까지 함께 죽는다. 그래서 프로세스로 실행하고, 앱은 에이전트와 같은 파이썬으로 두었다.

2.3 실패의 구분

에이전트 쪽 render() 는 예외를 내지 않는다. 연결 거부, 응답 중 끊김, 시간 초과는 모두 결과로 돌아온다.

구분reason사용자에게 보일 뜻
서비스 쪽unavailable busy internal렌더링 실패, 잠시 뒤 다시 시도
차트 쪽timeout memory_exceeded chart_has_link helm_failed 등이 차트로는 렌더링할 수 없음

3. Helm Input Risks — helm 입력의 위험

3.1 재현 결과

helm 4.2 로 직접 재현했다.

입력helm 의 동작
스키마 $ref: file:///etc/hosts파일을 읽고 내용을 JSON 으로 해석하다 오류를 낸다
스키마 $ref: http://…그 주소로 접속한다
스키마 $schema: http://…메타 스키마를 받으러 그 주소로 접속한다
폴더 차트의 심볼릭 링크found symbolic link 를 알리고 차트 밖 파일을 템플릿으로 읽는다

3.2 스키마 검사

① 조건 읽기② 차트 받기③ POST /render④ 실행 전 검사⑤ helm 제한⑥ 결과⑦ 단속chart.py check_schema (요약)for key, value in node:if isinstance(value, str):if key in ("$ref", "$dynamicRef", "$recursiveRef") \1and not value.startswith("#"):1raise ChartRejected("schema_external_ref", ...)if key == "$schema" \2and value.removesuffix("#") not in _KNOWN_META_SCHEMAS:2raise ChartRejected("schema_external_ref", ...)else:stack.append(value)json.loads(data.decode(), object_pairs_hook=_Pairs)31파일과 주소를 읽는 참조문서 안(#…)만 받는다helm 이 file: 은 읽고 http: 는 접속했다2임의의 메타 스키마$schema 도 주소를 읽으러 간다helm 이 아는 초안 목록만 받는다3같은 키 두 번쌍을 그대로 두어 앞쪽 값도 검사한다
helm 을 실행하기 전에 values.schema.json 을 안쪽 하위 차트까지 검사한다
  • values.schema.json 은 하위 차트마다 있을 수 있다. 안쪽 charts/*.tgz 도 helm 이 읽으므로 풀지 않고 같은 규칙으로 읽는다
  • 다음 겹으로 helm 의 프록시를 닿지 않는 주소로 둔다. 바깥 주소로 가는 요청이 그 주소에서 끝난다

3.3 차트 풀기

입력처리
심볼릭 링크, 하드 링크어디를 가리키든 거절
.. 절대 경로, 역슬래시거절
장치 파일, 같은 경로 두 번거절
압축 폭탄푸는 바이트를 읽으면서 세어 64MiB 에서 중단
깊은 하위 차트네 단계까지

3.4 쓰지 않는 옵션

  • --enable-dns, --dry-run=server: 클러스터나 DNS 를 읽는다
  • --post-renderer, 플러그인: 임의 실행 파일을 돌린다
  • --dependency-update: 차트가 정한 저장소에서 받는다. CVE-2025-53547 의 경로다
  • 조건 값은 형식 검사를 통과해야 인자가 된다. --post-renderer=… 를 릴리스 이름으로 보내면 400 이 되고, 받은 값은 응답에 되돌려 싣지 않는다

4. Render Limits — 렌더링 제한

4.1 시작값

맥에서 잰 값은 04편 6장의 측정이다.

항목맥에서 잰 값 (공개 차트 21개, helm 직접 실행)시작값넘으면
시간중앙값 0.083초, 최대 0.6초5초프로세스 종료, timeout
출력최대 5.7MB20MB읽는 도중 종료, output_too_large
프로세스 메모리최대 137MB200MiB프로세스 종료, memory_exceeded
동시 실행2개기다리지 않고 429 busy
파드 메모리512Mi파드 재시작

값은 전부 환경변수로 바꾼다. 요청 본문을 읽기 전에 자리를 먼저 잡아, 넘치는 요청은 메모리를 쓰지 않는다.

4.2 프로세스 종료

① 조건 읽기② 차트 받기③ POST /render④ 실행 전 검사⑤ helm 제한⑥ 결과⑦ 단속helm.py run_template (요약)finished = asyncio.gather(*readers)memory = asyncio.ensure_future(watch_memory())try:done, _ = await asyncio.wait({finished, memory},1timeout=settings.timeout_seconds,1return_when=asyncio.FIRST_COMPLETED)if not done:return Rendered(reason="timeout", ...)if finished not in done:2return Rendered(reason="memory_exceeded", ...)2if isinstance(finished.exception(), _OutputTooLarge):3return Rendered(reason="output_too_large", ...)3...finally:_kill(process)4for task in (*readers, memory):task.cancel()await asyncio.wait_for(asyncio.gather(_drain(process.stdout),5_drain(process.stderr), process.wait()), _REAP_SECONDS)51시간시작값 5초2메모리/proc 을 50ms 마다 읽어 RSS 를 본다시작값 200MiB3출력읽으면서 세고 20MB 를 넘으면 끊는다4프로세스 그룹 종료호출이 취소돼도 지나간다5파이프 비우기읽기를 멈춘 채 process.wait() 를 기다리면asyncio 가 끝나지 않는다. 시험에서 찾았다
어느 경로로 나가든 프로세스 그룹을 끝내고 파이프를 비운다

처음 구현은 출력 제한을 넘기면 서비스가 영원히 멈췄다. 5번 상자의 동작을 몰랐기 때문이고, 큰 출력을 실제로 넣은 시험에서 드러났다.

4.3 파드 격리

  • automountServiceAccountToken: false, enableServiceLinks: false: 토큰과 다른 Service 주소를 주지 않는다
  • readOnlyRootFilesystem, uid 1000, capabilities 전부 제거, seccomp RuntimeDefault
  • 쓸 수 있는 곳은 emptyDir /work 뿐이고, 요청마다 임시 폴더를 만들어 끝나면 지운다
  • emptyDir 은 컨테이너 재시작에도 남는다. 앞 컨테이너가 죽으며 남긴 values 를 시작할 때 지운다
  • 렌더링 결과, values, helm stderr 는 로그에 남기지 않는다. 사유와 시간, 크기만 남긴다
  • CPU 제한 1코어를 둔다. 같은 노드의 에이전트가 함께 느려지는 것을 막기 위해서다

5. Argo CD Alignment — Argo CD 조건 맞추기

5.1 렌더링 조건

① 조건 읽기② 차트 받기③ POST /render④ 실행 전 검사⑤ helm 제한⑥ 결과⑦ 단속helm.py template_args (요약)args = ["template", str(chart), "--name-template", c.release_name]1if c.namespace: args += ["--namespace", c.namespace]2if c.kube_version: args += ["--kube-version", c.kube_version]3for path in value_paths:4args += ["--values", str(path)]4for version in c.api_versions:args += ["--api-versions", version]if not c.skip_crds: args.append("--include-crds")5if c.skip_schema_validation: args.append("--skip-schema-validation")if c.skip_tests: args.append("--skip-tests")1릴리스 이름helm.releaseName, 없으면 Application 이름2네임스페이스helm.namespace, 없으면 destination3Kubernetes 버전helm.kubeVersion, 없으면 클러스터 값v1.36.5-eks-… 를 1.36.5 로 정리4values 파일valueFiles 순서 그대로, 인라인은 마지막파일 이름은 서비스가 짓는다5CRD 포함skipCrds 가 없으면 기본으로 넣는다
순서와 옵션은 Argo CD v3.5.3 util/helm/cmd.go 의 template 과 같다

5.2 values 파일

  • 렌더링 파드는 경로가 아니라 내용을 받는다. 파일 이름은 서비스가 000.yaml 처럼 짓는다. 그래서 values 경로 조작(CVE-2022-24348)이 이 파드에서는 생기지 않는다
  • 경로를 읽는 일은 에이전트가 레포 안에서만 한다. 상대 경로는 차트 폴더, / 로 시작하면 레포 루트, $이름/… 은 ref 소스 기준이고, 레포 밖이나 밖을 가리키는 링크는 거절한다
  • [a, b, a] 는 -f a -f b 다. Argo CD 의 appendUnique 가 같은 파일을 처음 자리에 한 번만 쓴다
  • 인라인은 valuesObject 가 비어 있어도 values 보다 우선한다

같은 결과를 낼 수 없는 Application 은 다른 결과를 내지 않고 거절한다. helm.parameters, 원격 values, glob, 환경변수가 든 경로가 여기에 해당한다.

5.3 같은 결과의 확인

공개 차트 21개를 서비스와 helm 4.2.1 직접 실행으로 각각 렌더링해 비교했다. 맥의 컨테이너에서 쟀다.

결과차트 수내용
바이트까지 같음14
무작위 값만 다름7비밀번호, 인증서, 테스트 파드 이름
그 밖의 차이0객체로 풀어 칸마다 비교

5.4 Helm 버전

  • 렌더링 파드의 helm 은 dev Argo CD v3.5.3 의 repo-server 와 같은 4.2.1 로 고정했다. 버전과 sha256 을 이미지 빌드에서 대조하고, 응답 머리에 버전을 돌려준다
  • helm 3.22.1 과 비교하면 21개 중 4개 차트에서 무작위 값이 아닌 차이가 있었다. 대부분 limits: null 같은 null 값의 처리다
  • 고객 Argo CD 의 helm 이 다르면 비교가 어긋날 수 있다. 이 차이는 남아 있다

6. Network Isolation — 네트워크 격리

6.1 꺼져 있던 단속

격리 정책을 쓰기 전에 dev 클러스터를 먼저 확인했고, vpc-cni 의 NetworkPolicy 단속은 꺼져 있었다.

위치값
애드온 설정enableNetworkPolicy 없음
ConfigMap amazon-vpc-cnienable-network-policy-controller: false
노드 에이전트 인자--enable-network-policy=false

argocd 네임스페이스에는 NetworkPolicy 4개가 있었지만 아무것도 막지 않고 있었다. 객체가 있는 것만으로는 적용 여부를 알 수 없다.

6.2 단속을 켜기 전 점검

단속을 켜면 있던 정책 4개가 함께 적용된다.

① 조건 읽기② 차트 받기③ POST /render④ 실행 전 검사⑤ helm 제한⑥ 결과⑦ 단속agentkukie-dev 파드켠 뒤 직접 연결namespace argocdNetworkPolicy 4개 · 막는 것은 Ingress 뿐server80 · 443 → 8080. 전부 허용application-controllermetrics 만 받는다applicationsetdev 에서는 replicas 0repo-server:8081 repo-server:8084 metricsredis:6379 redisrepo → redis오류 로그 0건 · 앱 12개 Synced · Healthy열림argocd-repo-server:8081 막힘argocd-redis:6379 막힘단속을 켜면 있던 정책이 함께 적용된다 — 실제 쓰는 연결은 모두 허용 목록에 있었다빨간 점선은 정책이 걸린 파드다. 에이전트에서 argocd-server:80 은 열리고, repo-server 와 redis 는 막힌다
단속을 켜기 전에 실제 연결(파드 환경변수 · 포트 이름)과 정책을 대조한 결과

6.3 렌더링 파드 정책

① 조건 읽기② 차트 받기③ POST /render④ 실행 전 검사⑤ helm 제한⑥ 결과⑦ 단속base/helm-render/networkpolicy.yaml (요약)kind: NetworkPolicymetadata:name: helm-renderspec:podSelector:matchLabels:app.kubernetes.io/name: helm-renderpolicyTypes: [Ingress, Egress]1ingress:2- from:2- podSelector:2matchLabels:2app.kubernetes.io/name: agent2ports:2- {protocol: TCP, port: 8000}2egress: []31두 방향 모두 정책 대상적지 않은 방향은 기본 허용으로 남는다2들어오는 길web → helm-render:8000 시간 초과agent → helm-render:8000 연결됨3나가는 길1.1.1.1:443 · 메타데이터 주소 · API 서버 시간 초과클러스터 DNS 시간 초과, 이름으로는 어디도 못 찾음lo막히지 않는 곳자기 127.0.0.1:8000 은 정책 대상이 아니다이 서비스뿐이고, 스키마 검사가 먼저 거른다
kukie-dev 의 렌더링 파드에서 직접 연결해 본 결과
  • 처음 설계는 차트를 받으려고 DNS 와 공인 주소 443 을 열어 두는 안이었다
  • 렌더링 파드가 어디에도 나가지 않는 쪽이 경계가 단순하므로 전부 막았다

6.4 차트 내려받기의 위치

나가는 길을 막으면서 내려받기는 에이전트로 옮겼다. 고객이 정한 주소에 접속 정보가 있는 파드가 나가는 셈이므로 코드로 막는다.

① 조건 읽기② 차트 받기③ POST /render④ 실행 전 검사⑤ helm 제한⑥ 결과⑦ 단속chart_fetch.py (요약)if parts.scheme != "https" or parts.username or parts.password \1or port not in (None, 443):1raise FetchFailed("url_rejected", ...)infos = socket.getaddrinfo(host, 443, type=socket.SOCK_STREAM)2for info in infos:2if not checked.is_global or checked.is_multicast:2raise FetchFailed("address_blocked", ...)sock = socket.create_connection((self._address, 443), self.timeout)3self.sock = self._tls.wrap_socket(sock, server_hostname=self.host)3while chunk := body.read1(_CHUNK):4if received > limit: ...4if time.monotonic() > deadline: ...41주소 형식https · 443 만, 사용자 정보 금지2내부망 차단풀린 주소가 하나라도 공인이 아니면 거절리다이렉트마다 다시 검사한다3검사한 주소로 연결검사 뒤 이름이 바뀌어도 소용없다인증서는 원래 이름으로 확인한다4크기와 전체 시간조금씩 보내는 서버도 30초에 끊는다
접속 정보가 있는 파드가 고객이 정한 주소로 나가므로 코드로 막는다

저장소 목록(index.yaml)은 YAML 파서에 넣지 않고, 필요한 칸만 줄 단위로 읽는다.

입력파서에 넣었을 때 (에이전트 이미지에서 측정)줄 단위 읽기
괄호 2만 겹0.79초바로 끝남
괄호 5만 겹4.8초바로 끝남
괄호 40만 겹2분 넘게 끝나지 않아 중단바로 끝남
앨리어스 겹치기값을 글자로 바꿀 때 펼쳐짐글자일 뿐

이 변경은 리뷰의 “깊은 중첩이면 프로세스가 스택 넘침으로 죽는다"는 지적에서 시작했다. 재현해 보니 프로세스는 죽지 않았고, 시간이 깊이의 제곱으로 늘어 전체 시간 제한을 넘는 문제였다. 공개 차트 21개 중 20개를 이 방식으로 받았다. 나머지 하나는 목록의 주소가 OCI 라 지원 범위 밖이다.

6.5 적용 순서

순서적용이유
1ECR 만들기push 역할의 plan 이 이 저장소를 조회한다. 한 PR 에 두면 CI plan 이 실패한다
2push 역할, 레포 변수이미지 CI 가 이 역할을 쓴다
3단속 켜기, AppProject 허용허용 전에 정책이 들어오면 앱 전체 동기화가 실패한다
4파드 배포첫 이미지 전까지 ImagePullBackOff 이고 다른 파드에는 영향이 없다
5이미지 CIdigest 자동 커밋으로 파드가 뜬다

7. Results — 결과

7.1 나쁜 차트

dev 클러스터의 실제 에이전트 파드에서 호출했다. 모든 경우 에이전트는 /health 200, 재시작 0회였고 렌더링 파드도 재시작하지 않았다.

차트결과시간
무한 반복에 가까운 템플릿timeout5.03초
큰 출력 30MBmemory_exceeded4.96초
메모리 폭주memory_exceeded0.41초
심볼릭 링크chart_has_link0.02초
스키마 $refschema_external_ref0.02초
셋째 동시 요청busy즉시

파드까지 죽는 경우는 일회용 kind 에서 시험했다. 프로세스 감시를 끄고 메모리를 채우면 렌더링 파드가 OOMKilled(137)로 재시작했고, 에이전트에는 unavailable 로 돌아왔으며 몇 초 뒤 다시 렌더링됐다. 렌더링 중 파드 삭제와 파드 0개도 같은 결과였다.

7.2 실제 차트의 시간

t3a.medium 노드, CPU 제한 1코어에서 각 3회 쟀다.

차트출력시간
kyverno5.7MB1.72초
harbor54KB1.28초
kube-prometheus-stack5.2MB0.78초
argo-cd1.9MB0.75초
external-secrets2.0MB0.67초
podinfo5KB0.15초

맥 컨테이너의 서비스 시간(kyverno 0.36초)과 dev(1.72초)를 비교하면 3~5배 느리지만 5초 안에 든다. 시작값을 맥에서 잰 숫자로만 정하면 여유를 잘못 볼 수 있다.

큰 출력 차트는 맥에서 output_too_large(당시 프로세스 제한 220MiB) 였고, dev 에서는 memory_exceeded(200MiB) 로 끝났다. 어느 쪽이든 그 프로세스만 끝나지만, 출력 20MB 제한보다 메모리 제한이 먼저 걸릴 수 있다.

8. Discussion

8.1 확인 범위

내용확인 방법
차트 풀기, 스키마 검사, 인자, 내려받기단위 테스트
나쁜 차트, 격리, 나가는 길 차단dev 클러스터
파드 OOM, 렌더링 중 삭제, 파드 0개일회용 kind
서비스와 helm 직접 실행의 동일성공개 차트 21개, 맥의 컨테이너
스키마 메모리 고갈(CVE-2025-55199)재현하지 않았다. 패치된 helm 과 메모리 제한에 맡긴다

8.2 남은 한계

  • 렌더링 파드는 아직 값 찾기와 PR 경로에 연결되지 않았다. 지금은 Helm 앱을 PR 로 고칠 수 없다
  • kustomize build 는 아직 에이전트 파드에서 돈다
  • 렌더링 파드와 단속은 dev 에만 있다
  • Chart.yaml 의 의존성이 charts/ 에 없으면 missing_dependency 로 실패한다. Argo CD 는 이때 helm dependency build 를 돌린다
  • OCI 저장소와 인증이 필요한 저장소는 받지 못한다
  • 큰 요청과 큰 출력의 버퍼가 겹치면 512Mi 를 넘을 수 있다. 그때는 렌더링 파드만 재시작한다

핵심 메시지

  • 격리 단위: 위험한 입력을 다루는 파드
    • 공용 파드가 죽는 일과 렌더링이 실패하는 일을 나눈다
  • 막는 순서: helm 실행 전 검사, 실행 중 제한, 바깥의 네트워크
    • 프록시 설정 하나에 기대지 않는다
  • 정책의 효력: 객체가 아니라 단속 여부
    • 정책 4개가 있어도 CNI 가 꺼져 있으면 아무것도 막지 않는다
  • 재현 우선: 지적의 원인을 측정으로 확인
    • 깊은 중첩의 실제 문제는 스택이 아니라 제곱 시간이었다

9. References

Argo CD 가 helm 을 부르는 인자와 조건의 출처. 5장의 근거다.
→ argoproj/argo-cd — util/helm/cmd.go (v3.5.3)
→ argoproj/argo-cd — reposerver/repository/repository.go (v3.5.3)
helm template 의 옵션. 3.4 의 근거다.
→ Helm — helm template
VPC CNI 의 NetworkPolicy 단속과 설정 값. 6.1 과 6.2 의 근거다.
→ Amazon EKS — Limit Pod traffic with Kubernetes network policies
→ Kubernetes — Network Policies
설계에 참고한 취약점. 3.4, 5.2, 8.1 의 근거다.
→ CVE-2025-53547 — Helm 의존성 갱신과 심볼릭 링크
→ CVE-2025-55199 — Helm 스키마의 메모리 고갈
→ CVE-2022-24348 — Argo CD values 경로 조작