Inject registry creds into app rad deploy instead of provisioning a control-plane Secret - #12510
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
There was a problem hiding this comment.
Pull request overview
Updates the GitHub Actions Radius extension workflows/composite action to stop provisioning registry credentials on the control-plane cluster and instead pass registry credentials into the app rad deploy as parameters, enabling the app’s Radius.Security/secrets resource to materialize the registry Secret on the target cluster.
Changes:
- Removes the control-plane
kubectl create secret generic ghcr-registry-creds ...step from the sharedrun-rad-commandscomposite action. - Injects
registryUsername/registryPasswordvia--parameterson app deploy(s), while keeping secrets out of the recorded command string. - Updates Azure/AWS provider workflow comments and the extension README to reflect the new model.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| .github/extension/actions/run-rad-commands/action.yml | Removes control-plane Secret provisioning and injects registry credentials into rad deploy argv. |
| .github/extension/run-rad-commands-azure.yml | Updates comments and passes registry creds inputs to the shared action. |
| .github/extension/run-rad-commands-aws.yml | Updates comments and passes registry creds inputs to the shared action. |
| .github/extension/README.md | Updates documented workflow steps to match the new credential flow. |
Comments suppressed due to low confidence (1)
.github/extension/actions/run-rad-commands/action.yml:201
- In the custom rad_commands path, registryUsername/registryPassword get appended to any
deploycommand, not just the app deploy. This can break supported custom command scenarios like deployingradius-env.bicep(unknown parameters) and it also silently skips injection when the required inputs are empty. Only inject these parameters when the deploy target is the app file (and fail fast if creds are missing for that case).
fi
if [ -n "${RADIUS_DEPLOY_PARAMS//[[:space:]]/}" ]; then
while IFS= read -r _pname; do
[ -z "$_pname" ] && continue
_pval=$(printf '%s' "$RADIUS_DEPLOY_PARAMS" | jq -r --arg k "$_pname" '.[$k]')
…cret The Radius.Compute/containerImages recipe moved from Terraform to Bicep. The registry push/pull credentials must now reach the app deploy as rad deploy parameters so the app's Radius.Security/secrets resource (ghcr-registry-creds) materializes the registry Secret on the target cluster. - Pass registryUsername (github.actor) and registryPassword (GITHUB_TOKEN) as --parameters to both app-file rad deploy sites (custom-commands deploy branch and default deploy), via the existing argv array so secret values are not word-split and stay out of the recorded command string. Only when non-empty. - Remove the 'Provision registry credentials on control plane' step that ran kubectl create secret generic ghcr-registry-creds on the control-plane cluster. - Refresh action metadata, workflow comments, and README to match. Recipe-pack registration is unchanged (secret name stays ghcr-registry-creds). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
Guard against passing the same --parameters twice: only append the runner-injected registryUsername/registryPassword when RADIUS_DEPLOY_PARAMS does not already carry that key (older environments may still supply them). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
The rad-commands-result artifact captures rad stdout/stderr, but GitHub's log masking does not apply to artifact file contents. Collect the secret values (registry password / GITHUB_TOKEN and every RADIUS_DEPLOY_PARAMS value), register each with ::add-mask:: for defense-in-depth log redaction, and scrub them from each command's captured output before it is written into the artifact JSON. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
In the custom rad_commands path, image/application/registry parameters were appended to every deploy command. rad deploy rejects unknown parameters, so a custom command deploying a non-app template (e.g. radius-env.bicep) would fail. Gate the app-only parameters on the deploy target matching APP_FILE. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
671af20 to
17e4ebe
Compare
Address review feedback on the secret-redaction helpers: - add_secret now emits ::add-mask:: one line at a time, strips CR, and escapes '%' (the workflow-command escape char). A multiline secret previously left its second and later lines unmasked and printed verbatim, and an unescaped '%' could break or inject the workflow command. - Clarify that scrub_secrets double-quotes the search value inside the substitution, which disables pattern matching so glob metacharacters (* ? [ ] \) in a secret are matched literally rather than as a glob. Signed-off-by: sk593 <shruthikumar@microsoft.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
.github/extension/actions/run-rad-commands/action.yml:170
add_secretechoes the secret value directly into the::add-mask::workflow command. If a secret contains%,\n, or\r, GitHub Actions requires escaping (otherwise masking can fail and multiline values can inject extra log lines/commands). Escape the value for the workflow command while still storing the raw value for artifact scrubbing.
add_secret() {
[ -n "$1" ] || return 0
SECRET_VALUES+=("$1")
# Mask one line at a time. GitHub reliably masks a single-line value, but
# a multiline ::add-mask:: leaves the second and later lines unmasked (and
Temporarily set RADIUS_REF to the sk593-inject-registry-creds-on-deploy branch (radius-project/radius#12510) so end-to-end deploys use the updated run-rad-commands action that injects registryUsername/registryPassword as app-deploy parameters, materializing the ghcr-registry-creds Secret on the target cluster. RADIUS_REF drives both the fetched deploy/verify workflow templates and the {{RADIUS_REF}} the committed workflows pin their composite `uses:` actions to, so the PR-branch action is what actually runs. Made it overridable via RADIUS_DEPLOY_REF (mirroring DELETE_RADIUS_REF) so the branch can be repointed without a new core release. Revert to "main" once #12510 merges. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
.github/extension/actions/run-rad-commands/action.yml:195
scrub_secretsis not literal-safe: in Bash${var//pattern/repl}thepatternis always treated as a glob pattern, even when it comes from a quoted variable. Secrets containing glob metacharacters (notably[/], but also*/?) may fail to match (or overmatch) and leak into the uploaded artifact. Update the comment (it currently claims quoting disables pattern matching) and escape glob metacharacters before doing the substitution (or switch to a literal-safe replacer).
# the artifact never carries a credential that `rad` happened to echo. The
# search value is double-quoted inside the substitution, which disables
# pattern matching so it is compared as a literal string -- glob
# metacharacters (* ? [ ] \) in a secret are matched verbatim, not as
# patterns.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #12510 +/- ##
==========================================
- Coverage 53.91% 53.90% -0.02%
==========================================
Files 765 765
Lines 50689 50689
==========================================
- Hits 27328 27322 -6
- Misses 20793 20796 +3
- Partials 2568 2571 +3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
.github/extension/actions/run-rad-commands/action.yml:280
- The disallowed-command path (above this loop) records/logs the raw caller command string without passing it through scrub_string. If a disallowed command includes any value from REGISTRY_PASSWORD or RADIUS_DEPLOY_PARAMS inline, it can bypass redaction and end up in both logs and rad-commands-result.json. Scrub the command string before writing it to COMMANDS_JSON and stderr in that disallowed branch.
for cmd in "${CLEAN[@]}"; do
verb="${cmd%% *}"
if [ "$verb" = "deploy" ]; then
…WS containerImages Bicep - run-rad-commands action: determine the deploy target as the first non-flag argument after `deploy`, skipping flags and value-taking flag values, so `deploy -e dev app.bicep` still injects app-only parameters. - record(): escape workflow-command metacharacters (CR, newline, %) in the ::group:: title to prevent breakage/injection from caller command strings. - AWS workflow: switch Radius.Compute/containerImages to the Bicep recipe to match the Azure hosted recipe pack. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
…oducibility Introduce RADIUS_KUBE_RECIPES_REF (default 'latest') so the AWS inline recipe pack's Bicep containerImages source can be pinned to an immutable digest or a specific tag, instead of hard-coding the mutable ':latest' tag. A digest ref is joined with '@', a tag with ':'. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
…ams JSON - Scrub the recorded command `display` with the known SECRET_VALUES before it reaches the ::group:: title or the JSON `command` field, so a secret passed inline via --parameters can't be uploaded in the result artifact. - Validate RADIUS_DEPLOY_PARAMS is a JSON object once, up front, and reference the validated copy everywhere; suppress jq stderr so a malformed value can't spill raw secret content into the logs. Malformed input is rejected with a generic message and treated as empty. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
- deploy_params_has_key() now returns false when the key's value is JSON null or an empty string, so runner-provided registry credentials are still injected when an older params secret carries the key with no usable value. - When building the mask list, emit nothing for a JSON null instead of the literal string "null", so masking/scrubbing can't redact unrelated "null" output. A genuine string value is still masked. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
add_secret now records each stripped, non-empty line of a secret in SECRET_VALUES in addition to the full value. Masking is emitted per line, so without this the artifact scrubber only matched the exact multi-line string and a value echoed line-by-line by rad could leak into the uploaded artifact. Lines are added before % escaping so they match the real output. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
The custom rad_commands deploy path gated app-only parameter injection on a raw string compare, which missed equivalent spellings of the app file. Normalize both sides -- strip a matched pair of surrounding quotes (read -ra preserves them) and a single leading './' -- so `deploy ./.radius/app.bicep` and `deploy ".radius/app.bicep"` still inject image/deploy params/registry creds. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
56d695a to
8a215a1
Compare
sylvainsf
left a comment
There was a problem hiding this comment.
Looks great, very well thought out.
Radius functional test overviewClick here to see the test run details
Test Status⌛ Building Radius and pushing container images for functional tests... |
Resolve README conflict from #12510 (registry creds injected into the app deploy instead of a control-plane Secret). Keep the custom-types / recipe-pack step and the OCI state-archive wording; adopt main's updated run-rad-commands step and drop the obsolete control-plane registry-cred provisioning step. Renumber the deploy stages accordingly. Signed-off-by: sk593 <shruthikumar@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Summary
The
Radius.Compute/containerImagesrecipe moved from a Terraform recipe to a Bicep recipe. Under the new model, the app's.radius/app.bicepdeclares aRadius.Security/secretsresource namedradius-ghcr-registry-creds(keysusername/password) fed by two params:registryUsername(plain) andregistryPassword(@secure()). That resource materializes the registry push/pull Secret on the target cluster, and the recipe reads it by the name the recipe pack registers.So the credentials must now reach the app deploy as
rad deployparameters, valued from the runner identity —registryUsername = ${{ github.actor }}andregistryPassword = ${{ secrets.GITHUB_TOKEN }}— instead of being provisioned as a Kubernetes Secret on the control-plane cluster.Changes
.github/extension/actions/run-rad-commands/action.yml--parameters registryUsername=…and--parameters registryPassword=…to the app-filerad deployinvocations (the default deploy branch, and the custom-commandsdeploybranch only when the deploy target isAPP_FILE). Built into the existing argv array so special characters aren't word-split, added only when non-empty and not already supplied viaRADIUS_DEPLOY_PARAMS, and kept out of the recorded command string.Provision registry credentials on control planestep (kubectl create secret generic radius-ghcr-registry-creds …). The Secret is now created on the target cluster by the app'sRadius.Security/secretsresource duringrad deploy.rad-commands-resultartifact.description, header comment, and input descriptions.run-rad-commands-azure.yml/run-rad-commands-aws.ymlcontainerImagesRegistry/containerImagesRegistrySecretName=radius-ghcr-registry-creds; the AWS inline pack passesregistry/registrySecretName=radius-ghcr-registry-creds. Only the now-stale comments that described control-plane provisioning were updated for accuracy..github/extension/README.mdregistryUsername/registryPasswordrunner credentials in the run step.Notes
rad deploy(radius-env.bicep), which does not declare them — doing so would makeradreject unknown parameters.registryPasswordis secret: passed via an argv array, never echoed or written into the recorded/logged command string.This is the upstream half of a change coordinated with
radius-project/ai-extensions, which stops auto-generatingregistryUsername/registryPasswordin the app bicep so the workflow can inject the runner values instead.radius-ghcr-registry-creds(theradius-prefix was added in a follow-up to this branch for parity with the ai-extensionsradius-app-bicepskill). The name stays consistent across all three points: the workflow value → the recipe-pack parameter (containerImagesRegistrySecretName/registrySecretName) → the app'sRadius.Security/secretsresource. No recipe-pack change inresource-types-contribis needed since it takes the name as a parameter.