Better Security Audits with AppArmor and SecComp via DockerSlim

Combine the power of tools like SecComp, NGINX, and Docker.
Aug 01, 2022

Photo by Ilya Pavlov on Unsplash

One principle of cloud-native architecture includes the concept that every component should care about its own security, meaning that it should have some sort of perimeter and a posture that is secure by default. You can use AppArmor and SecComp to achieve this within containers and ensure that they have secure base runtimes. For more information about exactly what they can do, as well as instructions for manually adding them to containers, you can find excellent resources on SlimAI’s blog.

What Is DockerSlim?

DockerSlim is an open source utility that aims to simplify the developer’s experience when it comes to building small and secure containers. Its minification abilities can shrink the size of a container up to 30 times. In addition, it rolls AppArmor and SecComp functionality into a security profile which it then applies to the container that it’s building. As detailed in a previous tutorial, these security tools ensure that the container is only allowed to do what it is designed to do.

DockerSlim uses a set of commands to perform these functions, including:

  • build - analyzes, profiles, and optimizes your container image (including generating security profiles).
  • xray - performs static analysis and can reverse engineer the Dockerfile.
  • lint - analyzes the Dockerfile used to create the container.
  • profile - performs analysis on a container.

In addition, Slim.ai runs Slim SaaS, which provides a nice clean user interface for the DockerSlim functionality with additional integrations for third-party services (like GitHub) to make it that much easier to use. Slim SaaS even offers a free tier, so you can check it out with no strings attached.

Creating a Container with DockerSlim

Prerequisites: Getting DockerSlim and a Runtime

Before you can get started with DockerSlim, there are two rather important prerequisites. The first is that you must have DockerSlim’s binaries at your disposala container runtime on your machine, and the second is that you must have DockerSlim’s binaries at your disposal.

The most commonly used container runtime is containerd, which is available on its own or as part of an offering like Docker Community Edition. Docker CE is available to download (with instructions) at https://docs.docker.com/get-docker/.

Installation instructions for DockerSlim are available at https://dockersl.im/install.html. For the purposes of this tutorial, we will use the tar distribution.

root@ubuntu:\~# curl https://downloads.dockerslim.com/releases/1.37.6/dist_linux.tar.gz | tar
-xz
    % Total     % Received % Xferd Average Speed Time Time Time Current                                 Dload Upload Total Spent Left Speed
100 9332k  100  9332k   0       0 9.9M 0 --:--:-- --:--:-- --:--:-- 9.9M
root@ubuntu:\~# mv dist_linux/docker-slim* /usr/local/bin/
root@ubuntu:\~# ls -lF /usr/local/bin/
total 25744
-rwxr-xr-x 1 501 staff 19959808 Apr 23 06:05 docker-slim*
-rwxr-xr-x 1 501 staff 6402048 Apr 23 06:05 docker-slim-sensor*
root@ubuntu:\~# docker-slim --version
docker-slim version linux|Transformer|1.37.6|26a36c88a94c677efd734e874ba081dabb84a224|2022-04-23_06:03:56AM |

Step 1: Deciding Which Container to Use

Now that you’re up and running with all of the capabilities that you’ll need for DockerSlim, you need a container to work on. NGINX is an easy to use and popular container, so we will use it as an example.

Let’s retrieve the container and view its size:

root@ubuntu:\~# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
1fe172e4850f: Pull complete
35c195f487df: Pull complete
213b9b16f495: Pull complete
a8172d9e19b9: Pull complete
f5eee2cb2150: Pull complete
93e404ba8667: Pull complete
Digest: sha256:859ab6768a6f26a79bc42b231664111317d095a4f04e4b6fe79ce37b3d199097
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
root@ubuntu:\~# docker images nginx
REPOSITORY     TAG      IMAGE ID         CREATED    SIZE
nginx            latest fa5269854a5e     2 weeks ago 142MB
root@ubuntu:\~# docker-slim xray nginx | wc -l
236

According to xray, the container is 142MB in size and has 236 lines of configuration detail.

Step 2: (Re)Building the Container Using DockerSlim

This part is very straightforward. You simply need to run docker-slim build. Once your build finishes, we’ll look at the size again. (Notice that xray is much less complicated.)

