소개
네임스페이스는 리소스를 구분해 주지만 네트워크 트래픽을 자동으로 차단하지는 않습니다. NetworkPolicy 는 클러스터의 네트워크 플러그인이 해당 정책을 적용한다는 전제하에, 선택된 Pod 가 어떤 연결을 보내거나 받을 수 있는지 정의합니다. 이 실습 환경에서는 준비된 Calico 네트워크가 이를 적용합니다.
먼저 트래픽이 기본적으로 열려 있음을 확인한 뒤 기본 거부 정책을 적용하고, 승인된 네임스페이스에서 레이블이 지정된 클라이언트만 허용합니다.
이 실습은 안전한 정책 적용 절차를 따릅니다. 먼저 정상 동작 기준을 확인하고, 트래픽을 차단한 다음, 의도한 최소 예외만 추가하고, 허용되는 경우와 차단되는 경우를 모두 테스트합니다. 허용된 클라이언트만 테스트하면 범위가 지나치게 넓은 정책을 놓칠 수 있습니다.
서버와 클라이언트 배포
이 단계에서는 애플리케이션 네임스페이스와 클라이언트 네임스페이스를 각각 만들고, NGINX 서버와 계속 실행되는 클라이언트 두 개를 배포합니다.
아래 명령은 네트워크 실험의 핵심이 관계없는 YAML 설정에 묻히지 않도록 익숙한 명령형 형식을 사용합니다. 한 클라이언트에는 role=approved 레이블을 지정하고, 다른 클라이언트에는 role=untrusted 레이블을 지정합니다. 클라이언트 네임스페이스에는 access=approved 레이블을 지정합니다. 이후 정책 선택기에서는 이 두 레이블 수준을 모두 사용합니다.
cd /home/labex/project/network-policy
kubectl create namespace practice-apps
kubectl create namespace practice-clients
kubectl label namespace practice-clients access=approved
서버를 생성하고 외부에 노출합니다. kubectl expose는 Deployment 가 생성한 app=protected-web Pod 레이블과 일치하는 선택기를 사용하는 ClusterIP Service 를 만듭니다.
kubectl create deployment protected-web -n practice-apps --image=nginx:1.27-alpine --replicas=1
kubectl expose deployment protected-web -n practice-apps --port=80 --target-port=80
진단용 클라이언트 Pod 두 개를 생성합니다. 각 Pod 의 절전 루프는 반복적인 kubectl exec 테스트를 수행할 수 있도록 계속 실행 상태를 유지합니다.
kubectl run은 각 독립 실행형 Pod 를 직접 생성합니다. --labels는 정책이 선택할 식별자를 부여하고, --restart=Never는 kubectl 이 워크로드 컨트롤러를 생성하지 않도록 하며, --는 kubectl 옵션과 컨테이너 내부에서 실행되는 sh -c 명령을 구분합니다. 대기 명령에서 --all은 practice-clients의 모든 Pod 가 준비 상태가 되어야 함을 의미합니다.
kubectl run approved-client -n practice-clients --image=busybox:1.36 --image-pull-policy=IfNotPresent --labels=role=approved --restart=Never -- sh -c 'while true; do sleep 30; done'
kubectl run untrusted-client -n practice-clients --image=busybox:1.36 --image-pull-policy=IfNotPresent --labels=role=untrusted --restart=Never -- sh -c 'while true; do sleep 30; done'
kubectl wait --for=condition=Ready pod -n practice-clients --all --timeout=120s
kubectl rollout status deployment/protected-web -n practice-apps --timeout=60s
네임스페이스를 분리하는 것만으로는 두 클라이언트가 모두 Service 에 접근하는 것을 막을 수 없습니다.
준비 상태 확인 명령은 두 클라이언트 Pod 와 서버 Deployment 가 정상적으로 준비되었음을 보여 주는 다음과 같은 결과로 끝나야 합니다.
pod/approved-client condition met
pod/untrusted-client condition met
deployment "protected-web" successfully rolled out
이 시점에서 레이블은 클라이언트를 구분할 뿐 접근을 제한하지는 않습니다. 레이블이 트래픽 규칙으로 작동하려면 NetworkPolicy 선택기가 해당 레이블을 사용해야 합니다.
초기 트래픽이 열려 있음을 확인
이 단계에서는 두 클라이언트에서 네임스페이스를 가로질러 Service 에 요청을 보냅니다. 정규화된 이름에는 Service 이름과 네임스페이스가 모두 포함됩니다.
같은 네임스페이스 안에서는 protected-web만으로 충분합니다. 하지만 practice-clients에서 접근할 때는 protected-web.practice-apps를 사용해야 클러스터 DNS 가 Service 이름과 네임스페이스를 모두 확인할 수 있습니다. 정책을 적용하기 전에 두 클라이언트를 모두 테스트하면, 이후 요청이 실패할 경우 원인을 DNS 나 Service 설정이 아니라 정책으로 좁힐 수 있습니다.
상태 프로브 실습에서와 마찬가지로 wget -qO-는 진행률 출력 없이 응답 본문을 표시합니다. --timeout=3은 차단된 요청이 무기한 대기하지 않도록 하며, grep으로 파이프하면 NGINX 의 식별 가능한 텍스트만 출력됩니다.
cd /home/labex/project/network-policy
kubectl exec approved-client -n practice-clients -- wget -qO- --timeout=3 http://protected-web.practice-apps | grep 'Welcome to nginx'
kubectl exec untrusted-client -n practice-clients -- wget -qO- --timeout=3 http://protected-web.practice-apps | grep 'Welcome to nginx'
kubectl get networkpolicies -n practice-apps
해당 Pod 를 선택하는 NetworkPolicy 가 없으면 수신 트래픽은 허용됩니다. 이는 네임스페이스 자체가 네트워크 보안 경계라는 흔한 오해를 바로잡아 줍니다.
두 명령 모두 Welcome to nginx와 일치해야 하며, kubectl get networkpolicies는 리소스가 없다고 표시해야 합니다. 이것이 이후 의도적으로 변경할 정상 동작 기준입니다.
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
No resources found in practice-apps namespace.
페이지 텍스트가 두 번 반복되는 것은 격리를 시작하기 전에 두 클라이언트가 동일한 Service 에 접근할 수 있음을 보여 줍니다.
수신 트래픽에 기본 거부 적용
이 단계에서는 practice-apps의 모든 Pod 를 선택하고 어떤 수신 출처도 허용하지 않습니다.
NetworkPolicy 는 해당 정책이 선택한 Pod 와 policyTypes에 지정된 트래픽 방향에 대해서만 제한적으로 작동합니다. 여기서 podSelector: {}는 practice-apps의 모든 Pod 를 선택하며, ingress 허용 규칙을 지정하지 않으면 수신 트래픽이 기본적으로 거부됩니다.
cd /home/labex/project/network-policy
cat <<'EOF' > default-deny.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: default-deny-ingress, namespace: practice-apps}
spec:
podSelector: {}
policyTypes: [Ingress]
EOF
kubectl apply -f default-deny.yaml
kubectl exec approved-client -n practice-clients -- wget -qO- --timeout=3 http://protected-web.practice-apps || echo 'blocked as expected'
빈 podSelector는 정책이 속한 네임스페이스의 모든 Pod 를 선택합니다. 예상된 타임아웃은 수신 트래픽이 이제 거부되었음을 의미하며, Service 가 사라졌다는 뜻이 아닙니다.
|| echo ...는 wget이 예상대로 0 이 아닌 종료 코드를 반환하더라도 터미널이 계속 진행되도록 처리합니다. 이 실패는 통제된 실험에서 중요한 증거입니다. 서버 Pod 는 계속 준비 상태이며, 변경된 것은 네트워크 접근 허용 여부뿐입니다.
networkpolicy.networking.k8s.io/default-deny-ingress created
wget: download timed out
command terminated with exit code 1
blocked as expected
차단된 패킷에는 애플리케이션 응답이 돌아오지 않기 때문에 타임아웃이 발생하는 것은 정상입니다. 마지막 줄은 셸이 이 예상된 실패를 사람이 읽기 쉬운 결과로 의도적으로 변환했음을 보여 줍니다.
승인된 클라이언트 하나만 허용
이 단계에서는 승인된 네임스페이스와 승인된 Pod 레이블을 모두 요구하는 허용 정책을 추가합니다.
NetworkPolicy 는 누적 방식으로 적용됩니다. 기본 거부 정책은 그대로 유지되고, 두 번째 정책이 허용된 출처 하나를 추가합니다. 선택된 서버 Pod 는 출처 네임스페이스와 출처 Pod 가 모두 일치하는 경우에만 TCP 포트 80 을 수신할 수 있습니다.
cd /home/labex/project/network-policy
cat <<'EOF' > allow-approved.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: allow-approved-client, namespace: practice-apps}
spec:
podSelector: {matchLabels: {app: protected-web}}
policyTypes: [Ingress]
ingress:
- from:
- namespaceSelector: {matchLabels: {access: approved}}
podSelector: {matchLabels: {role: approved}}
ports:
- {protocol: TCP, port: 80}
EOF
kubectl apply -f allow-approved.yaml
kubectl exec approved-client -n practice-clients -- wget -qO- --timeout=3 http://protected-web.practice-apps | grep 'Welcome to nginx'
kubectl exec untrusted-client -n practice-clients -- wget -qO- --timeout=3 http://protected-web.practice-apps || echo 'untrusted client blocked'
두 선택기가 동일한 from 항목 안에 있으므로 둘 다 일치해야 합니다. 항목을 분리하면 논리적 OR 로 처리됩니다.
승인된 클라이언트의 요청은 다시 성공하고, 신뢰할 수 없는 클라이언트의 요청은 계속 타임아웃되어야 합니다. 이 두 결과를 함께 확인해야 정책이 모든 수신 트래픽을 다시 허용한 것이 아니라 선택적으로 적용되었음을 입증할 수 있습니다.
networkpolicy.networking.k8s.io/allow-approved-client created
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
wget: download timed out
command terminated with exit code 1
untrusted client blocked
결과를 하나의 쌍으로 해석하세요. 먼저 approved-client에는 페이지 내용이 표시되고, 이어서 untrusted-client에는 타임아웃이 발생합니다. 성공한 요청만 확인해서는 규칙의 범위가 충분히 좁은지 입증할 수 없습니다.
요약
네임스페이스만으로는 기본적으로 트래픽이 격리되지 않는다는 점을 확인하고, 수신 트래픽에 기본 거부를 적용한 뒤, 네임스페이스·Pod·프로토콜·포트 조건을 조합해 하나의 클라이언트만 허용했습니다.


