Skip to content

chore: bump toolkit to go 1.25 to fix CVEs - #18645

Open
Muhammad Falak R Wani (mfrw) wants to merge 9 commits into
3.0-devfrom
mfrw/toolkit-update-go-1.25
Open

chore: bump toolkit to go 1.25 to fix CVEs#18645
Muhammad Falak R Wani (mfrw) wants to merge 9 commits into
3.0-devfrom
mfrw/toolkit-update-go-1.25

Conversation

@mfrw

Copy link
Copy Markdown
Member
  • 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.
@mfrw
Muhammad Falak R Wani (mfrw) requested a review from a team as a code owner August 27, 2026 11:48
@microsoft-github-policy-service microsoft-github-policy-service Bot added documentation Improvements or additions to documentation Tools 3.0-dev PRs Destined for AzureLinux 3.0 labels Aug 27, 2026
Every workflow that builds the toolkit runs make with REBUILD_TOOLS=y,
and tools.mk gates that on the go directive in toolkit/tools/go.mod.
GitHub's ubuntu-latest ships Go 1.24, so raising the directive to 1.25
turns each of those jobs into:

    tools.mk:34: *** Go version 'go1.24.13' is less than minimum
    required version 'go1.25.0'.  Stop.

go-test-coverage.yml is unaffected only because it is the one workflow
that already installs its own Go.

Add a composite action that reads the directive and hands the release
series to actions/setup-go, and use it in the five workflows that build
tools. Deriving the version keeps CI from drifting away from the gate it
has to satisfy; asking for the series rather than the exact directive
keeps picking up patch releases instead of pinning to whichever one the
minimum happens to name.

sudo resets PATH to secure_path, which does not include the runner tool
cache, so the make calls have to run under sudo env "PATH=$PATH" for any
of this to be visible to the build. go-test-coverage.yml already does
this.

Two of the five, check-package-cgmanifest and check-source-signatures,
pass today only because they skip make when no spec changed, so they
would have failed on the next spec-touching PR regardless.

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.

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 chkconfig before 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.


# Returns zero when the given go binary is 1.25 or newer, e.g. go1.25.14, go1.26.0, go1.31rc1.
go_is_supported() {
"$1" version 2>/dev/null | grep -qE 'go1\.(2[5-9]|[3-9][0-9])'
Comment thread toolkit/tools/go.mod
module github.com/microsoft/azurelinux/toolkit/tools

go 1.24.0
go 1.25.0

- **Azure Linux 2.0 (CBL-Mariner)**: Validated with `msft-golang-1.24.1`
- **Azure Linux 3.0**: Validated with `golang-1.24.3`
- **Azure Linux 2.0 (CBL-Mariner)**: Not supported. `install-prereqs` refuses outright because 2.0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

2.0 is not supported we can remove this.

@@ -7,10 +7,11 @@ This page outlines the requirements for building with the Azure Linux toolkit on

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Toolio Iglesias🗺️] Doc-vs-behavior check: the doc says install-prereqs "refuses outright" on Azure Linux 2.0, but prerequisites-mariner.sh (unchanged in this PR) has no distro or Go-version guard — it unconditionally runs tdnf -y install golang … with no refusal path. On 2.0 it would install the older golang package and then fail later in make with a confusing Go-version error rather than a clean refusal. Recommend either adding the refusal logic to the script so behavior matches, or softening the wording to describe what actually happens (unsupported; build fails on the Go-version check). Aligns with the existing draft note to remove the 2.0 line.

Comment on lines +119 to +122
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

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.

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.

Comment on lines +169 to +179
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"

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.

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.

Comment on lines +168 to +169
go_installed_root="$(go_root)"
link_go "$go_installed_root"

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.

This seems to go against the FIX_GO_LINKS value, if it's false. Also, when it's true we end up linking twice. Why not do it only once inside if [ "$FIX_GO_LINKS" = true ]; below?

Comment on lines +119 to 173
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
# Installed, just not on PATH. Relinking below is enough, and reinstalling would
# overwrite a toolchain that may well be newer than the one pinned here.
echo "Found $("$go_existing_root/bin/go" version) in $go_existing_root, reusing it..."
else
echo "Checking apt for '$GO_APT_PACKAGE' (not packaged before Ubuntu 26.04)..."
if apt install -y "$GO_APT_PACKAGE" && [ -x "$GO_APT_ROOT/bin/go" ]; then
echo "Installed $GO_APT_PACKAGE from apt."
else
echo "'$GO_APT_PACKAGE' is unavailable on this release, using the upstream toolchain..."
go_arch="$(dpkg --print-architecture)"
case "$go_arch" in
amd64) go_sha256="$GO_SHA256_AMD64"; go_sha256_var=GO_SHA256_AMD64 ;;
arm64) go_sha256="$GO_SHA256_ARM64"; go_sha256_var=GO_SHA256_ARM64 ;;
*)
echo "ERROR: no upstream Go build is pinned for architecture '$go_arch'." >&2
echo "Install Go $GO_VERSION or newer manually, then re-run with --no-install-prereqs." >&2
exit 1
;;
esac