# docker-slim build nginx
docker-slim: message='join the Gitter channel to ask questions or to share your feedback'
info='https://gitter.im/docker-slim/community'
docker-slim: message='join the Discord server to ask questions or to share your feedback'
info='https://discord.gg/9tDyxYS'docker-slim: message='Github discussions'
. . .
cmd=build state=completed
cmd=build info=results status='MINIFIED' by='11.68X' size.original='142 MB' size.optimized='12 MB'
cmd=build info=results image.name='nginx.slim' image.size='12 MB' has.data='true'
cmd=build info=results
artifacts.location='/tmp/docker-slim-state/.docker-slim-state/images/fa5269854a5e615e51a72b17ad3fd1e01268f278a6684c8ed3c5f0cdce3f230b/artifacts'
cmd=build info=results artifacts.report='creport.json'
cmd=build info=results artifacts.dockerfile.reversed='Dockerfile.fat'
cmd=build info=results artifacts.dockerfile.optimized='Dockerfile'
cmd=build info=results artifacts.seccomp='nginx-seccomp.json'
cmd=build info=results artifacts.apparmor='nginx-apparmor-profile'
cmd=build state=done
cmd=build info=commands message='use the xray command to learn more about the optimize image'
cmd=build info=report file='slim.report.json'
root@ubuntu:\~# docker images "nginx*"
REPOSITORY    TAG      IMAGE ID        CREATED      SIZE
nginx.slim    latest   f10116856a64    49 seconds ago 12.1MB
nginx           latest fa5269854a5e    2 weeks ago  142MB
root@ubuntu:\~# docker-slim xray nginx.slim | wc -l
78

As you can see, it created a new container called nginx.slim, with a 90% reduction in size and ⅓ the number of steps in the Dockerfile configuration.

There are also a few other details in the output that you’ll need to know. One is the artifacts.location, which is where you will find any files that are created, including the SecComp and AppArmor profiles. You should also note the names of the profiles and the reverse-engineered Dockerfile (original and minified).

root@ubuntu:\~# ls -lF
/tmp/docker-slim-state/.docker-slim-state/images/fa5269854a5e615e51a72b17ad3fd1e01268f278a6684c
8ed3c5f0cdce3f230b/artifacts/
total 12200
-rw-r--r-- 1 root root         493 May 11 04:00 Dockerfile
-rw-r--r-- 1 root root         3658 May 11 04:00 Dockerfile.fat
-rw-r--r-- 1 root root 64691 May 11 04:00 creport.json
-rw-r--r-- 1 root root 12408320 May 11 04:00 files.tar
-rw-r--r-- 1 root root         2587 May 11 04:00 nginx-apparmor-profile
-rw-r--r-- 1 root root         1935 May 11 04:00 nginx-seccomp.json

Note: You might want to use flags at this stage. For example, you can use --copy-meta-artifacts to store the profiles in a directory that will be easier to find when you run the build command.

Step 3: Viewing the Created Profiles

You can view the contents of the container, including the security profiles, in the artifacts directory.

root@ubuntu:artifacts# head -5 nginx-*
==> nginx-apparmor-profile <==

