11# secure-example
22
3- This is an example repository showing how to securely release using GoReleaser
4- and GitHub Actions.
3+ This example outlines how to securely release using GoReleaser and GitHub
4+ Actions.
55
6- ## Workflows
6+ ## Components
77
8- - ` build ` : runs tests
9- - ` release ` : runs goreleaser
10- - ` security ` : security scans: grype, govulncheck, codeql, license-check
8+ We'll go over a few things: GitHub settings, GoReleaser configuration, and
9+ GitHub actions.
1110
12- ## How it works
11+ ### GitHub Settings
1312
14- GoReleaser manages the entire thing, basically.
13+ These are some things I recommend you do:
1514
16- It will:
15+ 1 . General > Require contributors to sign off on web-based commits Loading
16+ 1 . General > Enable release immutability
17+ 1 . Actions > General > Require approval for all external contributors
18+ 1 . Actions > General > Read repository contents and packages permissions
19+ 1 . Rules > New Ruleset > Import a ruleset > [ this file] ( ./.github/ruleset.json )
20+ 1 . Advanced Security > Private vulnerability reporting
21+ 1 . Advanced Security > Dependency graph
22+ 1 . Advanced Security > Automatic dependency submission
23+ 1 . Advanced Security > Dependabot security updates
1724
18- - build using the Go Mod Proxy as source of truth
19- - create archives
20- - call ` syft ` to create the SBOMs
21- - create the checksum file
22- - sign it with ` cosign `
23- - create the github release
24- - push artifacts there
25- - create a docker image (with SBOM) using the binary it just built (thus, the binary inside the docker image is the same as the one released)
26- - sign the docker image with ` cosign ` as well
25+ There's much more you can change, these are the things I usually do.
2726
28- ## Verifying
27+ ### GoReleaser Configuration
28+
29+ The [ provided configuration] ( ./.goreleaser.yaml ) is commented out and each section
30+ links to the relevant documentation, but here's a rundown:
31+
32+ - we build for a couple of platforms using the Go mod proxy
33+ - we create archives for both the binaries as well as for the source
34+ - we create and sign a checksums file (using [ Cosign] [ cosign ] )
35+ - we create SBOMs of all archives (using [ Syft] [ syft ] )
36+ - all these files are uploaded to the GitHub release
37+ - we create a Docker image manifest, which also includes SBOMs
38+ - we then sign the image
39+
40+ ### GitHub Actions
41+
42+ We have 3 workflows set up, let's go over them.
43+
44+ #### Build
45+
46+ The [ build workflow] ( ./.github/workflows/build.yml ) doesn't do much: it checks
47+ out the code, installs Go, and runs ` go test ` .
48+
49+ #### Security
50+
51+ The [ security workflow] ( ./.github/workflows/security.yml ) does a lot more, as it
52+ has a couple of jobs:
53+
54+ 1 . ` codeql ` : as the name implies, runs the recommended [ CodeQL] [ codeql ] queries for Go and
55+ Actions;
56+ 1 . ` grype ` : runs [ Grype] [ ] , which scans for known vulnerabilities;
57+ 1 . ` govulncheck ` : runs the standard [ Go vulnerability checker] [ govulncheck ] ;
58+ 1 . ` dependency-review ` : runs only on pull requests, and checks if any
59+ dependencies being added or updated are allowed.
60+
61+ All these jobs report their status using
62+ [ Static Analysis Results Interchange Format (SARIF)] ( https://sarifweb.azurewebsites.net/ ) ,
63+ so any findings will show as security alerts in the
64+ [ Security > Code scanning] ( /security/code-scanning ) tab.
65+
66+ #### Release
67+
68+ Finally, the [ release workflow] ( ./.github/workflows/release.yml ) .
69+ Its main job is to, well, release our software, and it uses [ GoReleaser] [ ] for
70+ that (surprise!).
71+ But to do that, we first set up [ Docker] [ docker ] , [ Cosign] [ cosign ] , and
72+ [ Syft] [ syft ] .
73+ Then, we run the glorious [ goreleaser-action] [ ] , which does all the heavy
74+ lifting.
75+ Then, after all is said and done, we attest our build artifacts.
76+
77+ #### Why all the SHA1s?
78+
79+ As you may have noticed, all the actions are pinned to the SHA1 of their
80+ respective tags.
81+
82+ This is recommended, as an attacker might take over an action and re-publish
83+ malicious code under the same tags.
84+
85+ Using only the major versions (like ` @v4 ` ) is also not so good, as you are then
86+ even more clueless about what is actually being run.
87+
88+ If you want to pin all the actions in your repositories, I recommend using
89+ [ pinata] [ ] .
90+
91+ ## Releasing
92+
93+ To create a new release, create and push a new tag.
94+ You can get the next semantic version using [ svu] [ ] :
95+
96+ ``` bash
97+ git tag -s $( svu n)
98+ git push --tags
99+ ```
100+
101+ And then go over to [ the actions tab] ( /actions/workflows/release.yml ) and wait
102+ for the release to finish.
103+
104+ ## Verifying the artifacts
105+
106+ Your users will need to know how to verify the artifacts, and this is what this
107+ section is all about.
108+
109+ The first thing we need to do, is get the current latest version:
29110
30111``` bash
31112export VERSION=" $( gh release list -L 1 -R goreleaser/example-secure --json=tagName -q ' .[] | .tagName' ) "
32113```
33114
34- ### Checksums
115+ Then, we download the ` checksums.txt ` file, and verify its signature:
35116
36- ``` shell
117+ ``` bash
37118wget https://github.com/goreleaser/example-secure/releases/download/$VERSION /checksums.txt
38119cosign verify-blob \
39120 --certificate-identity ' https://github.com/goreleaser/example-secure/.github/workflows/release.yml@refs/tags/$VERSION' \
@@ -43,53 +124,81 @@ cosign verify-blob \
43124 ./checksums.txt
44125```
45126
127+ This should succeed - which means that we can from now on verify any artifact
128+ from the release with this checksum file!
129+
46130You can then download any file you want from the release, and verify it with, for example:
47131
48- ``` shell
132+ ``` bash
49133wget https://github.com/goreleaser/example-secure/releases/download/$VERSION /example_linux_amd64.tar.gz
50134sha256sum --ignore-missing -c checksums.txt
51135```
52136
53- And both should say "OK".
137+ Which should, ideally, say "OK".
54138
55- ### SBOMs
56-
57- You can then inspect the ` .sbom ` file to see the entire dependency tree of the
139+ You can then inspect the SBOM file to see the entire dependency tree of the
58140binary, check for vulnerable dependencies and whatnot.
59141
60142To get the SBOM of an artifact, you can use the same download URL, adding
61- ` .sbom.json ` to the end of the URL:
143+ ` .sbom.json ` to the end of the URL, and we can then check it out with ` grype ` :
62144
63- ``` shell
145+ ``` bash
64146wget https://github.com/goreleaser/example-secure/releases/download/$VERSION /example_linux_amd64.tar.gz.sbom.json
65147sha256sum --ignore-missing -c checksums.txt
66148grype sbom:example_linux_amd64.tar.gz.sbom.json
67149```
68150
69- ### Attestations
70-
71- This example also publishes build attestations.
72- You can verify any artifact with:
151+ Finally, we can also use the ` gh ` CLI to verify the attestations:
73152
74- ``` shell
153+ ``` bash
75154gh attestation verify \
76155 --owner goreleaser \
77156 * .tar.gz
78157```
79158
80- ### Docker image
159+ Docker images are a bit simpler, you can verify them with [ Cosign] [ cosign ]
160+ and [ Grype] [ grype ] directly, and check the attestations as well.
161+
162+ Signature:
81163
82- ``` shell
164+ ``` bash
83165cosign verify \
84166 --certificate-identity ' https://github.com/goreleaser/example-secure/.github/workflows/release.yml@refs/tags/$VERSION' \
85167 --certificate-oidc-issuer ' https://token.actions.githubusercontent.com' \
86168 ghcr.io/goreleaser/example-secure:$VERSION
87169```
88170
89- The images are also attested:
171+ Vulnerabilities:
172+
173+ ``` bash
174+ grype docker:ghcr.io/goreleaser/example-secure:$VERSION
175+ ```
176+
177+ Attestations:
90178
91- ``` shell
179+ ``` bash
92180gh attestation verify \
93181 --owner goreleaser \
94182 oci://ghcr.io/goreleaser/example-secure:$VERSION
95183```
184+
185+ If all these checks are OK, you have a pretty good indication that everything
186+ is good.
187+
188+ ---
189+
190+ I really hope this helps - and please feel free to open PRs improving things you
191+ think need improving, or issues to discuss any concerns you might have.
192+
193+ Thanks for reading!
194+
195+ [ syft ] : https://github.com/anchore/syft
196+ [ cosign ] : https://github.com/sigstore/cosign
197+ [ grype ] : https://github.com/anchore/grype
198+ [ codeql ] : https://codeql.github.com/
199+ [ govulncheck ] : https://go.dev/blog/govulncheck
200+ [ goreleaser ] : https://goreleaser.com
201+ [ docker ] : https://docker.io
202+ [ pinata ] : https://github.com/caarlos0/pinata
203+ [ svu ] : https://github.com/caarlos0/svu
204+ [ goreleaser-action ] : https://github.com/goreleaser/goreleaser-action
0 commit comments