Kubernetes Cluster Hardening¶
Network security¶
Kubernetes clusters are often considered as black boxes, which conduct network security teams to focus on securing the network outside the cluster. However, probably because of a lack of knowledge, the internal cluster network is not secured at all. Kubernetes offers an out of the box feature named Network Policies which allows users to create a multi tier architecture inside the clusters.
Let's create a basic two tiers architectures inside a cluster. It will be composed of a MySQL database and a phpMyAdmin application.
The demo code will be hosted on github
Demo Application Architecture
Pre-requisite¶
Network policies are implemented by a network plugin. To use network policies, you must be using a networking solution which supports NetworkPolicy. Creating a NetworkPolicy resource without a controller that implements it will have no effect.
(extract from the kubernetes doc)
As the demo runs on an AKS clusters, the chosen network policy is calico.
Container Network Interface¶
CNI (Container Network Interface), a Cloud Native Computing Foundation project, consists of a specification and libraries for writing plugins to configure network interfaces in Linux containers, along with a number of supported plugins. CNI concerns itself only with network connectivity of containers and removing allocated resources when the container is deleted. Because of this focus, CNI has a wide range of support and the specification is simple to implement.
(source)
Deploy components¶
Execute the command below to create :
- the app and data namespaces
- the mysql pod and service
- the phpMyAdmin pod and service
kubectl apply -f namespaces.yaml
Warning
The *-policy.yaml-hold
files must be named *-policy.yaml-hold
to avoid to be deployed
Verify the deployment¶
-
Open a tunnel to the phpmyadmin service
kubectl port-forward -n app svc/phpmyadmin 8080:80
-
Connect the
phpMyAdmin UI
onhttp://localhost:8080
- The phpMyAdmin web site should be accessible
Apply default network policies¶
The purpose of this policy is to block any ingress or egress traffic using the data namespace. It is wise to apply a such rule to all the namespaces and explicitly allows only the minium required connections.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: data
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Apply this NetworkPolicy will break the connection between phpMyAdmin and the backend MySQL datatabase.
Authorize traffic¶
The NetworkPolicy below will enable the traffic on port 3306 from pods, in namespaces configured with the label tier=app
, which have the label app=phpmyadmin.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-phpmyadmin-ingress
namespace: data
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
tier: app
podSelector:
matchLabels:
app: phpmyadmin
# - ipBlock:
# cidr: 172.17.0.0/16
# except:
# - 172.17.1.0/24
ports:
- protocol: TCP
port: 3306
Info
Find more information about NetworkPolicy in the official documentation
Security compliance¶
Admission webhook¶
Admission webhooks are HTTP callbacks that receive admission requests and do something with them. You can define two types of admission webhooks, validating admission webhook and mutating admission webhook. Mutating admission webhooks are invoked first, and can modify objects sent to the API server to enforce custom defaults. After all object modifications are complete, and after the incoming object is validated by the API server, validating admission webhooks are invoked and can reject requests to enforce custom policies.
(source)
Open Policy Agent (OPA) Gatekeeper¶
It is an open source project used to enforce a set of compliance rules. As example, it may enforce the usage of a company docker registry and reject any other registries. It can also help to analyze the incoming request content and ensure that the container does not run as root.
OPA Gatekeeper comes with a public library provided users with a pre-defined set of bets practices.
Hands-on pre-requisite¶
OPA GateKeeper must be deployed on the demo cluster.
Example¶
In this example, we will only allow phpMyAdmin and mysql to be deployed in the cluster.
Deploy the rule template¶
The rule template will a create a Custom Resource Definition (CRD) which will be usable to enforce some practices.
The ConstraintTemplate below is a standard one used to enforce container image source repository. The Rego language is used to specify the template behaviour, this abstraction prevent the hardship of custom admission webhook creation and make simpler the creation of constraints policies.
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8sallowedrepos
annotations:
description: Requires container images to begin with a repo string from a specified
list.
spec:
crd:
spec:
names:
kind: K8sAllowedRepos
validation:
# Schema for the `parameters` field
openAPIV3Schema:
type: object
properties:
repos:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sallowedrepos
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
satisfied := [good | repo = input.parameters.repos[_] ; good = startswith(container.image, repo)]
not any(satisfied)
msg := sprintf("container <%v> has an invalid image repo <%v>, allowed repos are %v", [container.name, container.image, input.parameters.repos])
}
violation[{"msg": msg}] {
container := input.review.object.spec.initContainers[_]
satisfied := [good | repo = input.parameters.repos[_] ; good = startswith(container.image, repo)]
not any(satisfied)
msg := sprintf("container <%v> has an invalid image repo <%v>, allowed repos are %v", [container.name, container.image, input.parameters.repos])
}
Enforce a constraint¶
Execute the code below
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
name: allowed-phpmyadmin
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces:
- "app"
parameters:
repos:
- "phpmyadmin"
This constraint will forbid the deployment of containers other than the phpmyadmin
one. Because OPA Gatekeeper templates are used to create new Custom Resource Definition, it becomes easy to add constraints.
Execute a deployment test¶
Execute the code below
apiVersion: v1
kind: Pod
metadata:
labels:
run: pods
name: pods
namespace: app
spec:
containers:
- image: nginx
name: nginx
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
---
apiVersion: v1
kind: Pod
metadata:
labels:
run: pods
name: pods
namespace: app
spec:
containers:
- image: phpmyadmin
name: phpmyadmin
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
The console will show and error about the nginx deployment, indeed the nginx container is not authorized to be deployed in the app namespace
Sum-up¶
This hand-ons is just an overview of two technology which can be used to ensure security of a kubernetes cluster. I advise trainees to go deeper in a network plugin such as calico and to get familiar with the Rego language as it will allow them to create amazing custom rules.