profile nginx-apparmor-profile flags=(attach_disconnected,mediate_deleted) {
    network,


==> nginx-seccomp.json <==
{
    "defaultAction": "SCMP_ACT_ERRNO",
    "architectures": \[
          "SCMP_ARCH_X86_64"

     ],

Save these profiles so that you can reap their full benefits in future deployments.

Step 4: Pushing the Container to a Common Registry

Next, you’ll want to push the container to a common registry so that others can pull it. There are only a couple of steps to follow.

The first is to identify the registry you’ll be posting to and log in. In this case, we’ll be using GitHub’s package repository.

The second step is to rename the image so the Docker command line knows where to send it.

The third step is the actual push.

root@ubuntu:\~# docker login ghcr.io
Username: vincepower
Password:
Login Succeeded
root@ubuntu:\~# docker tag nginx.slim:latest ghcr.io/vincepower/nginx-docker-slim:latest
root@ubuntu:\~# docker images
REPOSITORY                              TAG       IMAGE ID      CREATED           SIZE
ghcr.io/vincepower/nginx-docker-slim    latest    f10116856a64  About an hour ago 12.1MB
nginx.slim                              latest    f10116856a64  About an hour ago 12.1MB
nginx                                   latest    fa5269854a5e  2 weeks ago       142MB
root@ubuntu:\~# docker push ghcr.io/vincepower/nginx-docker-slim:latest
The push refers to repository \[ghcr.io/vincepower/nginx-docker-slim\]
93a4cee8172b: Mounted from vincepower/nginx-docker-slim/nginx.slim
latest: digest: sha256:6263560da032abe802e316a5f23d799ea46c259f5f8501300b8bff5020d87cc6 size:
528

And now the image is ready to be used by any cluster that can pull from that registry.

Deploying a DockerSlim Container into a Production Environment

Step 1: Creating a Deployment for Our Container

Since most production environments that run containers are using Kubernetes these days, we will use it for our example. For our purposes, we will assume that it’s a single node running on a Linux host. For more complex multi-node scenarios (like running Red Hat OpenShift or using a hosted version like GKE), you’ll need to apply additional configurations, such as adding or modifying daemon sets to write the profiles to the local disks on the cluster nodes. There are also other options that use Kubernetes-native objects to manage profiles, such as apparmor-manager.

In Kubernetes, the minimum deployment is a pod that contains a single container. For the purposes of this tutorial, we will include a service with a NODE_PORT so we can access the web server to make sure that it still works with the security rules in place.

Here is our k8s-deploy.yaml:

apiVersion: v1
kind: Namespace
metadata:
  name: slim-ns
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx-slim-app
  namespace: slim-ns
  labels:
        app.kubernetes.io/name: nginx-slim-app
spec:
   containers:
   - name: nginx-slim
         image: ghcr.io/vincepower/nginx-docker-slim:latest
         ports:
         - containerPort: 80
         name: http-web-svc
---
apiVersion: v1
kind: Service
metadata:
   name: nginx-slim-svc
   namespace: slim-ns
spec:
   type: NodePort
   selector:
        app.kubernetes.io/name: nginx-slim-app
   ports:
        - port: 80
        nodePort: 30003
        targetPort: 80

Next, we will apply this to the cluster:

root@ubuntu:\~/nginx-docker-slim# kubectl apply -f k8s-deploy.yaml
namespace/slim-ns created
pod/nginx-slim-app created
service/nginx-slim-svc created |

And is it running? Yes.

Step 2: Adding the Auto-Generated SecComp Profile to the Deployment

Now, we need to put the profile on the Kubernetes nodes (using the default directory.) Then, we’ll copy the SecComp profile into that directory so it can be referenced from within the cluster.

root@ubuntu:\~# mkdir -p /var/lib/kubelet/seccomp
root@ubuntu:\~# cp ./nginx-docker-slim/nginx-seccomp.json /var/lib/kubelet/seccomp/ |

Next, we’ll modify our pod deployment to include the reference to the new security profile, then reapply it. Our spec will look like this, with new additions are in bold:

---
apiVersion: v1
kind: Pod
metadata:
  name: nginx-slim-app
  namespace: slim-ns
  labels:
    app.kubernetes.io/name: nginx-slim-app
spec:
    securityContext:
    seccompProfile:
        type: Localhost
        localhostProfile: nginx-seccomp.json
    containers:
      - name: nginx-slim
        image: ghcr.io/vincepower/nginx-docker-slim:latest
        securityContext:
          allowPrivilegeEscalation: false
        ports:
          - containerPort: 80
            name: http-web-svc

Now, we’ll apply the new pod configuration. Since we’re adding security, we need to remove the existing pod and recreate it. Modifying more than just the container version and a couple other minor attributes of a running pod is forbidden.

root@ubuntu:\~/nginx-docker-slim# kubectl delete -f k8s-deploy.yaml
namespace "slim-ns" deleted
pod "nginx-slim-app" deleted
service "nginx-slim-svc" deleted
root@ubuntu:\~/nginx-docker-slim# kubectl apply -f k8s-deploy.yaml
namespace/slim-ns created
pod/nginx-slim-app created
service/nginx-slim-svc created

There you have it. We have successfully deployed a kernel into a production-grade Kubernetes cluster with kernel-level restrictions on the system calls that it can make.

You might want to confirm that the pod is running and responding to curl commands:

root@u:\~# kubectl get pods
NAME            READY   STATUS RESTARTS    AGE
nginx-slim-app    1/1  Running   0              60s
root@u:\~# curl -I http://localhost:30003/
HTTP/1.1 200 OK
Server: nginx/1.21.6
Date: Sat, 14 May 2022 22:25:59 GMT
Content-Type: text/htmlContent-Length: 615
Last-Modified: Tue, 25 Jan 2022 15:03:52 GMT
Connection: keep-alive
ETag: "61f01158-267"
Accept-Ranges: bytes

Step 3: Adding the Auto-Generated AppArmor Profile to the Deployment

AppArmor, which is used by the Debian/Ubuntu family of Linux distributions, is a little more complicated to use than SecComp. The work of adding it in a manner that it’s enforced by a pod is the same, but it takes more than just copying a file into place to make it available on every host where the container could run.

While most Linux distributions have this in place, it’s a good idea to ensure that it’s enabled on every node. To do so, you need to check the value of the AppArmor system parameter. The second command shown below also lists all of the profiles that are available on the node and notes if they are being enforced.

root@ubuntu:\~# cat /sys/module/apparmor/parameters/enabled
Y
root@ubuntu:\~# cat /sys/kernel/security/apparmor/profiles
cri-containerd.apparmor.d (enforce)
/snap/snapd/15534/usr/lib/snapd/snap-confine(enforce)
/snap/snapd/15534/usr/lib/snapd/snap-confine//mount-namespace-capture-helper (enforce)
docker-default (enforce)
snap.lxd.migrate (enforce)
snap.lxd.lxc-to-lxd (enforce)
snap.lxd.lxd (enforce)
snap.lxd.hook.remove (enforce)
snap.lxd.lxc (enforce)
snap.lxd.hook.install (enforce)
snap.lxd.hook.configure (enforce)
snap.lxd.daemon (enforce)
snap.lxd.buginfo (enforce)
snap.lxd.check-kernel (enforce)
snap.lxd.benchmark (enforce)snap.lxd.activate (enforce)
/snap/snapd/15177/usr/lib/snapd/snap-confine (enforce)
/snap/snapd/15177/usr/lib/snapd/snap-confine//mount-namespace-capture-helper (enforce)
snap-update-ns.lxd (enforce)
/{,usr/}sbin/dhclient (enforce)
/usr/lib/connman/scripts/dhclient-script (enforce)
/usr/lib/NetworkManager/nm-dhcp-helper (enforce)
/usr/lib/NetworkManager/nm-dhcp-client.action (enforce)
/usr/sbin/tcpdump (enforce)
/usr/lib/snapd/snap-confine (enforce)
/usr/lib/snapd/snap-confine//mount-namespace-capture-helper (enforce)
lsb_release (enforce)
man_groff (enforce)
man_filter (enforce)
/usr/bin/man (enforce)
nvidia_modprobe (enforce)
nvidia_modprobe//kmod (enforce)

After you’ve confirmed that it’s enabled (which is what the “Y” on the second line means), it’s time to distribute the AppArmor profile created by DockerSlim. We’ll use the apparmor_parser command on each host to add the profile to the cache.

root@ubuntu:\~# apparmor_parser nginx-apparmor-profile
root@ubuntu:\~# cat /sys/kernel/security/apparmor/profiles | grep -i nginx
nginx-apparmor-profile (enforce)

At this point, we’ve run into issues with a specific library on Ubuntu systems, so we need to install apparmor-utilities and run aa-complain on each host.

The final step is to add the AppArmor profile to the pod spec as an annotation in the metadata section, as follows (additions in bold):

metadata:
  name: nginx-slim-app
  namespace: slim-ns
  labels:
    app.kubernetes.io/name: nginx-slim-app
  annotations:
    container.apparmor.security.beta.kubernetes.io/nginx-slim:
localhost/nginx-apparmor-profile
spec:
  containers:
    - name: nginx-slim |

Now, it will use the auto-generated profiles created by DockerSlim so that you’re using a secure location when it’s deployed.

Conclusion

Applying SecComp and AppArmor profiles to containers provides a solid and secure base for applications running in a production environment. While this doesn’t give you a silver bullet against all potential security concerns, it will go a long way towards eliminating a wide array of potential attack vectors. DockerSlim’s ability to both reduce the footprint of a container (by eliminating unused components) and create custom profiles puts that secure base within reach of any developer who works in a Modern Application Development environment.

Related Articles

5 Most Commonly Asked DockerSlim Questions

We enlisted DockerSlim expert and Slim.AI Developer Experience Engineer to dive into how container slimming works.

Primož Ajdišek

Technical Staff

5 Ways Slim Containers Save You Money

Do slim containers really save you money on your cloud bill? Are there cost advantages to smaller containers? Find out here.

Chris Tozzi

Automating DockerSlim in Your CICD Pipeline

Using GitHub Actions, you can refine container images automatically making them smaller, faster to load, and more secure by default – all without sacrificing any capabilities.

Nicolas Bohorquez

Contributor

Building Apps Using Cloud Native Buildpacks

Getting started with this innovative technique

Vince Power

Contributor

Building DockerSlim into a Jenkins Pipeline

A step by step tutorial on building DockerSlim into your CI/CD pipeline.

Clarifying the Complex: Meet Ivan Velichko, Container Dude at Slim.AI

Ivan recently joined the team at Slim.AI, and we sat down with him to learn more about the path that led him here.

Ivan Velichko

Container Dude

Container Insights: Dissecting the World's Most Popular Containers

Join Ayse Kaya in this series, as she creates her 2022 Container Report Chalk Full of Important Security Findings for Developers.

Ayse Kaya

Analytics & Strategy

Container of the Week: Python & Flask

Our weekly breakdown of a popular container

What We Discovered Analyzing the Top 100 Public Container Images

Complexity abounds in modern development

Ayse Kaya

Analytics & Strategy

2022 Public Container Report

Vulnerabilities continue to increase and developers are struggling to keep up.

Ayse Kaya

Analytics & Strategy

Containerizing Python Apps for Lambda

A tutorial on deploying AWS Lambda using containers, Python edition.

Docker Containers for Your Raspberry Pi

Compact PCs need compact apps

Martin Wimpress

Community

Explore and analyze a Docker container with DockerSlim X-Ray

Understanding container composition

Martin Wimpress

Community

Five Proven Ways to Debug a Container

When Things Just Are Not Working

Theofanis Despoudis

Contributor

Five Things You Should Never Ship to Production in a Container

Here is our take on five things to avoid when creating a container or shipping it to production.

Chris Tozzi

Increasing Your CI/CD Velocity with Slim Containers

We’ll explain what Slim Containers are, how they speed up the build process, and how they can improve the efficiency of your testing.

Mike Mackrory

Contributor

Integrate Testing into Your Container Pipeline

A closer look at testing within container pipelines, CI/CD, software delivery, and containerization.

Faith Kilonzi

Software Engineer

Reducing Docker Image Size - Slimming vs Compressing

Know the difference

Pieter van Noordennen

Growth

Serverless Applications and Docker

How to Scale the Latest Trend in Infrastructure

Pieter van Noordennen

Growth

Slim.AI Docker Extension for Docker Desktop

How to access our Docker Extension and try it for yourself.

Josh Viney

Product

Slimming a Rails Application with DockerSlim

Dissect a simple Rails application container using DockerSlim to analyze, optimize, and deploy your product more quickly.

Theofanis Despoudis

Contributor

Where Do You Store Your Container Images?

Container Registry Options are Growing in Number and Complexity

Pieter van Noordennen

Growth

Using AppArmor and SecComp Profiles for Security Audits

Conduct better container security audits using tools like SecComp, NGINX, and Docker.

What’s in your container?

Why Docker Layers matter for container optimization

Pieter van Noordennen

Growth

Why Developers Shouldn't Have to Be Infrastructure Experts, Too

Simplifying processes required to containerize and deploy cloud-native apps.

Chris Tozzi

A New Workflow for Cloud Development

Leverage the benefits of containerization without the headaches & hassle

John Amaral

CEO

Why Don’t We Practice Container Best Practices?

Container best practices are easy to understand, hard to do

John Amaral

CEO

5 Common Container Exploits

From Malware, to Access Control Risks, and Beyond

Chris Tozzi

Contributor

The 4th S of Software Supply Chain Security

An approach to Front Line Software Supply Chain Security (SSCS).

John Amaral

CEO