Skip to content

hosting-signin-app: keep, verify and add the sign-in app's client secret — never rotate, never show (Plugins#1719) - #4109

Merged
meshweaver-cloud[bot] merged 2 commits into
mainfrom
hosting/1719-signin-app
Sep 12, 2026
Merged

meshweaver-cloud[bot] merged 2 commits into
mainfrom
hosting/1719-signin-app

Conversation

@rbuergi

@rbuergi rbuergi commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Operator half of MeshWeaver.Plugins#1719 (the plan half — an "Ensure sign-in app secret" step after the vault step — is the paired Plugins PR; Pairs-with: none — this PR removes no public surface).

The hand step it replaces (partly — read on)

Memex docs/new-deployment.md step 1, "The sign-in app → <prefix>Authentication-Microsoft-ClientSecret":

APP_ID=$(az ad app create --display-name "<Name> Portal (<host>)" --sign-in-audience AzureADMultipleOrgs --web-redirect-uris "https://<host>/signin-microsoft" …) · az ad sp create · az ad app credential reset … --query password -o tsv > /tmp/<id>-secret 2>/dev/null · az keyvault secret set … --file

What is automated, what is verification-only, and why

New command hosting-signin-app --name <id> --host <host> --vault … --object … [--client-id <app id>] [--years 1]:

State What the step does Right it needs
vault object present kept (never minted over, never rotated) and, with --client-id, the app is verified to list https://<host>/signin-microsoft — a readable app missing it is RED naming az ad app update; an identity that cannot read the app reports signin_app_verify=unknown with the hand command and does not fail the provision Graph Application.Read.All for the verify — not granted to hosting-operator today, so on the fleet this reports unknown until it is
absent, record names the app a credential is added (az ad app credential reset --append — existing credentials stay valid; this adds, it never rotates), az's stderr echo of the secret discarded, the value stored through a mode-600 --file, read back, signin_secret=created + expiry Graph Application.ReadWrite.OwnedBy on an app the identity owns (or Application.ReadWrite.All) — not granted; without it the step is RED with the exact hand commands
absent, no signIn.microsoftClientId RED, deliberately: registering the app needs the Graph right AND writing the new client id back to the git-owned record — the message carries the runbook's exact commands and the record field to set —

So today, on the fleet, the step is the verification half for the two absent cases (RED with the manual command) and a no-op keep for the present case. Granting hosting-operator Application.ReadWrite.OwnedBy + making it an owner of each instance's app turns the middle row on with no code change.

Gates run

$ bash deploy/aks/operator/test/run-tests.sh
325 passed, 0 failed          (291 on main; +34 new hosting-signin-app cases — the stub prints the minted secret on stderr like the real az, proving it is discarded)
$ shellcheck -S warning deploy/aks/operator/bin/hosting-* deploy/aks/operator/test/stubs/signin-app/az
(clean)

hosting-operator.yml: plan list, tracked-file floor 23 → 24 (will need main merged and the floor re-counted once the sibling PRs land), stub list.

Ships how

Hop (C): hosting-operator:<sha> on merge to main; reaches a Provision when operator.image on Deployments/memex moves. Merging is not shipping.

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings September 12, 2026 14:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical vault handling and CI tracking issues remain, along with credential-ordering and invalid recovery-command problems.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds an AKS operator command to preserve, verify, and append Entra sign-in app secrets without rotating existing credentials.

Changes:

  • Adds hosting-signin-app lifecycle logic.
  • Adds Azure CLI stubs and behavioral tests.
  • Registers the command in CI and lifecycle documentation.
File summaries
File Summary
deploy/aks/operator/test/stubs/signin-app/az Azure CLI stub for sign-in app and secret scenarios.
deploy/aks/operator/test/run-tests.sh Behavioral and security test coverage.
deploy/aks/operator/bin/hosting-signin-app Secret handling and redirect verification; unresolved vault error handling, mutation ordering, and invalid recovery commands remain.
deploy/aks/INSTANCE-LIFECYCLE.md Documents the operator lifecycle step.
.github/workflows/hosting-operator.yml CI registration and tracking guard; the tracked-file floor and diagnostic count are incorrect.
Review details
  • Files reviewed: 4/5 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/workflows/hosting-operator.yml Outdated
