ci: pin rustup-init version and verify SHA-256 in the public toolchain template#502
Open
MGudgin wants to merge 1 commit into
mainmicrosoft/mxc:mainfrom
user/gudge/pin_rustup_init_sha256microsoft/mxc:user/gudge/pin_rustup_init_sha256Copy head branch name to clipboard
Open
ci: pin rustup-init version and verify SHA-256 in the public toolchain template#502MGudgin wants to merge 1 commit intomainmicrosoft/mxc:mainfrom user/gudge/pin_rustup_init_sha256microsoft/mxc:user/gudge/pin_rustup_init_sha256Copy head branch name to clipboard
MGudgin wants to merge 1 commit into
mainmicrosoft/mxc:mainfrom
user/gudge/pin_rustup_init_sha256microsoft/mxc:user/gudge/pin_rustup_init_sha256Copy head branch name to clipboard
Conversation
There was a problem hiding this comment.
Pull request overview
Hardens the Azure Pipelines “public rustup” bootstrap used by fork-PR builds, lint, and macOS builds by pinning the rustup-init version to an immutable archive URL and adding a SHA-256 sidecar verification step prior to execution.
Changes:
- Add a
rustupInitVersiontemplate parameter (default1.28.2) and downloadrustup-initfromhttps://static.rust-lang.org/rustup/archive/<ver>/<triple>/. - Verify the downloaded
rustup-initagainst the corresponding.sha256sidecar before running the installer. - Expand template documentation to describe the threat model and remaining limitations (integrity vs. origin authenticity).
Show a summary per file
| File | Description |
|---|---|
| .azure-pipelines/templates/Rust.Toolchain.Public.yml | Pins rustup-init and verifies SHA-256 before running rustup to harden public toolchain bootstrapping. |
Copilot's findings
- Files reviewed: 1/1 changed files
- Comments generated: 1
MGudgin
pushed a commit
that referenced
this pull request
Jun 7, 2026
Copilot review feedback on PR #502: `\ = \` made the non-Windows download use the **build target** triple. That breaks cross- compile jobs -- specifically the unofficial linux/arm64 build, which runs on an `ubuntu-latest` x64 agent and would now try to execute an `aarch64-unknown-linux-gnu` `rustup-init`. `rustup-init` must match the agent host architecture; the target toolchain is selected separately via the `--target \` flag we already pass. Fix: detect the host architecture from `uname -m` and translate to a Rust triple via an explicit `switch` per OS (macOS / Linux). Unknown host arches throw with a clear message rather than silently downloading an unrunnable binary. The Windows path is unchanged -- both windows/x64 and windows/arm64 builds use x64 agents, so the hardcoded `x86_64-pc-windows-msvc` host triple is correct there. Also logs the resolved host vs. target triples to the job output so a future cross-compile mismatch is one `grep` away. ## Verification python -c \"import yaml; yaml.safe_load(open(<file>))\" OK parameters: 3, steps: 1 OK No call-site or behaviour change for the in-use jobs as long as the agent host architecture matches what `uname -m` returns on it (`x86_64` on the ubuntu-latest pool, `arm64` on the macOS arm64 pool used by `Mac.Build.Job.yml` for the aarch64 build). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Member
Author
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
…n template `.azure-pipelines/templates/Rust.Toolchain.Public.yml` is the public- bootstrap rustup install used by lint, fork-PR builds, and the macOS build. It previously fetched `rustup-init` from the convenience redirectors `https://win.rustup.rs/x86_64` and `https://sh.rustup.rs` and executed it **without any integrity check** on every trusted run. ## Threat Those URLs are convenience redirectors that always return the latest rustup release. A compromise of the redirect, the rustup origin, or the TLS chain would silently propagate into every trusted build that uses this template. The lint job in particular also rewires crates.io to an anonymous public mirror via `.azure-pipelines/.cargo/config.public.toml`, so a compromised `rustup-init` could expand the blast radius into any compiled artifact the lint job touches. ## Fix This template now: 1. **Pins the rustup version** via a new `rustupInitVersion` parameter (default `1.28.2`) and fetches from the immutable archive URL `static.rust-lang.org/rustup/archive/<ver>/<host-triple>/`. That URL is byte-stable for a given (version, triple); the previous `latest` URL would defeat any pinned SHA the first time rust-lang shipped a new release, which is what made hash pinning impossible under the old layout. 2. **Selects the host triple from the actual agent**, not from the build target. Windows agents are always x64 (windows/arm64 builds run on x64 agents and cross-compile via `--target`), so the Windows download is hardcoded to `x86_64-pc-windows-msvc`. On Linux / macOS the host arch is read from `uname -m` and translated to a Rust triple via an explicit per-OS switch; unknown architectures throw with a clear message rather than silently downloading an unrunnable binary. The TARGET toolchain is still installed via `rustup-init --target \`. The resolved host / target triples are logged to the job output so any future cross-compile mismatch is one `grep` away. 3. **Verifies the SHA-256 sidecar** from the same origin against the downloaded `rustup-init` BEFORE executing. This matches what `rustup` itself does for self-update. On mismatch the job fails loudly with both expected and actual hashes so an operator can audit. 4. **Documents the residual threat clearly:** SHA-256 sidecar verification defends against integrity-of-payload (in-flight corruption, TLS MITM, truncation) but **not** against authenticity-of-origin (a compromise of `static.rust-lang.org` itself would corrupt both the binary and the sidecar). Defending against the latter requires upstream GPG release-key verification and is out of scope here. ## Trade-off considered and rejected Switching the lint job to `RustInstaller@1` + `CargoAuthenticate` (matching `Rust.Build.Steps.Official.yml`) would have removed the public bootstrap entirely BUT also broken fork-PR linting -- external contributors have no `System.AccessToken` for the internal `Mxc-Azure-Feed`. The integrity-verification approach preserves fork-PR linting while still meaningfully hardening trusted runs. ## Compatibility The two existing call sites (`.azure-pipelines/jobs/Lint.Job.yml` and `.azure-pipelines/templates/Rust.Build.Steps.Unofficial.yml`) are unchanged: the new `rustupInitVersion` parameter has a default, so backward compatibility is automatic. Bumping the pin in future is a single-line change in this file. ## Verification YAML parses (PyYAML): 3 parameters, 1 step OK No call-site changes needed (default-valued parameter) OK No Rust code touched -> no cargo build/clippy/test rerun N/A CI rerun deferred to actual pipeline execution on this PR (the lint job exercises the changed template directly). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2e1f71d to
65add56
Compare
Member
Author
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Hardens the public-bootstrap rustup install used by lint, fork-PR builds, and the macOS build. The template previously fetched
rustup-initfrom the convenience redirectorshttps://win.rustup.rs/x86_64/https://sh.rustup.rsand executed it without any integrity check on every trusted run; the lint job in particular also rewirescrates.ioto an anonymous public mirror via.azure-pipelines/.cargo/config.public.toml, so a compromise of the redirector or the rustup origin could expand into any compiled artifact the lint job touches.The template now:
rustupInitVersionparameter (default1.28.2) and fetches from the immutable archive URLstatic.rust-lang.org/rustup/archive/<ver>/<host-triple>/. That URL is byte-stable for a given (version, triple), so the SHA-256 check below is meaningful — the previouslatestURL would defeat any pinned hash the first time rust-lang shipped a new release.--target), so the Windows download is hardcoded tox86_64-pc-windows-msvc. On Linux / macOS the host arch is read fromuname -mand translated to a Rust triple via an explicit per-OS switch; unknown architectures throw with a clear message rather than silently downloading an unrunnable binary. The TARGET toolchain is still installed viarustup-init --target.rustup-initBEFORE executing. On mismatch the job fails loudly with both expected and actual hashes. Matches whatrustupitself does for self-update.static.rust-lang.orgwould corrupt both the binary and the sidecar). Defending against the latter requires upstream GPG release-key verification and is out of scope.Trade-off considered and rejected
Switching the lint job to
RustInstaller@1+CargoAuthenticate(matchingRust.Build.Steps.Official.yml) would remove the public bootstrap entirely BUT also break fork-PR linting — external contributors have noSystem.AccessTokenfor the internalMxc-Azure-Feed. The integrity-verification approach preserves fork-PR linting while still meaningfully hardening trusted runs.Compatibility
The two existing call sites (
.azure-pipelines/jobs/Lint.Job.ymland.azure-pipelines/templates/Rust.Build.Steps.Unofficial.yml) are unchanged: the newrustupInitVersionparameter has a default, so backward compatibility is automatic. Bumping the pin in future is a single-line change in this template. The resolved host / target triples are logged to the job output on every run, so a future cross-compile mismatch is onegrepaway.Verification