Skip to content

Commit 3405edb

Browse files
wata727bendrucker
andauthored
Introduce plugin keyless verification (#2159)
* Introduce plugin keyless verification * Update docs/user-guide/plugins.md Co-authored-by: Ben Drucker <[email protected]> --------- Co-authored-by: Ben Drucker <[email protected]>
1 parent b0a9d36 commit 3405edb

File tree

11 files changed

+1057
-85
lines changed

11 files changed

+1057
-85
lines changed

cmd/init.go

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cmd
22

33
import (
4+
"errors"
45
"fmt"
56
"log"
67
"os"
@@ -13,6 +14,10 @@ import (
1314
)
1415

1516
func (cli *CLI) init(opts Options) int {
17+
if plugin.IsExperimentalModeEnabled() {
18+
_, _ = color.New(color.FgYellow).Fprintln(cli.outStream, `Experimental mode is enabled. This behavior may change in future versions without notice`)
19+
}
20+
1621
workingDirs, err := findWorkingDirs(opts)
1722
if err != nil {
1823
cli.formatter.Print(tflint.Issues{}, fmt.Errorf("Failed to find workspaces; %w", err), map[string][]byte{})
@@ -57,14 +62,13 @@ func (cli *CLI) init(opts Options) int {
5762
builder.Reset()
5863
fmt.Fprintf(cli.outStream, "Installing \"%s\" plugin...\n", pluginCfg.Name)
5964

60-
sigchecker := plugin.NewSignatureChecker(installCfg)
61-
if !sigchecker.HasSigningKey() {
62-
_, _ = color.New(color.FgYellow).Fprintln(cli.outStream, `No signing key configured. Set "signing_key" to verify that the release is signed by the plugin developer`)
63-
}
64-
6565
_, err = installCfg.Install()
6666
if err != nil {
67-
return fmt.Errorf("Failed to install a plugin; %w", err)
67+
if errors.Is(err, plugin.ErrPluginNotVerified) {
68+
_, _ = color.New(color.FgYellow).Fprintln(cli.outStream, `No signing key configured. Set "signing_key" to verify that the release is signed by the plugin developer`)
69+
} else {
70+
return fmt.Errorf("Failed to install a plugin; %w", err)
71+
}
6872
}
6973

7074
any_installed = true

docs/developer-guide/plugins.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,12 @@ The requirements to support automatic installation are as follows:
5151
- The release must contain a checksum file for the zip file with the name `checksums.txt`
5252
- The checksum file must contain a sha256 hash and filename
5353

54-
When signing a release, the release must additionally meet the following requirements:
54+
When signing a release, one of the following requirements must be met:
5555

56-
- The release must contain a signature file for the checksum file with the name `checksums.txt.sig`
57-
- The signature file must be binary OpenPGP format
56+
- PGP signing key
57+
- The release must contain a signature file for the checksum file with the name `checksums.txt.sig`
58+
- The signature file must be binary OpenPGP format
59+
- [Artifact Attestation](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds)
60+
- Include a step in your GitHub Actions workflow that uses the [`attest-build-provenance` action](https://github.com/actions/attest-build-provenance) for `checksums.txt`.
5861

5962
Releases that meet these requirements can be easily created by following the GoReleaser config in the template repository.

docs/user-guide/environment_variables.md

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Below is a list of environment variables available in TFLint.
88
- Configure the config file path. See [Configuring TFLint](./config.md).
99
- `TFLINT_PLUGIN_DIR`
1010
- Configure the plugin directory. See [Configuring Plugins](./plugins.md).
11+
- `TFLINT_EXPERIMENTAL`
12+
- Enable experimental features. Note that experimental features are subject to change without notice. Currently only [Keyless Verification](./plugins.md#keyless-verification-experimental) are supported.
1113
- `TF_VAR_name`
1214
- Set variables for compatibility with Terraform. See [Compatibility with Terraform](./compatibility.md).
1315
- `TF_DATA_DIR`

docs/user-guide/plugins.md

+10
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ Plugin developer's PGP public signing key. When this attribute is set, TFLint wi
5454

5555
Plugins under the terraform-linters organization (AWS/GCP/Azure ruleset plugins) can use the built-in signing key, so this attribute can be omitted.
5656

57+
If the plugin developer generates [Artifact Attestation](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds), you can omit this attribute. See [Keyless Verification](#keyless-verification-experimental) for details.
58+
5759
## Plugin directory
5860

5961
Plugins are usually installed under `~/.tflint.d/plugins`. Exceptionally, if you already have `./.tflint.d/plugins` in your working directory, it will be installed there.
@@ -132,3 +134,11 @@ plugin "terraform" {
132134
```
133135

134136
If you have tflint-ruleset-terraform manually installed, the bundled plugin will not be automatically enabled. In this case the manually installed version takes precedence.
137+
138+
## Keyless verification (experimental)
139+
140+
If the plugin developer has generated [Artifact Attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds), TFLint will automatically verify them and prove that the plugin binary was built in that repository.
141+
142+
This verification is experimental and optional: it is only attempted if there is no PGP public signing key, and if there is no artifact attestation, a warning will be output, not an error. If you want to require all plugin installs to be signed with a PGP signing key or an artifact attestation, you can force this behavior to be enabled by setting the `TFLINT_EXPERIMENTAL=1`. This behavior will be the default in future versions, but is subject to change without notice.
143+
144+
Note that this validation, like the PGP signing key, does not guarantee that the plugin is secure. It only attests the source repository/revision from which it was built. It prevents direct upload of malicious release artifacts to GitHub or manipulation of download requests. If an attacker has control over the repository and can perform execution during a build, any resulting malicious release will still be considered "verified."

go.mod

+79-18
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ require (
99
github.com/fatih/color v1.18.0
1010
github.com/go-test/deep v1.1.1
1111
github.com/google/go-cmp v0.6.0
12-
github.com/google/go-github/v53 v53.2.0
12+
github.com/google/go-github/v67 v67.0.0
1313
github.com/google/uuid v1.6.0
1414
github.com/hashicorp/go-plugin v1.6.2
1515
github.com/hashicorp/go-uuid v1.0.3
@@ -21,6 +21,7 @@ require (
2121
github.com/mattn/go-colorable v0.1.13
2222
github.com/mitchellh/go-homedir v1.1.0
2323
github.com/owenrumney/go-sarif/v2 v2.3.3
24+
github.com/sigstore/sigstore-go v0.6.2
2425
github.com/sourcegraph/go-lsp v0.0.0-20200429204803-219e11d77f5d
2526
github.com/sourcegraph/jsonrpc2 v0.2.0
2627
github.com/spf13/afero v1.11.0
@@ -38,58 +39,118 @@ require (
3839
)
3940

4041
require (
41-
cloud.google.com/go v0.112.0 // indirect
42+
cloud.google.com/go v0.112.1 // indirect
4243
cloud.google.com/go/compute/metadata v0.5.0 // indirect
4344
cloud.google.com/go/iam v1.1.6 // indirect
44-
cloud.google.com/go/storage v1.36.0 // indirect
45+
cloud.google.com/go/storage v1.39.1 // indirect
4546
github.com/Masterminds/semver/v3 v3.3.0 // indirect
46-
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect
4747
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
48-
github.com/aws/aws-sdk-go v1.44.122 // indirect
48+
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
49+
github.com/aws/aws-sdk-go v1.51.6 // indirect
4950
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
50-
github.com/cloudflare/circl v1.3.7 // indirect
51+
github.com/blang/semver v3.5.1+incompatible // indirect
52+
github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 // indirect
53+
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
54+
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect
5155
github.com/felixge/httpsnoop v1.0.4 // indirect
56+
github.com/fsnotify/fsnotify v1.7.0 // indirect
57+
github.com/go-chi/chi v4.1.2+incompatible // indirect
58+
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
5259
github.com/go-logr/logr v1.4.1 // indirect
5360
github.com/go-logr/stdr v1.2.2 // indirect
61+
github.com/go-openapi/analysis v0.23.0 // indirect
62+
github.com/go-openapi/errors v0.22.0 // indirect
63+
github.com/go-openapi/jsonpointer v0.21.0 // indirect
64+
github.com/go-openapi/jsonreference v0.21.0 // indirect
65+
github.com/go-openapi/loads v0.22.0 // indirect
66+
github.com/go-openapi/runtime v0.28.0 // indirect
67+
github.com/go-openapi/spec v0.21.0 // indirect
68+
github.com/go-openapi/strfmt v0.23.0 // indirect
69+
github.com/go-openapi/swag v0.23.0 // indirect
70+
github.com/go-openapi/validate v0.24.0 // indirect
5471
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
5572
github.com/golang/protobuf v1.5.4 // indirect
73+
github.com/google/certificate-transparency-go v1.2.1 // indirect
74+
github.com/google/go-containerregistry v0.20.2 // indirect
5675
github.com/google/go-querystring v1.1.0 // indirect
5776
github.com/google/s2a-go v0.1.7 // indirect
5877
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
59-
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
78+
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
6079
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
6180
github.com/hashicorp/go-getter v1.7.6 // indirect
6281
github.com/hashicorp/go-hclog v1.6.3 // indirect
82+
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
6383
github.com/hashicorp/go-safetemp v1.0.0 // indirect
84+
github.com/hashicorp/hcl v1.0.0 // indirect
6485
github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
6586
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
6687
github.com/hashicorp/yamux v0.1.1 // indirect
88+
github.com/in-toto/attestation v1.1.0 // indirect
89+
github.com/in-toto/in-toto-golang v0.9.0 // indirect
90+
github.com/inconshreveable/mousetrap v1.1.0 // indirect
91+
github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b // indirect
6792
github.com/jmespath/go-jmespath v0.4.0 // indirect
68-
github.com/klauspost/compress v1.15.11 // indirect
93+
github.com/josharian/intern v1.0.0 // indirect
94+
github.com/klauspost/compress v1.17.4 // indirect
95+
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect
96+
github.com/magiconair/properties v1.8.7 // indirect
97+
github.com/mailru/easyjson v0.7.7 // indirect
6998
github.com/mattn/go-isatty v0.0.20 // indirect
7099
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
71100
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
101+
github.com/mitchellh/mapstructure v1.5.0 // indirect
72102
github.com/oklog/run v1.0.0 // indirect
73-
github.com/ulikunitz/xz v0.5.10 // indirect
103+
github.com/oklog/ulid v1.3.1 // indirect
104+
github.com/opencontainers/go-digest v1.0.0 // indirect
105+
github.com/opentracing/opentracing-go v1.2.0 // indirect
106+
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
107+
github.com/pkg/errors v0.9.1 // indirect
108+
github.com/sagikazarmark/locafero v0.4.0 // indirect
109+
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
110+
github.com/sassoftware/relic v7.2.1+incompatible // indirect
111+
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
112+
github.com/shibumi/go-pathspec v1.3.0 // indirect
113+
github.com/sigstore/protobuf-specs v0.3.2 // indirect
114+
github.com/sigstore/rekor v1.3.6 // indirect
115+
github.com/sigstore/sigstore v1.8.9 // indirect
116+
github.com/sigstore/timestamp-authority v1.2.2 // indirect
117+
github.com/sourcegraph/conc v0.3.0 // indirect
118+
github.com/spf13/cast v1.6.0 // indirect
119+
github.com/spf13/cobra v1.8.1 // indirect
120+
github.com/spf13/pflag v1.0.5 // indirect
121+
github.com/spf13/viper v1.18.2 // indirect
122+
github.com/subosito/gotenv v1.6.0 // indirect
123+
github.com/theupdateframework/go-tuf v0.7.0 // indirect
124+
github.com/theupdateframework/go-tuf/v2 v2.0.0 // indirect
125+
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
126+
github.com/transparency-dev/merkle v0.0.2 // indirect
127+
github.com/ulikunitz/xz v0.5.11 // indirect
74128
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
75129
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
76130
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
77131
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
132+
go.mongodb.org/mongo-driver v1.14.0 // indirect
78133
go.opencensus.io v0.24.0 // indirect
79-
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect
80-
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect
81-
go.opentelemetry.io/otel v1.22.0 // indirect
82-
go.opentelemetry.io/otel/metric v1.22.0 // indirect
83-
go.opentelemetry.io/otel/trace v1.22.0 // indirect
84-
golang.org/x/mod v0.19.0 // indirect
134+
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 // indirect
135+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 // indirect
136+
go.opentelemetry.io/otel v1.27.0 // indirect
137+
go.opentelemetry.io/otel/metric v1.27.0 // indirect
138+
go.opentelemetry.io/otel/trace v1.27.0 // indirect
139+
go.uber.org/multierr v1.11.0 // indirect
140+
go.uber.org/zap v1.27.0 // indirect
141+
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
142+
golang.org/x/mod v0.20.0 // indirect
85143
golang.org/x/sync v0.9.0 // indirect
86144
golang.org/x/sys v0.27.0 // indirect
145+
golang.org/x/term v0.26.0 // indirect
87146
golang.org/x/time v0.5.0 // indirect
88147
golang.org/x/tools v0.23.0 // indirect
89-
google.golang.org/api v0.162.0 // indirect
90-
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
148+
google.golang.org/api v0.172.0 // indirect
149+
google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect
91150
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
92151
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
93152
google.golang.org/protobuf v1.34.2 // indirect
94-
gopkg.in/yaml.v2 v2.4.0 // indirect
153+
gopkg.in/ini.v1 v1.67.0 // indirect
154+
gopkg.in/yaml.v3 v3.0.1 // indirect
155+
k8s.io/klog/v2 v2.120.1 // indirect
95156
)

0 commit comments

Comments
 (0)