Skip to content

Commit d03b5ed

Browse files
authored
Authenticate releases using the embedded verification key. (#192)
* Authenticate releases using the embedded verification key. Fixes #15. Signed-off-by: Piotr Sikora <piotrsikora@google.com> * review: use bool flag and go:embed .gpg file. Signed-off-by: Piotr Sikora <code@piotrsikora.dev> * review: style. Signed-off-by: Piotr Sikora <code@piotrsikora.dev> * Kick CI. Signed-off-by: Piotr Sikora <code@piotrsikora.dev> --------- Signed-off-by: Piotr Sikora <piotrsikora@google.com> Signed-off-by: Piotr Sikora <code@piotrsikora.dev>
1 parent 586d98b commit d03b5ed

10 files changed

Lines changed: 130 additions & 8 deletions

File tree

MODULE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,6 @@ use_repo(
2727
"com_github_gofrs_flock",
2828
"com_github_hashicorp_go_version",
2929
"com_github_mitchellh_go_homedir",
30+
"org_golang_x_crypto",
3031
"org_golang_x_term",
3132
)

core/core.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1395,7 +1395,7 @@ func downloadInstallerToCAS(installerURL, bazeliskHome string, config config.Con
13951395
tmpInstallerFile := fmt.Sprintf("%x-installer", tmpInstallerBytes)
13961396

13971397
// Download the installer
1398-
installerPath, err := httputil.DownloadBinary(installerURL, temporaryDownloadDir, tmpInstallerFile, config)
1398+
installerPath, err := httputil.DownloadBinary(installerURL, temporaryDownloadDir, tmpInstallerFile, config, true)
13991399
if err != nil {
14001400
return "", fmt.Errorf("failed to download installer: %w", err)
14011401
}

core/repositories.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ func (r *Repositories) DownloadFromBaseURL(baseURL, version, destDir, destFile s
219219
}
220220

221221
url := fmt.Sprintf("%s/%s/%s", baseURL, version, srcFile)
222-
return httputil.DownloadBinary(url, destDir, destFile, config)
222+
return httputil.DownloadBinary(url, destDir, destFile, config, false)
223223
}
224224

225225
// BuildURLFromFormat returns a Bazel download URL based on formatURL.
@@ -282,7 +282,7 @@ func (r *Repositories) DownloadFromFormatURL(config config.Config, formatURL, ve
282282
return "", err
283283
}
284284

285-
return httputil.DownloadBinary(url, destDir, destFile, config)
285+
return httputil.DownloadBinary(url, destDir, destFile, config, false)
286286
}
287287

288288
// 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.

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/gofrs/flock v0.13.0
1010
github.com/hashicorp/go-version v1.7.0
1111
github.com/mitchellh/go-homedir v1.1.0
12+
golang.org/x/crypto v0.47.0
1213
golang.org/x/term v0.39.0
1314
)
1415

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
1212
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1313
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
1414
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
15+
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
16+
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
1517
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
1618
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
1719
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=

httputil/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ go_library(
1010
"fake.go",
1111
"httputil.go",
1212
],
13+
embedsrcs = ["bazel-release.pub.gpg"],
1314
importpath = "github.com/bazelbuild/bazelisk/httputil",
1415
visibility = ["//visibility:public"],
1516
deps = [
1617
"//config",
1718
"//httputil/progress",
1819
"@com_github_bgentry_go_netrc//netrc",
1920
"@com_github_mitchellh_go_homedir//:go-homedir",
21+
"@org_golang_x_crypto//openpgp:go_default_library",
2022
],
2123
)
2224

httputil/bazel-release.pub.gpg

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
-----BEGIN PGP PUBLIC KEY BLOCK-----
2+
3+
mQINBFdEmzkBEACzj8tMYUau9oFZWNDytcQWazEO6LrTTtdQ98d3JcnVyrpT16yg
4+
I/QfGXA8LuDdKYpUDNjehLtBL3IZp4xe375Jh8v2IA2iQ5RXGN+lgKJ6rNwm15Kr
5+
qYeCZlU9uQVpZuhKLXsWK6PleyQHjslNUN/HtykIlmMz4Nnl3orT7lMI5rsGCmk0
6+
1Kth0DFh8SD9Vn2G4huddwxM8/tYj1QmWPCTgybATNuZ0L60INH8v6+J2jJzViVc
7+
NRnR7mpouGmRy/rcr6eY9QieOwDou116TrVRFfcBRhocCI5b6uCRuhaqZ6Qs28Bx
8+
4t5JVksXJ7fJoTy2B2s/rPx/8j4MDVEdU8b686ZDHbKYjaYBYEfBqePXScp8ndul
9+
XWwS2lcedPihOUl6oQQYy59inWIpxi0agm0MXJAF1Bc3ToSQdHw/p0Y21kYxE2pg
10+
EaUeElVccec5poAaHSPprUeej9bD9oIC4sMCsLs7eCQx2iP+cR7CItz6GQtuZrvS
11+
PnKju1SKl5iwzfDQGpi6u6UAMFmc53EaH05naYDAigCueZ+/2rIaY358bECK6/VR
12+
kyrBqpeq6VkWUeOkt03VqoPzrw4gEzRvfRtLj+D2j/pZCH3vyMYHzbaaXBv6AT0e
13+
RmgtGo9I9BYqKSWlGEF0D+CQ3uZfOyovvrbYqNaHynFBtrx/ZkM82gMA5QARAQAB
14+
tEdCYXplbCBEZXZlbG9wZXIgKEJhemVsIEFQVCByZXBvc2l0b3J5IGtleSkgPGJh
15+
emVsLWRldkBnb29nbGVncm91cHMuY29tPokCPgQTAQIAKAIbAwYLCQgHAwIGFQgC
16+
CQoLBBYCAwECHgECF4AFAlsGueoFCQeEhaQACgkQPVkZtEhFfuCojRAAqtUaEbK8
17+
zVAPssZDRPun0k1XB3hXxEoe5kt00cl51F+KLXN2OM5gOn2PcUw4A+Ci+48cgt9b
18+
hTWwWuC9OPn9OCvYVyuTJXT189Pmg+F9l3zD/vrD5gdFKDLJCUPo/tRBTDQqrRGA
19+
JssWIzvGR65O2AosoIcj7VAfNj34CBHm25abNpGnWmkiREZzElLFqjTR+FwAMxyA
20+
VJnPbn+K1zyi9xUZKcL1QzKcHBTPFAdZR6zTII/+03n4wAL/w8+x/A1ocmE7jxCI
21+
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
74+
MZaF+SBEZXd1ZSqB5Q==
75+
=laPs
76+
-----END PGP PUBLIC KEY BLOCK-----

