Skip to content

Commit

Permalink
internal: Verify provider signatures on install
Browse files Browse the repository at this point in the history
Providers installed from the registry are accompanied by a list of
checksums (the "SHA256SUMS" file), which is cryptographically signed to
allow package authentication. The process of verifying this has multiple
steps:

- First we must verify that the SHA256 hash of the package archive
  matches the expected hash. This could be done for local installations
  too, in the future.
- Next we ensure that the expected hash returned as part of the registry
  API response matches an entry in the checksum list.
- Finally we verify the cryptographic signature of the checksum list,
  using the public keys provided by the registry.

Each of these steps is implemented as a separate PackageAuthentication
type. The local archive installation mechanism uses only the archive
checksum authenticator, and the HTTP installation uses all three in the
order given.

The package authentication system now also returns a result value, which
is used by command/init to display the result of the authentication
process.

There are three tiers of signature, each of which is presented
differently to the user:

- Signatures from the embedded HashiCorp public key indicate that the
  provider is officially supported by HashiCorp;
- If the signing key is not from HashiCorp, it may have an associated
  trust signature, which indicates that the provider is from one of
  HashiCorp's trusted partners;
- Otherwise, if the signature is valid, this is a community provider.
  • Loading branch information
alisdair committed Apr 15, 2020
1 parent 8d71337 commit a78890d
Show file tree
Hide file tree
Showing 16 changed files with 1,222 additions and 66 deletions.
11 changes: 11 additions & 0 deletions command/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,17 @@ func (c *InitCommand) getProviders(earlyConfig *earlyconfig.Config, state *state
fmt.Sprintf("Error while installing %s v%s: %s.", provider.ForDisplay(), version, err),
))
},
FetchPackageSuccess: func(provider addrs.Provider, version getproviders.Version, localDir string, authResult *getproviders.PackageAuthenticationResult) {
var warning string
if authResult != nil {
warning = authResult.Warning
}
if warning != "" {
warning = c.Colorize().Color(fmt.Sprintf("\n [reset][yellow]Warning: %s[reset]", warning))
}

c.Ui.Info(fmt.Sprintf("- Installed %s v%s (%s)%s", provider.ForDisplay(), version, authResult, warning))
},
}

mode := providercache.InstallNewProvidersOnly
Expand Down
8 changes: 6 additions & 2 deletions command/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,10 @@ func TestInit_providerSource(t *testing.T) {
t.Errorf("wrong version selections after upgrade\n%s", diff)
}

outputStr := ui.OutputWriter.String()
if want := "Installed hashicorp/test v1.2.3 (verified checksum)"; !strings.Contains(outputStr, want) {
t.Fatalf("unexpected output: %s\nexpected to include %q", outputStr, want)
}
}

func TestInit_getUpgradePlugins(t *testing.T) {
Expand Down Expand Up @@ -1086,7 +1090,7 @@ func TestInit_getProviderMissing(t *testing.T) {

args := []string{}
if code := c.Run(args); code == 0 {
t.Fatalf("expceted error, got output: \n%s", ui.OutputWriter.String())
t.Fatalf("expected error, got output: \n%s", ui.OutputWriter.String())
}

if !strings.Contains(ui.ErrorWriter.String(), "no available releases match") {
Expand Down Expand Up @@ -1604,7 +1608,7 @@ func installFakeProviderPackagesElsewhere(t *testing.T, cacheDir *providercache.
if err != nil {
t.Fatalf("failed to prepare fake package for %s %s: %s", name, versionStr, err)
}
err = cacheDir.InstallPackage(context.Background(), meta)
_, err = cacheDir.InstallPackage(context.Background(), meta)
if err != nil {
t.Fatalf("failed to install fake package for %s %s: %s", name, versionStr, err)
}
Expand Down
12 changes: 12 additions & 0 deletions internal/getproviders/mock_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package getproviders

import (
"archive/zip"
"crypto/sha256"
"fmt"
"io"
"io/ioutil"
"os"

Expand Down Expand Up @@ -168,6 +170,14 @@ func FakeInstallablePackageMeta(provider addrs.Provider, version Version, target
return PackageMeta{}, close, fmt.Errorf("failed to close the mock zip file: %s", err)
}

// Compute the SHA256 checksum of the generated file, to allow package
// authentication code to be exercised.
f.Seek(0, io.SeekStart)
h := sha256.New()
io.Copy(h, f)
checksum := [32]byte{}
h.Sum(checksum[:0])

meta := PackageMeta{
Provider: provider,
Version: version,
Expand All @@ -181,6 +191,8 @@ func FakeInstallablePackageMeta(provider addrs.Provider, version Version, target
// (At the time of writing, no caller actually does that, but who
// knows what the future holds?)
Filename: fmt.Sprintf("terraform-provider-%s_%s_%s.zip", provider.Type, version.String(), target.String()),

Authentication: NewArchiveChecksumAuthentication(checksum),
}
return meta, close, nil
}
Loading

0 comments on commit a78890d

Please sign in to comment.