Network Policy (Universal Containerizer)

This document will demonstrate how to manipulate policy for Calico using Policies. Specifically, we will:

  • Set labels on our workload at launch
  • Configure policy based off these labels

To demonstrate this, we will use Marathon to launch an nginx webserver using the Universal Containerizer. Then, we will launch basic curl task which will repeatedly curl the webserver.

Note: This example assumes you are running in a DC/OS environment since it uses the DC/OS DNS to access the web server. It is easy enough to adjust this demo for non-DC/OS environments by replacing the webserver.marathon.containerip.dcos.thisdcos.directory DNS name with the IP address of the web server container.

Setting Labels

When launching tasks, assign arbitrary labels in the task’s ipAddress field. These labels will be passed to Calico’s CNI plugin which will store them in the Labels field of the corresponding workload endpoint.

{
  "id": "webserver",
  "container": {
    "type": "MESOS",
    "docker": {
      "image": "nginx"
    }
  },
  "ipAddress": {
      "networkName": "calico",
      "labels": {
        "role": "webserver",
        "deployment": "production",
        "tenant": "3"
      }
  },
  "cpu": 0.25
}
{
  "id": "client",
  "cmd": "while true; do let COUNTER+=1; curl --connect-timeout 1 -s webserver.marathon.containerip.dcos.thisdcos.directory > /dev/null && echo \"(connection $COUNTER succesful)\" || echo \"($COUNTER timed out)\"; sleep 1; done",
  "ipAddress": {
      "networkName": "calico",
      "labels": {
        "role": "client",
        "deployment": "production",
        "tenant": "3"
      }
  },
  "cpu": 0.25
}

Upon launching this application, the task’s logs should repeatedly show (connection succesful) as the Default Profile will allow this connection.

Configuring the Default Profile

Calico configures a base profile that applies to any container that does not match a policy. This profile allows all containers to communicate with one another. Therefore, the logs for the client should show repeatedly successful connections. Connections made from any Agent besides the one running the task will fail.

For the purposes of this demo, we will change that default profile to block incoming requests. This will make it easier to determine when our label-based Policy is being applied.

Run the following command to block incoming requests in the default Profile:

calicoctl apply -f - <<EOF
- apiVersion: projectcalico.org/v3
  kind: Profile
  metadata:
    name: calico
    tags:
    - calico
  spec:
    egress:
    - action: allow
    ingress:
    - action: deny
EOF

Note: You’ll need calicoctl configured to access your central etcd datastore. See help.

Checking the task’s log should show that these connections are no longer successful.

Configuring Policy

Now that the default profile is isolating our tasks, we will open up the necessary connections using Calico Policies.

Policy resources are defined globally, and include a set of ingress and egress rules and actions, where each rule can filter packets based on a variety of source or destination attributes (which includes selector based filtering using label selection).

Each policy resource also has a “main” selector that is used to determine which endpoints the policy is applied to based on the applied labels.

We can use calicoctl create to create two new policies for this:

calicoctl create -f -<<EOF
- apiVersion: projectcalico.org/v3
  kind: GlobalNetworkPolicy
  metadata:
    name: webserver
  spec:
    order: 0
    selector: role == 'webserver'
    ingress:
    - action: allow
      protocol: tcp
      source:
        selector: role == 'client'
      destination:
        ports:
        -  80
    - action: allow
      source:
        selector: role == 'webserver'
    egress:
    - action: allow
      destination:
        selector: role == 'webserver'
- apiVersion: projectcalico.org/v3
  kind: GlobalNetworkPolicy
  metadata:
    name: client
  spec:
    order: 0
    selector: role == 'client'
    egress:
    - action: allow
      protocol: tcp
      destination:
        selector: role == 'webserver'
        ports:
        -  80
EOF

Checking the client’s logs should show that it is able to access the container. Requests from other hosts or tasks with labels that do not match the required ones will be blocked. This includes Agents that are not running the webserver.