Kubernetes 파드에 default deny 정책 적용하기
Kubernetes 또는 Calico 네트워크 정책으로 파드에 default deny 정책을 적용하는 방법을 다뤄요. 정책이 없는(또는 잘못된) 파드는 명시적인 네트워크 정책이 작성될 때까지 트래픽이 차단되어, 클러스터의 보안 자세를 한층 강화할 수 있어요.
출처: 문서
본문
큰 그림
Kubernetes 또는 Calico 네트워크 정책을 사용해 Kubernetes 파드에 default deny 정책을 활성화해요.
활용 가치
default deny 네트워크 정책은 강화된 보안 자세를 제공해서, 정책이 없는(또는 잘못된 정책을 가진) 파드는 적절한 네트워크 정책이 정의될 때까지 트래픽이 허용되지 않도록 해요.
기능
이 하우투 가이드는 다음 Calico 기능을 사용해요:
- NetworkPolicy
- GlobalNetworkPolicy
개념
Default deny/allow 동작
Default allow는 별도로 지정하지 않는 한 기본적으로 모든 트래픽이 허용된다는 뜻이에요. Default deny는 명시적으로 허용되지 않는 한 기본적으로 모든 트래픽이 거부된다는 뜻이에요. Kubernetes 파드는 default allow예요. 다르게 지정하는 네트워크 정책이 정의되지 않는 한요.
Kubernetes와의 호환성을 위해 Calico 네트워크 정책 강제 적용은 Kubernetes 파드의 표준 규칙을 따라요:
- 어떤 네트워크 정책도 파드에 적용되지 않으면, 그 파드로/부터의 모든 트래픽이 허용돼요.
- type ingress인 하나 이상의 네트워크 정책이 파드에 적용되면, 그 정책들이 명시적으로 허용한 ingress 트래픽만 허용돼요.
- type egress인 하나 이상의 네트워크 정책이 파드에 적용되면, 그 정책들이 명시적으로 허용한 egress 트래픽만 허용돼요.
다른 엔드포인트 타입(VM, 호스트 인터페이스)의 경우 기본 동작은 트래픽 거부예요. 네트워크 정책이 명시적으로 허용한 트래픽만 허용되고, 엔드포인트에 적용된 네트워크 정책이 없더라도 마찬가지예요.
시작하기 전에
다음 섹션의 샘플 Calico 네트워크 정책을 적용하려면 calicoctl을 설치하세요.
방법
default deny 네트워크 정책 만들기
설치 직후에는 네임스페이스 단위 default deny 네트워크 정책을 만들어서, 정책을 갖추고 테스트하기 전까지 정책이 없거나 잘못된 정책을 가진 파드를 보호하는 것이 모범 사례예요.
다음 예시에서는 engineering 네임스페이스의 모든 워크로드에 대해 Calico default deny NetworkPolicy를 만들어요.
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
name: default-deny
namespace: engineering
spec:
selector: all()
types:
- Ingress
- Egress
다음은 engineering 네임스페이스의 모든 파드에 대한 동일한 역할의 default deny Kubernetes 네트워크 정책이에요.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: engineering
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
글로벌 default deny 정책 만들기
default deny 정책은 원치 않는 트래픽(ingress와 egress)을 기본적으로 차단해 주기 때문에, Kubernetes와 Calico 정책의 default deny/allow 동작을 일일이 기억할 필요가 없어요. 이 정책은 측면(lateral) 악성 공격의 위험을 완화하는 데도 도움이 돼요.
모범 사례 #1: 허용 후 스테이징, 그다음 거부
허용하려는 트래픽에 대한 정책 작성을 완료한 다음에 글로벌 default deny 정책을 만드는 것을 권장해요. 다음 단계는 원치 않는 트래픽을 차단하도록 클러스터를 테스트하고 잠그는 모범 사례를 요약한 거예요:
- 글로벌 default deny 정책을 만들고 스테이징 환경에서 테스트하세요. (정책이 deny로 전환될 경우 차단될 모든 트래픽을 보여줘요.)
- 1단계에서 차단된 것으로 표시된 트래픽을 개별적으로 허용하는 네트워크 정책을 만들어, 거부되는 연결이 없어질 때까지 반복하세요.
- 글로벌 default deny 정책을 강제 적용하세요.
모범 사례 #2: 범위를 비시스템(non-system) 파드로 한정
글로벌 default deny 정책은 전체 클러스터에 적용돼요. 모든 네임스페이스의 모든 워크로드, 호스트(VM의 하이퍼바이저 또는 컨테이너의 컨테이너 런타임을 실행하는 컴퓨터), Kubernetes 컨트롤 플레인과 Calico 컨트롤 플레인 노드와 파드를 포함해요.
그렇기 때문에 다음 예시처럼 비시스템 파드를 위한 글로벌 default deny 정책을 만드는 것이 모범 사례예요.
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: deny-app-policy
spec:
namespaceSelector: kubernetes.io/metadata.name not in {"calico-system", "kube-public", "kube-system", "tigera-operator"}
types:
- Ingress
- Egress
egress:
# allow all namespaces to communicate to DNS pods
- action: Allow
protocol: UDP
destination:
selector: 'k8s-app == "kube-dns"'
ports:
- 53
- action: Allow
protocol: TCP
destination:
selector: 'k8s-app == "kube-dns"'
ports:
- 53
다음 사항에 유의하세요:
- 이 정책을 "global default deny"라고 부르지만, 위 정책은 트래픽을 명시적으로 거부하지 않아요.
namespaceSelector로 트래픽을 선택하지만 허용 규칙을 지정하지 않으면, 다른 모든 정책이 평가된 후에 그 트래픽이 거부돼요. 이 설계 덕분에 default-deny 정책에 특정 순서(우선순위)를 보장할 필요도 없어요. kube-dns접근을 허용하면 파드별 정책이 단순해져요. 모든 정책에 DNS 규칙을 복제할 필요가 없거든요.- 이 정책은 컨트롤 플레인 네임스페이스를 제외하기 위해
spec.namespaceselector필드에 부정(negative) 셀렉터를 사용해요. 이 예시에서는calico-system,kube-public,kube-system,tigera-operator네임스페이스에요. 설치 구성은 환경마다 다를 수 있으니, 비슷한 정책을 구현하기 전에 무엇이 설치되어 있는지 꼭 확인하세요.
스테이징 환경에서 정책을 강제 적용하기 전에, 필요한 트래픽을 차단하지 않는지 확인하세요.
절대 이렇게 하지 마세요!
다음 정책은 동작하고 겉보기에도 괜찮아 보여요. 하지만 모범 사례 #2에서 설명했듯이 범위가 너무 넓어서 클러스터를 망가뜨릴 수 있어요. 따라서 스테이징 환경에서 허용되는 트래픽을 검증했더라도 이런 유형의 정책은 추가하지 않는 것을 권장해요.
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: default.default-deny
spec:
tier: default
selector: all()
types:
- Ingress
- Egress
추가 리소스
더 알아보기 (Learn more)
Kubernetes 파드는 기본적으로 모든 트래픽이 허용된다는 점을 기억하세요. 글로벌 default deny 정책을 만들 때는 반드시 namespaceSelector로 시스템 네임스페이스를 제외하고, 스테이징에서 먼저 검증한 뒤 강제 적용하는 순서를 지키는 게 안전해요.