A few months ago, we published a guide to setting up Kubernetes network policies, which focused exclusively on ingress network policies. This follow-up post explains how to enhance your network policies to also control allowed egress.
A Brief Recap: What are Network Policies?
Network policies are used in Kubernetes to specify how groups of pods are allowed to communicate with each other and with external network endpoints. They can be thought of as the Kubernetes equivalent of a firewall. As with most Kubernetes objects, network policies are extremely flexible and powerful – if you know the exact communications patterns of the services in your application, you can use network policies to restrict communications to exactly what’s required and nothing more.
Ingress vs. Egress
Network policies can be used to specify both allowed ingress to pods and allowed egress from pods. These specifications work as one would expect:
- traffic to a pod from an external network endpoint outside the cluster is allowed if ingress from that endpoint is allowed to the pod.
- traffic from a pod to an external network endpoint outside the cluster is allowed if egress is allowed from the pod to that endpoint.
- traffic from one pod (A) to another (B) is allowed if and only if egress is allowed from A to B and ingress is allowed to B from A. Note that controls are unidirectional – for traffic from B to be allowed to initiate a connection to A, egress must be allowed from B to A and ingress to B from A.
Set Up Ingress First!
First things first - we recommend setting up network policies for ingress and operationalizing it successfully before setting up egress network policies.
Why? First, it is simpler to not do both at once, since otherwise it can be hard to know whether a network connection was blocked because of ingress or egress configurations. Second, and more important, egress network policies are typically harder to operationalize. Restricting egress often breaks apps in unexpected ways. While it is usually relatively straightforward to figure out from which network endpoints we expect communications to a pod, it is, in practice, usually much harder to figure out to which network endpoints connections from a pod go. This challenge arises because:
- deployments often query a bunch of external services as part of regular functioning. Depending on how they handle timeouts when reaching out to these services (for example, whether it is best effort or they fail hard), functionality can be impacted in subtle and hard-to-observe ways.
- deployments need to be able to talk to a DNS server to be able to talk to anything else, unless they are reaching out to services directly by IP.
To set up ingress policies, you can follow our aforementioned guide.
Isolate your pods for egress
Each network policy has a podSelector
field, which selects a group of (zero or more) pods. When a pod is selected by a network policy, the network policy is said to apply to it.
Further, each network policy can apply to ingress, egress, or both, depending on the value of the policyTypes
field (if this field is not specified in the YAML, its value defaults based on the presence of ingress and egress rules in the policy; since the defaulting logic is subtle, we recommend always specifying it explicitly).
In what follows, we will use the term “egress network policy” to denote any network policy that applies to egress (irrespective of whether the policy also applies to ingress).
By default, if no egress network policy applies to a pod, it is non-isolated for egress. (Note that isolation is evaluated independently for ingress and egress; it is possible for a pod to be isolated for neither, for exactly one, or for both). When a pod is non-isolated for egress, all network egress is allowed from the pod.
The moment one egress network policy applies to a pod, the pod is isolated for egress. For isolated pods, network egress is allowed only if it is permitted by at least one of the egress network policies that applies to it (that is, network policies are by allow list-only).
Therefore, the first step to setting up egress network policies is to isolate your pods for egress. We recommend starting out by applying a “default-deny-all” policy, which will isolate all pods for egress.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all-egress
spec:
podSelector: {}
egress:
- to:
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 53
policyTypes:
- Egress
Note that this policy allows connections to port 53 on any IP by default, to facilitate DNS lookups. As a consequence, while it guards against accidental unintended egress, it is not a general protection against data exfiltration since attackers can just exfil to port 53.
If you know, for certain, what DNS servers your pods will use, you can scope down this access further.
For example, to narrow DNS down to the kube-dns
service only, you can do the following:
- Label the
kube-system
namespace:kubectl label namespace kube-system networking/namespace=kube-system
- Apply the following network policy:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all-egress
spec:
podSelector: {}
egress:
- to:
- namespaceSelector:
matchLabels:
networking/namespace: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 53
policyTypes:
- Egress
Important Note: Since network policies are namespaced resources, you will need to create this policy for each namespace. You can do so by running
kubectl -n <namespace> create -f <filename>
for each namespace. Do not apply this on the kube-system namespace unless you know what you’re doing, since it can break cluster functionality.
Explicitly allow Internet egress for pods that need it
With just the default-deny-all-egress
policy in place in every namespace, none of your pods will be able to reach out to the Internet, but in most applications, at least some pods will need to. To permit this setup, one approach would be to designate labels that are applied to those pods for which Internet egress is allowed, and to create a network policy that targets those labels. For example, the following network policy allows traffic from pods having the networking/allow-internet-egress=true
label to all network endpoints (including those external to the cluster). Note that again, as in the previous section, you will have to create this policy for every namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: internet-egress
spec:
podSelector:
matchLabels:
networking/allow-internet-egress: "true"
egress:
- {}
policyTypes:
- Egress
For a more locked-down set of policies, you would ideally want to specify finer-grained CIDR blocks as well as explicitly list out allowed ports and protocols. However, this policy provides a good starting point, with much greater security than the default.
Explicitly allow necessary pod-to-pod communications
If you isolated your pods for ingress, and then explicitly allowed pod-to-pod communications until your app worked, you probably found that all such communications was blocked the moment you applied the default-deny-all-egress
policy. This behavior is expected: every connection that you allow in the ingress direction, you will now need to add to the allow list in the egress direction as well. Whether you followed one of our suggestions (such as allowing all intra-namespace communications, using a source-sink approach, or adding connections between individual pods to an allow list), or went with a custom approach, simply follow the below algorithm for every ingress policy you constructed to construct corresponding egress policies.
Constructing the complementary egress policy
For an ingress policy that allows communications from one group of pods to the other, the complementary egress policy is fairly straightforward to construct. First, change the policyTypes
field to be an array containing only Egress
. Take the spec.podSelector
, and put it inside a spec.egress.to.podSelector
block. Remove the ingress.from
section, but take the ingress.from.podSelector
from there and make it the spec.podSelector
of the new egress policy. If the ingress policy was an intra-namespace policy, you’re done!
For a cross-namespace policy, assuming that you have labeled each namespace with the network/namespace: <ns-name>
label as we suggested in our ingress guide (recall that you can do so by running
kubectl label namespace <name> networking/namespace=<name>
) you need to put the namespace selected in ingress.from.namespaceSelector
in the ingress policy as metadata.namespace
of the egress policy and select the namespace specified in the metadata.namespace
of the ingress policy in the egress policy’s egress.to.namespaceSelector
field.
Here’s an example:
Summary
Just as we noted in our ingress post, these recommendations provide a good starting point, but network policies are much more complicated. If you’re interested in exploring them in more detail, be sure to check out the Kubernetes tutorial as well as some handy network policy recipes.
Here at Red Hat, we’ve spent a lot of time thinking about how to operationalize network policies. The StackRox Kubernetes Security Platform automatically suggests and can generate network policies that enable just those communications paths your applications need. You can learn more about these capabilities in our network policy enforcement discussion.
저자 소개
채널별 검색
오토메이션
기술, 팀, 인프라를 위한 IT 자동화 최신 동향
인공지능
고객이 어디서나 AI 워크로드를 실행할 수 있도록 지원하는 플랫폼 업데이트
오픈 하이브리드 클라우드
하이브리드 클라우드로 더욱 유연한 미래를 구축하는 방법을 알아보세요
보안
환경과 기술 전반에 걸쳐 리스크를 감소하는 방법에 대한 최신 정보
엣지 컴퓨팅
엣지에서의 운영을 단순화하는 플랫폼 업데이트
인프라
세계적으로 인정받은 기업용 Linux 플랫폼에 대한 최신 정보
애플리케이션
복잡한 애플리케이션에 대한 솔루션 더 보기
오리지널 쇼
엔터프라이즈 기술 분야의 제작자와 리더가 전하는 흥미로운 스토리
제품
- Red Hat Enterprise Linux
- Red Hat OpenShift Enterprise
- Red Hat Ansible Automation Platform
- 클라우드 서비스
- 모든 제품 보기
툴
체험, 구매 & 영업
커뮤니케이션
Red Hat 소개
Red Hat은 Linux, 클라우드, 컨테이너, 쿠버네티스 등을 포함한 글로벌 엔터프라이즈 오픈소스 솔루션 공급업체입니다. Red Hat은 코어 데이터센터에서 네트워크 엣지에 이르기까지 다양한 플랫폼과 환경에서 기업의 업무 편의성을 높여 주는 강화된 기능의 솔루션을 제공합니다.