Skip to content

Commit

Permalink
packer: ensure versions match for remote installs
Browse files Browse the repository at this point in the history
Since we're hardening what Packer is able to load locally when it comes
to plugins, we need also to harden the installation process a bit.

While testing we noticed some remotes had published their plugins with
version mismatches between the tag and the binary.
This was not a problem in the past, as Packer did not care for this,
only the binary name was important, and the plugin could be installed
without problem.

Nowadays however, since Packer enforces the plugin version reported in
the name to be the same as the plugin self-reported version, this makes
it impossible for the installed plugin to load anymore in such an
instance.

Therefore in order to limit confusion, and so users are able to
understand the problem and report it to the plugins with that mismatch,
we reject the installations that expose this mismatch, and report it to
the user if they cannot install anything else.
  • Loading branch information
lbajolet-hashicorp committed Apr 12, 2024
1 parent 40db75f commit fc79674
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 6 deletions.
54 changes: 54 additions & 0 deletions packer/plugin-getter/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,60 @@ func (pr *Requirement) InstallLatest(opts InstallOptions) (*Installation, error)
return nil, errs
}

// Save binary to temp so we can ensure it is really the version advertised
tempOutput, err := os.CreateTemp("", fmt.Sprintf("packer-plugin*%s", opts.BinaryInstallationOptions.Ext))
if err != nil {
log.Printf("[ERROR] failed to create temp plugin executable: %s", err)
return nil, multierror.Append(errs, err)
}
tempPluginPath := tempOutput.Name()

_, err = io.Copy(tempOutput, copyFrom)
if err != nil {
log.Printf("[ERROR] failed to copy uncompressed binary to %q: %s", tempPluginPath, err)
return nil, multierror.Append(errs, err)
}

err = tempOutput.Chmod(0755)
if err != nil {
log.Printf("[ERROR] failed to change permissions of extracted binary: %s", err)
return nil, multierror.Append(errs, err)
}

// Not a problem on most platforms, but unsure Windows will let us execute an already
// open file, so we close it temporarily to avoid problems
_ = tempOutput.Close()

desc, err := GetPluginDescription(tempPluginPath)
if err != nil {
err := fmt.Errorf("failed to describe plugin binary %q: %s", tempPluginPath, err)
errs = multierror.Append(errs, err)
continue
}

descVersion, err := goversion.NewSemver(desc.Version)
if err != nil {
err := fmt.Errorf("invalid self-reported version %q: %s", desc.Version, err)
errs = multierror.Append(errs, err)
continue
}
if descVersion.Prerelease() != "" {
err := fmt.Errorf("release v%s binary reports version %q, which is unsupported. This is likely a problem with the plugin release workflows.", version, desc.Version)
errs = multierror.Append(errs, err)
continue
}

if descVersion.Compare(version) != 0 {
log.Printf("[ERROR] binary reported version (%q) is different from the expected %q, skipping", desc.Version, version.String())
continue
}

copyFrom, err = os.OpenFile(tempPluginPath, os.O_RDONLY, 0755)
if err != nil {
log.Printf("[ERROR] failed to re-open temporary plugin file %q: %s", tempPluginPath, err)
return nil, multierror.Append(errs, err)
}

outputFile, err := os.OpenFile(outputFileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
err := fmt.Errorf("failed to create %s: %w", outputFileName, err)
Expand Down
16 changes: 10 additions & 6 deletions packer/plugin-getter/plugins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,12 +201,14 @@ func TestRequirement_InstallLatest(t *testing.T) {
ChecksumFileEntries: map[string][]ChecksumFileEntry{
"2.10.0": {{
Filename: "packer-plugin-amazon_v2.10.0_x6.0_darwin_amd64.zip",
Checksum: "43156b1900dc09b026b54610c4a152edd277366a7f71ff3812583e4a35dd0d4a",
Checksum: "5763f8b5b5ed248894e8511a089cf399b96c7ef92d784fb30ee6242a7cb35bce",
}},
},
Zips: map[string]io.ReadCloser{
"github.com/hashicorp/packer-plugin-amazon/packer-plugin-amazon_v2.10.0_x6.0_darwin_amd64.zip": zipFile(map[string]string{
"packer-plugin-amazon_v2.10.0_x6.0_darwin_amd64": "v2.10.0_x6.0_darwin_amd64",
// Make the false plugin echo an output that matches a subset of `describe` for install to work
"packer-plugin-amazon_v2.10.0_x6.0_darwin_amd64": `#!/bin/sh
echo '{"version":"v2.10.0","api_version":"x6.0"}'`,
}),
},
},
Expand Down Expand Up @@ -248,12 +250,13 @@ func TestRequirement_InstallLatest(t *testing.T) {
ChecksumFileEntries: map[string][]ChecksumFileEntry{
"2.10.1": {{
Filename: "packer-plugin-amazon_v2.10.1_x6.1_darwin_amd64.zip",
Checksum: "90ca5b0f13a90238b62581bbf30bacd7e2c9af6592c7f4849627bddbcb039dec",
Checksum: "51451da5cd7f1ecd8699668d806bafe58a9222430842afbefdc62a6698dab260",
}},
},
Zips: map[string]io.ReadCloser{
"github.com/hashicorp/packer-plugin-amazon/packer-plugin-amazon_v2.10.1_x6.1_darwin_amd64.zip": zipFile(map[string]string{
"packer-plugin-amazon_v2.10.1_x6.1_darwin_amd64": "v2.10.1_x6.1_darwin_amd64",
"packer-plugin-amazon_v2.10.1_x6.1_darwin_amd64": `#!/bin/sh
echo '{"version":"v2.10.1","api_version":"x6.1"}'`,
}),
},
},
Expand Down Expand Up @@ -295,12 +298,13 @@ func TestRequirement_InstallLatest(t *testing.T) {
ChecksumFileEntries: map[string][]ChecksumFileEntry{
"2.10.0": {{
Filename: "packer-plugin-amazon_v2.10.0_x6.1_linux_amd64.zip",
Checksum: "825fc931ae0cb151df0c56be41a17a9136c4d1f1ee73ddb8ed6baa17cef31afa",
Checksum: "5196f57f37e18bfeac10168db6915caae0341bfc4168ebc3d2b959d746cebd0a",
}},
},
Zips: map[string]io.ReadCloser{
"github.com/hashicorp/packer-plugin-amazon/packer-plugin-amazon_v2.10.0_x6.1_linux_amd64.zip": zipFile(map[string]string{
"packer-plugin-amazon_v2.10.0_x6.1_linux_amd64": "v2.10.0_x6.1_linux_amd64",
"packer-plugin-amazon_v2.10.0_x6.1_linux_amd64": `#!/bin/sh
echo '{"version":"v2.10.0","api_version":"x6.1"}'`,
}),
},
},
Expand Down

0 comments on commit fc79674

Please sign in to comment.