Skip to content

chore: harden Docker base images, Production mail config, and the provisioning wait in tests - #1414

Merged
iammukeshm merged 4 commits into
mainfrom
chore/hardening-1412
Sep 30, 2026
Merged

iammukeshm merged 4 commits into
mainfrom
chore/hardening-1412

Conversation

@iammukeshm

Copy link
Copy Markdown
Member

Summary

Three hardening items from #1412, one commit each.

1. Docker: off nightly base images (chore(docker))

  • src/Host/FSH.Starter.Api/Dockerfile and src/Host/FSH.Starter.DbMigrator/Dockerfile now use the supported mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled instead of dotnet/nightly/aspnet. No other dotnet/nightly refs in the repo (SDK build stages were already on sdk:10.0; clients/ don't use .NET images).
  • Drift kept on purpose and documented in both Dockerfiles: compose images stay chiseled (smaller, no shell); the csproj ContainerFamily (SDK publish, AWS deploy) stays noble because chiseled strips libgssapi_krb5 and Npgsql's GSSAPI probe then logs a noisy (benign) load error (changed in 23df90c). ContainerFamily is untouched.

2. Config: Production SMTP keys (fix(config))

  • appsettings.Production.json had Host/Port/UserName/Password directly under MailOptions; SmtpOptions binds from MailOptions:Smtp, so they bound to nothing. Moved them into Smtp. appsettings.json / appsettings.Development.json already nest under SMTP (binding is case-insensitive); AppHost env vars already use MailOptions__Smtp__*. The DbMigrator has no appsettings files.
  • Behaviour note: before, Production silently inherited the base smtp.ethereal.email:587; now the Production blanks actually apply, so SMTP must be configured for prod (mail fails at send time, not at startup, since there is no startup validation).

3. Tests: shared provisioning wait (test(integration))

  • New src/Tests/Integration.Tests/Infrastructure/TenantProvisioningWait.cs, extracted from fix(identity): run session cleanup inside each tenant's context #1406: reads TenantProvisioningStatusDto, trusts only the overall Status, throws on Failed with the step and error, and the timeout message includes the last status/step.
  • Removed 27 private copies (26 of them matched "Completed" anywhere in the body, returning after the first step) and pointed all call sites at the shared helper. Polls that already read the Status field and return the DTO for assertions (PollUntilTerminalAsync etc.) are unchanged.

Verification

  • dotnet build src/FSH.Starter.slnx: 0 warnings, 0 errors.
  • docker build of both the API and DbMigrator Dockerfiles from the repo root (the compose build context) succeeded; API image runs as user 1654 on runtime 10.0.12.
  • Integration tests for all 27 affected classes: 136 passed, 0 failed.

Closes #1412

🤖 Generated with Claude Code

iammukeshm and others added 4 commits September 29, 2026 16:54
Both production Dockerfiles pulled mcr.microsoft.com/dotnet/nightly/aspnet,
left over from when .NET 10 was in preview. Nightly images are unsupported
and can change under you. Use the supported aspnet:10.0-noble-chiseled.

Also document the deliberate drift: the Dockerfiles (docker compose) stay
chiseled, while the csproj ContainerFamily (SDK publish, AWS deploy) is
full noble because chiseled strips libgssapi_krb5 and Npgsql's GSSAPI
probe then logs a noisy load error.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Host/Port/UserName/Password sat directly under MailOptions, where nothing
binds them (SmtpOptions lives at MailOptions:Smtp). Move them into the Smtp
section so Production overrides actually apply.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…l Status

27 test classes carried their own copy of WaitForProvisioningAsync, and 26
treated "Completed" anywhere in the status body as done. The body lists
each step, so that matched as soon as the first step finished, before the
tenant admin was seeded - a likely flake source.

Extract the correct wait from #1406 (reads TenantProvisioningStatusDto and
trusts only its Status field, throws on Failed with the step and error)
into Infrastructure/TenantProvisioningWait and use it everywhere. The
timeout message now reports the last status and step.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The keys now bind, so a Port of 0 would override the base 587 and break
an operator who only sets the host.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@iammukeshm
iammukeshm merged commit 03ccaaf into main Sep 30, 2026
17 checks passed
@iammukeshm
iammukeshm deleted the chore/hardening-1412 branch September 30, 2026 03:07
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.

Hardening: nightly base images in prod Dockerfiles, mis-nested Production mail config, early-return provisioning wait in tests

1 participant