…ret — never rotate, never show (MeshWeaver.Plugins#1719)

Runbook step 1 registered the multi-tenant Entra app, minted its secret and wrote
<prefix>Authentication-Microsoft-ClientSecret by hand. What this step automates and
what it cannot is decided by the identity's Graph rights and stated:

  present object  → KEPT (never minted over, never rotated). With --client-id the
                    app is verified to list https://<host>/signin-microsoft; an
                    identity without Application.Read.All reports
                    signin_app_verify=unknown with the hand command (not red); a
                    readable app missing the URI is RED naming az ad app update.
  absent + app id → a credential is ADDED (az ad app credential reset --append:
                    existing credentials stay valid), stderr discarded (az echoes
                    the secret there), stored through a mode-600 --file, read back;
                    signin_secret=created + expiry. Needs Application.ReadWrite.OwnedBy
                    on an app the identity owns — else RED with the exact commands.
  absent, no id   → RED: registering the app needs the Graph right AND the client id
                    written back to the git-owned record; the runbook's commands are
                    named. Not automated, deliberately.

Tests: 34 cases against an az stub that, like the real az, prints the minted secret
on stderr — proving it is discarded. Workflow: plan list, floor 24, stub list.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@rbuergi

rbuergi commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

Corrected the tracked-file floor in hosting-operator.yml: main already tracks 24 files under deploy/aks/operator/bin/ (21 commands + run.sh + _common.sh + _audit.jq) while the guard declared 23 and its comment said "20 commands" — stale, so one lost file would have passed. This PR adds one command, so the floor is now 25 with the comment re-counted. 🚨 The four sibling PRs (#4103, #4105, #4109, #4110) each do the same on the same line: whichever lands after the first must merge main and set the floor to the real count at that point (26, 27, 28), not keep 25.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

390 tests  ±0   390 ✅ ±0   59s ⏱️ +11s
  2 suites ±0     0 💤 ±0 
  2 files   ±0     0 ❌ ±0 

Results for commit 3315860. ± Comparison against base commit 69e587c.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

259 tests  ±0   259 ✅ ±0   2m 15s ⏱️ -1s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 3315860. ± Comparison against base commit 69e587c.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

508 tests  ±0   508 ✅ ±0   44s ⏱️ ±0s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 3315860. ± Comparison against base commit 69e587c.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

1 331 tests  ±0   1 329 ✅ ±0   3m 59s ⏱️ -10s
    3 suites ±0       2 💤 ±0 
    3 files   ±0       0 ❌ ±0 

Results for commit 3315860. ± Comparison against base commit 69e587c.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

2 022 tests  ±0   2 022 ✅ ±0   4m 57s ⏱️ +13s
    4 suites ±0       0 💤 ±0 
    4 files   ±0       0 ❌ ±0 

Results for commit 3315860. ± Comparison against base commit 69e587c.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

    6 files  ±0      6 suites  ±0   8m 12s ⏱️ -24s
2 841 tests ±0  2 649 ✅ ±0  192 💤 ±0  0 ❌ ±0 
2 845 runs  ±0  2 653 ✅ ±0  192 💤 ±0  0 ❌ ±0 

Results for commit 3315860. ± Comparison against base commit 69e587c.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

   17 files  ±0     17 suites  ±0   21m 9s ⏱️ -8s
7 351 tests ±0  7 157 ✅ ±0  194 💤 ±0  0 ❌ ±0 
7 355 runs  ±0  7 161 ✅ ±0  194 💤 ±0  0 ❌ ±0 

Results for commit 3315860. ± Comparison against base commit 69e587c.

♻️ This comment has been updated with latest results.

#4100 (hosting-kv-ensure --db-connection) landed first. Both conflicts were "both
sides added": the INSTANCE-LIFECYCLE guarantee list keeps main's extended kv-ensure
bullet AND the signin-app one, and the behaviour suite keeps both blocks (352 passed,
0 failed = 325 + main's 27). The workflow's tracked-stub list is the union.

The tracked-file floor is CORRECTED rather than carried: main declared 23 while
already tracking 24 (its comment said "20 commands" — stale, so one lost script would
have passed). With hosting-signin-app the real count is 25, and the floor and its
comment now say 25.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rbuergi

rbuergi commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

Merge resolved by content; floor settled at 25 — measured, not assumed.

origin/main merged in (no rebase), one push. All three conflicts were both sides added, and both sides are kept:

File Resolution
deploy/aks/INSTANCE-LIFECYCLE.md main's extended hosting-kv-ensure bullet (#4100) and this PR's guarantee bullet
deploy/aks/operator/test/run-tests.sh main's hosting-kv-ensure block and this PR's block
.github/workflows/hosting-operator.yml tracked-stub list = the union of both lists

The floor, and how it was counted. git ls-tree -r --name-only origin/main deploy/aks/operator/bin/ | wc -l = 24 on main (tip 69e587c5c): 21 hosting-* commands + run.sh + _common.sh + _audit.jq. main declares 23 with a comment saying "20 commands" — stale, so one lost script would have passed. This PR adds exactly one command, so the true count for this tree is 25 (22 commands + 3), and the guard and its comment now say 25. Verified on the pushed head: tracked=25, commands=22, floor=25.

Guards run on the pushed head: deploy/aks/operator/test/run-tests.sh green (both test blocks coexist), shellcheck -S warning clean over _common.sh, run.sh and every hosting-*. No run was re-run wholesale.

The follow-up is still needed and is now precisely scoped. Each of #4103/#4105/#4109/#4110 sets the floor true for its own merge, so as the siblings land the real count rises 25 → 26 → 27 → 28 while the floor stays 25 — up to 3 files of slack. After all four have merged, a one-line PR sets -lt 28 and expected 28 (25 commands + run.sh + _common.sh + _audit.jq). It cannot be pre-set here: 28 would fail the guard for whichever lands first.

🤖 Generated with Claude Code

@meshweaver-cloud
meshweaver-cloud Bot added this pull request to the merge queue Sep 12, 2026
Merged via the queue into main with commit 14216b1 Sep 12, 2026
36 checks passed
@rbuergi
rbuergi deleted the hosting/1719-signin-app branch October 10, 2026 13:11
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.

2 participants