Problem
The current Dockerfile uses mcr.microsoft.com/dotnet/sdk:10.0 (builder) and mcr.microsoft.com/dotnet/aspnet:10.0 (runtime), both Debian-based, which carry known CVEs and a larger attack surface than necessary for production deployments.
Pain points with the current approach:
- CVEs in base images requiring constant monitoring and patching
- No built-in SBOM (Software Bill of Materials) or provenance attestations
- No formal security compliance certifications (SLSA, CIS benchmarks)
- Larger attack surface from unnecessary packages in the Debian base
- Manual effort required to stay current with security updates
Proposed Solution
Migrate the Dockerfile to use Docker Hardened Images (DHI) — Docker's official minimal, security-hardened base images — for both stages.
DHI provides:
- Up to 95% reduction in attack surface vs. standard images
- Near-zero CVEs, actively maintained by Docker's security team
- Built-in SBOM, provenance attestations, SLSA Build Level 3
- Free and open source (Apache 2.0)
Expected behavior after implementation:
- Application continues to function identically
- Dramatically reduced vulnerability count in base images
- Full supply chain transparency with built-in attestations
Suggested Approach
1. Update base images
Builder stage:
# Replace:
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS builder
# With:
FROM dhi.io/dotnet/sdk:10.0-debian13-dev AS builder
Runtime stage:
# Replace:
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
# With:
FROM dhi.io/dotnet/aspnet:10.0-debian13 AS runtime
2. Adapt health check
DHI runtime images are minimal and do not include curl by default. The current apt-get install curl layer should be removed or replaced.
Option A (recommended): Replace curl with a .NET-based HTTP call in healthcheck.sh:
#!/bin/sh
dotnet-httpclient http://localhost:9000/health || exit 1
Or use wget if available in the DHI variant.
Option B: Install curl explicitly in the runtime stage (verify availability in DHI Debian variant).
3. Key files to modify
Dockerfile — update base images, replace apt-get curl install
scripts/healthcheck.sh — adapt health check if removing curl
.github/workflows/dotnet.yml — update CI/CD if dhi.io authentication is needed
4. Architecture considerations
- Multi-stage build preserved
- Non-root
aspnetcore user maintained
/storage volume mount unchanged
- No application code changes required
Note: relationship to other Docker optimization issues
This issue focuses on security hardening. A separate issue covers switching the runtime to aspnet:10.0-alpine for a ~100–150 MB size reduction. Both can be implemented together or sequentially.
Acceptance Criteria
References
Problem
The current Dockerfile uses
mcr.microsoft.com/dotnet/sdk:10.0(builder) andmcr.microsoft.com/dotnet/aspnet:10.0(runtime), both Debian-based, which carry known CVEs and a larger attack surface than necessary for production deployments.Pain points with the current approach:
Proposed Solution
Migrate the Dockerfile to use Docker Hardened Images (DHI) — Docker's official minimal, security-hardened base images — for both stages.
DHI provides:
Expected behavior after implementation:
Suggested Approach
1. Update base images
Builder stage:
Runtime stage:
2. Adapt health check
DHI runtime images are minimal and do not include
curlby default. The currentapt-get install curllayer should be removed or replaced.Option A (recommended): Replace
curlwith a .NET-based HTTP call inhealthcheck.sh:Or use
wgetif available in the DHI variant.Option B: Install curl explicitly in the runtime stage (verify availability in DHI Debian variant).
3. Key files to modify
Dockerfile— update base images, replaceapt-getcurl installscripts/healthcheck.sh— adapt health check if removing curl.github/workflows/dotnet.yml— update CI/CD ifdhi.ioauthentication is needed4. Architecture considerations
aspnetcoreuser maintained/storagevolume mount unchangedNote: relationship to other Docker optimization issues
This issue focuses on security hardening. A separate issue covers switching the runtime to
aspnet:10.0-alpinefor a ~100–150 MB size reduction. Both can be implemented together or sequentially.Acceptance Criteria
/storage) works correctlyaspnetcore) has correct permissionsdocker buildx imagetools inspect --format "{{json .SBOM}}")References