A proposal for implementing a new resource - NetworkPolicy - which
will enable definition of ingress policies for selections of pods.
The design for this proposal has been created by, and discussed
extensively within the Kubernetes networking SIG. It has been implemented
and tested using Kubernetes API extensions by various networking solutions already.
In this design, users can create various NetworkPolicy objects which select groups of pods and
define how those pods should be allowed to communicate with each other. The
implementation of that policy at the network layer is left up to the
chosen networking solution.
> Note that this proposal does not yet include egress / cidr-based policy, which is still actively undergoing discussion in the SIG. These are expected to augment this proposal in a backwards compatible way.
The above structures will be represented in v1beta1 as a json encoded annotation like so:
```yaml
kind: Namespace
apiVersion: v1
metadata:
annotations:
net.beta.kubernetes.io/network-policy: |
{
"ingress": {
"isolation": "DefaultDeny"
}
}
```
### NetworkPolicy Go Definition
For a namespace with ingress isolation, connections to pods in that namespace (from any source) are prevented.
The user needs a way to explicitly declare which connections are allowed into pods of that namespace.
This is accomplished through ingress rules on `NetworkPolicy`
objects (of which there can be multiple in a single namespace). Pods selected by
one or more NetworkPolicy objects should allow any incoming connections that match any
ingress rule on those NetworkPolicy objects, per the network plugin’s capabilities.
NetworkPolicy objects and the above namespace isolation both act on _connections_ rather than individual packets. That is to say that if traffic from pod A to pod B is allowed by the configured
policy, then the return packets for that connection from B -> A are also allowed, even if the policy in place would not allow B to initiate a connection to A. NetworkPolicy objects act on a broad definition of _connection_ which includes both TCP and UDP streams. If new network policy is applied that would block an existing connection between two endpoints, the enforcer of policy
should terminate and block the existing connection as soon as can be expected by the implementation.
We propose adding the new NetworkPolicy object to the `extensions/v1beta1` API group for now.
The SIG also considered the following while developing the proposed NetworkPolicy object:
- A per-pod policy field. We discounted this in favor of the loose coupling that labels provide, similar to Services.
- Per-Service policy. We chose not to attach network policy to services to avoid semantic overloading of a single object, and conflating the existing semantics of load-balancing and service discovery with those of network policy.
```go
type NetworkPolicy struct {
TypeMeta
ObjectMeta
// Specification of the desired behavior for this NetworkPolicy.
Spec NetworkPolicySpec
}
type NetworkPolicySpec struct {
// Selects the pods to which this NetworkPolicy object applies. The array of ingress rules
// is applied to any pods selected by this field. Multiple network policies can select the
// same set of pods. In this case, the ingress rules for each are combined additively.
// This field is NOT optional and follows standard unversioned.LabelSelector semantics.
// An empty podSelector matches all pods in this namespace.
The following pseudo-code attempts to define when traffic is allowed to a given pod when using this API.
```python
def is_traffic_allowed(traffic, pod):
"""
Returns True if traffic is allowed to this pod, False otherwise.
"""
if not pod.Namespace.Spec.NetworkPolicy.Ingress.Isolation:
# If ingress isolation is disabled on the Namespace, use cluster default.
return clusterDefault(traffic, pod)
elif traffic.source == pod.node.kubelet:
# Traffic is from kubelet health checks.
return True
else:
# If namespace ingress isolation is enabled, only allow traffic
# that matches a network policy which selects this pod.
for network_policy in network_policies(pod.Namespace):
if not network_policy.Spec.PodSelector.selects(pod):
# This policy doesn't select this pod. Try the next one.
continue
# This policy selects this pod. Check each ingress rule
# defined on this policy to see if it allows the traffic.
# If at least one does, then the traffic is allowed.
for ingress_rule in network_policy.Ingress or []:
if ingress_rule.matches(traffic):
return True
# Ingress isolation is DefaultDeny and no policies match the given pod and traffic.
return false
```
### Potential Future Work / Questions
- A single podSelector per NetworkPolicy may lead to managing a large number of NetworkPolicy objects, each of which is small and easy to understand on its own. However, this may lead for a policy change to require touching several policy objects. Allowing an optional podSelector per ingress rule additionally to the podSelector per NetworkPolicy object would allow the user to group rules into logical segments and define size/complexity ratio where it makes sense. This may lead to a smaller number of objects with more complexity if the user opts in to the additional podSelector. This increases the complexity of the NetworkPolicy object itself. This proposal has opted to favor a larger number of smaller objects that are easier to understand, with the understanding that additional podSelectors could be added to this design in the future should the requirement become apparent.
- Is the `Namespaces` selector in the `NetworkPolicyPeer` struct too coarse? Do we need to support the AND combination of `Namespaces` and `Pods`?
### Examples
1) Only allow traffic from frontend pods on TCP port 6379 to backend pods in the same namespace.
```yaml
kind: Namespace
apiVersion: v1
metadata:
name: myns
annotations:
net.beta.kubernetes.io/network-policy: |
{
"ingress": {
"isolation": "DefaultDeny"
}
}
---
kind: NetworkPolicy
apiVersion: extensions/v1beta1
metadata:
name: allow-frontend
namespace: myns
spec:
podSelector:
matchLabels:
role: backend
ingress:
- from:
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 6379
```
2) Allow TCP 443 from any source in Bob's namespaces.
```yaml
kind: NetworkPolicy
apiVersion: extensions/v1beta1
metadata:
name: allow-tcp-443
spec:
podSelector:
matchLabels:
role: frontend
ingress:
- ports:
- protocol: TCP
port: 443
from:
- namespaceSelector:
matchLabels:
user: bob
```
3) Allow all traffic to all pods in this namespace.