echo "Installing Go $GO_VERSION ($go_arch) from https://go.dev/dl..."
go_tmp_dir="$(mktemp -d)"
trap 'rm -rf "$go_tmp_dir"' EXIT
curl -fsSL -o "$go_tmp_dir/go.tar.gz" \
"https://go.dev/dl/go${GO_VERSION}.linux-${go_arch}.tar.gz"
if echo "$go_sha256 $go_tmp_dir/go.tar.gz" | sha256sum --status -c -; then
echo "Checksum OK."
else
echo "ERROR: go${GO_VERSION}.linux-${go_arch}.tar.gz does not match its pinned checksum." >&2
echo " expected: $go_sha256" >&2
echo " actual: $(sha256sum < "$go_tmp_dir/go.tar.gz" | cut -d ' ' -f 1)" >&2
echo "Either the download was corrupted or tampered with, or GO_VERSION was changed in" >&2
echo "$0 without refreshing $go_sha256_var. The expected value for a given release is" >&2
echo "published at https://go.dev/dl. Nothing has been installed." >&2
exit 1
fi

if [ -e /usr/local/go ]; then
echo "WARNING: replacing the Go installation in /usr/local/go, it is older than $GO_VERSION." >&2
fi
rm -rf /usr/local/go
tar -C /usr/local -xzf "$go_tmp_dir/go.tar.gz"
fi
fi

go_installed_root="$(go_root)"
link_go "$go_installed_root"
fi
else
echo "Skipping installation of prerequisite packages..."
fi

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.

This has gotten a bit lengthy. Could you extract the Go installation steps entirely into their own function (including fixing the links)?

Comment on lines +4 to +33
name: "Set up the toolkit's Go"
description: "Installs the Go release the toolkit requires, as declared by toolkit/tools/go.mod."
runs:
using: "composite"
steps:
- name: Determine the required Go version
id: required-go
shell: bash
run: |
# toolkit/scripts/tools.mk gates every REBUILD_TOOLS=y build on the 'go' directive, so
# read it here instead of hardcoding a version that would drift away from it.
directive="$(awk '/^go [0-9]+\.[0-9]+/ { print $2; exit }' toolkit/tools/go.mod)"
if [ -z "$directive" ]; then
echo "ERROR: no 'go' directive found in toolkit/tools/go.mod" >&2
exit 1
fi

# Ask for the release series rather than the exact directive: the directive is a minimum,
# and installing it verbatim would hold CI on a patch release that is missing later
# security fixes.
echo "version=$(echo "$directive" | cut -d. -f1,2)" >> "$GITHUB_OUTPUT"

- name: Set up Go
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
with:
go-version: "${{ steps.required-go.outputs.version }}"

- name: Report the active Go version
shell: bash
run: go version && command -v go

@PawelWMS Pawel Winogrodzki (PawelWMS) Aug 27, 2026

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.

I think we should be using toolkit's install-prereqs-and-configure or install-prereqs target instead of custom-picking Go now. With this change, we're introducing parallel logic to install requirements for the toolkit, so it may get out of sync with install-prereqs-and-configure and requires extra maintenance.

Extra benefit: install-prereqs* also puts the new binary under a path seen by sudo, so we could drop all env PATH="$PATH".

Comment on lines +31 to +32
the script names it and warns that it will be used instead. Remove it, or the build will later fail
its Go version check.

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.

The wording of the last sentence is a bit strong. The user doesn't have to do anything, if the /usr/local/bin version of Go is at or above the required version.

checksums are version-specific, and a stale one aborts the install with a message naming the
constant that needs refreshing.

Bumping the *major* release (1.25 → 1.26) additionally requires, all in one change:

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.

Major is the first digit.

Suggested change
Bumping the *major* release (1.25 → 1.26) additionally requires, all in one change:
Bumping the *minor* release (1.25 → 1.26) additionally requires, all in one change:


- `--fix-go-links`: Creates symbolic links for Go binaries to make them available in your PATH
- `--fix-go-links`: Re-creates the `/usr/bin` symlinks for the Go binaries, pointing them at
whichever Go root is installed (`/usr/lib/go-1.25` or `/usr/local/go`). The prerequisites

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.

Nit-pick to make future updates easier:

Suggested change
whichever Go root is installed (`/usr/lib/go-1.25` or `/usr/local/go`). The prerequisites
whichever Go root is installed (`/usr/lib/go-1.<minor_version>` or `/usr/local/go`). The prerequisites

Comment thread toolkit/tools/go.mod
module github.com/microsoft/azurelinux/toolkit/tools

go 1.24.0
go 1.25.0

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.

Considering Copilot's comment: maybe let's go to at least 1.26 now?

Comment thread toolkit/tools/go.mod
module github.com/microsoft/azurelinux/toolkit/tools

go 1.24.0
go 1.25.0

@PawelWMS Pawel Winogrodzki (PawelWMS) Aug 27, 2026

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.

This makes the PR check fail, because the worker images are running the old 1.24.X version of Go. It's an issue with the workers, not this change, but please hold off on merging this until we fix it. I'm looking into it right now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3.0-dev PRs Destined for AzureLinux 3.0 documentation Improvements or additions to documentation Tools

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants