Kubernetes/Kubernetes Workshop/Manageability and Security

From Wikitech

Overview

At the end of this module, you should be able to:

  • Implement security practices when using Kubernetes.

Step 1: Updating container images

Kubernetes (k8s) is a new execution environment for code, similar to a new operating system, albeit with familiar roots. It is powerful, flexible, and complex, and like other software, it gets new features with each new version.

A new version typically also addresses vulnerabilities that can be used by an attacker to abuse the system. As k8s becomes more successful, guidelines on using and configuring k8s for a safe experience are released.

How to Update a Container Image

Over time software (including base images) installed in a Docker container will become outdated. So far, you have used base images downloaded from Docker Hub without much thought as to their origin or patch level.

Using base images regardless of their origin is sufficient for learning purposes; however, it is not ideal for production purposes. Worthy of note is how you have used images under the control of specific, trustworthy organizations such as Ubuntu and Debian. Of the many Docker images on Docker Hub, most images contain vulnerabilities, many of them with high severity.

Since using an available image is often the quickest way to get an application up and running, it is tempting to do so but quite possibly a bad idea for a production system.

Moreover, attackers have started infiltrating docker registries. If you do use any external images, be sure to exercise some caution about their purpose and the data you store in these systems.

You can perform a quick check on the images you have been using:

1. Create a Dockerfile:

Dockerfile
FROM ubuntu
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get upgrade -y

2. Reducing the /etc/apt/sources.list file to only security sources, will give you an output similar to:

The following packages will be upgraded:
gcc-10-base libgcc-s1 libgnutls30 libseccomp2 libstdc++6 perl-base

It is good practice to include Operating System (OS) updates in the Dockerfile. Rebuilding and redeploying application images following standards set by your security team is also a good security practice.

Step 2: Running the container with limited privileges

By default, containers run as the root user, which applies to all the applications you have run so far. 3. Create a Dockerfile:

Dockerfile
FROM ubuntu
RUN apt-get update && apt-get install -y ca-certificates python3
ADD printuid.py /
RUN chmod u+x ./printuid.py
CMD ["/printuid.py"]

4. Create a Python script:

printuid.py
#!/usr/bin/python3
import os
print(os.getuid())

5. Build and run your Docker image:

$ docker build --tag <tag> .
$ docker run <image>
0

Getting an output of 0 implies that you are running the container as the root user.

Running Containers on minikube You have been running images on minikube by first uploading images to Docker Hub and then pulling the images from Docker Hub to be run on minikube. However, minikube can run local images, but this process uses a different Docker installation that keeps its images.

1. Build this new Docker installation by running the commands listed below:

$ eval $(minikube docker-env)
$ docker images

2. Rebuild and retag the image so minikube can access the image locally:

$ docker build --tag <tag> .
$ docker images

job.yaml

apiVersion: batch/v1
kind: Job
metadata:
 name: <name>
spec:
 template:
   spec:
     containers:
      - name: <name>
        image: <image_name>:<tag>
        imagePullPolicy: Never
     restartPolicy: Never

3. Now run the image as a job and check the output. Your pod’s name would be different:

$ minikube start
$ kubectl apply -f job.yaml
$ kubectl get pods
NAME                         READY   STATUS      RESTARTS   AGE
printuid-h56kk               0/1     Completed   0          36s
$ kubectl logs printuid-h56kk
0

4. You are running this script as the root user, but you can change the user in the Dockerfile:

FROM ubuntu
RUN apt-get update && apt-get install -y ca-certificates python3
ADD printuid.py /
USER nobody
CMD ["python3”, “printuid.py"]

5. Rebuild and run your docker file and rerun to check:

$ docker build --tag <tag> .
$ docker run <image>
65534

6. Run your image as a job and check the output. Your pod’s name would be different:

$ kubectl apply -f job.yaml
$ kubectl get pods
NAME                         READY   STATUS      RESTARTS   AGE
printuid-h56kk               0/1     Completed   0          36s
$ kubectl logs printuid-h56kk
65534

