LabHub
배우기 러닝패스 코스

Kubernetesネットワーク — 本物のクラスタで

ポリシーに「拒否」はない

LabHub 에서 이어서 보기

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

한 줄 요약

NetworkPolicy 에는 거부라는 개념이 없습니다. 파드에 정책이 하나도 없으면 전부 허용이고, 하나라도 붙는 순간 그 방향이 허용 목록으로 뒤집힙니다.

概念マップ: 전부 허용・아무것도 허용하지 않는・받는 쪽・오류가 없어서 더 헷갈립니다.

왜 이 전환을 알아야 하나

쿠버네티스의 기본값은 전부 허용입니다. 어느 네임스페이스의 어느 파드든 다른 모든 파드에 닿습니다. 침입자가 웹 파드 하나를 잡으면 그 자리에서 데이터베이스가 보입니다.

기본값이 뒤집히는 지점

NetworkPolicy 의 동작 방식이 직관과 다릅니다.

파드에 정책이 하나도 붙지 않으면 전부 허용이고, 하나라도 붙는 순간 그 파드는 그 방향에 대해 허용 목록 방식으로 바뀝니다.

그래서 정책에는 '거부' 라는 개념이 아예 없습니다. default-deny 라는 이름의 정책도 사실은 아무것도 허용하지 않는 정책일 뿐입니다.

spec:
  podSelector: {}          # 이 네임스페이스의 모든 파드
  policyTypes: [Ingress]   # ingress 만 다룬다
  # ingress: 항목이 없다 → 허용하는 것이 하나도 없다

이 전환을 모르면 "정책 하나 추가했을 뿐인데 관계없는 것이 끊겼다" 가 됩니다.

방향을 헷갈리지 않는 법

정책은 언제나 받는 쪽에 붙습니다.

필드 고르는 것
podSelector 보호받는 파드 (db)
ingress.from 허용할 출처 (api)

보내는 쪽에 붙이면 문법은 통과하고 오브젝트도 만들어지는데 아무 일도 일어나지 않습니다. 오류가 없어서 더 헷갈립니다.

여러 정책은 더해진다

같은 파드에 붙은 정책이 여럿이면 합집합입니다. 뒤에 붙인 정책이 앞의 것을 덮어쓰지 않습니다. 그래서 "좁히려고 정책을 추가했는데 더 넓어지는" 일이 생깁니다. 좁히려면 기존 정책을 고쳐야 합니다.

가장 자주 나는 사고

egress 를 기본 차단하는 순간 DNS 가 함께 끊깁니다. CoreDNS 로 나가는 53번 포트도 egress 이기 때문입니다.

증상은 "이름을 못 찾습니다" 이고, CoreDNS 는 멀쩡하며, 다른 네임스페이스는 정상입니다. 그래서 아무도 방금 붙인 네트워크 정책을 의심하지 않습니다.

처음 정책을 넣는 순서

기본 차단부터 켜면 그 순간 전부 끊깁니다. 순서가 있습니다.

1. 무엇이 통신하는지 먼저 본다. 정책을 쓰기 전에 실제 흐름을 봅니다. Cilium 이면 Hubble, 그 밖에는 conntrack 이나 애플리케이션 로그로 그립니다.

hubble observe --namespace labhub-prod --last 500   -o json | jq -r '"\(.source.namespace)/\(.source.pod_name) → \(.destination.namespace)/\(.destination.pod_name):\(.l4.TCP.destination_port)"'   | sort | uniq -c | sort -rn

2. 허용 정책을 먼저 넣는다. 아직 기본 차단이 없으므로 아무것도 막히지 않습니다.

3. 그 다음 기본 차단을 켠다. 이때 빠뜨린 것이 드러납니다.

4. DNS 를 꼭 확인한다. egress 를 쓰는 순간 DNS(53/UDP)도 막힙니다. 이름 조회가 안 되면 모든 것이 타임아웃으로 보여 원인을 찾기 어렵습니다.

egress:
  - to:
      - namespaceSelector:
          matchLabels: {kubernetes.io/metadata.name: kube-system}
        podSelector:
          matchLabels: {k8s-app: kube-dns}
    ports:
      - {protocol: UDP, port: 53}
      - {protocol: TCP, port: 53}

정책이 못 막는 것

네트워크 정책은 만능이 아닙니다. 못 막는 것을 알아야 다른 층으로 보냅니다.

못 막는 것 대신
같은 파드 안 컨테이너끼리 파드를 나눈다
hostNetwork 파드 파드 시큐리티로 hostNetwork 를 금지
노드 자체에서 나가는 트래픽 노드 방화벽·보안그룹
L7(경로·메서드) Cilium L7 정책, 서비스 메시
이미 맺힌 연결 정책은 새 연결에만 적용된다

마지막 줄이 함정입니다. 정책을 넣어도 진행 중이던 연결은 계속 살아 있습니다. 그래서 "정책을 넣었는데 아직 통한다" 가 나옵니다. 확인하려면 새 연결을 맺어 봐야 합니다.

디버깅 순서

# 1. 이 파드를 고르는 정책이 있나 (없으면 기본 허용)
kubectl get netpol -n <ns> -o json | jq -r '
  .items[] | select(.spec.podSelector.matchLabels // {} | to_entries | length > 0) |
  "\(.metadata.name): \(.spec.podSelector.matchLabels) \(.spec.policyTypes)"'

# 2. 양쪽 다 열려 있나 — 출발지 egress, 도착지 ingress
# 3. 실제로 막혔는지 확인 (Connection refused 는 정책이 아니다)
kubectl exec -n <ns> <pod> -- nc -zv -w3 <대상> <포트>

3번의 구분이 중요합니다. 정책에 막히면 timeout 이고, 아무도 안 듣고 있으면 refused 입니다. refused 가 나오면 정책 문제가 아닙니다.

실무에서 진짜 중요한 것

egress 를 막을 때는 DNS 를 같은 커밋에서 함께 엽니다. CoreDNS 로 나가는 53번도 egress 라 함께 끊기는데, 증상은 "이름을 못 찾습니다" 이고 CoreDNS 는 멀쩡하며 다른 네임스페이스는 정상입니다. 그래서 아무도 방금 붙인 정책을 의심하지 않습니다.

정책은 언제나 받는 쪽에 붙입니다. 보내는 쪽에 붙이면 문법은 통과하고 오브젝트도 만들어지는데 아무 일도 일어나지 않습니다. 오류가 없어서 더 오래 헤맵니다.

좁히려면 정책을 추가하지 말고 기존 정책을 고칩니다. 같은 파드에 붙은 정책은 합집합이라, 뒤에 붙인 것이 앞의 것을 덮어쓰지 않습니다. 좁히려고 추가했는데 더 넓어지는 일이 여기서 나옵니다.

다음 실습에서 이 사고를 직접 일으켰다가 고칩니다. 그리고 고칠 때 UDP 53 만 열면 왜 더 나쁜 상태가 되는지도 함께 봅니다.