This project demonstrates how to containerize a FastAPI application using Docker, then progressively harden the container image and secure the CI/CD pipeline using real-world DevSecOps practices.
By combining multi-stage Docker builds, non-root execution, runtime hardening, and automated vulnerability scanning, this project simulates a production-grade container workflow with security built in by default.
- Build a minimal and secure Docker image for a Python FastAPI web app
- Apply container runtime hardening techniques
- Enforce DevSecOps practices through automated CI pipeline scanning
- Catch vulnerabilities early using Trivy
- Run containers using least privilege principles
| Tool | Purpose |
|---|---|
| FastAPI | Python-based API framework |
| Docker | Containerization |
| Trivy | Vulnerability scanner |
| GitHub Actions | CI/CD pipeline |
| Uvicorn | ASGI web server for FastAPI |
- Multi-stage Docker builds
- Non-root container user (
appuser) - Hardened container runtime:
- Read-only filesystem
- All Linux capabilities dropped (
--cap-drop=ALL) - No privilege escalation (
--security-opt no-new-privileges:true) - Automated Trivy scan via GitHub Actions
- Fails pipeline if CRITICAL or HIGH CVEs are found
.trivyignoreto manage accepted or non-exploitable CVEs
| Area | Status |
|---|---|
| Multi-stage build | ✅ |
| Non-root user | ✅ (USER appuser) |
| Read-only filesystem | ✅ (--read-only) |
| Dropped Linux capabilities | ✅ (--cap-drop=ALL) |
| No privilege escalation | ✅ (--security-opt no-new-privileges:true) |
| CI vulnerability scan | ✅ (Trivy via GitHub Actions) |
| Clean dependency install | ✅ (--no-cache-dir) |
.
├── .github/
│ └── workflows/
│ └── trivy-scan.yml # GitHub Actions CI pipeline
├── .gitignore # Ignored by git
├── .trivyignore # Ignored vulnerability list
├── Dockerfile # Multi-stage hardened image
├── main.py # FastAPI app
├── requirements.txt # Python dependencies
├── README.md # Project overview & instructions- Build the Docker image
docker build -t hardening-project .- Run the container with hardened flags
docker run \
--read-only \
--cap-drop=ALL \
--security-opt no-new-privileges:true \
-p 8000:80 \
hardening-project- Test the app
Open
http://localhost:8000/healthor run:
curl http://localhost:8000/healthExpected response:
{"status": "ok"}Scan the image for vulnerabilities:
trivy image --exit-code 1 --severity CRITICAL,HIGH hardening-project
Add --ignore-unfixed- Use
--ignore-unfixedto skip CVEs with no patch available.
The trivy-scan.yml workflow performs the following on every push or pull_request:
- Builds the Docker image
- Installs the latest Trivy
- Scans the image
- Fails the pipeline if CRITICAL or HIGH vulnerabilities are found
uses: aquasecurity/[email protected]
with:
image-ref: 'hardening-project'
format: 'table'
exit-code: '1'
ignore-unfixed: true
severity: 'CRITICAL,HIGH'Some vulnerabilities (e.g., in uvicorn) may:
- Only affect development mode (which is disabled)
- Be unexploitable in your container due to runtime restrictions
- Have no available fix from maintainers yet
In these cases, they're documented and ignored via .trivyignore but are not exploitable due to read-only FS and dropped capabilities
Pull requests are welcome! If you find improvements, feel free to open an issue or fork and submit a PR.