Skip to content

Commit a78890d

Browse files
committed
internal: Verify provider signatures on install
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.
1 parent 8d71337 commit a78890d

16 files changed

+1222
-66
lines changed

command/init.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,17 @@ func (c *InitCommand) getProviders(earlyConfig *earlyconfig.Config, state *state
508508
fmt.Sprintf("Error while installing %s v%s: %s.", provider.ForDisplay(), version, err),
509509
))
510510
},
511+
FetchPackageSuccess: func(provider addrs.Provider, version getproviders.Version, localDir string, authResult *getproviders.PackageAuthenticationResult) {
512+
var warning string
513+
if authResult != nil {
514+
warning = authResult.Warning
515+
}
516+
if warning != "" {
517+
warning = c.Colorize().Color(fmt.Sprintf("\n [reset][yellow]Warning: %s[reset]", warning))
518+
}
519+
520+
c.Ui.Info(fmt.Sprintf("- Installed %s v%s (%s)%s", provider.ForDisplay(), version, authResult, warning))
521+
},
511522
}
512523

513524
mode := providercache.InstallNewProvidersOnly

command/init_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,10 @@ func TestInit_providerSource(t *testing.T) {
930930
t.Errorf("wrong version selections after upgrade\n%s", diff)
931931
}
932932

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

935939
func TestInit_getUpgradePlugins(t *testing.T) {
@@ -1086,7 +1090,7 @@ func TestInit_getProviderMissing(t *testing.T) {
10861090

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

10921096
if !strings.Contains(ui.ErrorWriter.String(), "no available releases match") {
@@ -1604,7 +1608,7 @@ func installFakeProviderPackagesElsewhere(t *testing.T, cacheDir *providercache.
16041608
if err != nil {
16051609
t.Fatalf("failed to prepare fake package for %s %s: %s", name, versionStr, err)
16061610
}
1607-
err = cacheDir.InstallPackage(context.Background(), meta)
1611+
_, err = cacheDir.InstallPackage(context.Background(), meta)
16081612
if err != nil {
16091613
t.Fatalf("failed to install fake package for %s %s: %s", name, versionStr, err)
16101614
}

internal/getproviders/mock_source.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package getproviders
22

33
import (
44
"archive/zip"
5+
"crypto/sha256"
56
"fmt"
7+
"io"
68
"io/ioutil"
79
"os"
810

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

173+
// Compute the SHA256 checksum of the generated file, to allow package
174+
// authentication code to be exercised.
175+
f.Seek(0, io.SeekStart)
176+
h := sha256.New()
177+
io.Copy(h, f)
178+
checksum := [32]byte{}
179+
h.Sum(checksum[:0])
180+
171181
meta := PackageMeta{
172182
Provider: provider,
173183
Version: version,
@@ -181,6 +191,8 @@ func FakeInstallablePackageMeta(provider addrs.Provider, version Version, target
181191
// (At the time of writing, no caller actually does that, but who
182192
// knows what the future holds?)
183193
Filename: fmt.Sprintf("terraform-provider-%s_%s_%s.zip", provider.Type, version.String(), target.String()),
194+
195+
Authentication: NewArchiveChecksumAuthentication(checksum),
184196
}
185197
return meta, close, nil
186198
}

0 commit comments

Comments
 (0)