internal/getproviders: package authenticator for our new-style hashes
Earlier we introduced a new package hashing mechanism that is compatible with both packed and unpacked packages, because it's a hash of the contents of the package rather than of the archive it's delivered in. However, we were using that only for the local selections file and not for any remote package authentication yet. The provider network mirrors protocol includes new-style hashes as a step towards transitioning over to the new hash format in all cases, so this new authenticator is here in preparation for verifying the checksums of packages coming from network mirrors, for mirrors that support them. For now this leaves us in a kinda confusing situation where we have both NewPackageHashAuthentication for the new style and NewArchiveChecksumAuthentication for the old style, which for the moment is represented only by a doc comment on the latter. Hopefully we can remove NewArchiveChecksumAuthentication in a future commit, if we can get the registry updated to use the new hashing format.
This commit is contained in:
parent
23a8bdd522
commit
146e983c36
|
@ -114,6 +114,44 @@ func (checks packageAuthenticationAll) AuthenticatePackage(localLocation Package
|
|||
return authResult, nil
|
||||
}
|
||||
|
||||
type packageHashAuthentication struct {
|
||||
RequiredHash string
|
||||
}
|
||||
|
||||
// NewPackageHashAuthentication returns a PackageAuthentication implementation
|
||||
// that checks whether the contents of the package match whichever of the
|
||||
// given hashes is most preferred by the current version of Terraform.
|
||||
//
|
||||
// This uses the hash algorithms implemented by functions Hash and MatchesHash.
|
||||
// The PreferredHash function will select which of the given hashes is
|
||||
// considered by Terraform to be the strongest verification, and authentication
|
||||
// succeeds as long as that chosen hash matches.
|
||||
func NewPackageHashAuthentication(validHashes []string) PackageAuthentication {
|
||||
requiredHash := PreferredHash(validHashes)
|
||||
return packageHashAuthentication{
|
||||
RequiredHash: requiredHash,
|
||||
}
|
||||
}
|
||||
|
||||
func (a packageHashAuthentication) AuthenticatePackage(localLocation PackageLocation) (*PackageAuthenticationResult, error) {
|
||||
if a.RequiredHash == "" {
|
||||
// Indicates that none of the hashes given to
|
||||
// NewPackageHashAuthentication were considered to be usable by this
|
||||
// version of Terraform.
|
||||
return nil, fmt.Errorf("this version of Terraform does not support any of the checksum formats given for this provider")
|
||||
}
|
||||
|
||||
matches, err := PackageMatchesHash(localLocation, a.RequiredHash)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to verify provider package checksums: %s", err)
|
||||
}
|
||||
|
||||
if matches {
|
||||
return &PackageAuthenticationResult{result: verifiedChecksum}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("provider package doesn't match the expected checksum %q", a.RequiredHash)
|
||||
}
|
||||
|
||||
type archiveHashAuthentication struct {
|
||||
WantSHA256Sum [sha256.Size]byte
|
||||
}
|
||||
|
@ -127,6 +165,10 @@ type archiveHashAuthentication struct {
|
|||
// (represented by PackageLocalDir) does not retain access to the original
|
||||
// source archive. Therefore this authenticator will return an error if its
|
||||
// given localLocation is not PackageLocalArchive.
|
||||
//
|
||||
// NewPackageHashAuthentication is preferable to use when possible because
|
||||
// it uses the newer hashing scheme (implemented by function Hash) that
|
||||
// can work with both packed and unpacked provider packages.
|
||||
func NewArchiveChecksumAuthentication(wantSHA256Sum [sha256.Size]byte) PackageAuthentication {
|
||||
return archiveHashAuthentication{wantSHA256Sum}
|
||||
}
|
||||
|
|
|
@ -97,6 +97,67 @@ func TestPackageAuthenticationAll_failure(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Package hash authentication requires a zip file or directory fixture and a
|
||||
// known-good set of hashes, of which the authenticator will pick one. The
|
||||
// result should be "verified checksum".
|
||||
func TestPackageHashAuthentication_success(t *testing.T) {
|
||||
// Location must be a PackageLocalArchive path
|
||||
location := PackageLocalDir("testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/2.0.0/linux_amd64")
|
||||
|
||||
wantHashes := []string{
|
||||
// Known-good HashV1 result for this directory
|
||||
"h1:qjsREM4DqEWECD43FcPqddZ9oxCG+IaMTxvWPciS05g=",
|
||||
}
|
||||
|
||||
auth := NewPackageHashAuthentication(wantHashes)
|
||||
result, err := auth.AuthenticatePackage(location)
|
||||
|
||||
wantResult := PackageAuthenticationResult{result: verifiedChecksum}
|
||||
if result == nil || *result != wantResult {
|
||||
t.Errorf("wrong result: got %#v, want %#v", result, wantResult)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("wrong err: got %s, want nil", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Package has authentication can fail for various reasons.
|
||||
func TestPackageHashAuthentication_failure(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
location PackageLocation
|
||||
err string
|
||||
}{
|
||||
"missing file": {
|
||||
PackageLocalArchive("testdata/no-package-here.zip"),
|
||||
"failed to verify provider package checksums: lstat testdata/no-package-here.zip: no such file or directory",
|
||||
},
|
||||
"checksum mismatch": {
|
||||
PackageLocalDir("testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/2.0.0/linux_amd64"),
|
||||
"provider package doesn't match the expected checksum \"h1:invalid\"",
|
||||
},
|
||||
"invalid zip file": {
|
||||
PackageLocalArchive("testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/terraform-provider-null_2.1.0_linux_amd64.zip"),
|
||||
"failed to verify provider package checksums: zip: not a valid zip file",
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Empty expected hash, either because we'll error before we
|
||||
// reach it, or we want to force a checksum mismatch.
|
||||
auth := NewPackageHashAuthentication([]string{"h1:invalid"})
|
||||
result, err := auth.AuthenticatePackage(test.location)
|
||||
|
||||
if result != nil {
|
||||
t.Errorf("wrong result: got %#v, want nil", result)
|
||||
}
|
||||
if gotErr := err.Error(); gotErr != test.err {
|
||||
t.Errorf("wrong err: got %q, want %q", gotErr, test.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Archive checksum authentication requires a file fixture and a known-good
|
||||
// SHA256 hash. The result should be "verified checksum".
|
||||
func TestArchiveChecksumAuthentication_success(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue