Using Distroless Images to Minimize Container Attack Surfaces

AI Preface: My opinions on the subject. This is an AI experiment. AI was used for the experiment, however, everything in the post is human written or script/tool generated results, excluding the three different option titles.

Security comes in layers. There are tons of factors when it comes to securing anything involving technology. As an example, the most secure password policies are fairly useless if your passwords have been leaked. Or, you could take a fortress as another example. Are you prepared for an apocalypse if you have barbed wire, guard towers, a perimeter fence, etc, etc etc, but, you just happen to leave the backdoor unlocked in case you need to run. There will also be a balance struggle between risk versus effort to mitigate. Which leads me to this post.

Securing or cleaning up images, especially base images, can minimize the attack surface simply by removing unnecessary packages. Beyond that, you can create an ecosystem where you are running only what is necessary. As companies began their cloud formation journeys legacy debt was created by simply "lifting and shifting", IE. This app was running on an Ubuntu 18 VM, put it on an Ubuntu 18 image. Let's walk through what it looks like to take a fairly standard base image and the effort to secure it.

The Image

The Dockerfile here is fairly simple. It formerly was the Dockerfile that this site was built on. Emphasizing my original point earlier on security in layers, I have not been overly concerned about the possibility of it being exploited. It is an http site with no access to dynamic data. The container itself isn't privileged and requests have to traverse through multiple layers to get to the container. A great lab rat.

# Stage 1: Build stage
FROM python:3.9 AS builder

# Copy your Python script to the working directory
COPY / /

# Install any necessary dependencies
RUN pip install -r requirements.txt

# Run the Python script to generate files
RUN python build.py

FROM httpd:latest

COPY --from=builder /_site /usr/local/apache2/htdocs/

RUN sed -i '/LoadModule rewrite_module/s/^#//g' /usr/local/apache2/conf/httpd.conf && \
  sed -i 's#AllowOverride [Nn]one#AllowOverride All#' /usr/local/apache2/conf/httpd.conf

Using AI

This effort took two prompts to Gemini 4 Argon via Open Router at a cost of 2.7 and 2.1 cents respectively. It took zero modifications beyond that to meet the requirements of a web server with http rewrite. I call this out because, I would never suggest you blindly accept AI recommendations. I also intentionally used a flagship model with a technical focus opposed to a general focus model. You know, similar to what an enterprise would have access to, hint hint.

The first prompt was:

what is the most secure way to make an http server, distroless docker image. I want to replace an apache base image, such as this:

insert Dockerfile

The output produced three different options plus some additional context around other fixes and hardening at runtime:

  1. Option 1: Static Go Server in distroless/static:nonroot (Most Secure)
  2. Option 2: Chainguard Distroless Nginx (Best Enterprise / CVE-Zero Option)
  3. Option 3: Ultra-Minimal Rust Server (scratch image)

Since my goal was to use a distroless image I followed up with this:

write the complete option 1 to include the URL rewriting. I want an entire solution.

The output generated a .dockerignore, Caddyfile and Dockerfile. Since the steps I gave it implied it was running a python script to generate a site - which it is - the steps took into account the static site generation script. The Dockerfile produced was a multi-stage build containing three stages, the static site generation, fetching the binary from the caddy image (which I found interesting) and lastly the distroless image with the site and configuration files. The Caddyfile contained security focused settings, added headers, configured URL rewriting and even added custom error handling.

Size Assessment

When it comes to security, I would say in most cases less is more (obviously a generalization). A by product, that I wanted to call out of minimizing the image is the footprint it leaves from a space utilization perspective. I wrote about image (pictures) optimization related to this site but never got into the bloat associated from an operational stand point.

The original size of the http image with the site and all of its contents was 60MB.

The distroless, Caddy-based image's final size is 34MB.

Spoiler Alert: Prior to the image (pictures) optimization post the image (like Docker image) was 123MB. The image is 28% of the size we started at.

Security Assessment

I kept that security assessment very basic. I simply ran Trivvy in the pipeline during the builds. We will compare and highlight the findings. We will not be investigating the exploitability of any of these findings, again, we are just looking to mitigate exposure and risk.

Original Apache Image

Total: 155 (MEDIUM: 97, HIGH: 57, CRITICAL: 1)

┌─────────────────────┬─────────────────┬──────────┬──────────────┬──────────────────────────────────────┬───────────────┬──────────────────────────────────────────────────────────────┐
│       Library       │  Vulnerability  │ Severity │    Status    │          Installed Version           │ Fixed Version │                            Title                             │
├─────────────────────┼─────────────────┼──────────┼──────────────┼──────────────────────────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ bsdutils            │ CVE-2026-76642  │ HIGH     │ affected     │ 1:2.41.5-0+deb13u1                   │               │ util-linux: util-linux: failed external mount helper still   │
│                     │                 │          │              │                                      │               │ runs privileged X-mount post-hooks                           │
│                     │                 │          │              │                                      │               │ https://avd.aquasec.com/nvd/cve-2026-76642                   │

