From a9b338ffecb28d06f9d52a9024f01c5a0d28f52f Mon Sep 17 00:00:00 2001 From: Piotr Sikora Date: Thu, 29 Oct 2020 21:52:40 +0000 Subject: [PATCH 1/4] Authenticate releases using the embedded verification key. Fixes #15. Signed-off-by: Piotr Sikora --- WORKSPACE | 28 ++++++++++++++++++++ core/repositories.go | 2 +- go.mod | 1 + go.sum | 7 +++++ httputil/BUILD | 1 + httputil/httputil.go | 36 ++++++++++++++++++++++++- repositories/gcs.go | 60 +++++++++++++++++++++++++++++++++++++++--- repositories/github.go | 2 +- 8 files changed, 131 insertions(+), 6 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index dba44563..4b6f6b21 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -34,6 +34,34 @@ go_repository( version = "v0.24.3", ) +go_repository( + name = "org_golang_x_crypto", + importpath = "golang.org/x/crypto", + sum = "h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=", + version = "v0.0.0-20201016220609-9e8e0b390897", +) + +go_repository( + name = "org_golang_x_net", + importpath = "golang.org/x/net", + sum = "h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=", + version = "v0.0.0-20190404232315-eb5bcb51f2a3", +) + +go_repository( + name = "org_golang_x_sys", + importpath = "golang.org/x/sys", + sum = "h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=", + version = "v0.0.0-20190412213103-97732733099d", +) + +go_repository( + name = "org_golang_x_text", + importpath = "golang.org/x/text", + sum = "h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=", + version = "v0.3.0", +) + gazelle_dependencies() go_repository( diff --git a/core/repositories.go b/core/repositories.go index 0fb0a4da..624e88fb 100644 --- a/core/repositories.go +++ b/core/repositories.go @@ -175,7 +175,7 @@ func (r *Repositories) DownloadFromBaseURL(baseURL, version, destDir, destFile s } url := fmt.Sprintf("%s/%s/%s", baseURL, version, srcFile) - return httputil.DownloadBinary(url, destDir, destFile) + return httputil.DownloadBinary(url, "", "", destDir, destFile) } // CreateRepositories creates a new Repositories instance with the given repositories. Any nil repository will be replaced by a dummy repository that raises an error whenever a download is attempted. diff --git a/go.mod b/go.mod index 1aac2062..a3d9e3b8 100644 --- a/go.mod +++ b/go.mod @@ -6,4 +6,5 @@ require ( github.com/bazelbuild/rules_go v0.24.3 github.com/hashicorp/go-version v1.2.1 github.com/mitchellh/go-homedir v1.1.0 + golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 ) diff --git a/go.sum b/go.sum index a1d9d191..735a227b 100644 --- a/go.sum +++ b/go.sum @@ -4,3 +4,10 @@ github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pB github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/httputil/BUILD b/httputil/BUILD index 7374867f..3598cc92 100644 --- a/httputil/BUILD +++ b/httputil/BUILD @@ -5,4 +5,5 @@ go_library( srcs = ["httputil.go"], importpath = "github.com/bazelbuild/bazelisk/httputil", visibility = ["//visibility:public"], + deps = ["@org_golang_x_crypto//openpgp:go_default_library"], ) diff --git a/httputil/httputil.go b/httputil/httputil.go index 5a2b09d9..a73f6823 100644 --- a/httputil/httputil.go +++ b/httputil/httputil.go @@ -3,12 +3,14 @@ package httputil import ( "fmt" + "golang.org/x/crypto/openpgp" "io" "io/ioutil" "log" "net/http" "os" "path/filepath" + "strings" "time" ) @@ -51,7 +53,7 @@ func ReadRemoteFile(url string, token string) ([]byte, error) { } // DownloadBinary downloads a file from the given URL into the specified location, marks it executable and returns its full path. -func DownloadBinary(originURL, destDir, destFile string) (string, error) { +func DownloadBinary(originURL, signatureURL, verificationKey, destDir, destFile string) (string, error) { err := os.MkdirAll(destDir, 0755) if err != nil { return "", fmt.Errorf("could not create directory %s: %v", destDir, err) @@ -91,6 +93,38 @@ func DownloadBinary(originURL, destDir, destFile string) (string, error) { return "", fmt.Errorf("could not chmod file %s: %v", tmpfile.Name(), err) } + if signatureURL != "" && verificationKey != "" { + signature, err := getClient().Get(signatureURL) + if err != nil { + return "", fmt.Errorf("HTTP GET %s failed: %v", signatureURL, err) + } + defer signature.Body.Close() + + if signature.StatusCode != 200 { + return "", fmt.Errorf("HTTP GET %s failed with error %v", signatureURL, signature.StatusCode) + } + + keys, err := openpgp.ReadArmoredKeyRing(strings.NewReader(verificationKey)) + if err != nil { + return "", fmt.Errorf("failed to load the embedded Verification Key") + } + + if len(keys) != 1 { + return "", fmt.Errorf("failed to load the embedded Verification Key") + } + + tmpfile.Seek(0, io.SeekStart) + + entity, err := openpgp.CheckDetachedSignature(keys, tmpfile, signature.Body) + if err != nil { + return "", fmt.Errorf("failed to verify the downloaded file using signature from %s", signatureURL) + } + + for _, identity := range entity.Identities { + log.Printf("Signed by %s", identity.Name) + } + } + tmpfile.Close() err = os.Rename(tmpfile.Name(), destinationPath) if err != nil { diff --git a/repositories/gcs.go b/repositories/gcs.go index 57dc28f2..294698da 100644 --- a/repositories/gcs.go +++ b/repositories/gcs.go @@ -19,6 +19,60 @@ const ( candidateBaseURL = "https://releases.bazel.build" nonCandidateBaseURL = "https://storage.googleapis.com/bazel-builds/artifacts" lastGreenBaseURL = "https://storage.googleapis.com/bazel-untrusted-builds/last_green_commit/" + verificationKey = ` +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBFdEmzkBEACzj8tMYUau9oFZWNDytcQWazEO6LrTTtdQ98d3JcnVyrpT16yg +I/QfGXA8LuDdKYpUDNjehLtBL3IZp4xe375Jh8v2IA2iQ5RXGN+lgKJ6rNwm15Kr +qYeCZlU9uQVpZuhKLXsWK6PleyQHjslNUN/HtykIlmMz4Nnl3orT7lMI5rsGCmk0 +1Kth0DFh8SD9Vn2G4huddwxM8/tYj1QmWPCTgybATNuZ0L60INH8v6+J2jJzViVc +NRnR7mpouGmRy/rcr6eY9QieOwDou116TrVRFfcBRhocCI5b6uCRuhaqZ6Qs28Bx +4t5JVksXJ7fJoTy2B2s/rPx/8j4MDVEdU8b686ZDHbKYjaYBYEfBqePXScp8ndul +XWwS2lcedPihOUl6oQQYy59inWIpxi0agm0MXJAF1Bc3ToSQdHw/p0Y21kYxE2pg +EaUeElVccec5poAaHSPprUeej9bD9oIC4sMCsLs7eCQx2iP+cR7CItz6GQtuZrvS +PnKju1SKl5iwzfDQGpi6u6UAMFmc53EaH05naYDAigCueZ+/2rIaY358bECK6/VR +kyrBqpeq6VkWUeOkt03VqoPzrw4gEzRvfRtLj+D2j/pZCH3vyMYHzbaaXBv6AT0e +RmgtGo9I9BYqKSWlGEF0D+CQ3uZfOyovvrbYqNaHynFBtrx/ZkM82gMA5QARAQAB +tEdCYXplbCBEZXZlbG9wZXIgKEJhemVsIEFQVCByZXBvc2l0b3J5IGtleSkgPGJh +emVsLWRldkBnb29nbGVncm91cHMuY29tPokCVQQTAQgAPwIbAwYLCQgHAwIGFQgC +CQoLBBYCAwECHgECF4AWIQRxodDvz+tigf0EN8k9WRm0SEV+4AUCXsoWGgUJC0fh +4QAKCRA9WRm0SEV+4NDCD/9c5rhZREBlikdi5QYRq1YOkwzJLXFoVe0FonEwMuWK +fQzT/rIwyh14tssptU5+eXwTEXL0ZDskgzvrFSpzjQZzcSG/gzNCATNfrZpC2nfE +SxMKOeIwQedn26YIHCI8s9tEQ7BSvfBfJgqfIo3IURhmfzNMj+qszca+3IDYAlAy +8lxUVbJcIQ0apnAdnIadtydzca56mMN7ma+btddaWLpAdyfUvQ/Zsx3TYYLF7inQ +km0JpzISN0fGngzGNDGNmtHNhCdSpyfkr+7fvpbKAYkSH7uZ1AIPDyHdLIwDQnX2 +kbLRkxKncKGSDhUSdlJTl0x36cU+xmgO15FFdOyk3BUfrlfDrgXIBjeX8KNh9TV6 +HgFFR/mNONoJ93ZvZQNO2s1gbPZJe3VJ1Q5PMLW1sdl8q8JthBwT/5TJ1k8E5VYj +jAc8dl+RAALxqj+eo5xI45o1FdV5s1aGDjbwFoCIhGCy2zaog1q5wnhmEptAAD0S +TVbJSpwNiLlPIcGVaCjXp8Ow3SzOGTRKIjFTO/I6FiSJOpgfri07clXmnb4ETjou +mUdglg8/8nQ120zHEOqoSzzIbTNUDjNZY8SuY6Ig3/ObQ/JAFS0i6h74KLfXUZzn +uETY7KURLdyPAhL37Hb9FDhvkJCUO/l6eqDh9jk1JjB7Cvb7hEvnbvDrr2hWNAL7 +RrkCDQRXRJs5ARAA55/1VBlDpV/ElUyLmRyPCz/V+msHdinyw4Mv5DJQupuZwlMy +vxPPzc7GmsIfk1zuOzDWirNs22r43ak6dsAvpcU+iVBi46MqUcbNtC+kfxlKiToD +PCs82rdfCgHT7XYDzrCWlqNQ9++BqM2OYRIxyEucizeofWPlrJUgKvu8fWLVZ6bY +n4L/PqAhobhuSjRcoB5Tp81hGa4cscKIGIqhymfnguaY8viJ83tHPUqQJoApNPy8 +q1pWHSDV6zBv71beqV2b6cBzp7VqNYOIuqE6ZNBFWuCG3zRc9ia2/bHxx2TGAQJt +PpPzitm0xkB3GGN06YnnSCE+f2j+7F0IO6uFlSy7ho0PoSFbDgR91kJK3S0ZBZx4 +H21cIpWWBzf9Nd1M4H3O7KhnGSZDq6+tXZ9/F/ZUvCZHpQlJewDPY9315Ymacf5C +Zk8xeE5UUIxFMdOxF8B7Itb6rbFWv+tzWdX/0/M8/b0ZJhVvngWzuh/agdS4E5an +f7ahGWM96jPRIQEb9DRN2YGp9hOiX2sZqkhxE5zWqD2gdXp2ZAxMCTHf4ijzOVsO +nde7b5BqC0JL73gNwf1iOHyCAzqGiFfah8/odBTDhMsdVMsjSIxzcwlwRnzy+hBs +dYpP19ieJCMoERJTbUgSspPdhY/Y4ChzlFHjiAKYT6vXiYcKS04stCtHqwEAEQEA +AYkCPAQYAQgAJgIbDBYhBHGh0O/P62KB/QQ3yT1ZGbRIRX7gBQJeyhYlBQkLR+Hs +AAoJED1ZGbRIRX7g3Y8P/iuOAHmyCMeSELvUs9ZvLYJKGzmz67R8fJSmgst/Bs3p +dWCAjGE56M6UgZzHXK+fBRWFPDOXT64XNq0UIG7tThthwe4Gdvg/5rWG61Pe/vCZ +2FkMAlEMkuufZYMcw9jItHMKLcYyW/jtN9EzCX+vM6SZlu4o8la5rCIBEaiKfzft +a/dRMjW+RqQnU31NQCDAy3zoGUCQumJtv3GVbMYHIrRZua2yyNo9Iborh2SVdBbK +v9WJKH4JcCHd0/XDGdys6EXeATIIRxchumkmxpIg87OhsC0n5yuH1FnFIFQEjbYX +bb46F7ZFT+8Tov+lgMEw4CZmps4uvvZlKbIH4Zi/ULiobwvm2ad3nejWICmGmHYz +ro6t08hdcY6GnOzCpDwx9yHechMCkU3KEE98nb/CxcmA4VzDHudTJe7o0OyaSarh +6D5WcXf7D9FfcKmUD9xaCsfXh66OCksMVGE1JctrO1wQTF2jTdTUq7mmi30tlM+o +JjVk65OSOd4JYol8auzE4oXOfsNzXbyvj7WzM1v5m7C45jOL+Ly7I3IUzZNfF41J +AMmSd73EOoR9YH4qTrL3jx69Ekf7ww70Qea5enLE8xUgQfGTOaEHxkFcEovmzv54 +6IVe083iK8alXD/9OUTaDY9NwMnOn1K1aU2XOfliGGLgwwaHg+wVFh5rZIHsDl7v +=Embu +-----END PGP PUBLIC KEY BLOCK----- +` ) var ( @@ -106,7 +160,7 @@ func (gcs *GCSRepo) DownloadRelease(version, destDir, destFile string) (string, } url := fmt.Sprintf("%s/%s/release/%s", candidateBaseURL, version, srcFile) - return httputil.DownloadBinary(url, destDir, destFile) + return httputil.DownloadBinary(url, url+".sig", verificationKey, destDir, destFile) } func (gcs *GCSRepo) removeCandidates(history []string, lastN int) ([]string, error) { @@ -189,7 +243,7 @@ func (gcs *GCSRepo) DownloadCandidate(version, destDir, destFile string) (string baseVersion := versionComponents[0] rcVersion := "rc" + versionComponents[1] url := fmt.Sprintf("%s/%s/%s/%s", candidateBaseURL, baseVersion, rcVersion, srcFile) - return httputil.DownloadBinary(url, destDir, destFile) + return httputil.DownloadBinary(url, url+".sig", verificationKey, destDir, destFile) } // CommitRepo @@ -210,5 +264,5 @@ func (gcs *GCSRepo) GetLastGreenCommit(bazeliskHome string, downstreamGreen bool func (gcs *GCSRepo) DownloadAtCommit(commit, destDir, destFile string) (string, error) { log.Printf("Using unreleased version at commit %s", commit) url := fmt.Sprintf("%s/%s/%s/bazel", nonCandidateBaseURL, platforms.GetPlatform(), commit) - return httputil.DownloadBinary(url, destDir, destFile) + return httputil.DownloadBinary(url, "", "", destDir, destFile) } diff --git a/repositories/github.go b/repositories/github.go index 0be03d64..1034e31f 100644 --- a/repositories/github.go +++ b/repositories/github.go @@ -59,5 +59,5 @@ func (gh *GitHubRepo) DownloadVersion(fork, version, destDir, destFile string) ( return "", err } url := fmt.Sprintf(urlPattern, fork, version, filename) - return httputil.DownloadBinary(url, destDir, destFile) + return httputil.DownloadBinary(url, "", "", destDir, destFile) } From b238cc35a17a86954541f183b0162d6ba5ab6c13 Mon Sep 17 00:00:00 2001 From: Piotr Sikora Date: Tue, 27 Jan 2026 20:10:11 -0500 Subject: [PATCH 2/4] review: use bool flag and go:embed .gpg file. Signed-off-by: Piotr Sikora --- core/core.go | 2 +- core/repositories.go | 4 +- httputil/BUILD | 1 + httputil/bazel-release.pub.gpg | 76 ++++++++++++++++++++++++++++++++++ httputil/httputil.go | 10 ++++- repositories/gcs.go | 60 ++------------------------- repositories/github.go | 2 +- 7 files changed, 92 insertions(+), 63 deletions(-) create mode 100644 httputil/bazel-release.pub.gpg diff --git a/core/core.go b/core/core.go index 4a5aade0..47b1d848 100644 --- a/core/core.go +++ b/core/core.go @@ -1395,7 +1395,7 @@ func downloadInstallerToCAS(installerURL, bazeliskHome string, config config.Con tmpInstallerFile := fmt.Sprintf("%x-installer", tmpInstallerBytes) // Download the installer - installerPath, err := httputil.DownloadBinary(installerURL, "", "", temporaryDownloadDir, tmpInstallerFile, config) + installerPath, err := httputil.DownloadBinary(installerURL, temporaryDownloadDir, tmpInstallerFile, config, true) if err != nil { return "", fmt.Errorf("failed to download installer: %w", err) } diff --git a/core/repositories.go b/core/repositories.go index cad7c4fb..8427953d 100644 --- a/core/repositories.go +++ b/core/repositories.go @@ -219,7 +219,7 @@ func (r *Repositories) DownloadFromBaseURL(baseURL, version, destDir, destFile s } url := fmt.Sprintf("%s/%s/%s", baseURL, version, srcFile) - return httputil.DownloadBinary(url, "", "", destDir, destFile, config) + return httputil.DownloadBinary(url, destDir, destFile, config, false) } // BuildURLFromFormat returns a Bazel download URL based on formatURL. @@ -282,7 +282,7 @@ func (r *Repositories) DownloadFromFormatURL(config config.Config, formatURL, ve return "", err } - return httputil.DownloadBinary(url, "", "", destDir, destFile, config) + return httputil.DownloadBinary(url, destDir, destFile, config, false) } // CreateRepositories creates a new Repositories instance with the given repositories. Any nil repository will be replaced by a dummy repository that raises an error whenever a download is attempted. diff --git a/httputil/BUILD b/httputil/BUILD index 6907c509..a17d0643 100644 --- a/httputil/BUILD +++ b/httputil/BUILD @@ -10,6 +10,7 @@ go_library( "fake.go", "httputil.go", ], + embedsrcs = ["bazel-release.pub.gpg"], importpath = "github.com/bazelbuild/bazelisk/httputil", visibility = ["//visibility:public"], deps = [ diff --git a/httputil/bazel-release.pub.gpg b/httputil/bazel-release.pub.gpg new file mode 100644 index 00000000..97c579c6 --- /dev/null +++ b/httputil/bazel-release.pub.gpg @@ -0,0 +1,76 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBFdEmzkBEACzj8tMYUau9oFZWNDytcQWazEO6LrTTtdQ98d3JcnVyrpT16yg +I/QfGXA8LuDdKYpUDNjehLtBL3IZp4xe375Jh8v2IA2iQ5RXGN+lgKJ6rNwm15Kr +qYeCZlU9uQVpZuhKLXsWK6PleyQHjslNUN/HtykIlmMz4Nnl3orT7lMI5rsGCmk0 +1Kth0DFh8SD9Vn2G4huddwxM8/tYj1QmWPCTgybATNuZ0L60INH8v6+J2jJzViVc +NRnR7mpouGmRy/rcr6eY9QieOwDou116TrVRFfcBRhocCI5b6uCRuhaqZ6Qs28Bx +4t5JVksXJ7fJoTy2B2s/rPx/8j4MDVEdU8b686ZDHbKYjaYBYEfBqePXScp8ndul +XWwS2lcedPihOUl6oQQYy59inWIpxi0agm0MXJAF1Bc3ToSQdHw/p0Y21kYxE2pg +EaUeElVccec5poAaHSPprUeej9bD9oIC4sMCsLs7eCQx2iP+cR7CItz6GQtuZrvS +PnKju1SKl5iwzfDQGpi6u6UAMFmc53EaH05naYDAigCueZ+/2rIaY358bECK6/VR +kyrBqpeq6VkWUeOkt03VqoPzrw4gEzRvfRtLj+D2j/pZCH3vyMYHzbaaXBv6AT0e +RmgtGo9I9BYqKSWlGEF0D+CQ3uZfOyovvrbYqNaHynFBtrx/ZkM82gMA5QARAQAB +tEdCYXplbCBEZXZlbG9wZXIgKEJhemVsIEFQVCByZXBvc2l0b3J5IGtleSkgPGJh +emVsLWRldkBnb29nbGVncm91cHMuY29tPokCPgQTAQIAKAIbAwYLCQgHAwIGFQgC +CQoLBBYCAwECHgECF4AFAlsGueoFCQeEhaQACgkQPVkZtEhFfuCojRAAqtUaEbK8 +zVAPssZDRPun0k1XB3hXxEoe5kt00cl51F+KLXN2OM5gOn2PcUw4A+Ci+48cgt9b +hTWwWuC9OPn9OCvYVyuTJXT189Pmg+F9l3zD/vrD5gdFKDLJCUPo/tRBTDQqrRGA +JssWIzvGR65O2AosoIcj7VAfNj34CBHm25abNpGnWmkiREZzElLFqjTR+FwAMxyA +VJnPbn+K1zyi9xUZKcL1QzKcHBTPFAdZR6zTII/+03n4wAL/w8+x/A1ocmE7jxCI +cgq7vaHSpGmigU2+TXckUslIgIC64iqYBpPvFAPNlqXmo9rDfL2Imyyuz1ep7j/b +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 +MZaF+SBEZXd1ZSqB5Q== +=laPs +-----END PGP PUBLIC KEY BLOCK----- \ No newline at end of file diff --git a/httputil/httputil.go b/httputil/httputil.go index 18bef4d9..427aabd5 100644 --- a/httputil/httputil.go +++ b/httputil/httputil.go @@ -2,6 +2,7 @@ package httputil import ( + _ "embed" b64 "encoding/base64" "errors" "fmt" @@ -42,6 +43,9 @@ var ( NotFound = errors.New("not found") ) +//go:embed bazel-release.pub.gpg +var verificationKey string + // Clock keeps track of time. It can return the current time, as well as move forward by sleeping for a certain period. type Clock interface { Sleep(time.Duration) @@ -189,7 +193,7 @@ func tryFindNetrcFileCreds(host string) (string, error) { } // DownloadBinary downloads a file from the given URL into the specified location, marks it executable and returns its full path. -func DownloadBinary(originURL, signatureURL, verificationKey, destDir, destFile string, config config.Config) (string, error) { +func DownloadBinary(originURL, destDir, destFile string, config config.Config, verifySignature bool) (string, error) { err := os.MkdirAll(destDir, 0755) if err != nil { return "", fmt.Errorf("could not create directory %s: %v", destDir, err) @@ -249,7 +253,9 @@ func DownloadBinary(originURL, signatureURL, verificationKey, destDir, destFile return "", fmt.Errorf("could not chmod file %s: %v", tmpfile.Name(), err) } - if signatureURL != "" && verificationKey != "" { + if verifySignature { + signatureURL := originURL + ".sig" + signature, err := get(signatureURL, "") if err != nil { return "", fmt.Errorf("HTTP GET %s failed: %v", signatureURL, err) diff --git a/repositories/gcs.go b/repositories/gcs.go index d6a5a266..04b89c3e 100644 --- a/repositories/gcs.go +++ b/repositories/gcs.go @@ -22,60 +22,6 @@ const ( ltsBaseURL = "https://releases.bazel.build" commitBaseURL = "https://storage.googleapis.com/bazel-builds/artifacts" lastGreenCommitURL = "https://storage.googleapis.com/bazel-builds/last_green_commit/github.com/bazelbuild/bazel.git/publish-bazel-binaries" - verificationKey = ` ------BEGIN PGP PUBLIC KEY BLOCK----- - -mQINBFdEmzkBEACzj8tMYUau9oFZWNDytcQWazEO6LrTTtdQ98d3JcnVyrpT16yg -I/QfGXA8LuDdKYpUDNjehLtBL3IZp4xe375Jh8v2IA2iQ5RXGN+lgKJ6rNwm15Kr -qYeCZlU9uQVpZuhKLXsWK6PleyQHjslNUN/HtykIlmMz4Nnl3orT7lMI5rsGCmk0 -1Kth0DFh8SD9Vn2G4huddwxM8/tYj1QmWPCTgybATNuZ0L60INH8v6+J2jJzViVc -NRnR7mpouGmRy/rcr6eY9QieOwDou116TrVRFfcBRhocCI5b6uCRuhaqZ6Qs28Bx -4t5JVksXJ7fJoTy2B2s/rPx/8j4MDVEdU8b686ZDHbKYjaYBYEfBqePXScp8ndul -XWwS2lcedPihOUl6oQQYy59inWIpxi0agm0MXJAF1Bc3ToSQdHw/p0Y21kYxE2pg -EaUeElVccec5poAaHSPprUeej9bD9oIC4sMCsLs7eCQx2iP+cR7CItz6GQtuZrvS -PnKju1SKl5iwzfDQGpi6u6UAMFmc53EaH05naYDAigCueZ+/2rIaY358bECK6/VR -kyrBqpeq6VkWUeOkt03VqoPzrw4gEzRvfRtLj+D2j/pZCH3vyMYHzbaaXBv6AT0e -RmgtGo9I9BYqKSWlGEF0D+CQ3uZfOyovvrbYqNaHynFBtrx/ZkM82gMA5QARAQAB -tEdCYXplbCBEZXZlbG9wZXIgKEJhemVsIEFQVCByZXBvc2l0b3J5IGtleSkgPGJh -emVsLWRldkBnb29nbGVncm91cHMuY29tPokCVQQTAQgAPwIbAwYLCQgHAwIGFQgC -CQoLBBYCAwECHgECF4AWIQRxodDvz+tigf0EN8k9WRm0SEV+4AUCXsoWGgUJC0fh -4QAKCRA9WRm0SEV+4NDCD/9c5rhZREBlikdi5QYRq1YOkwzJLXFoVe0FonEwMuWK -fQzT/rIwyh14tssptU5+eXwTEXL0ZDskgzvrFSpzjQZzcSG/gzNCATNfrZpC2nfE -SxMKOeIwQedn26YIHCI8s9tEQ7BSvfBfJgqfIo3IURhmfzNMj+qszca+3IDYAlAy -8lxUVbJcIQ0apnAdnIadtydzca56mMN7ma+btddaWLpAdyfUvQ/Zsx3TYYLF7inQ -km0JpzISN0fGngzGNDGNmtHNhCdSpyfkr+7fvpbKAYkSH7uZ1AIPDyHdLIwDQnX2 -kbLRkxKncKGSDhUSdlJTl0x36cU+xmgO15FFdOyk3BUfrlfDrgXIBjeX8KNh9TV6 -HgFFR/mNONoJ93ZvZQNO2s1gbPZJe3VJ1Q5PMLW1sdl8q8JthBwT/5TJ1k8E5VYj -jAc8dl+RAALxqj+eo5xI45o1FdV5s1aGDjbwFoCIhGCy2zaog1q5wnhmEptAAD0S -TVbJSpwNiLlPIcGVaCjXp8Ow3SzOGTRKIjFTO/I6FiSJOpgfri07clXmnb4ETjou -mUdglg8/8nQ120zHEOqoSzzIbTNUDjNZY8SuY6Ig3/ObQ/JAFS0i6h74KLfXUZzn -uETY7KURLdyPAhL37Hb9FDhvkJCUO/l6eqDh9jk1JjB7Cvb7hEvnbvDrr2hWNAL7 -RrkCDQRXRJs5ARAA55/1VBlDpV/ElUyLmRyPCz/V+msHdinyw4Mv5DJQupuZwlMy -vxPPzc7GmsIfk1zuOzDWirNs22r43ak6dsAvpcU+iVBi46MqUcbNtC+kfxlKiToD -PCs82rdfCgHT7XYDzrCWlqNQ9++BqM2OYRIxyEucizeofWPlrJUgKvu8fWLVZ6bY -n4L/PqAhobhuSjRcoB5Tp81hGa4cscKIGIqhymfnguaY8viJ83tHPUqQJoApNPy8 -q1pWHSDV6zBv71beqV2b6cBzp7VqNYOIuqE6ZNBFWuCG3zRc9ia2/bHxx2TGAQJt -PpPzitm0xkB3GGN06YnnSCE+f2j+7F0IO6uFlSy7ho0PoSFbDgR91kJK3S0ZBZx4 -H21cIpWWBzf9Nd1M4H3O7KhnGSZDq6+tXZ9/F/ZUvCZHpQlJewDPY9315Ymacf5C -Zk8xeE5UUIxFMdOxF8B7Itb6rbFWv+tzWdX/0/M8/b0ZJhVvngWzuh/agdS4E5an -f7ahGWM96jPRIQEb9DRN2YGp9hOiX2sZqkhxE5zWqD2gdXp2ZAxMCTHf4ijzOVsO -nde7b5BqC0JL73gNwf1iOHyCAzqGiFfah8/odBTDhMsdVMsjSIxzcwlwRnzy+hBs -dYpP19ieJCMoERJTbUgSspPdhY/Y4ChzlFHjiAKYT6vXiYcKS04stCtHqwEAEQEA -AYkCPAQYAQgAJgIbDBYhBHGh0O/P62KB/QQ3yT1ZGbRIRX7gBQJeyhYlBQkLR+Hs -AAoJED1ZGbRIRX7g3Y8P/iuOAHmyCMeSELvUs9ZvLYJKGzmz67R8fJSmgst/Bs3p -dWCAjGE56M6UgZzHXK+fBRWFPDOXT64XNq0UIG7tThthwe4Gdvg/5rWG61Pe/vCZ -2FkMAlEMkuufZYMcw9jItHMKLcYyW/jtN9EzCX+vM6SZlu4o8la5rCIBEaiKfzft -a/dRMjW+RqQnU31NQCDAy3zoGUCQumJtv3GVbMYHIrRZua2yyNo9Iborh2SVdBbK -v9WJKH4JcCHd0/XDGdys6EXeATIIRxchumkmxpIg87OhsC0n5yuH1FnFIFQEjbYX -bb46F7ZFT+8Tov+lgMEw4CZmps4uvvZlKbIH4Zi/ULiobwvm2ad3nejWICmGmHYz -ro6t08hdcY6GnOzCpDwx9yHechMCkU3KEE98nb/CxcmA4VzDHudTJe7o0OyaSarh -6D5WcXf7D9FfcKmUD9xaCsfXh66OCksMVGE1JctrO1wQTF2jTdTUq7mmi30tlM+o -JjVk65OSOd4JYol8auzE4oXOfsNzXbyvj7WzM1v5m7C45jOL+Ly7I3IUzZNfF41J -AMmSd73EOoR9YH4qTrL3jx69Ekf7ww70Qea5enLE8xUgQfGTOaEHxkFcEovmzv54 -6IVe083iK8alXD/9OUTaDY9NwMnOn1K1aU2XOfliGGLgwwaHg+wVFh5rZIHsDl7v -=Embu ------END PGP PUBLIC KEY BLOCK----- -` ) // GCSRepo represents a Bazel repository on Google Cloud Storage that contains Bazel releases, release candidates and Bazel binaries built at arbitrary commits. @@ -250,7 +196,7 @@ func (gcs *GCSRepo) DownloadLTS(version, destDir, destFile string, config config } url := fmt.Sprintf("%s/%s/%s/%s", ltsBaseURL, baseVersion, folder, srcFile) - return httputil.DownloadBinary(url, url+".sig", verificationKey, destDir, destFile, config) + return httputil.DownloadBinary(url, destDir, destFile, config, true) } // CommitRepo @@ -279,7 +225,7 @@ func (gcs *GCSRepo) DownloadAtCommit(commit, destDir, destFile string, config co return "", err } url := fmt.Sprintf("%s/%s/%s/bazel", commitBaseURL, platform, commit) - return httputil.DownloadBinary(url, "", "", destDir, destFile, config) + return httputil.DownloadBinary(url, destDir, destFile, config, false) } // RollingRepo @@ -328,5 +274,5 @@ func (gcs *GCSRepo) DownloadRolling(version, destDir, destFile string, config co releaseVersion := strings.Split(version, "-")[0] url := fmt.Sprintf("%s/%s/rolling/%s/%s", ltsBaseURL, releaseVersion, version, srcFile) - return httputil.DownloadBinary(url, url+".sig", verificationKey, destDir, destFile, config) + return httputil.DownloadBinary(url, destDir, destFile, config, true) } diff --git a/repositories/github.go b/repositories/github.go index 9d8a7b87..305ae929 100644 --- a/repositories/github.go +++ b/repositories/github.go @@ -91,5 +91,5 @@ func (gh *GitHubRepo) DownloadVersion(fork, version, destDir, destFile string, c return "", err } url := fmt.Sprintf(urlPattern, fork, version, filename) - return httputil.DownloadBinary(url, "", "", destDir, destFile, config) + return httputil.DownloadBinary(url, destDir, destFile, config, false) } From e265eddbe10d2cfc4039a0603fbf5111a0911a25 Mon Sep 17 00:00:00 2001 From: Piotr Sikora Date: Tue, 27 Jan 2026 20:56:44 -0500 Subject: [PATCH 3/4] review: style. Signed-off-by: Piotr Sikora --- httputil/httputil.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/httputil/httputil.go b/httputil/httputil.go index 427aabd5..9f15a93d 100644 --- a/httputil/httputil.go +++ b/httputil/httputil.go @@ -2,6 +2,7 @@ package httputil import ( + "bytes" _ "embed" b64 "encoding/base64" "errors" @@ -16,7 +17,6 @@ import ( "path/filepath" "regexp" "strconv" - "strings" "time" netrc "github.com/bgentry/go-netrc/netrc" @@ -41,10 +41,10 @@ var ( MaxRequestDuration = time.Second * 30 retryHeaders = []string{"Retry-After", "X-RateLimit-Reset", "Rate-Limit-Reset"} NotFound = errors.New("not found") -) -//go:embed bazel-release.pub.gpg -var verificationKey string + //go:embed bazel-release.pub.gpg + VerificationKey []byte +) // Clock keeps track of time. It can return the current time, as well as move forward by sleeping for a certain period. type Clock interface { @@ -266,7 +266,7 @@ func DownloadBinary(originURL, destDir, destFile string, config config.Config, v return "", fmt.Errorf("HTTP GET %s failed with error %v", signatureURL, signature.StatusCode) } - keys, err := openpgp.ReadArmoredKeyRing(strings.NewReader(verificationKey)) + keys, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(VerificationKey)) if err != nil { return "", fmt.Errorf("failed to load the embedded Verification Key") } From 40796075bdf29f60c03697267875a300564c9456 Mon Sep 17 00:00:00 2001 From: Piotr Sikora Date: Mon, 16 Feb 2026 19:00:20 -0500 Subject: [PATCH 4/4] Kick CI. Signed-off-by: Piotr Sikora