# Building a Secure Docker Image

# 🛡️ Building a Secure Docker Image – Step-by-Step Lab

When it comes to container security, your Docker image is the foundation. The smaller and more focused the image, the smaller the attack surface. In this hands-on lab, we’ll build a secure Docker image from a Django application using **multi-stage builds** and **Distroless** base images.

---

## 🔧 Step 1: Clone the Source Code

Let’s start by cloning the sample Django project that we'll use for this exercise.

```bash
git clone https://gitlab.practical-devsecops.training/pdso/django.nv.git
cd django.nv
```

You should now be inside the `django.nv` project folder containing all the source files.

---

## 🐳 Step 2: Build a Basic Docker Image (Alpine-based)

We’ll first build a simple image using `python:3-alpine3.15`, a lightweight base image.

```bash
docker build -t django.nv:1.0 .
```

After the build completes, check your images:

```bash
docker images
```

### 📦 Expected Output

```bash
REPOSITORY   TAG   IMAGE ID       CREATED         SIZE
django.nv    1.0   071343727408   Few seconds ago 106MB
```

✅ Small, neat image! But can we do better? Let’s take a look at the Dockerfile.

```bash
cat Dockerfile
```

### 📋 Dockerfile (Alpine version)

```dockerfile
FROM python:3-alpine3.15
WORKDIR /app
COPY . .
RUN apk add --no-cache gawk sed bash grep bc coreutils \
    && pip install -r requirements.txt \
    && chmod +x run.sh
EXPOSE 8000
CMD ["/app/run.sh"]
```

Good for starters. But not secure enough for production. Let’s level up.

---

## 🔐 Step 3: Enhance Security with Distroless and Multi-Stage Build

**Distroless** images by Google contain only your application and its runtime dependencies—no package manager, shell, or even `ls`. This dramatically reduces attack surface.

### 🏗️ Multi-Stage Dockerfile (Distroless)

Let’s rewrite our Dockerfile using a build image and a final distroless image.

```bash
cat >Dockerfile<<EOF
FROM debian:11-slim AS build

COPY requirements.txt .
RUN apt-get update \
    && apt-get install --no-install-suggests --no-install-recommends --yes python3-venv gcc libpython3-dev \
    && python3 -m venv /venv \
    && /venv/bin/pip install --upgrade pip

FROM build AS build-venv
COPY requirements.txt /requirements.txt
RUN /venv/bin/pip install -r /requirements.txt

FROM gcr.io/distroless/python3-debian11

WORKDIR /app
COPY --from=build-venv /venv /venv
COPY . .
RUN /venv/bin/python3 manage.py migrate \
    && /venv/bin/python3 manage.py loaddata fixtures/*

EXPOSE 8000
ENTRYPOINT ["/venv/bin/python3", "manage.py", "runserver", "0.0.0.0:8000"]
EOF
```

Now build the new image:

```bash
docker build -t django.nv:1.1 .
```

Check the image list again:

```bash
docker images
```

### 📦 Expected Output

```bash
REPOSITORY                           TAG     IMAGE ID       CREATED         SIZE
django.nv                            1.1     dedad500d9de   Just now        104MB
gcr.io/distroless/python3-debian11   latest  aa70b0553cf1   N/A             54.5MB
```

👍 Almost same size, but significantly more secure!

---

## ▶️ Step 4: Run the Secure Container

Now we’ll run the new container:

```bash
docker run -d --name django -p 8000:8000 django.nv:1.1
docker ps
```

Visit: [http://localhost:8000](http://localhost:8000/) to check if the app is running.

---

## 🔍 Step 5: Test for Restricted Access

Let’s see what happens when we try to get shell access into the container.

```bash
docker exec -it django bash
```

🚫 You’ll get an error like:

```bash
exec: "bash": executable file not found in $PATH
```

Try with `sh`:

```bash
docker exec -it django sh
```

✅ If you’re lucky, you might get shell access. Try running a few commands:

```bash
hostname
ls -l
```

🚫 Most commands will fail — no shell utilities available!

This is **by design**. It prevents attackers from using familiar tools to exploit the container.

---

## 💡 Final Thoughts

Distroless images:

* ✅ Minimize vulnerabilities
    
* ✅ Reduce attack surface
    
* ✅ Are ideal for production
    

But:

* ❌ They are hard to debug
    
* ⚠️ You might need additional tooling like sidecars or enhanced logging for production observability
    

---

## 📚 Additional Resources

* [Dockerfile Best Practices](https://www.docker.com/blog/intro-guide-to-dockerfile-best-practices)
    
* [Securing Containers with Cosign + Distroless](https://www.cncf.io/blog/2021/09/14/how-to-secure-containers-with-cosign-and-distroless-images)
    
* [Distroless GitHub Repo](https://github.com/GoogleContainerTools/distroless)
    

---
