plugin/discovery: verify checksum matches Registry response

This commit is contained in:
findkim 2019-03-21 11:17:15 -05:00
parent 2632ccc5d3
commit 1a32617d5e
3 changed files with 73 additions and 26 deletions

View File

@ -36,6 +36,12 @@ files using the keys downloaded from the Terraform Registry. This may mean that
the publisher of the provider removed the key it was signed with, or that the
distributed files were changed after this version was released.`
const checksumVerificationError = `Checksum verification error:
The checksum for provider distribution %q from the Terraform Registry
did not match the source (%s).
This may mean that the distributed files were changed after this version
was released.`
var httpClient *http.Client
var errVersionNotFound = errors.New("version not found")
@ -391,21 +397,21 @@ func (i *ProviderInstaller) PurgeUnused(used map[string]PluginMeta) (PluginMetaS
return removed, errs
}
func (i *ProviderInstaller) getProviderChecksum(urls *response.TerraformProviderPlatformLocation) (string, error) {
func (i *ProviderInstaller) getProviderChecksum(resp *response.TerraformProviderPlatformLocation) (string, error) {
// Get SHA256SUMS file.
shasums, err := getFile(urls.ShasumsURL)
shasums, err := getFile(resp.ShasumsURL)
if err != nil {
return "", fmt.Errorf("error fetching checksums: %s", err)
}
// Get SHA256SUMS.sig file.
signature, err := getFile(urls.ShasumsSignatureURL)
signature, err := getFile(resp.ShasumsSignatureURL)
if err != nil {
return "", fmt.Errorf("error fetching checksums signature: %s", err)
}
// Verify the GPG signature returned from the Registry.
asciiArmor := urls.SigningKeys.GPGASCIIArmor()
asciiArmor := resp.SigningKeys.GPGASCIIArmor()
signer, err := verifySig(shasums, signature, asciiArmor)
if err != nil {
log.Printf("[ERROR] error verifying signature: %s", err)
@ -430,8 +436,13 @@ func (i *ProviderInstaller) getProviderChecksum(urls *response.TerraformProvider
identity := strings.Join(identities, ", ")
log.Printf("[DEBUG] verified GPG signature with key from %s", identity)
// Extract checksum for this os/arch platform binary.
return checksumForFile(shasums, urls.Filename), nil
// Extract checksum for this os/arch platform binary and verify against Registry
checksum := checksumForFile(shasums, resp.Filename)
if checksum == "" || checksum != resp.Shasum {
return "", fmt.Errorf(checksumVerificationError, resp.Filename, resp.ShasumsURL)
}
return checksum, nil
}
func (i *ProviderInstaller) hostname() (string, error) {

View File

@ -606,13 +606,14 @@ func TestProviderChecksum(t *testing.T) {
tests := []struct {
Name string
URLs *response.TerraformProviderPlatformLocation
Resp *response.TerraformProviderPlatformLocation
Err bool
}{
{
"good",
&response.TerraformProviderPlatformLocation{
Filename: "terraform-provider-template_0.1.0_darwin_amd64.zip",
Shasum: "3c3e7df78b1f0161a3f941c271d5501f7b5e5f2c53738e7a371459712f5d4726",
ShasumsURL: "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS",
ShasumsSignatureURL: "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS.sig",
SigningKeys: response.SigningKeyList{
@ -653,32 +654,68 @@ func TestProviderChecksum(t *testing.T) {
},
true,
},
{
"mismatch checksum",
&response.TerraformProviderPlatformLocation{
Filename: "terraform-provider-template_0.1.0_darwin_amd64.zip",
Shasum: "force mismatch",
ShasumsURL: "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS",
ShasumsSignatureURL: "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS.sig",
SigningKeys: response.SigningKeyList{
GPGKeys: []*response.GPGKey{
&response.GPGKey{
ASCIIArmor: string(hashicorpKey),
},
},
},
},
true,
},
{
"missing checksum for file",
&response.TerraformProviderPlatformLocation{
Filename: "terraform-provider-template_0.1.0_darwin_amd64_missing_checksum.zip",
Shasum: "checksum",
ShasumsURL: "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS",
ShasumsSignatureURL: "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS.sig",
SigningKeys: response.SigningKeyList{
GPGKeys: []*response.GPGKey{
&response.GPGKey{
ASCIIArmor: string(hashicorpKey),
},
},
},
},
true,
},
}
i := ProviderInstaller{}
for _, test := range tests {
sha256sum, err := i.getProviderChecksum(test.URLs)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
t.Run(test.Name, func(t *testing.T) {
sha256sum, err := i.getProviderChecksum(test.Resp)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// get the expected checksum for our os/arch
sumData, err := ioutil.ReadFile("testdata/terraform-provider-template_0.1.0_SHA256SUMS")
if err != nil {
t.Fatal(err)
}
// get the expected checksum for our os/arch
sumData, err := ioutil.ReadFile("testdata/terraform-provider-template_0.1.0_SHA256SUMS")
if err != nil {
t.Fatal(err)
}
expected := checksumForFile(sumData, test.URLs.Filename)
expected := checksumForFile(sumData, test.Resp.Filename)
if sha256sum != expected {
t.Fatalf("expected: %s\ngot %s\n", sha256sum, expected)
}
if sha256sum != expected {
t.Fatalf("expected: %s\ngot %s\n", sha256sum, expected)
}
})
}
}

View File

@ -2,7 +2,6 @@ package discovery
import (
"bytes"
"log"
"strings"
"golang.org/x/crypto/openpgp"
@ -13,7 +12,7 @@ import (
func verifySig(data, sig []byte, armor string) (*openpgp.Entity, error) {
el, err := openpgp.ReadArmoredKeyRing(strings.NewReader(armor))
if err != nil {
log.Fatal(err)
return nil, err
}
return openpgp.CheckDetachedSignature(el, bytes.NewReader(data), bytes.NewReader(sig))