Understanding a Containers Attack Surface

We will go over the following:

  • Analyze the application
  • Base image
  • Vulnerabilities 
  • Package dependencies

Analyze the application

Before getting started with a deployment in Kubernetes, we first must understand the requirements or dependencies necessary for your application to function correctly. After that, we will structure the Kubernetes objects around the application to have only the functionality it needs to do its job. This is also known as the principle of least privilege and is a beneficial tactic to harden your containers.

Let’s take a look at the simpleservice application.

Input

cd ~/simpleservice/
cat Dockerfile

Output

FROM python:2.7-onbuild
MAINTAINER Michael Hausenblas
ENV REFRESHED_AT 2017-04-24T13:50
CMD [ "python", "./simpleservice.py" ]

Now the Dockerfile doesn’t tell us much about the application. This is probably because the variables have been abstracted away. When we deploy the service, the application will probably want a port to listen on.

Let’s check the application code to learn more about it.

Input

cat simpleservice.py

Output

#!/usr/bin/env python
...

Now it is a reasonably large output, but the core components are displayed at the top of the python application.

##############################################################################
# The following enviroment variables can be overridden
# to simulate different behaviour of the simple service:

# By default simple service serves on 9876 but you can
# make it listen on a different port by setting the env
# variable `PORT0` (also: it listens on all network interfaces,
# i.e. 0.0.0.0).
PORT = os.getenv('PORT0', 9876)

# By default simple service reports this value in
# the /endpoint0 unless overridden by below env variable.
VERSION = os.getenv('SIMPLE_SERVICE_VERSION', "0.5.0")

# By default the `/health` endpoint returns a HTTP code 200
# immediately but you can define the range with the following
# env variables.
#
# Examples:
# HEALTH_MIN=1000 HEALTH_MAX=2000 ... delays between 1 sec and 3 sec
# HEALTH_MAX=500                  ... delays up to 0.5 sec
HEALTH_MIN = os.getenv('HEALTH_MIN', 0) # in milliseconds
HEALTH_MAX = os.getenv('HEALTH_MAX', 0) # in milliseconds

These are the variables that can be altered.

PORT
VERSION
HEALTH_MIN
HEALTH_MAX

With that in mind, let's make sure our container is as up-to-date and secure as possible.


Base image

This simple service is very old, which means many vulnerabilities may be associated with the base image. In the fast-paced container-centric world, we should aim to keep our base images as fresh and up to date as possible. The speed of our ability to upgrade also protects us from lingering issues that hackers will learn to exploit over time.

This Dockerfile uses the following image as its base image.

FROM python:2.7-onbuild

and if we build utilizing this base image, we get the following:

Input

podman build . --tag=simple-python-2.7
podman images

Output

REPOSITORY                TAG          IMAGE ID      CREATED         SIZE
localhost/simple-python   2.7          481db3a2b9f4  33 seconds ago  711 MB
docker.io/library/python  2.7-onbuild  3f246dd60a17  3 years ago     706 MB

Let's test out our service!

Input

podman run -P simple-python-2.7

Output

... INFO This is simple service in version v0.5.0 listening on port 9876 [at line 142]

Okay, we pulled our base image and built a simple containerized service. But the python:2.7-onbuild service was last updated three years ago. Let's see if we can find something more relevant.


Vulnerabilities

Having vulnerabilities inside your container image is typical and small vulnerabilities are hard to avoid. What we do want to avoid being critical and exploitable vulnerabilities. This can be done by scanning the container image and using verified scanned images for Red Hat or other providers.

To learn more about vulnerabilities, check out the OWASP foundations detailed breakdown of vulnerabilities

We can use the python:3.9 image to build our service. It has been scanned for vulnerabilities, and we get only have to make a few small code changes. You can view the package at DockerHub

First, we'll need to change the Dockerfile to be more explicit with what is happening in the container. At the same time, we do want to abstract most commands away to Kubernetes. The core components needed to run locally should still be present.

Input

cat <<EOF > Dockerfile
FROM python:3.9
MAINTAINER Michael Hausenblas
ENV REFRESHED_AT 2017-04-24T13:50
WORKDIR /app
RUN pip3 install tornado
COPY simpleservice.py simpleservice.py
CMD [ "python", "simpleservice.py" ]
EOF

Package dependencies

There are a few packages necessary for this application to run. Not only to we need to be aware to the default packages in the python3.9 image, but we should ensure that our package dependencies are not vulnerable as well.

For example, the tornado package that we will have to install. we can check the dependencies associaated with it online with a few tools from Stack Overflow or google insights.

[Take a look through the website(https://deps.dev/pypi/tornado/6.1/dependencies)] and find some packages you use regularly.

Next, we have to change four lines of code to bring them up to Python 3 compatibility. We are going to replace except Exception, e: with except getopt.GetoptError as e:

sed -i 's/    except Exception, e:/    except getopt.GetoptError as e:/' simpleservice.py

And let's repeat this process to ensure our application still runs!

podman build . -t python-3.9
podman images

Output

REPOSITORY                TAG         IMAGE ID      CREATED         SIZE
localhost/python-3.9      latest      82ae542a657d  1 second ago    947 MB
docker.io/library/python  3.9         1372f931a98b  5 days ago      936 MB

Let's test out our service :)

Input

podman run -P python-3.9

Output

... INFO This is simple service in version v0.5.0 listening on port 9876 [at line 142]

Awesome!

This simple act updated our base image over years of lacking CVE data into a new stable version of python. This option may not always be available to you, depending on your applications. But it, the core concept of verifying your base image and keeping up to date on vulnerabilities is certainly an achievable task.

Beyond KBE

KBE Community Forum Have more questions? Join our KBE community forum sponsored by Red Hat Learning and get your questions answered real time or start a discussion with hundreds of learning community members. Engage with the KBE community and meet fellow KBE members, contributors, and subject matter experts. Join now!