7. Now, to run the simpleapache image as a non-root user, you would create a Dockerfile with contents similar to:

 
Dockerfile
FROM ubuntu
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update
RUN apt-get install -y apache2
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2
ENV APACHE_RUN_DIR /var/run
ENV APACHE_PID_FILE /var/run/apache2.pid
RUN echo 'Hello Apache in Docker' > /var/www/html/index.html
RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf 
RUN sed -r -i 's?isten 80?isten 8080?' /etc/apache2/ports.conf
RUN sed -r -i 's?:80?:8080?' /etc/apache2/sites-available/000-default.conf
RUN chown -R nobody /var/log/apache2
RUN chown -R nobody /var/run
RUN chown -R nobody /var/lock
RUN service apache2 restart
EXPOSE 8080
CMD ["/usr/sbin/apachectl", "-D FOREGROUND"]

8. Build and run your Docker image:

 
$ docker build --tag <tag> .
$ docker run -p 8080:80 <image>

9. To uninstall this Docker installation, run:

 
$ eval $(minikube docker-env --unset)

Hands-on Demo: Namespaces

You use Namespaces to partition a Kubernetes cluster logically. For example, you can create a namespace based on your work environments such as development, staging, and production or for different teams or applications.

So far, you have run all our containers in the same namespace called default. Using a namespace will provide separation between these environments, minimizing the possibility of name collisions and making the cluster easier to manage.

Namespaces are a Kubernetes construct, and they are to be created and added to the deployment files. In this demo, you will run the cronpywpchksumbot-dev application in two separate namespaces—development and production.

1. Create a script for the development namespace:

cronpywpchksumbot-dev.yaml

 
apiVersion: v1
kind: Namespace
metadata:
 name: cronpywpchksumbot-dev

2. The deployment script for this namespace is similar to:

cronpywpchksumbotdeployment1.yaml

apiVersion: batch/v1beta1
kind: CronJob
metadata:
 name: cronpywpchksumbot
 namespace: cronpywpchksumbot-dev
spec:
 schedule: "*/5 * * * *"
 jobTemplate:
   spec:
     template:
       spec:
         containers:
         - name: pywpchksumbot
           image: <userid>/pywpchksumbot
           imagePullPolicy: IfNotPresent
         restartPolicy: OnFailure

3. Create a deployment:

$ kubectl apply -f cronpywpchksumbot-dev.yaml
$ kubectl apply -f cronpywpchksumbotdeployment1.yaml

4. Create a script for the production workspace: cronpywpchksumbot-prod.yaml

apiVersion: v1
kind: Namespace
metadata:
 name: cronpywpchksumbot-prod

5. The deployment script for this namespace is similar to: cronpywpchksumbotdeployment2.yaml

apiVersion: batch/v1beta1
kind: CronJob
metadata:
 name: cronpywpchksumbot
 namespace: cronpywpchksumbot-prod
spec:
 schedule: "*/15 * * * *"
 jobTemplate:
   spec:
     template:
       spec:
         containers:
         - name: pywpchksumbot
           image: <userid>/pywpchksumbot
           imagePullPolicy: IfNotPresent
         restartPolicy: OnFailure

6. Create a deployment:

$ kubectl apply -f cronpywpchksumbot-prod.yaml
$ kubectl apply -f cronpywpchksumbotdeployment2.yaml

7. You now have to add the --namespace=<namespace> to the command kubectl get cronjobs to get information on the cronjob. Alternatively, you can use the --all-namespaces option to get information on all namespaces.

$ kubectl get cronjobs --namespace=cronpywpchksumbot-dev
NAME                SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cronpywpchksumbot   */5 * * * *   False     0        <none>          83s

Step 3: Kubernetes security and management best practices

Some Kubernetes (k8s) security and management best practices include:

  • Regularly update container images.
  • Run containers as a regular user, not as the root user.
  • Run containers in a separate namespace, not in the default shared namespace.
  • Limit the number of container registries.
  • Regularly update Kubernetes.
  • Regularly update and manage the underlying Operating System (OS) that runs Kubernetes.


Next Module

Module 9: Kubernetes at the Wikimedia Foundation (WMF)

Previous Module