While a lot of people are calling network policies the Kubernetes equivalent of a firewall, they probably wouldn’t be called network policies if that were really the case. Although network policies are comparable to security features like firewalls, they mostly pertain to rules, and therefore a more accurate comparison would be with “firewall rules” or security groups in the Cloud that are used to manage permissions.
Unlike a firewall – which would basically prevent containers from doing whatever they want whenever they want – network policies also enable us to identify specific guidelines for how different groups of containers (or pods) are allowed to communicate with each other. The ability to set up such rules and guidelines is critical, especially since modern applications require that pods be able to communicate with each other while security measures communications cannot be left open (which is the Kubernetes default).
One best practice – and probably the most important one – is to change the way we think about Kubernetes as a platform. We’re used to working on systems where everything is safe by default and we have to actually mess something up to make it unsafe. Here it’s quite the opposite: everything is unsafe by default, which is something that you need to get used to and always keep in mind.
For example, imagine you set up a pod and forget to set up any network policies for it: you were either too tired or thought it would be a task better suited for the next morning. Either way, you would expect all connections to be denied since no policies were specified, when in fact the opposite is true. What you now have is the container version of an open wi-fi router with any and all connections being permitted, because the default setting is to allow traffic from all sources if no network policy is applied.
A common network policy that can quickly help you avoid such a situation can be found here and used to blacklist all pods using this policy so they can’t communicate with other pods until another network policy explicitly allows them to do so. In such a case, this network policy becomes obsolete and the new one takes over. This policy is usually used when you want to run a pod undisturbed (with horse blinds on) or in the case where you temporarily want to isolate traffic to a particular service. A deny-all then needs to be followed up by either an allow-all policy that allows traffic between all pods in a particular namespace, or more specific policies like this, that only allow traffic from certain pods.
Choosing the right Plugin
Kubernetes is surprisingly future-proof, and you can keep adding on and taking off interesting bits and pieces. An important point to note is that while Kubernetes does support the NetworkPolicy resource, simply creating it without an appropriate plugin does absolutely nothing. In fact, even the Kubernetes documentation points you to various other providers for plugins, while reminding you that the default network plugin does not support policies. This is why getting familiar with the Plugins and figuring out which ones suit you best must be your next priority, especially since Network Policies basically live and die on these Plugins. Whichever plugin you decide to go with, just make sure that it supports network policies: popular choices include Calico, Flannel, Kube-router, and Weave Net. Additionally, plugins that adhere to the CNI standards are generally a good choice, as they are monitored by the CNCF and are designed for interoperability.
Ingress and egress
After you have your network policy plugins in place, the next priority needs to be understanding how the traffic works and how network policies apply differently to incoming and outgoing traffic. Unlike a firewall, which is designed to keep the bad guys out, here you have ingress policies for the traffic coming in and egress policies for the traffic going out. What this means is that when a network policy is applied to a pod, the pod expects the policy to have a specific “guest list” (also called a whitelist) of who’s allowed to come to the party and a separate list of who’s allowed to leave. While each may be equally important, the most immediate gain comes from setting up ingress policies, so starting with ingress is a good practice to follow. Additionally, while it is optional to specify whether a policy includes ingress or egress rules, the best practice is to specify this explicitly.
A quick and ready-to-use policy for egress rules, like this one for instance, will not allow your application to establish connections outside your pod. This could be desirable when you have a few versions of your application running like a US version and an EU version, and you don’t want them accessing each other's services.
Deny the default
Now that you have a basic idea of how the policies work and how they need to be applied, the next step is actually securing your connections. Since the default setting is to allow all communications, the best practice for beginners is to start with a basic “default-deny-all” network policy that works by denying all connections unless explicitly “whitelisted.” It’s a bit more advanced than the first policy we used, which blacklisted everything because, unlike that one that becomes void, this will work with other policies.
Here’s an example of a basic default deny-all policy that changes your default from open to closed. It then needs another policy like this one that allows traffic from a particular namespace, or this one that will only allow traffic from some pods in a namespace. Additionally, there are also network policies with multiple selectors, allowing you to more specifically customize how traffic is routed.
In addition to giving you a safe “ground-zero” to start building your connections from, this prevents deleting network policies and causing the associated pods to revert back to their default “unsafe” states. This is because any pod without a network policy is looked at by Kubernetes as “non-isolated,” and hence, free to do as it pleases. That’s why simply deleting a network policy without a “default-deny-all” in place can cause a lot of unnecessary and undesired confusion. The best practice is to deny the default settings before you connect your pods to the internet.
Attention to detail
From here on, it’s mostly about learning how to use labels, and labeling your pods appropriately so that they can be whitelisted or denied access as required. Labels allow users to specify in detail how they would like pod-to-pod communication to work in their application. It’s also important to always be alert and aware of aspects like Kubernetes being backward compatible with older versions. This includes those that don’t have important security updates.
This is why it’s crucial to pay attention to the little details, like merging code from an older version. Again, remember that Kubernetes is a “pro-developer” at its heart, and developers need their applications to communicate with each other, preferably with a minimal amount of setup. The good news, however, is that while network policies were added later as an afterthought, Kubernetes was built with the knowledge that a lot of such “afterthoughts” were going to be needed.
Twain Taylot is a Fixate IO Contributor and began his career at Google, where, among other things, he was involved in technical support for the AdWords team. His work involved reviewing stack traces, and resolving issues affecting both customers and the Support team, and handling escalations. Later, he built branded social media applications, and automation scripts to help startups better manage their marketing operations. Today, as a technology journalist he helps IT magazines, and startups change the way teams build and ship applications.