blob: 69c51f036c675a429a4db5b3787ef3d1caeaedc3 [file] [log] [blame]
package tfinstall
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/hashicorp/go-getter"
"golang.org/x/crypto/openpgp"
)
func ensureInstallDir(installDir string) (string, error) {
if installDir == "" {
return ioutil.TempDir("", "tfexec")
}
if _, err := os.Stat(installDir); err != nil {
return "", fmt.Errorf("could not access directory %s for installing Terraform: %w", installDir, err)
}
return installDir, nil
}
func downloadWithVerification(ctx context.Context, tfVersion string, installDir string, appendUserAgent string) (string, error) {
osName := runtime.GOOS
archName := runtime.GOARCH
// setup: ensure we have a place to put our downloaded terraform binary
tfDir, err := ensureInstallDir(installDir)
if err != nil {
return "", err
}
httpGetter := &getter.HttpGetter{
Netrc: true,
Client: newHTTPClient(appendUserAgent),
}
client := getter.Client{
Ctx: ctx,
Getters: map[string]getter.Getter{
"https": httpGetter,
},
}
client.Mode = getter.ClientModeAny
// firstly, download and verify the signature of the checksum file
sumsTmpDir, err := ioutil.TempDir("", "tfinstall")
if err != nil {
return "", err
}
defer os.RemoveAll(sumsTmpDir)
sumsFilename := "terraform_" + tfVersion + "_SHA256SUMS"
sumsSigFilename := sumsFilename + ".72D7468F.sig"
sumsURL := fmt.Sprintf("%s/%s/%s", baseURL, tfVersion, sumsFilename)
sumsSigURL := fmt.Sprintf("%s/%s/%s", baseURL, tfVersion, sumsSigFilename)
client.Src = sumsURL
client.Dst = sumsTmpDir
err = client.Get()
if err != nil {
return "", fmt.Errorf("error fetching checksums at URL %s: %w", sumsURL, err)
}
client.Src = sumsSigURL
err = client.Get()
if err != nil {
return "", fmt.Errorf("error fetching checksums signature: %s", err)
}
sumsPath := filepath.Join(sumsTmpDir, sumsFilename)
sumsSigPath := filepath.Join(sumsTmpDir, sumsSigFilename)
err = verifySumsSignature(sumsPath, sumsSigPath)
if err != nil {
return "", err
}
// secondly, download Terraform itself, verifying the checksum
url := tfURL(tfVersion, osName, archName)
client.Src = url
client.Dst = tfDir
client.Mode = getter.ClientModeDir
err = client.Get()
if err != nil {
return "", err
}
return filepath.Join(tfDir, "terraform"), nil
}
// verifySumsSignature downloads SHA256SUMS and SHA256SUMS.sig and verifies
// the signature using the HashiCorp public key.
func verifySumsSignature(sumsPath, sumsSigPath string) error {
el, err := openpgp.ReadArmoredKeyRing(strings.NewReader(hashicorpPublicKey))
if err != nil {
return err
}
data, err := os.Open(sumsPath)
if err != nil {
return err
}
sig, err := os.Open(sumsSigPath)
if err != nil {
return err
}
_, err = openpgp.CheckDetachedSignature(el, data, sig)
return err
}
func tfURL(tfVersion, osName, archName string) string {
sumsFilename := "terraform_" + tfVersion + "_SHA256SUMS"
sumsURL := fmt.Sprintf("%s/%s/%s", baseURL, tfVersion, sumsFilename)
return fmt.Sprintf(
"%s/%s/terraform_%s_%s_%s.zip?checksum=file:%s",
baseURL, tfVersion, tfVersion, osName, archName, sumsURL,
)
}