Skip to content

Commit

Permalink
Introduce plugin keyless verification
Browse files Browse the repository at this point in the history
  • Loading branch information
wata727 committed Dec 1, 2024
1 parent e40c538 commit f0b8d52
Show file tree
Hide file tree
Showing 11 changed files with 1,057 additions and 85 deletions.
16 changes: 10 additions & 6 deletions cmd/init.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"errors"
"fmt"
"os"

Expand All @@ -11,6 +12,10 @@ import (
)

func (cli *CLI) init(opts Options) int {
if plugin.IsExperimentalModeEnabled() {
_, _ = color.New(color.FgYellow).Fprintln(cli.outStream, `Experimental mode is enabled. This behavior may change in future versions without notice`)
}

workingDirs, err := findWorkingDirs(opts)
if err != nil {
cli.formatter.Print(tflint.Issues{}, fmt.Errorf("Failed to find workspaces; %w", err), map[string][]byte{})
Expand Down Expand Up @@ -47,14 +52,13 @@ func (cli *CLI) init(opts Options) int {
if os.IsNotExist(err) {
fmt.Fprintf(cli.outStream, "Installing \"%s\" plugin...\n", pluginCfg.Name)

sigchecker := plugin.NewSignatureChecker(installCfg)
if !sigchecker.HasSigningKey() {
_, _ = 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`)
}

_, err = installCfg.Install()
if err != nil {
return fmt.Errorf("Failed to install a plugin; %w", err)
if errors.Is(err, plugin.ErrPluginNotVerified) {
_, _ = 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`)
} else {
return fmt.Errorf("Failed to install a plugin; %w", err)
}
}

fmt.Fprintf(cli.outStream, "Installed \"%s\" (source: %s, version: %s)\n", pluginCfg.Name, pluginCfg.Source, pluginCfg.Version)
Expand Down
9 changes: 6 additions & 3 deletions docs/developer-guide/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ The requirements to support automatic installation are as follows:
- The release must contain a checksum file for the zip file with the name `checksums.txt`
- The checksum file must contain a sha256 hash and filename

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

- The release must contain a signature file for the checksum file with the name `checksums.txt.sig`
- The signature file must be binary OpenPGP format
- PGP signing key
- The release must contain a signature file for the checksum file with the name `checksums.txt.sig`
- The signature file must be binary OpenPGP format
- [Artifact Attestation](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds)
- 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`.

Releases that meet these requirements can be easily created by following the GoReleaser config in the template repository.
2 changes: 2 additions & 0 deletions docs/user-guide/environment_variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Below is a list of environment variables available in TFLint.
- Configure the config file path. See [Configuring TFLint](./config.md).
- `TFLINT_PLUGIN_DIR`
- Configure the plugin directory. See [Configuring Plugins](./plugins.md).
- `TFLINT_EXPERIMENTAL`
- Enable experimental features. Note that experimental features are subject to change without notice. Currently only [Keyless Verification](./plugins.md#keyless-verification-experimental) are supported.
- `TF_VAR_name`
- Set variables for compatibility with Terraform. See [Compatibility with Terraform](./compatibility.md).
- `TF_DATA_DIR`
Expand Down
10 changes: 10 additions & 0 deletions docs/user-guide/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ Plugin developer's PGP public signing key. When this attribute is set, TFLint wi

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

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.

## Plugin directory

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.
Expand Down Expand Up @@ -132,3 +134,11 @@ plugin "terraform" {
```

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.

## Keyless verification (experimental)

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.

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.

Note that this validation, like the PGP signing key, does not guarantee that the plugin is secure. Moreover it only guarantees the repository it was built from, not the signer, so it is not secure if an attacker has control over the repository.
97 changes: 79 additions & 18 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/fatih/color v1.18.0
github.com/go-test/deep v1.1.1
github.com/google/go-cmp v0.6.0
github.com/google/go-github/v53 v53.2.0
github.com/google/go-github/v67 v67.0.0
github.com/google/uuid v1.6.0
github.com/hashicorp/go-plugin v1.6.2
github.com/hashicorp/go-uuid v1.0.3
Expand All @@ -21,6 +21,7 @@ require (
github.com/mattn/go-colorable v0.1.13
github.com/mitchellh/go-homedir v1.1.0
github.com/owenrumney/go-sarif/v2 v2.3.3
github.com/sigstore/sigstore-go v0.6.2
github.com/sourcegraph/go-lsp v0.0.0-20200429204803-219e11d77f5d
github.com/sourcegraph/jsonrpc2 v0.2.0
github.com/spf13/afero v1.11.0
Expand All @@ -38,58 +39,118 @@ require (
)

require (
cloud.google.com/go v0.112.0 // indirect
cloud.google.com/go v0.112.1 // indirect
cloud.google.com/go/compute/metadata v0.5.0 // indirect
cloud.google.com/go/iam v1.1.6 // indirect
cloud.google.com/go/storage v1.36.0 // indirect
cloud.google.com/go/storage v1.39.1 // indirect
github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/aws/aws-sdk-go v1.44.122 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go v1.51.6 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 // indirect
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.23.0 // indirect
github.com/go-openapi/errors v0.22.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/loads v0.22.0 // indirect
github.com/go-openapi/runtime v0.28.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/strfmt v0.23.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/validate v0.24.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/certificate-transparency-go v1.2.1 // indirect
github.com/google/go-containerregistry v0.20.2 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-getter v1.7.6 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/in-toto/attestation v1.1.0 // indirect
github.com/in-toto/in-toto-golang v0.9.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/klauspost/compress v1.15.11 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sassoftware/relic v7.2.1+incompatible // indirect
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/sigstore/protobuf-specs v0.3.2 // indirect
github.com/sigstore/rekor v1.3.6 // indirect
github.com/sigstore/sigstore v1.8.9 // indirect
github.com/sigstore/timestamp-authority v1.2.2 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.18.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/theupdateframework/go-tuf v0.7.0 // indirect
github.com/theupdateframework/go-tuf/v2 v2.0.0 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/transparency-dev/merkle v0.0.2 // indirect
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect
go.opentelemetry.io/otel v1.22.0 // indirect
go.opentelemetry.io/otel/metric v1.22.0 // indirect
go.opentelemetry.io/otel/trace v1.22.0 // indirect
golang.org/x/mod v0.19.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 // indirect
go.opentelemetry.io/otel v1.27.0 // indirect
go.opentelemetry.io/otel/metric v1.27.0 // indirect
go.opentelemetry.io/otel/trace v1.27.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/term v0.26.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.23.0 // indirect
google.golang.org/api v0.162.0 // indirect
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/api v0.172.0 // indirect
google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
)
Loading

0 comments on commit f0b8d52

Please sign in to comment.