Containers have become a standard deployment mechanism for modern .NET applications.
A typical application may move through several stages before reaching production:
Source Code
|
v
Build
|
v
Container Image
|
v
Container Registry
|
v
CI/CD Pipeline
|
v
Production
At every stage, there is an important question:
How can you prove that the container running in production is the same trusted artifact that was built by the expected publisher?
A container image digest can prove that a particular artifact has not changed since the digest was calculated. A digital signature adds another important property: it can associate that artifact with a trusted signing identity.
.NET 11 Preview 3 introduced signed official .NET container images. Microsoft announced that .NET container images are now signed, providing an additional supply-chain security signal for consumers of the official images.
This does not eliminate the need for dependency scanning, SBOMs, provenance, access controls, or runtime security. Instead, signing provides another layer in a broader software supply-chain security model.
What Does a Signed Container Image Mean?
A container image consists of metadata and filesystem layers identified by cryptographic digests.
A simplified representation is:
Container Image
|
+-- Manifest
|
+-- Configuration
|
+-- Layer 1
|
+-- Layer 2
|
+-- Layer 3
A signature binds the publisher's identity to the artifact descriptor, including its digest.
Conceptually:
Image Digest
|
v
Cryptographic Signature
|
v
Trusted Publisher Identity
When a consumer verifies the image, the verification process can establish:
The signature is cryptographically valid.
The signature corresponds to the artifact being consumed.
The signing identity is trusted according to the configured policy.
The artifact has not been modified since it was signed.
Microsoft's OCI artifact guidance describes signing as a way to establish both integrity and authenticity.
Why Container Signing Matters
Consider a normal container deployment:
Registry
|
v
Pull Image
|
v
Run Container
The registry may contain multiple tags:
myapp:latest
myapp:1.2
myapp:production
Tags are convenient, but they are mutable references.
A digest is more precise:
myapp@sha256:<digest>
Signing adds publisher identity to that immutable artifact reference.
The resulting trust model becomes:
Expected Publisher
|
v
Signed Artifact
|
v
Verified Digest
|
v
Deployment
This is significantly stronger than simply trusting a tag.
.NET 11 Official Container Images
Microsoft announced signed .NET container images as part of .NET 11 Preview 3.
Official .NET container images are available in different variants for runtime, ASP.NET Core, SDK, operating system, architecture, and other characteristics. Microsoft documents a tagging scheme based on the target framework, operating system, architecture, image type, and image variant.
A typical .NET application might use:
FROM mcr.microsoft.com/dotnet/aspnet:11.0 AS base
WORKDIR /app
EXPOSE 8080
and then:
FROM mcr.microsoft.com/dotnet/sdk:11.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet publish \
-c Release \
-o /app/publish \
--no-restore
The final stage can copy the published application into the runtime image:
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApplication.dll"]
The signing of Microsoft's official image is useful when that image is used as a trusted base layer, but your resulting application image still needs its own supply-chain controls.
Signing the Base Image Is Not Enough
This distinction is important.
Suppose your Dockerfile contains:
FROM mcr.microsoft.com/dotnet/aspnet:11.0
The official base image may be signed.
Your application image is then built on top of it:
Microsoft Signed Base Image
|
v
Your Application Files
|
v
Your Container Image
The resulting application image is a different artifact.
Therefore, organizations should consider signing their own final images as part of their CI/CD pipeline.
The trust chain should look more like:
Trusted Base Image
|
v
Controlled Build
|
v
Application Image
|
v
Organization Signature
|
v
Registry
|
v
Deployment Verification
OCI Signatures and Notation
The Notary Project provides tooling for signing and verifying OCI artifacts.
Notation is the command-line tool used to perform these operations.
Microsoft's current Azure Container Registry documentation describes Notation as a tool for signing and verifying OCI artifacts, including container images.
The workflow is conceptually:
Build Image
|
v
Push Image
|
v
Sign Image
|
v
Push Signature
|
v
Verify Before Deployment
This separates image creation from artifact trust.
Sign an Image With Notation
Suppose an image has already been pushed to a registry:
myregistry.example.com/orders-api:1.0
Notation can sign the image:
notation sign \
myregistry.example.com/orders-api:1.0
The exact command depends on the configured signing key provider.
In a real organization, the signing key should not simply be stored as a plaintext file on a developer workstation.
Microsoft documents integrations with signing services such as Azure Key Vault and Azure Artifact Signing.
Why Key Management Matters
The security of a signed image depends heavily on the protection of the signing identity.
Consider:
Private Signing Key
|
v
Image Signature
|
v
Trusted Deployment
If an attacker obtains the signing credential, they may be able to produce artifacts that appear to originate from a trusted publisher.
Therefore:
Protect private signing keys.
Avoid storing long-lived keys in source repositories.
Restrict who can sign.
Audit signing operations.
Rotate credentials according to organizational policy.
Separate development and production signing identities.
Azure Artifact Signing provides an alternative to managing certificate lifecycle directly through Azure Key Vault and uses short-lived certificates with a managed signing experience.
Verify a Signed Image
Signing is useful only if consumers actually verify the signature.
Notation supports verification:
notation verify \
myregistry.example.com/orders-api@sha256:<digest>
Microsoft's documentation demonstrates verification using a trust policy and trust store.
The verification process can be represented as:
Image
|
v
Find Signature
|
v
Validate Signature
|
v
Validate Certificate / Identity
|
v
Evaluate Trust Policy
|
+---- Trusted ----> Continue
|
+---- Untrusted --> Block
This is where signing becomes an enforceable security control rather than simply metadata attached to an image.
Trust Policies Are Critical
A signature can be mathematically valid while still coming from an identity your organization does not trust.
For example:
Image
|
+-- Valid signature
|
+-- Signer = UnknownDeveloper
Cryptographically valid does not automatically mean authorized.
A trust policy defines which identities are trusted for specific registry scopes.
A simplified policy concept looks like:
{
"version": "1.0",
"trustPolicies": [
{
"name": "production-images",
"registryScopes": [
"myregistry.example.com/orders"
],
"signatureVerification": {
"level": "strict"
},
"trustedIdentities": [
"expected-publisher"
]
}
]
}
The exact trust-store and identity configuration depends on the signing system.
Microsoft's Notation examples use trust policies to associate registry scopes with trust stores and trusted identities.
Verify by Digest, Not Just by Tag
Consider:
orders-api:production
A tag can move from one image digest to another.
For stronger deployment controls, use a digest:
orders-api@sha256:abc123...
Then verify the signature against that exact artifact.
A useful deployment pipeline is:
Build
|
v
Push
|
v
Resolve Digest
|
v
Sign Digest
|
v
Verify Digest
|
v
Deploy Digest
This prevents the deployment stage from accidentally resolving a mutable tag to a different image.
Integrating Signing Into CI/CD
A practical pipeline can be structured as follows:
Source
|
v
Build
|
v
Unit Tests
|
v
Security Scans
|
v
Container Build
|
v
Push
|
v
Sign
|
v
Verify
|
v
Deploy
The signing operation should occur after the final image has been built and pushed.
The deployment stage should verify the signature rather than assuming that the previous pipeline step was successful.
Join the conversation! Your thoughts help the community grow.