...
... fast forward ...
...

│                     ├─────────────────┤          │              │                                      ├───────────────┼──────────────────────────────────────────────────────────────┤
│                     │ CVE-2026-85091  │          │              │                                      │               │ zlib versions 1.3.1.2 through 1.3.2 contain a heap buffer    │
│                     │                 │          │              │                                      │               │ overflow vul ......                                          │
│                     │                 │          │              │                                      │               │ https://avd.aquasec.com/nvd/cve-2026-85091                   │
└─────────────────────┴─────────────────┴──────────┴──────────────┴──────────────────────────────────────┴───────────────┴──────────────────────────────────────────────────────────────┘

The critical is in libxml2, CVE-2026-6653 is rated an 9.8 by NVD, however, Ubuntu and RedHat both rate it lower. Though, we are still looking at 155 findings to take into consideration.

New Caddy/distroless Image

Total: 4 (MEDIUM: 2, HIGH: 2, CRITICAL: 0)

┌───────────────────────────────┬─────────────────┬──────────┬──────────┬───────────────────┬───────────────┬────────────────────────────────────────────────────────────┐
│            Library            │  Vulnerability  │ Severity │  Status  │ Installed Version │ Fixed Version │                           Title                            │
├───────────────────────────────┼─────────────────┼──────────┼──────────┼───────────────────┼───────────────┼────────────────────────────────────────────────────────────┤
│ github.com/go-jose/go-jose/v3 │ CVE-2026-116587 │ HIGH     │ affected │ v3.0.5            │               │ github.com/go-jose/go-jose: go-jose: Denial of Service via │
│                               │                 │          │          │                   │               │ crafted RSA parameters in JWKs                             │
│                               │                 │          │          │                   │               │ https://avd.aquasec.com/nvd/cve-2026-116587                │
│                               ├─────────────────┼──────────┤          │                   ├───────────────┼────────────────────────────────────────────────────────────┤
│                               │ CVE-2026-116585 │ MEDIUM   │          │                   │               │ github.com/go-jose/go-jose: go-jose: Denial of Service via │
│                               │                 │          │          │                   │               │ empty ciphertext buffer during JWE decryption...           │
│                               │                 │          │          │                   │               │ https://avd.aquasec.com/nvd/cve-2026-116585                │
│                               ├─────────────────┤          │          │                   ├───────────────┼────────────────────────────────────────────────────────────┤
│                               │ CVE-2026-116586 │          │          │                   │               │ github.com/go-jose/go-jose: go-jose: Denial of Service via │
│                               │                 │          │          │                   │               │ deeply nested JSON parsing                                 │
│                               │                 │          │          │                   │               │ https://avd.aquasec.com/nvd/cve-2026-116586                │
├───────────────────────────────┼─────────────────┼──────────┼──────────┼───────────────────┼───────────────┼────────────────────────────────────────────────────────────┤
│ github.com/go-jose/go-jose/v4 │ CVE-2026-116587 │ HIGH     │ fixed    │ v4.1.5            │ 4.1.6         │ github.com/go-jose/go-jose: go-jose: Denial of Service via │
│                               │                 │          │          │                   │               │ crafted RSA parameters in JWKs                             │
│                               │                 │          │          │                   │               │ https://avd.aquasec.com/nvd/cve-2026-116587                │
└───────────────────────────────┴─────────────────┴──────────┴──────────┴───────────────────┴───────────────┴────────────────────────────────────────────────────────────┘

Yep, that is everything. This also includes the results of the caddy go binary; of which, one had a fix at time of scanning. By building out the image different, we have reduced the vulnerability exposure by 97.4%. This puts us in a way more manageable state.

Additional Improvements to Take

Overall Impression

For five cents of compute, it is pretty impressive. These are the types of activities I feel like AI can be effectively used. The image is in a much better spot for minimal amount of effort, time and cost. These are the type of activities that excite me for the future of self hosted AI. Not to get too deep into my AI philosophies (sorry, will continue to reference it), but, AI is here to stay for both the good and bad. Just like anything, it should be used responsibly. Since enterprises are hopping on the AI train, I do feel this would be an effective way to mitigate risk at a low cost. Don't worry though, I am sure a human's error will still leave the backdoor cracked open for a bad actor. That's life. Thanks for reading!