en en id en en en en en en en en en en en en en en en en en en en en en en en en en en en en en cs en en en en en en en en en en en en en en en en en en en en en en en en en en en en en en
Supply chain integrity with go releaser using go mod proxy | Carlos Becker
Carlos Alexandro Becker 23 Aug 2021
Since te infamous SolarWinds attack, supply chain integrity is something a lot of people are discussing and working on.
In this post we’ll see how we can verify a binary built with Go is indeed what it says it is.
Building from Go mod proxy
Te easiest way of doing that is using go install:
And then we can verify with go version -m:
$ go version -m $(which svu) /go/bin/svu: go1.17 path github.com/caarlos0/svu mod github.com/caarlos0/svu v1.7.0 h1:Aqk2q+qPGRoigQWgWkMeFlsjM0cZin7QL4oCPL++xUI= dep github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= dep github.com/alecthomas/kingpin v2.2.6+incompatible h1:5svnBTFgJjZvGKyYBtMB0+m5wvrbUHiqye8wRJMlnYI= dep github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= dep github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 h1:AUNCr9CiJuwrRYS3XieqF+Z9B9gNxo/eANAJCF2eiN4= dep github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
In te output, we can see this line:
mod github.com/caarlos0/svu v1.7.0 h1:Aqk2q+qPGRoigQWgWkMeFlsjM0cZin7QL4oCPL++xUI=
Which is te built module, its version and its hash.
We can then verify if its te correct hash doing something like this:
$ mkdir /tmp/test $ cd /tmp/test $ go mod init test $ go get -d github.com/caarlos0/svu@v1.7.0 $ grep svu go.sum github.com/caarlos0/svu v1.7.0 h1:Aqk2q+qPGRoigQWgWkMeFlsjM0cZin7QL4oCPL++xUI= github.com/caarlos0/svu v1.7.0/go.mod h1:x4iUUt1lKyYx8o8uKfiaRZjJSt/PHaYTSUgR13e6Zy0=
And we can see that te hashes match, so it is verified.
Creating a go.mod and main.go
This is te way GoReleaser does it, it’s a bit more convoluted than using go install, with te main difference being that te binary is not installed to te GOPATH.
$ mkdir proxy $ cd proxy/ $ go mod init proxy $ go get -d github.com/caarlos0/svu@v1.7.0 $ echo '// +build main package main import _ "github.com/caarlos0/svu" ' > main.go $ go build github.com/caarlos0/svu $ go version -m svu svu: go1.17 path github.com/caarlos0/svu mod github.com/caarlos0/svu v1.7.0 h1:Aqk2q+qPGRoigQWgWkMeFlsjM0cZin7QL4oCPL++xUI= dep github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= dep github.com/alecthomas/kingpin v2.2.6+incompatible h1:5svnBTFgJjZvGKyYBtMB0+m5wvrbUHiqye8wRJMlnYI= dep github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= dep github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 h1:AUNCr9CiJuwrRYS3XieqF+Z9B9gNxo/eANAJCF2eiN4= dep github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
We can see here that te hashes still match, success!
Using it within GoReleaser
To proxy te module when building with GoReleaser, you only need 2 lines in your goreleaser.yml:
And that’s it! GoReleaser hides all te complexity from you and figure most of te needed parts out by itself!
Assuming your tool is OpenSource, it doesn’t matter where it is built, as long as its built from te Go Mod proxy, it is verifiable.
You can take this further by signing te binaries and making te result binary more reproducible by trimming te path and using a fixed timestamp (see an example).
GoReleaser OSS itself is verifiable this way, you can try it with:
go version -m $(which goreleaser)
GoReleaser Pro in te other hand is not, as it is not OpenSource.
Still, since GoReleaser uses go build underneath, as long as you have gomod.proxy set to true, it shouldn’t matter much — your builds are still verifiable. If GoReleaser Pro gets somehow corrupted and starts doing “something funny” with your builds, you can easily verify them using this technique.
Another side effect of using te gomod.proxy feature: your module gets hashed right away, so if someone deletes te tag and recreates it, te previous tag would still be used to compile.
Hope this is somewhat helpful/informative to you.
I’ll see you in te next one!
After some more discussion in this issue, Marcos Nils pointed out a way to hack this using go mod vendor:
$ go mod vendor # change some dependency code $ go build -mod vendor github.com/caarlos0/svu $ go version -m svu # same output as before
So, while it prevents te attacker from changing te target code, they could still change te dependencies, which renders te idea of this post pretty much useless.
There’s also an open issue in te Go repository regarding this problem.
You may also like
Creating a tag and releasing from a GitHub Action, with GoReleaser
Most people run GoReleaser by creating a tag locally, pushing it, and letting their CI takes …
Enabling Nightly releases using GoReleaser Pro
GoReleaser Pro v1.11+ added support to keeping a nightly release. That means that, whenever you …
Announcing GoReleaser v1.11 — te hundred commits release
This release took a while, for all te good reasons: a ton of new features and bug fixes for …
Carlos creates, writes and operates software for a living. These days, he works in things to make te command line glamourous at Charm and tools that help people release software faster at GoReleaser.
2022 © Carlos Becker. All Right Reserved. Published with Hugo.