LabHub
学习 学习路径 课程

CCA — Cilium 认证助理

上新策略之前,先看清谁会被拦下

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

목표

진짜 Cilium 1.20.1(enable-policy=default)에서 정책이 언제부터 무엇을 막는지 확인합니다. 방향별 기본 거부, 엔드포인트 감사 모드로 미리 걸어 보기, 감사 해제 뒤의 실제 차단, ingress: [{}] 의 정반대 의미, enableDefaultDeny=false, 두 정책 종류가 한 저장소에 들어가는 모습을 엔드포인트 적용 열·실제 요청·Hubble 판정으로 함께 봅니다.

왜 중요한가

운영 중인 서비스에 egress 정책을 처음 거는 순간이 가장 위험합니다. 문서에 없던 의존성(이 실습의 legacy)이 있으면 그 호출이 곧바로 끊깁니다. Cilium 의 감사 모드는 판정을 계산하되 거부하지 않고 흐름에 AUDITED 로 남기므로, 누가 막힐지 먼저 보고 나서 강제할 수 있습니다.

다만 감사 모드는 엔드포인트 단위라 이미 강제 중이던 방향까지 느슨해집니다. 또 같은 모양의 YAML 도 쿠버네티스 NetworkPolicy 와 CiliumNetworkPolicy 에서 뜻이 다를 수 있습니다. 적용 열이 Enabled 라고 해서 기본 거부가 켜졌다는 보장도 없습니다. 이런 차이는 문서만 읽어서는 헷갈리기 쉬워 직접 요청을 보내 확인합니다.

클러스터 전체 모드를 always 로 바꾸면 정책이 없는 엔드포인트(coredns 등)까지 막힙니다. 개발 VM 에서 되돌린 뒤 서비스가 돌아오기까지 약 70초가 걸려 이 실습에서는 바꾸지 않습니다. 감사 모드도 엔드포인트 하나에만 켭니다.

환경 준비에 약 5분이 걸립니다. 세션이 끝나면 /root/cca-audit 의 파일은 사라집니다.

