Network Policies and netpol library
Network Policies configure connectivity for pods (and by extension, services) within a namespace. Think of it as a simple firewall, specifying which connections (“from pod x”, “to namespace y”, “on port 8080”, etc.) are allowed.
By default, networking is not restricted. A netpol.NetworkPolicy is created to isolate pods, blocking all traffic except for listed exceptions.
NOTE: In the future, we will have a cluster-level “deny by default” policy, and declaring a NetworkPolicy will be required to unisolate pods.
Alas, the Kubernetes NetworkPolicy syntax is confusing, verbose, and it’s very easy to shoot yourself in the foot and
accidentally allow or block all traffic. Please use the netpol library instead of raw Kubernetes policies.
Terminology
ingresstraffic - incoming connections to podsegresstraffic - outgoing connections from pods
Don’t confuse ingress network policy with kube.Ingress objects.
Example simple policy
This policy applies to all pods within a namespace, and:
- allows unrestricted egress traffic
- allows ingress only:
- between pods in the namespace
- from
monitoring-clusternamespace - to allow Victoria Metrics (\equivPrometheus) metrics scraping - from
ingress-nginxnamespace - to allowkube.Ingressobjects to work
netpol: ns.Contain(netpol.NetworkPolicy('appname')) {
ingress: [
netpol.allowFrom([
netpol.withinNamespace,
netpol.namespace('monitoring-cluster'),
netpol.namespace('ingress-nginx'),
]),
],
egress: netpol.unrestricted,
}
Default policy - block by default
An empty policy blocks all ingress and egress traffic to and from all pods within a namespace:
netpol: ns.Contain(netpol.NetworkPolicy('appname')),
This is equivalent to:
netpol: ns.Contain(netpol.NetworkPolicy('appname')) {
ingress: [],
egress: [],
}
Empty arrays mean “no exceptions from blocking”.
Allow unrestricted traffic
Use netpol.unrestricted as a shorthand for “no restrictions whatsoever” for all ingress and/or
all egress:
netpol: ns.Contain(netpol.NetworkPolicy('appname')) {
ingress: netpol.unrestricted,
egress: netpol.unrestricted,
}
Specify allowed traffic
Use netpol.allowFrom() (for ingress) and netpol.allowTo() (for egress) to specify which traffic
is allowed. Example:
netpol: ns.Contain(netpol.NetworkPolicy('appname')) {
ingress: [
netpol.allowFrom([
// any pod in the namespace in which NetworkPolicy is defined
netpol.withinNamespace,
// any pod in `foo-bar` namespace
netpol.namespace('foo-bar'),
// any IP address matching given CIDR
netpol.cidr('10.14.0.0/16'),
// any IP address within 185.236.240.32/28, except 185.236.240.40
netpol.cidr('185.236.240.32/28', except=['185.236.240.40/32']),
]),
],
egress: [
netpol.allowTo([
// any pod in the cluster
netpol.withinCluster,
]),
],
}
Note that allowlists are additive - you cannot block traffic once allowed by a rule.
It’s also possible to select pods matching labels, namespaces matching labels, and matching pods within matching namespaces, but there’s no syntax sugar for these right now. See Kubernetes docs for details.
Specify allowed ports
By default, allowFrom/allowTo() rules apply to traffic on any port. Pass toPorts=[] argument to
apply exceptions only to those ports.
Use allowFromWorld(toPorts=[]) to allow unrestricted access to certain ports while blocking others.
Port syntax:
toPorts=[2137]- TCP port 2137toPorts=['http']- porthttpnamed on the pod spectoPorts=[22, 80, 443]- a list of portstoPorts=[{ port: 32000, endPort: 32768 }]- a range of ports (must be numeric)toPorts=[{ protocol: 'UDP', port: 53 }]- a UDP port
Example:
netpol: ns.Contain(netpol.NetworkPolicy('appname')) {
ingress: [
// allow traffic within namespace on *any* port
netpol.allowFrom([
netpol.withinNamespace
]),
// allow traffic from `monitoring-cluster` but only to ports 9090 and 9092
netpol.allowFrom([
netpol.namespace('monitoring-cluster')
], toPorts=[9090, 9092]),
// allow traffic from anywhere but only on named port `auth`
netpol.allowFromWorld(toPorts=['auth']),
],
}
Specify pods targetted by a NetworkPolicy
By default, a NetworkPolicy applies to all pods within a namespace. You can narrow down its scope
by passing target: with a Deployment/StatefulSet/DaemonSet. Example:
netpol: ns.Contain(netpol.NetworkPolicy('appname')) {
target: top.deployment,
}
You can also target pods by labels:
netpol: ns.Contain(netpol.NetworkPolicy('appname')) {
target: { matchLabels: { app: 'foo-bar' } }
}
Gotchas
- Active TCP connections generally continue working even after a restriction is applied. Deployment restart may be required to see the changes
- FIXME: Services with type=LoadBalancer are not effectively firewalled and will allow traffic on exposed ports. While
<loadBalancerIp>:<port>will appear firewalled,<nodeip>:<nodeport>will still allow traffic.
Resources
The netpol library hopes to make network policies easy to understand.
But, if you want to gain a deeper understanding, you might find these resources helpful:
- Kubernetes Documentation for NetworkPolicy
- Kubernetes API Reference for NetworkPolicy
kubectl describe netpol/NAME_OF_NETPOLprovides a readable textual representation of netpols- Network Policy Editor - visual representation of netpols (but pls don’t use their generated yamls directly)
- Run a copy of
//personal/radex/netpol-playgroundin your personal namespace to play around with pod connectivity