httputil/httputil.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
package httputil
33

44
import (
5+
"bytes"
6+
_ "embed"
57
b64 "encoding/base64"
68
"errors"
79
"fmt"
10+
"golang.org/x/crypto/openpgp"
811
"io"
912
"log"
1013
"math/rand"
@@ -38,6 +41,9 @@ var (
3841
MaxRequestDuration = time.Second * 30
3942
retryHeaders = []string{"Retry-After", "X-RateLimit-Reset", "Rate-Limit-Reset"}
4043
NotFound = errors.New("not found")
44+
45+
//go:embed bazel-release.pub.gpg
46+
VerificationKey []byte
4147
)
4248

4349
// Clock keeps track of time. It can return the current time, as well as move forward by sleeping for a certain period.
@@ -187,7 +193,7 @@ func tryFindNetrcFileCreds(host string) (string, error) {
187193
}
188194

189195
// DownloadBinary downloads a file from the given URL into the specified location, marks it executable and returns its full path.
190-
func DownloadBinary(originURL, destDir, destFile string, config config.Config) (string, error) {
196+
func DownloadBinary(originURL, destDir, destFile string, config config.Config, verifySignature bool) (string, error) {
191197
err := os.MkdirAll(destDir, 0755)
192198
if err != nil {
193199
return "", fmt.Errorf("could not create directory %s: %v", destDir, err)
@@ -247,6 +253,40 @@ func DownloadBinary(originURL, destDir, destFile string, config config.Config) (
247253
return "", fmt.Errorf("could not chmod file %s: %v", tmpfile.Name(), err)
248254
}
249255

256+
if verifySignature {
257+
signatureURL := originURL + ".sig"
258+
259+
signature, err := get(signatureURL, "")
260+
if err != nil {
261+
return "", fmt.Errorf("HTTP GET %s failed: %v", signatureURL, err)
262+
}
263+
defer signature.Body.Close()
264+
265+
if signature.StatusCode != 200 {
266+
return "", fmt.Errorf("HTTP GET %s failed with error %v", signatureURL, signature.StatusCode)
267+
}
268+
269+
keys, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(VerificationKey))
270+
if err != nil {
271+
return "", fmt.Errorf("failed to load the embedded Verification Key")
272+
}
273+
274+
if len(keys) != 1 {
275+
return "", fmt.Errorf("failed to load the embedded Verification Key")
276+
}
277+
278+
tmpfile.Seek(0, io.SeekStart)
279+
280+
entity, err := openpgp.CheckDetachedSignature(keys, tmpfile, signature.Body)
281+
if err != nil {
282+
return "", fmt.Errorf("failed to verify the downloaded file using signature from %s", signatureURL)
283+
}
284+
285+
for _, identity := range entity.Identities {
286+
log.Printf("Signed by %s", identity.Name)
287+
}
288+
}
289+
250290
tmpfile.Close()
251291
err = os.Rename(tmpfile.Name(), destinationPath)
252292
if err != nil {

repositories/gcs.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ func (gcs *GCSRepo) DownloadLTS(version, destDir, destFile string, config config
196196
}
197197

198198
url := fmt.Sprintf("%s/%s/%s/%s", ltsBaseURL, baseVersion, folder, srcFile)
199-
return httputil.DownloadBinary(url, destDir, destFile, config)
199+
return httputil.DownloadBinary(url, destDir, destFile, config, true)
200200
}
201201

202202
// CommitRepo
@@ -225,7 +225,7 @@ func (gcs *GCSRepo) DownloadAtCommit(commit, destDir, destFile string, config co
225225
return "", err
226226
}
227227
url := fmt.Sprintf("%s/%s/%s/bazel", commitBaseURL, platform, commit)
228-
return httputil.DownloadBinary(url, destDir, destFile, config)
228+
return httputil.DownloadBinary(url, destDir, destFile, config, false)
229229
}
230230

231231
// RollingRepo
@@ -274,5 +274,5 @@ func (gcs *GCSRepo) DownloadRolling(version, destDir, destFile string, config co
274274

275275
releaseVersion := strings.Split(version, "-")[0]
276276
url := fmt.Sprintf("%s/%s/rolling/%s/%s", ltsBaseURL, releaseVersion, version, srcFile)
277-
return httputil.DownloadBinary(url, destDir, destFile, config)
277+
return httputil.DownloadBinary(url, destDir, destFile, config, true)
278278
}

repositories/github.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,5 @@ func (gh *GitHubRepo) DownloadVersion(fork, version, destDir, destFile string, c
9191
return "", err
9292
}
9393
url := fmt.Sprintf(urlPattern, fork, version, filename)
94-
return httputil.DownloadBinary(url, destDir, destFile, config)
94+
return httputil.DownloadBinary(url, destDir, destFile, config, false)
9595
}

0 commit comments

Comments
 (0)