단계

  1. kubectl apply -f /opt/fixtures/cca-audit/workload.yaml 로 cca-audit 에 파드 api·ledger·legacy·frontend·batch(모두 8080 서버)와 같은 이름의 서비스를 띄우세요. 정책을 걸기 전에 /root/cca-audit/baseline.json 을 만듭니다 — pods 아래 다섯 앱 이름마다 uid(파드), endpoint_id, identity(CiliumEndpoint 의 status), ingress·egress(에이전트 cilium-dbg endpoint list 의 POLICY 열 값), 그리고 최상위 batch_to_api(batch 파드에서 http://api:8080/ 요청의 HTTP 코드 문자열, 응답이 없으면 "000")와 recorded_at(기록한 순간의 UTC 시각, date -u +%Y-%m-%dT%H:%M:%SZ).
  2. cca-audit 에 CiliumNetworkPolicy api-ingress 를 만드세요 — endpointSelector app=api, ingress 한 항목(fromEndpoints app=frontend, toPorts TCP "8080"), egress 는 두지 않습니다. 적용이 반영되면 /root/cca-audit/direction.jsonfrontend_to_api, batch_to_api, api_to_legacy(각 요청의 HTTP 코드 문자열, 응답 없음은 "000")와 api_ingress·api_egress(api 엔드포인트의 POLICY 열 값), recorded_at(기록한 순간의 UTC 시각) 을 기록합니다. 3단계의 egress 정책보다 먼저 기록해야 합니다.
  3. api 엔드포인트에 감사 모드를 켜세요(cilium-dbg endpoint config <api 번호> PolicyAuditMode=Enabled). 그다음 CNP api-egress 를 적용합니다 — endpointSelector app=api, egress 두 항목: (1) toEndpoints app=ledger, TCP "8080" (2) toEndpoints k8s:io.kubernetes.pod.namespace: kube-system·k8s:k8s-app: kube-dns, 포트 "53" protocol ANY. api→ledger, api→legacy, batch→api 를 요청한 뒤 에이전트 안에서 hubble observe 로 cca-audit 흐름을 읽어 /root/cca-audit/audit.json 에 기록합니다 — endpoint_id, audit_mode, api_ingress·api_egress(POLICY 열), api_to_legacy·batch_to_api(코드 문자열), flows(api→legacy 와 batch→api 의 AUDITED 흐름이 들어 있는 compact 출력 줄 목록).
  4. api 엔드포인트의 감사 모드를 끄세요(PolicyAuditMode=Disabled). 같은 요청을 다시 보내고 /root/cca-audit/enforce.jsonaudit_mode, api_ingress·api_egress, api_to_ledger·api_to_legacy·batch_to_api(코드 문자열), flows(감사를 끈 뒤 api→legacy 와 batch→api 의 DROPPED 흐름이 든 compact 줄 목록) 를 기록합니다.
  5. cca-audit 에 두 정책을 만드세요 — 표준 NetworkPolicy np-empty(podSelector app=batch, policyTypes [Ingress], ingress: [{}])와 CiliumNetworkPolicy cnp-empty(endpointSelector app=frontend, ingress: [{}]). ledger 파드에서 batch 와 frontend 로 요청해 /root/cca-audit/empty.jsonbatch_from_ledger·frontend_from_ledger(코드 문자열), batch_ingress·frontend_ingress(각 엔드포인트의 ingress POLICY 열) 를 기록합니다.
  6. cca-audit 에 CNP ledger-observe 를 만드세요 — endpointSelector app=ledger, enableDefaultDeny: {ingress: false}, ingress 한 항목(fromEndpoints app=api, TCP "8080"). api→ledger 와 batch→ledger 를 요청하고 흐름을 읽어 /root/cca-audit/observe.jsonledger_ingress(POLICY 열), api_to_ledger·batch_to_ledger(코드 문자열), flows(두 요청의 ledger 쪽 INGRESS policy-verdict 줄이 든 compact 줄 목록) 를 기록합니다.
  7. 에이전트에서 cilium-dbg policy get -o json 을 읽어 cca-audit 네임스페이스 규칙마다 라벨 io.cilium.k8s.policy.nameio.cilium.k8s.policy.derived-from 을 찾으세요. /root/cca-audit/sources.jsonderived_from(정책 이름 → derived-from 값 사전, 다섯 개)과 revision(출력의 revision 숫자) 을 기록합니다.
  8. /root/cca-audit/report.txt키=값 일곱 줄을 씁니다 — api_audit_mode(지금 api 의 PolicyAuditMode), api_policy_columns(api 의 ingress 열/egress 열, 예: A/B 형식), api_to_legacy(allowed 또는 denied), np_empty_ingress_rule·cnp_empty_ingress_rule(각각 allow-all 또는 deny-all), ledger_ingress_column, ledger_default_deny_ingress(ledger-observe 의 enableDefaultDeny.ingress 값, true 또는 false). 값은 모두 지금 상태와 일치해야 합니다.

참고

정책이 하나도 없을 때의 적용 상태를 먼저 적어 둔다

kubectl apply -f /opt/fixtures/cca-audit/workload.yaml 로 cca-audit 에 파드 api·ledger·legacy·frontend·batch(모두 8080 서버)와 같은 이름의 서비스를 띄우세요. 정책을 걸기 전에 /root/cca-audit/baseline.json 을 만듭니다 — pods 아래 다섯 앱 이름마다 uid(파드), endpoint_id, identity(CiliumEndpoint 의 status), ingress·egress(에이전트 cilium-dbg endpoint list 의 POLICY 열 값), 그리고 최상위 batch_to_api(batch 파드에서 http://api:8080/ 요청의 HTTP 코드 문자열, 응답이 없으면 "000")와 recorded_at(기록한 순간의 UTC 시각, date -u +%Y-%m-%dT%H:%M:%SZ).

cilium-config 의 enable-policy 가 default 이면, 어떤 정책도 선택하지 않은 엔드포인트는 막히지 않습니다. endpoint list 의 두 POLICY 열은 방향별로 기본 거부가 켜졌는지를 보여 줍니다. 파드에는 curl 이 없으니 python3 의 urllib 로 요청하세요. 이 기록은 뒤 정책이 생기기 에 만들어져야 합니다 — 채점기는 recorded_at 을 정책의 생성 시각과 비교합니다(파일 수정 시각은 보지 않으니 나중에 고쳐 저장해도 됩니다).

들어오는 쪽만 잠갔는데 나가는 쪽은 그대로다

cca-audit 에 CiliumNetworkPolicy api-ingress 를 만드세요 — endpointSelector app=api, ingress 한 항목(fromEndpoints app=frontend, toPorts TCP "8080"), egress 는 두지 않습니다. 적용이 반영되면 /root/cca-audit/direction.jsonfrontend_to_api, batch_to_api, api_to_legacy(각 요청의 HTTP 코드 문자열, 응답 없음은 "000")와 api_ingress·api_egress(api 엔드포인트의 POLICY 열 값), recorded_at(기록한 순간의 UTC 시각) 을 기록합니다. 3단계의 egress 정책보다 먼저 기록해야 합니다.

default 모드에서 기본 거부는 방향별로 켜집니다. 어떤 규칙이 ingress 부분을 가지고 엔드포인트를 선택하면 그 방향만 허용 목록 방식이 됩니다. 막힌 요청은 거절이 아니라 응답이 없으니 짧은 제한 시간을 두세요. 반영에는 몇 초가 걸릴 수 있어 폴링이 안전합니다.

새 egress 정책을 감사 모드로 먼저 걸어 누가 막힐지 본다

api 엔드포인트에 감사 모드를 켜세요(cilium-dbg endpoint config <api 번호> PolicyAuditMode=Enabled). 그다음 CNP api-egress 를 적용합니다 — endpointSelector app=api, egress 두 항목: (1) toEndpoints app=ledger, TCP "8080" (2) toEndpoints k8s:io.kubernetes.pod.namespace: kube-system·k8s:k8s-app: kube-dns, 포트 "53" protocol ANY. api→ledger, api→legacy, batch→api 를 요청한 뒤 에이전트 안에서 hubble observe 로 cca-audit 흐름을 읽어 /root/cca-audit/audit.json 에 기록합니다 — endpoint_id, audit_mode, api_ingress·api_egress(POLICY 열), api_to_legacy·batch_to_api(코드 문자열), flows(api→legacy 와 batch→api 의 AUDITED 흐름이 들어 있는 compact 출력 줄 목록).

감사 모드는 정책 판정을 계산하되 거부 대신 통과시키고 흐름에 AUDITED 로 남깁니다. 엔드포인트 단위 옵션이라 그 엔드포인트의 모든 방향에 적용된다는 점을 2단계 결과와 비교해 보세요. 링 버퍼는 몇 분이면 밀리므로 흐름은 관측 직후 파일로 남기고, --since 에 요청 직전 시각을 주면 필요한 줄만 모입니다. egress 를 잠그면 이름 해석도 egress 트래픽이라는 것을 잊지 마세요.

감사를 풀자 같은 두 요청이 실제로 떨어진다

api 엔드포인트의 감사 모드를 끄세요(PolicyAuditMode=Disabled). 같은 요청을 다시 보내고 /root/cca-audit/enforce.jsonaudit_mode, api_ingress·api_egress, api_to_ledger·api_to_legacy·batch_to_api(코드 문자열), flows(감사를 끈 뒤 api→legacy 와 batch→api 의 DROPPED 흐름이 든 compact 줄 목록) 를 기록합니다.

감사 모드가 보여 준 AUDITED 가 그대로 DROPPED 가 되는지, 허용한 목적지(ledger)는 여전히 통과하는지 확인하세요. 흐름의 끝부분이 판정 사유입니다. 앞 단계에서 남은 오래된 DROPPED 줄과 섞이지 않게 감사를 끄기 직전 시각부터 읽으세요.

똑같이 ingress: [{}] 라고 썼는데 한쪽은 열리고 한쪽은 닫혔다

cca-audit 에 두 정책을 만드세요 — 표준 NetworkPolicy np-empty(podSelector app=batch, policyTypes [Ingress], ingress: [{}])와 CiliumNetworkPolicy cnp-empty(endpointSelector app=frontend, ingress: [{}]). ledger 파드에서 batch 와 frontend 로 요청해 /root/cca-audit/empty.jsonbatch_from_ledger·frontend_from_ledger(코드 문자열), batch_ingress·frontend_ingress(각 엔드포인트의 ingress POLICY 열) 를 기록합니다.

두 API 는 비어 있는 규칙 항목을 다르게 해석합니다. 쿠버네티스 NetworkPolicy 에서 from 이 없는 ingress 항목은 모든 출발지를 뜻합니다. Cilium 에서 fromEndpoints 같은 출발지 필드가 없는 규칙 항목은 아무도 허용하지 않고 기본 거부만 켭니다. 둘 다 적용 열은 같게 보인다는 점도 확인하세요.

적용 열은 Enabled 인데 아무도 막히지 않는다

cca-audit 에 CNP ledger-observe 를 만드세요 — endpointSelector app=ledger, enableDefaultDeny: {ingress: false}, ingress 한 항목(fromEndpoints app=api, TCP "8080"). api→ledger 와 batch→ledger 를 요청하고 흐름을 읽어 /root/cca-audit/observe.jsonledger_ingress(POLICY 열), api_to_ledger·batch_to_ledger(코드 문자열), flows(두 요청의 ledger 쪽 INGRESS policy-verdict 줄이 든 compact 줄 목록) 를 기록합니다.

enableDefaultDeny 를 끈 규칙은 엔드포인트의 기본 모드를 정할 때 빠집니다. 그래서 규칙은 계산되지만 거부로 이어지지 않습니다. policy-verdict 줄에서 policy-verdict: 바로 뒤의 매치 종류가 두 요청에서 어떻게 다른지 비교하세요. POLICY 열 하나만으로 기본 거부 여부를 판단하면 안 되는 이유입니다.

두 종류의 정책은 한 저장소에 들어간다

에이전트에서 cilium-dbg policy get -o json 을 읽어 cca-audit 네임스페이스 규칙마다 라벨 io.cilium.k8s.policy.nameio.cilium.k8s.policy.derived-from 을 찾으세요. /root/cca-audit/sources.jsonderived_from(정책 이름 → derived-from 값 사전, 다섯 개)과 revision(출력의 revision 숫자) 을 기록합니다.

Cilium 은 표준 NetworkPolicy 도 자기 규칙 형식으로 옮겨 CNP 와 같은 저장소에 넣고, 어디서 왔는지를 라벨로 붙입니다. 규칙마다 Labels 목록의 key/value 를 보세요. revision 은 저장소가 바뀔 때마다 올라갑니다.

적용 모드 사고 보고서

/root/cca-audit/report.txt키=값 일곱 줄을 씁니다 — api_audit_mode(지금 api 의 PolicyAuditMode), api_policy_columns(api 의 ingress 열/egress 열, 예: A/B 형식), api_to_legacy(allowed 또는 denied), np_empty_ingress_rule·cnp_empty_ingress_rule(각각 allow-all 또는 deny-all), ledger_ingress_column, ledger_default_deny_ingress(ledger-observe 의 enableDefaultDeny.ingress 값, true 또는 false). 값은 모두 지금 상태와 일치해야 합니다.

앞 단계 기록을 옮기지 말고 지금 상태를 다시 읽으세요. 채점기도 요청을 다시 보내 판정합니다. 감사 모드가 다시 켜져 있다면 보고서의 여러 값이 달라집니다.