デフォルト拒否から始めるポリシー設計
한국어 원문으로 표시합니다.
목표
네임스페이스의 통신 기본값을 거부로 뒤집고, 필요한 경로만 라벨로 골라 여는 정책 세트를 직접 작성합니다.
왜 중요한가
NetworkPolicy 에서 가장 많이 틀리는 것은 문법이 아니라 방향과 결합 방식입니다. 정책은 트래픽을 보내는 쪽이 아니라 받는 쪽에 붙습니다. web 에서 api 로 가는 통신을 허용하려면 정책의 podSelector 가 api 여야 하고, web 은 ingress.from 안에 등장합니다. 또 from 배열의 항목끼리는 OR 이고 한 항목 안의 namespaceSelector 와 podSelector 는 AND 입니다. 하이픈 위치 하나로 "특정 네임스페이스의 특정 파드"가 "그 네임스페이스의 모든 파드 또는 아무 데나 있는 그 파드"로 바뀝니다. 그리고 이그레스를 막을 때는 DNS 예외를 반드시 짝으로 넣으세요. 실제 프로덕션에서 기본 거부만 먼저 넣었다가 이름 해석이 죽어 전체 서비스가 연쇄로 멈춘 사례가 반복해서 보고됩니다. 이 랩의 클러스터는 파드 간 실제 패킷이 흐르지 않으므로 채점은 정책 오브젝트의 내용과 셀렉터 의미로 이루어집니다. 문법과 사고방식을 여기서 굳히고, 실제 차단 확인은 CNI 가 있는 환경에서 하면 됩니다.
단계
- 네임스페이스
netpol-lab을 만들고/opt/lab/fixtures/k8s/workloads.yaml을 그 안에 적용하세요. 결과로web,api,db세 파드가 각각 자기 이름과 같은app라벨을 갖고,api파드는 추가로tier=backend라벨을 가져야 합니다. netpol-lab에 NetworkPolicydefault-deny-ingress를 만드세요.podSelector는 빈 객체{}이고policyTypes는Ingress하나만이며ingress규칙은 하나도 없어야 합니다.- NetworkPolicy
allow-web-to-api를 만드세요.podSelector.matchLabels.app은api,ingress[0].from[0]은podSelector.matchLabels.app: web하나만 갖습니다(같은 항목에namespaceSelector를 넣지 마세요). 허용 포트는 TCP8080입니다. - NetworkPolicy
default-deny-egress를 만드세요.policyTypes는Egress하나만입니다. 이어서allow-dns-egress를 만들되egress[0].to[0].namespaceSelector.matchLabels에kubernetes.io/metadata.name: kube-system을 쓰고,egress[0].ports에는 포트53을 UDP 와 TCP 두 항목으로 넣으세요. - 네임스페이스
monitoring을 만들고 라벨purpose=monitoring을 붙이세요. 그다음netpol-lab에 NetworkPolicyallow-from-monitoring을 만드세요.podSelector.matchLabels.app은api,ingress[0].from[0].namespaceSelector.matchLabels.purpose는monitoring, 허용 포트는9090입니다. - NetworkPolicy
allow-office-cidr을 만드세요.podSelector.matchLabels.app은web이고,ingress[0].from[0].ipBlock.cidr은10.0.0.0/16,except는10.0.5.0/24하나입니다. - NetworkPolicy
api-full을 만드세요.podSelector.matchLabels.app은api,policyTypes는Ingress와Egress둘 다입니다.ingress[0].from에는 항목이 2개 이상(예: web 파드와 monitoring 네임스페이스) 들어가야 하고,egress[0].to[0]은podSelector.matchLabels.app: db, 허용 포트는5432입니다. /root/ops/netpol/out/policy-map.json을 만드세요. 최상위 키는web,api,db세 개이고 각 값은 그 파드를 고르는 정책 이름의 배열입니다. 빈 셀렉터를 가진 정책은 세 파드 모두에 들어가야 하고,allow-web-to-api는api에만 들어가야 합니다(web이나db에 넣으면 안 됩니다).allow-office-cidr은web에,api-full은api에 들어갑니다. 최종적으로netpol-lab의 NetworkPolicy 는 6개 이상이어야 합니다.
참고
- 픽스처는
kubectl apply -f /opt/lab/fixtures/k8s/workloads.yaml -n netpol-lab으로 올립니다. 픽스처 파일 자체를 고치지 마세요. kubernetes.io/metadata.name은 쿠버네티스가 모든 네임스페이스에 자동으로 붙여 주는 라벨이라 별도 라벨링 없이 네임스페이스를 고를 수 있습니다. 반대로purpose=monitoring처럼 직접 정한 라벨은 네임스페이스에 실제로 붙여야 합니다.- 정책 내용 확인은
kubectl get netpol <이름> -n netpol-lab -o yaml이 가장 빠릅니다.kubectl apply --dry-run=client로 오타를 미리 거르세요. - 흔한 실수 1: 3번에서
podSelector를 web 으로 잡는 것. 정책은 받는 쪽에 붙습니다. - 흔한 실수 2: 4번에서 DNS 를 UDP 만 여는 것. 응답이 크면 TCP 53 으로 넘어갑니다.
- 흔한 실수 3: 7번에서
from항목 하나 안에 두 셀렉터를 넣는 것. 그것은 OR 이 아니라 AND 가 되어 훨씬 좁은 규칙이 됩니다. - 실습 파드는 실습마다 새로 뜨므로 앞 실습에서 만든 클러스터 상태는 남아 있지 않습니다. 네임스페이스와 파드는 이 실습 안에서 직접 만드세요. 운영 절차를 기억이 아니라 런북과 매니페스트로 남겨야 하는 이유가 바로 이것입니다.
실습용 네임스페이스와 라벨 붙은 파드 만들기
네임스페이스 netpol-lab 을 만들고 /opt/lab/fixtures/k8s/workloads.yaml 을 그 안에 적용하세요. 결과로 web, api, db 세 파드가 각각 자기 이름과 같은 app 라벨을 갖고, api 파드는 추가로 tier=backend 라벨을 가져야 합니다.
정책은 라벨로만 대상을 고릅니다. 픽스처에 세 파드가 준비돼 있으니 그대로 올리되, 어느 네임스페이스에 올리는지를 확인하세요.
인그레스 기본 거부 정책 만들기
netpol-lab 에 NetworkPolicy default-deny-ingress 를 만드세요. podSelector 는 빈 객체 {} 이고 policyTypes 는 Ingress 하나만이며 ingress 규칙은 하나도 없어야 합니다.
네임스페이스 전체를 고르는 셀렉터는 비어 있는 셀렉터입니다. 전부 거부한다는 뜻은 허용 규칙을 하나도 쓰지 않는 것입니다.
web 에서 api 로 가는 포트만 열기
NetworkPolicy allow-web-to-api 를 만드세요. podSelector.matchLabels.app 은 api, ingress[0].from[0] 은 podSelector.matchLabels.app: web 하나만 갖습니다(같은 항목에 namespaceSelector 를 넣지 마세요). 허용 포트는 TCP 8080 입니다.
정책은 보내는 쪽이 아니라 받는 쪽에 붙습니다. 같은 네임스페이스 안이므로 상대를 고를 때 네임스페이스 셀렉터를 섞지 마세요.
이그레스 기본 거부와 DNS 예외
NetworkPolicy default-deny-egress 를 만드세요. policyTypes 는 Egress 하나만입니다. 이어서 allow-dns-egress 를 만들되 egress[0].to[0].namespaceSelector.matchLabels 에 kubernetes.io/metadata.name: kube-system 을 쓰고, egress[0].ports 에는 포트 53 을 UDP 와 TCP 두 항목으로 넣으세요.
이그레스를 막으면 이름 해석부터 죽습니다. DNS 는 UDP 만으로 끝나지 않는다는 점과, 대상 네임스페이스를 고르는 표준 라벨이 무엇인지 확인하세요.
다른 네임스페이스에서 오는 트래픽 허용하기
네임스페이스 monitoring 을 만들고 라벨 purpose=monitoring 을 붙이세요. 그다음 netpol-lab 에 NetworkPolicy allow-from-monitoring 을 만드세요. podSelector.matchLabels.app 은 api, ingress[0].from[0].namespaceSelector.matchLabels.purpose 는 monitoring, 허용 포트는 9090 입니다.
네임스페이스를 고르려면 그 네임스페이스에 라벨이 실제로 붙어 있어야 합니다. 셀렉터가 고를 대상이 없으면 정책은 아무 의미가 없습니다.
IP 대역 허용과 예외 지정하기
NetworkPolicy allow-office-cidr 을 만드세요. podSelector.matchLabels.app 은 web 이고, ingress[0].from[0].ipBlock.cidr 은 10.0.0.0/16, except 는 10.0.5.0/24 하나입니다.
ipBlock 은 넓게 열고 좁게 빼는 방식으로 씁니다. 제외 대역은 반드시 허용 대역 안에 들어 있어야 합니다.
인그레스와 이그레스를 한 정책에 담기
NetworkPolicy api-full 을 만드세요. podSelector.matchLabels.app 은 api, policyTypes 는 Ingress 와 Egress 둘 다입니다. ingress[0].from 에는 항목이 2개 이상(예: web 파드와 monitoring 네임스페이스) 들어가야 하고, egress[0].to[0] 은 podSelector.matchLabels.app: db, 허용 포트는 5432 입니다.
from 배열의 항목끼리는 OR 입니다. 두 종류의 출처를 허용하려면 항목을 두 개로 나누세요. 나가는 쪽은 데이터베이스 포트만 열립니다.
파드별 적용 정책 지도 만들기
/root/ops/netpol/out/policy-map.json 을 만드세요. 최상위 키는 web, api, db 세 개이고 각 값은 그 파드를 고르는 정책 이름의 배열입니다. 빈 셀렉터를 가진 정책은 세 파드 모두에 들어가야 하고, allow-web-to-api 는 api 에만 들어가야 합니다(web 이나 db 에 넣으면 안 됩니다). allow-office-cidr 은 web 에, api-full 은 api 에 들어갑니다. 최종적으로 netpol-lab 의 NetworkPolicy 는 6개 이상이어야 합니다.
빈 셀렉터를 가진 정책은 모든 파드에 적용됩니다. 정책이 고르는 것은 받는 쪽이라는 점을 지도에 반영하세요.