Polycube Policies

pcn-k8s CNI implementes both standard kubernetes networking policy and advanced Polycube networking policies. The latter provide a more flexible, simpler and more advanced approach to filter the traffic that a pod should allow or block. They include all the features from Kubernetes Network Policies and present some additional features, which are going to be described here.

The isolation mode, a core concept in Kubernetes Network Policies, is followed by Polycube Network Policies, as well: pods allow all communication until a policy - either one - is applied to them, and at that moment they will start to allow only what’s explicitly specified in the policy.

Human Readable Policies

One of the goals of the Polycube Policies is to also encourage operators to write more effective policies, and the very first thing that can help this process is a friendly and understandable syntax.

In Polycube Policies, there is no need to use the [] and {} operators in multiple parts of the policy to select no/all pods: two convenient fields - dropAll and allowAll - are there just for this purpose, leaving no room for confusion.

apiVersion: polycube.network/v1beta
kind: PolycubeNetworkPolicy
metadata:
  name: deny-all
applyTo:
  target: pod
  withLabels:
    role: db
spec:
  ingressRules:
    dropAll: true

As shown, Polycube policies follow the natural language that humans speak - humans with a basic understanding of the English language, that is. As a matter of fact, when selecting the pods that the policy must be applied to, the applyTo field must be used, and the target field clearly specifies the need to protect a pod that has labels role: db, as finally stated in the withLabels field.

If you want to apply a policy to all pods on a given namespace, the following syntax may be used:

applyTo:
  target: pod
  any: true

Automatic type detection

There is no need to specify the policy type - Ingress or Egress - as it is automatically recognized, based on their existence in the policy YAML file.

Prevent Direct Access

if you want your pods to be contacted only through their Service(s) and block direct access, set the preventDirectAccess to true.

apiVersion: polycube.network/v1beta
kind: PolycubeNetworkPolicy
metadata:
  name: pod-drop-all
priority: 3
ingressRules:
  preventDirectAccess: true
  rules: ...

From now on, the pods will only allow connections that were made through their ClusterIP.

Explicit Priority

Priority can explicitly be declared by properly writing it in the policy, where a lower number indicates its importance in rules insertion.

It can be done by setting the priority field:

apiVersion: polycube.network/v1beta
kind: PolycubeNetworkPolicy
metadata:
  name: pod-drop-all
priority: 3

Since more recent policies always take priority (and thus are checked first), setting and explicit priority can help in those situations where you want to a policy to be checked before others.

Just to make an example: if you’d like to temporarily block all traffic to check for anomalies, there is no need for you to remove all existing policies and deploy one that drops all traffic, as you can simply give the latter a higher priority (i.e. 1) and deploy it: the higher priority will make it the first one to be checked and, as a result, all traffic would be blocked without modifying the other policies.

Strong distinction between the internal and external

The rules that can be specified are divided by what is internal to the cluster and what is outside.

This is done to prevent the clear bad behavior of using IPBlock to target pods. Peers are divided in two groups: pod and world.

The internal world can be specified like so:

ingressRules:
  rules:
    - action: allow
      from:
        peer: pod
        withLabels:
          role: api
        onNamespace:
          withNames:
            - beta
            - production

Once again, the syntax closely resembles a natural spoken language.

In Kubernetes Network Policies, namespaces can be targeted only by the labels they have: when wanting to target them, the operator is forced to assign labels to namespaces even if they just need to target very few of them. As the policy above shows, Polycube Policies provide a way to select namespaces by their names as well, while also providing the ability to do so by their labels.

The external world, instead, can be restricted by writing world in the peer field.

ingressRules:
  rules:
    - action: drop
      from:
        peer: world
        withIP:
          -  100.100.100.0/28
    - action: allow
      from:
        peer: world
        withIP:
          -  100.100.100.0/24

So, there is no need to write exceptions, as in Kubernetes Network Policies, because Polycube policies also have a clear distinction between actions.

Drop or Allow actions

ingressRules:
  rules:
  - from:
      peer: pod
        withLabels:
          role: api
       action: drop

To allow a connection, the actions that can be written are allow, pass, forward and permit.

The same applies when blocking connections, and the following words can be used: block, drop, prohibit and forbid.

The presence of multiple words to define a single action has been done to aid the definition of a policy, allowing for a more flexible semantic that is easier to remember.

This will help you create Blacklist-style policies by creating two or more policies: one, with a lower priority, that allows all pods in a certain port/protocol, and another one (or more) that will work as a blacklist of pods banned (i.e. those that are in the beta namespace).

This was a clear example of the flexibility of the Polycube Policies, but one must take very careful steps when creating a blacklist policy: although this could introduce some benefits, like lighter firewalls, it could also add some subtle inconsistencies and errors if are not created mindfully, like wrongly allowing pods to start connections.

Service-aware policies

Consider the following Polycube policy:

apiVersion: polycube.network/v1beta
kind: PolycubeNetworkPolicy
metadata:
  name: service-allow-api
applyTo:
  target: service
  withName: database
spec:
  ingressRules:
    rules:
      - from:
          peer: pod
          withLabels:
            role: api
        action: allow

By writing service as a target, Polycube will be aware of the fact that pods have a service applied to them and will make all the necessary steps to protect the pods according to it.

Supposing that service named database has 80 and 443 as targetPorts with protocol TCP, all the pods that apply such service will accept connections from pods that have label role: api, but only on the aforementioned ports and protocol.

This serves both as a convenient method for targeting pods without specifying the labels - withName: database can be seen as a clear shortcut in this case - and without specifying the ports as well.

Being service-aware means that firewalls will react to Service events, too: if later, the cluster’s needs change and only the more secure 443 port is decided to be supported, the service can be updated to reflect this change and the solution will react as well by removing the behavior it used to apply for port 8080.

The service-aware functionality is made for those particular use cases when a pod does not need a more advanced rule filtering, like allowing a pod on a certain port and allowing others on another one: as already mentioned, this is a convenient method for specifying all ports at once, and if such scenario is needed, it must be done by specifying pod as the peer instead of using service.

As a final note, only services with selectors are supported: services without selectors need to be selected by writing world as the peer.