Skip to content

feat: Forward Azure credentials into local containers#131

Merged
jongio merged 11 commits intomainfrom
authn
Feb 14, 2026
Merged

feat: Forward Azure credentials into local containers#131
jongio merged 11 commits intomainfrom
authn

Conversation

@jongio
Copy link
Copy Markdown
Owner

@jongio jongio commented Feb 13, 2026

Enables DefaultAzureCredential to work inside Docker containers during local development. This solves a long-standing pain point (azure-sdk-for-net#19167) where Azure SDKs cannot authenticate from within containers because there are no credentials available in the container environment.

How it works

When local.credentials: azd is set in azure.yaml, azd automatically:

  1. Starts an mTLS auth server on the host that proxies token requests to azd auth token
  2. Cross-compiles a Go shim binary named azd and volume-mounts it into the container at /usr/local/bin/azd
  3. Mounts ephemeral mTLS certificates into /run/secrets/azd-auth/

Inside the container, DefaultAzureCredentialAzureDeveloperCliCredential calls azd auth token, which hits the shim. The shim forwards the request over mTLS to the host, retrieves a real token, and returns it — all transparently to application code.

# azure.yaml — opt in per service
services:
  api:
    project: ./src/api
    language: dotnet
    local:
      credentials: azd

No code changes required — any Azure SDK (.NET, Python, Node.js, Java, Go) that supports AzureDeveloperCliCredential works automatically.

Security model

  • mTLS with ephemeral certificates — ECDSA P-256, 24-hour validity, TLS 1.3 minimum
  • Scope validation — printable ASCII only, https:// prefix required, max 512 bytes, shell metacharacters blocked
  • Rate limiting — 30 requests/minute per IP with IPv6 normalization
  • Defense in depth — scope validation in server, token provider, AND shim
  • No secrets in environment — certificates mounted as files, internal env vars sanitized before container creation
  • Opt-in only — must be explicitly enabled per service

Changes

Core feature (cli/src/internal/)

  • containerauth/build.go — Cross-compiles the shim binary for the container's architecture (amd64/arm64)
  • containerauth/shim/main.go — The shim binary that runs inside containers; validates scopes, makes mTLS requests to host
  • service/orchestrator.go — Auth server lifecycle management with cleanup-on-error
  • service/container_runner.go — Volume mounts and extra hosts injection when auth is enabled
  • service/types.goLocalServiceConfig with Credentials field, IsContainerAuthEnabled() helper
  • docker/exec.go + docker/types.go — Container architecture detection for cross-compilation
  • cmd/app/commands/run.go — Graceful shutdown ordering (services → auth server)

Schema

  • schemas/v1.1/azure.yaml.json — Added credentials enum under local config

Documentation (web/src/pages/)

  • reference/credential-forwarding.astro — Detailed reference page with architecture diagram, SDK support table, security model, and troubleshooting
  • index.astro — Homepage feature card
  • reference/containers.astro — Credential forwarding section
  • reference/azure-yaml.astrolocal.credentials config reference

Testing

  • containerauth/build_test.go — Architecture detection, host detection, extra hosts
  • docker/client_test.go — Container inspect parsing
  • container-auth-test/ — Full integration test suite:
    • test.ps1 — Automated E2E test (start server, run container, verify token retrieval)
    • pentest/main.go — 19-attack automated penetration test
    • pentest/heist.go — Proof-of-concept demonstrating accepted risk (in-container token access)

Maintenance

  • Updated all go.mod files to Go 1.25.7
  • Updated azd-core dependency to v0.4.2

Companion PR

The mTLS server, certificate generation, and token provider live in azd-core: jongio/azd-core#11

Testing

  • 5 unit tests in containerauth/ (arch detection, host detection, extra hosts, shim source)
  • 31 unit tests in azd-core/authn/ (certs, server, token validation)
  • 19-attack automated pentest (all attacks blocked)
  • Heist PoC confirms accepted risk boundary
  • mage preflight passes (vet, staticcheck, govulncheck, build, tests)
  • Manual E2E with Docker Desktop (Windows, macOS)
  • Manual E2E with native Docker (Linux)

- Created a new .NET project for testing Azure credential forwarding.
- Added comprehensive documentation for Azure Credential Forwarding in Docker, including quick start guides, architecture, supported scenarios, and troubleshooting steps.
- Included code examples for .NET, Python, and Node.js demonstrating the use of DefaultAzureCredential with credential forwarding.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 13, 2026

🚀 Website Preview

Your PR preview was available here.

Preview has been cleaned up as the PR was closed.

github-actions bot added a commit that referenced this pull request Feb 13, 2026
github-actions bot added a commit that referenced this pull request Feb 13, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 13, 2026

Codecov Report

❌ Patch coverage is 28.07018% with 123 lines in your changes missing coverage. Please review.
✅ Project coverage is 55.13%. Comparing base (a0c0d33) to head (6079a24).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
cli/src/internal/service/container_runner.go 0.00% 69 Missing ⚠️
cli/src/internal/service/orchestrator.go 0.00% 41 Missing ⚠️
cli/src/internal/containerauth/build.go 75.86% 4 Missing and 3 partials ⚠️
cli/src/cmd/app/commands/run.go 0.00% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #131      +/-   ##
==========================================
- Coverage   55.33%   55.13%   -0.20%     
==========================================
  Files         157      158       +1     
  Lines       23735    23903     +168     
==========================================
+ Hits        13133    13180      +47     
- Misses       9781     9899     +118     
- Partials      821      824       +3     
Flag Coverage Δ
unittests 55.13% <28.07%> (-0.20%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
cli/src/internal/docker/exec.go 34.48% <100.00%> (+2.68%) ⬆️
cli/src/internal/docker/types.go 100.00% <100.00%> (ø)
cli/src/internal/service/detector.go 82.29% <100.00%> (+0.25%) ⬆️
cli/src/internal/service/types.go 90.96% <100.00%> (+0.15%) ⬆️
cli/src/cmd/app/commands/run.go 12.66% <0.00%> (-0.09%) ⬇️
cli/src/internal/containerauth/build.go 75.86% <75.86%> (ø)
cli/src/internal/service/orchestrator.go 7.00% <0.00%> (-1.33%) ⬇️
cli/src/internal/service/container_runner.go 7.35% <0.00%> (-3.14%) ⬇️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 13, 2026

🚀 Test This PR

A preview build (0.11.7-pr131) is ready for testing!

🌐 Website Preview

Live Preview: https://jongio.github.io/azd-app/pr/131/

One-Line Install (Recommended)

PowerShell (Windows):

iex "& { $(irm https://raw.githubusercontent.com/jongio/azd-app/main/cli/scripts/install-pr.ps1) } -PrNumber 131 -Version 0.11.7-pr131"

Bash (macOS/Linux):

curl -fsSL https://raw.githubusercontent.com/jongio/azd-app/main/cli/scripts/install-pr.sh | bash -s 131 0.11.7-pr131

Uninstall

When you're done testing:

PowerShell (Windows):

iex "& { $(irm https://raw.githubusercontent.com/jongio/azd-app/main/cli/scripts/uninstall-pr.ps1) } -PrNumber 131"

Bash (macOS/Linux):

curl -fsSL https://raw.githubusercontent.com/jongio/azd-app/main/cli/scripts/uninstall-pr.sh | bash -s 131

Build Info:

What to Test:
Please review the PR description and test the changes described there.

github-actions bot added a commit that referenced this pull request Feb 13, 2026
@jongio
Copy link
Copy Markdown
Owner Author

jongio commented Feb 13, 2026

🧪 How to Test This PR Locally

Prerequisites

  • Docker Desktop running
  • azd auth login completed on your host (for the Azure token tests)

No other tooling is needed — the auth shim binary is pre-compiled and embedded in the CLI.

1. Install the PR build

Use the install commands from the comment above to install version 0.11.7-pr131.

2. Run the test project

This repo includes a ready-to-go test project at cli/tests/projects/integration/container-auth-test/:

cd cli/tests/projects/integration/container-auth-test
azd app run

3. Test the endpoints

In another terminal:

Endpoint What it tests Requires Azure login
curl http://localhost:8080/health Basic health check No
curl http://localhost:8080/check Validates shim binary + mTLS certs + env vars are injected into the container No
curl http://localhost:8080/token Acquires an Azure token via DefaultAzureCredential → shim → mTLS → host Yes
curl http://localhost:8080/azure Lists your Azure resource groups from inside the container Yes

What you should expect

/health — Returns {"status":"ok"}. Confirms the container is running.

/check — Returns a JSON report showing:

  • /usr/local/bin/azd shim binary exists in the container
  • ✅ mTLS certificates exist at /run/secrets/azd-auth/ (ca.pem, client.pem, client-key.pem)
  • ✅ Environment variables AZD_AUTH_HOST, AZD_AUTH_PORT, AZD_AUTH_CERTS_DIR are set

/token — Returns a real Azure access token acquired from inside the container. The flow: DefaultAzureCredentialAzureDeveloperCliCredential/usr/local/bin/azd auth token (shim) → mTLS → host auth server → real azd auth token → token returned to container.

/azure — Lists your Azure resource groups by name, proving end-to-end Azure API access from inside the container using forwarded credentials.

If you don't have Azure credentials

You can still verify the injection mechanics without being logged in:

curl http://localhost:8080/check

The /check endpoint validates that the shim, certs, and env vars are all properly mounted — no Azure login needed.

How to enable this for your own project

Add local.credentials: azd to your service in azure.yaml:

services:
  myapp:
    host: containerapp
    image: mcr.microsoft.com/dotnet/sdk:8.0
    local:
      credentials: azd

That single line enables credential forwarding for any containerized service.

github-actions bot added a commit that referenced this pull request Feb 13, 2026
github-actions bot added a commit that referenced this pull request Feb 13, 2026
github-actions bot added a commit that referenced this pull request Feb 13, 2026
github-actions bot added a commit that referenced this pull request Feb 14, 2026
github-actions bot added a commit that referenced this pull request Feb 14, 2026
The bin/ directory was not tracked in git, causing the all:bin go:embed
pattern to fail with 'no matching files found' during CI builds.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
github-actions bot added a commit that referenced this pull request Feb 14, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
github-actions bot added a commit that referenced this pull request Feb 14, 2026
@jongio jongio merged commit eec4b80 into main Feb 14, 2026
15 checks passed
@jongio jongio deleted the authn branch February 14, 2026 19:42
github-actions bot added a commit that referenced this pull request Feb 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant