chore: bump toolkit to go 1.25 to fix CVEs - #18645
chore: bump toolkit to go 1.25 to fix CVEs#18645Muhammad Falak R Wani (mfrw) wants to merge 12 commits into
Conversation
Muhammad Falak R Wani (mfrw)
commented
Aug 27, 2026
- chore: bump toolkit to go 1.25 to fix CVEs
- chore: install upstream go 1.25 on Ubuntu releases without a distro package
- fix: prefer the distro golang package on Ubuntu releases that have it
- fix: do not overwrite an existing Go toolchain in /usr/local/go
- fix: warn when another go on PATH shadows the installed toolchain
- docs: drop the stale msft-golang note from the Azure Linux prerequisites
- docs: record how and when to advance the pinned Go version
- fix: explain a checksum mismatch instead of printing sha256sum's FAILED
Co-authored-by: Daniel McIlvaney <damcilva@microsoft.com>
…ackage Ubuntu 26.04 packages Go 1.25 as golang-1.25-go, but 22.04 and 24.04 do not package it at all, so installing prerequisites there leaves the toolkit without a usable Go. The apt package also is not on PATH, which is why --fix-go-links exists. Drop golang-1.25-go from the apt list and instead check the Go on PATH, falling back to the pinned upstream tarball from go.dev when it is missing or older than 1.25. The download is verified against a hardcoded SHA256 before anything is unpacked, so a corrupted or tampered archive can neither be installed nor replace an existing toolchain. Unsupported architectures fail with an actionable message rather than a 404. This gives every supported release the same Go install path and puts go and gofmt on PATH without a separate step. Signed-off-by: Muhammad Falak R Wani <falakreyaz@gmail.com>
The previous commit unconditionally downloaded Go from go.dev whenever PATH had no Go 1.25+. That regressed Ubuntu 26.04, where golang-1.25-go exists: hosts that can reach an apt mirror but not the public internet went from working to failing. Try apt first and fall back to go.dev only when the package is genuinely unavailable. Success is decided by the filesystem (/usr/lib/go-1.25/bin/go) rather than by parsing apt output, whose "Candidate:" label is translated and would misfire under a non-English locale. Since Go can now land in either root, go_root() resolves it once and both the install path and --fix-go-links share that result.
The install gate only looked at the go on PATH. A user who had unpacked Go into /usr/local/go without linking it into /usr/bin -- or whose links had been removed -- would silently have that toolchain deleted and, if it was newer than the pinned version, downgraded to 1.25.14. Look for an installed toolchain in either root before installing anything and reuse it when it is 1.25 or newer, so the run only re-creates the symlinks. When /usr/local/go really is too old to keep, say so before removing it instead of doing it silently.
The script links /usr/bin/go, but that is not necessarily the go that runs: Ubuntu's default PATH and sudo's secure_path both list /usr/local/bin ahead of /usr/bin, so a go left there keeps winning. The failure that follows is remote from its cause -- tools.mk checks go_min_version much later, from a different script, and reports only that the version is too old. Name the shadowing binary and what it reports while we are still in the script that can explain it. Compare with -ef rather than by string so that reaching the same file through the /bin -> usr/bin symlink is not mistaken for a shadow.
The document already says Azure Linux 2.0 is unsupported, then a few lines later tells the reader what install-prereqs does on Azure Linux 2.0. Neither half of that note is true any more. toolkit.mk refuses outright when ID=mariner, and prerequisites-mariner.sh installs plain 'golang' with no msft-golang handling of any kind. Remove it, and say plainly why 2.0 is out.
Go supports only its two most recent majors, so a hardcoded GO_VERSION starts aging the day it lands. Nothing said so, and nothing said that the minimum version is also spelled out in go.mod, the coverage workflow and two prerequisite documents -- so the obvious next bump is a one-line edit that leaves four other places behind. Write the cadence and the full list of coupled locations down next to the pin, and point the script's header comment at it.
GO_VERSION and the two GO_SHA256_* constants have to move together, and
the most likely way to get this wrong is to bump one and forget the
others. All that told you was:
/tmp/tmp.XXXX/go.tar.gz: FAILED
sha256sum: WARNING: 1 computed checksum did NOT match
which does not say which release was expected, which constant is stale,
where to find the right value, or whether anything was left half-installed.
It reads like a compromised download even when it is a one-line editing
mistake -- and the reverse mistake, reading a real tampering incident as a
stale constant, is worse.
Print the release, both sums, the exact constant for the architecture in
hand, and the fact that nothing was installed.
There was a problem hiding this comment.
Pull request overview
Updates the toolkit’s Go toolchain requirements and installation paths, while aligning CI and package manifests.
Changes:
- Raises the toolkit minimum to Go 1.25 and updates dependencies.
- Adds reusable CI setup and Ubuntu installation logic.
- Reorders
chkconfigbefore dependent packages in manifests.
Reviewed changes
Copilot reviewed 14 out of 15 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
toolkit/tools/go.mod |
Updates Go and module requirements. |
toolkit/tools/go.sum |
Refreshes dependency checksums. |
toolkit/resources/manifests/package/update_manifests.sh |
Corrects package generation order. |
toolkit/resources/manifests/package/pkggen_core_x86_64.txt |
Reorders x86_64 packages. |
toolkit/resources/manifests/package/pkggen_core_aarch64.txt |
Reorders Arm64 packages. |
toolkit/docs/building/prerequisites-ubuntu.sh |
Adds version-aware Go installation. |
toolkit/docs/building/prerequisites-ubuntu.md |
Documents Ubuntu Go setup and updates. |
toolkit/docs/building/prerequisites-mariner.md |
Updates Azure Linux requirements. |
.github/workflows/go-test-coverage.yml |
Updates the expected Go version. |
.github/workflows/check-srpm-duplicates.yml |
Configures Go for toolkit builds. |
.github/workflows/check-source-signatures.yml |
Configures Go and preserves its PATH. |
.github/workflows/check-package-cgmanifest.yml |
Configures Go and preserves its PATH. |
.github/workflows/check-package-builds.yml |
Configures Go for package checks. |
.github/workflows/check-circular-deps.yml |
Configures Go for graph generation. |
.github/actions/setup-toolkit-go/action.yml |
Adds shared toolkit Go setup. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| module github.com/microsoft/azurelinux/toolkit/tools | ||
|
|
||
| go 1.24.0 | ||
| go 1.25.0 |
| if go_is_supported go; then | ||
| echo "Found $(go version), skipping Go installation..." | ||
| else | ||
| if go_existing_root="$(go_root)" && go_is_supported "$go_existing_root/bin/go"; then |
There was a problem hiding this comment.
Instead of having to pass potential Go commands into go_is_supported why not make go_is_supported check all paths automatically and accept no arguments? This would also make this if-else ladder a bit cleaner.
| link_go "$go_installed_root" | ||
| fi | ||
| else | ||
| echo "Skipping installation of prerequisite packages..." | ||
| fi | ||
|
|
||
| # Fix go 1.24 links if requested | ||
| # Fix go 1.25 links if requested | ||
| if [ "$FIX_GO_LINKS" = true ]; then | ||
| echo "Creating Go symlinks..." | ||
| ln -vsf /usr/lib/go-1.24/bin/go /usr/bin/go | ||
| ln -vsf /usr/lib/go-1.24/bin/gofmt /usr/bin/gofmt | ||
| if go_link_root="$(go_root)"; then | ||
| echo "Creating Go symlinks from $go_link_root..." | ||
| link_go "$go_link_root" |
There was a problem hiding this comment.
Same idea for link_go: we always pass the value from go_root into it, so why not call go_root from inside link_go and simplify things for the caller?
Also, the echo "Creating Go symlinks from $go_link_root..." call could be made from inside link_go as well. Right now we're inconsistent in printing that line btw.
| module github.com/microsoft/azurelinux/toolkit/tools | ||
|
|
||
| go 1.24.0 | ||
| go 1.25.0 |
There was a problem hiding this comment.
Considering Copilot's comment: maybe let's go to at least 1.26 now?
go_is_supported matched go1.25 anywhere in 'go version', so go1.25rc1 satisfied it. That prerelease predates go1.25.0, which is what go.mod asks for, so the script would decide the host was already fine, skip the install, and leave the build to reject the toolchain later. Require the stable form for the minimum series and keep accepting prereleases of later ones, which really are newer. Reported by Copilot in review.
Review feedback on three separate points: Going from 1.25 to 1.26 was called a major bump. The major version is the leading 1; these are minor releases. Reworded, and "two most recent major releases" became "release series" so the page does not use the same word for two different things a few lines apart. The shadowed-Go note told the reader to remove the offending binary or the build would fail. That is only true when the shadowing copy is too old -- a newer one works fine -- so it now says what the script itself says: remove it if the build reports an unsupported version. The --fix-go-links description hardcoded /usr/lib/go-1.25, which is one more place to remember on the next bump. Also drop the Azure Linux 2.0 bullet: 2.0 is unsupported, so describing what install-prereqs does there is noise.
bf895f9 to
b0cfcbb
Compare
Review feedback: go_is_supported took a go binary to test, link_go took a root to link, and every caller had to work out what to pass. The result was a nested ladder in the middle of the install block and two separate link call sites, one of which ran even when the caller had not asked for links and both of which ran when it had -- so a plain install-prereqs relinked twice. go_is_supported now answers the question the caller actually has, "is a usable Go already here", checking PATH and both roots itself. link_go resolves the root, prints where it is linking from, and reports the skip when there is nothing to link, so its two callers collapse into one. The version test that both need moved into go_version_ok. The tarball install, which was most of the nesting, is now install_go. go_root prefers a root that meets the minimum over one that does not, so a stale /usr/lib/go-1.25 no longer gets linked over a good /usr/local/go; it still falls back to whatever is installed so that --fix-go-links can repair links to an old toolchain. Linking now happens whenever prerequisites were installed, not only under --fix-go-links. Neither root is on PATH -- that is why the flag exists -- so skipping it would leave install-prereqs reporting success with an unusable toolchain.
The GitHub runners ship Go 1.24, so every workflow that builds the Go tools started failing the moment go.mod asked for 1.25: the toolchain is pinned to local, which turns a too-old Go into a hard error rather than a download. Review feedback was to fix this the same way a developer does, by running install-prereqs, instead of teaching CI a second way to get Go. That also keeps the runners honest about the rest of the prerequisites rather than relying on whatever the image happens to preinstall. install-prereqs is safe to parse on Go 1.24 -- the version check lives behind REBUILD_TOOLS, which defaults to n -- so there is no ordering problem in bootstrapping it. The plain target is used, not install-prereqs-and-configure, which would also install Docker. go-test-coverage.yml keeps setup-go: it exists to check the pinned version, so it should read go.mod rather than trust the same script under test.
b0cfcbb to
cfcd6dc
Compare