Kubernetes Network Policies Using Deny-All Default

      +
      The Autonomous Operator and Couchbase Server can be used with Kubernetes network policies although this is not officially supported currently.

      Tutorials are accurate at the time of writing but rely heavily on third party software. Tutorials are provided to demonstrate how a particular problem may be solved. Use of third party software is not supported by Couchbase. For further help in the event of a problem, contact the relevant software maintainer.

      Overview

      Refer to the concepts page on Kubernetes networking for an introduction to network policies and the rules required for Couchbase.

      Network policies should work with Couchbase Autonomous Operator deployments but are currently unsupported officially. This information is provided to document a functional set up for those that need it prior to official support. The assumption is standard Kubernetes configuration is used rather than any specific to a particular network plugin.

      This example uses the Calico network plugin to show how to enable network policies on a local development cluster. It shows examples of how to configure the various information required, actual deployments may differ.

      No rules are set up for any external traffic for Couchbase SDKs, helm or kubectl usage.

      We use the Helm chart here only to simplify the examples.

      Cluster setup

      The Kubernetes cluster needs to be configured to support network policies with an appropriate network plugin. Refer to the official documentation for details on this.

      As an example, the following script will create a Kubernetes-in-Docker (KIND) cluster locally with two Kubernetes nodes.

      $ kind create cluster --config - <<EOF
      kind: Cluster
      apiVersion: kind.x-k8s.io/v1alpha4
      networking:
        disableDefaultCNI: true    # disable kindnet
        podSubnet: 192.168.0.0/16  # set to Calico's default subnet
      nodes:
        - role: control-plane
        - role: worker
      EOF

      Once we have our Kubernetes cluster available, we can deploy the Calico network plugin to it which will manage the network policies for us.

      $ kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
      $ kubectl -n kube-system set env daemonset/calico-node FELIX_IGNORELOOSERPF=true
      $ kubectl -n kube-system set env daemonset/calico-node FELIX_XDPENABLED=false

      The example above is purely for demonstration purposes and is not guaranteed to be functional or recommended to be used in production.

      Couchbase Dynamic Admission Controller

      The typical and recommended deployment is a single cluster-wide dynamic admission controller (DAC) for Couchbase. For the purposes of the example we therefore deploy the DAC to the default namespace with no network policies applied.

      $ helm upgrade --install couchbase-dac couchbase/couchbase-operator --set install.couchbaseCluster=false,install.couchbaseOperator=false --namespace default --wait

      Couchbase Autonomous Operator

      We will need to supply the Kubernetes API server details along with the DAC endpoint used by the operator to network policy rules. We also need to supply the Kubernetes namespace to use, here we simply use test as the name.

      $ export API_SERVER_IP=$(kubectl get endpoints --namespace default kubernetes --output=json|jq '.subsets[0].addresses[0].ip' -r)
      $ export API_SERVER_PORT=$(kubectl get endpoints --namespace default kubernetes --output=json|jq '.subsets[0].ports[0].port' -r)
      $ export NAMESPACE=test
      The variables above will be used for substitution in the following steps.

      To provide a functional network policy we need to enable the rules specified in the Kubernetes networking page.

      ---
      apiVersion: v1
      kind: Namespace
      metadata:
        name: $NAMESPACE
      ---
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
        name: default-deny-all-except-dns
        namespace: $NAMESPACE
        # Prevent any traffic for all pods apart from that we specifically allow.
        # This is taken from the Kubernetes documentation with a tweak to allow DNS.
      spec:
        podSelector: {}
        policyTypes:
        - Ingress
        - Egress
        egress:
        # Allow DNS resolution, primarily for Couchbase pods but it triggers other
        # hard to debug connection issues sometimes too so be aware if this is moved to
        # the later rules.
        - ports:
          - port: 53
            protocol: UDP
          - port: 53
            protocol: TCP
      ---
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
        name: couchbase-operator-policy
        namespace: $NAMESPACE
        # The operator needs to talk to server pods in its namespace,
        # plus the K8S API and the DAC admission hook.
        #
        # If the operator is in another namespace (cluster wide) then the rules
        # will need a namespace selector to allow that.
        # Remember that any rules need to match on both ends to allow traffic, e.g.
        # if you have a deny-all default and only allow traffic out of the operator
        # but not in on the server pods then it will be prevented.
      spec:
        # Select operator pods
        podSelector:
          matchLabels:
            app.kubernetes.io/name: couchbase-operator
        # No ingress required as traffic is always out to other components.
        policyTypes:
          - Egress
        egress:
        # Allow all traffic to Couchbase Server pods
        - to:
          - podSelector:
              matchLabels:
                app: couchbase
        # Allow traffic to the K8S API server IP and port only
        - ports:
          - port: $API_SERVER_PORT
            protocol: TCP
          to:
          - ipBlock:
              cidr: $API_SERVER_IP/32
      ---
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
        name: couchbase-namespace-policy
        namespace: $NAMESPACE
        # Couchbase server pods need to talk to each other in the same namespace.
        # They also need to allow the operator to control them.
      spec:
        # Select all Couchbase Server pods
        podSelector:
          matchLabels:
            app: couchbase
        policyTypes:
          - Ingress
          - Egress
        ingress:
          # Allow all traffic from other Couchbase Server pods in the same namespace,
          - from:
            - podSelector:
                matchLabels:
                  app: couchbase
          # Allow all traffic from the operator.
          - from:
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: couchbase-operator
        egress:
          # Allow all traffic to other Couchbase Server pods in the same namespace.
          - to:
            - podSelector:
                matchLabels:
                  app: couchbase

      Make sure to use the variables defined previously as required. Once we have this network policy applied we can deploy Couchbase - again using the Helm chart.

      $ helm upgrade --install couchbase couchbase/couchbase-operator --set install.admissionController=false --namespace "$NAMESPACE"

      As seen above, only deploy the DAC once - we disable it for the second Helm chart deployment of the operator and server pods.