Skip to content

Commit

Permalink
step_port_forward: add access to localhost if necessary
Browse files Browse the repository at this point in the history
Fixes #104

For Virtualbox versions >= 7.0, access to the host's network
(via 10.0.2.2) is blocked by default unless the
`--nat-localhostreachableN=on` option is used.
  • Loading branch information
pdietl authored and lbajolet-hashicorp committed Feb 29, 2024
1 parent 1e59b35 commit 51cdce6
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 0 deletions.
51 changes: 51 additions & 0 deletions builder/virtualbox/common/step_port_forwarding.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import (
"context"
"fmt"
"log"
"os"
"strings"

"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/net"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"golang.org/x/mod/semver"
)

// This step adds a NAT port forwarding definition so that SSH or WinRM is available
Expand All @@ -34,6 +36,48 @@ type StepPortForwarding struct {
l *net.Listener
}

var vboxVerMinNeedLHReachable string

func init() {
vboxVerMinNeedLHReachable = semver.Canonical("v7.0")
if vboxVerMinNeedLHReachable == "" {
fmt.Fprintf(os.Stderr, "Constant version is invalid; this is a bug with the VirtualBox plugin, please open an issue to report it.\n")
os.Exit(1)
}
}

func addAccessToLocalhost(state multistep.StateBag) error {
driver := state.Get("driver").(Driver)
vmName := state.Get("vmName").(string)

vboxVer, err := driver.Version()
if err != nil {
return fmt.Errorf("Error getting VirtualBox version: %s", err)
}
if !strings.HasPrefix(vboxVer, "v") {
vboxVer = "v" + vboxVer
}
if !semver.IsValid(vboxVer) {
return fmt.Errorf("The VirtualBox version isn't a valid SemVer: %s", vboxVer)
}

vboxVer = semver.Canonical(vboxVer)

if semver.Compare(vboxVer, vboxVerMinNeedLHReachable) >= 0 {
command := []string{
"modifyvm", vmName,
"--nat-localhostreachable1",
"on",
}
if err := driver.VBoxManage(command...); err != nil {
return fmt.Errorf("Failed to configure host's local network as reachable for NAT interface: %s", err)
}
log.Printf("[TRACE] VirtualBox's version (%q) is >= %q, setting --nat-localhostreachable1 on", vboxVer, vboxVerMinNeedLHReachable)
}

return nil
}

func (s *StepPortForwarding) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packersdk.Ui)
Expand Down Expand Up @@ -80,6 +124,13 @@ func (s *StepPortForwarding) Run(ctx context.Context, state multistep.StateBag)
return multistep.ActionHalt
}

// Add the `--nat-localhostreachableN=on` option if necessary
if err := addAccessToLocalhost(state); err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}

// Create a forwarded port mapping to the VM
ui.Say(fmt.Sprintf("Creating forwarded port mapping for communicator (SSH, WinRM, etc) (host port %d)", commHostPort))
command = []string{
Expand Down
79 changes: 79 additions & 0 deletions builder/virtualbox/common/step_port_forwarding_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package common

import (
"reflect"
"testing"
)

func TestVirtualboxVersionIsAnInValidSemver(t *testing.T) {
state := testState(t)

state.Put("vmName", "foo")

driver := state.Get("driver").(*DriverMock)
driver.VersionResult = "v7.0.0abcd"
driver.VersionErr = nil

err := addAccessToLocalhost(state)

if err == nil {
t.Fatalf("We expected a failure but we got a success!")
}
}

func TestVirtualboxVersionNeedsLocalhostAccessFlag(t *testing.T) {
const versionRequiringFlag = "v7.0"
const vmName = "foo"

state := testState(t)

state.Put("vmName", vmName)

driver := state.Get("driver").(*DriverMock)
driver.VersionResult = versionRequiringFlag
driver.VersionErr = nil

err := addAccessToLocalhost(state)

if err != nil {
t.Fatalf("Unexpected failure with VBox version '%v': %v", versionRequiringFlag, err)
}

if len(driver.VBoxManageCalls) == 0 {
t.Fatal("VBoxManage wasn't called!")
} else if len(driver.VBoxManageCalls) > 1 {
t.Fatalf("Expected VBoxManage to be called once, but it was called %v times!", len(driver.VBoxManageCalls))
}

expectedArgs := []string{"modifyvm", vmName, "--nat-localhostreachable1", "on"}
args := driver.VBoxManageCalls[0]
if !reflect.DeepEqual(args, expectedArgs) {
t.Fatalf("Expected VBoxManage to be called with arguments %v, but it was called with arguments %v!", expectedArgs, args)
}
}

func TestVirtualboxVersionDoesNotNeedLocalhostAccessFlag(t *testing.T) {
const versionNotRequiringFlag = "v6.9"
const vmName = "foo"

state := testState(t)

state.Put("vmName", vmName)

driver := state.Get("driver").(*DriverMock)
driver.VersionResult = versionNotRequiringFlag
driver.VersionErr = nil

err := addAccessToLocalhost(state)

if err != nil {
t.Fatalf("Unexpected failure with VBox version '%v': %v", versionNotRequiringFlag, err)
}

if len(driver.VBoxManageCalls) != 0 {
t.Fatalf("Expected VBoxManage not to be called, but it was called %v times!", len(driver.VBoxManageCalls))
}
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/hashicorp/packer-plugin-sdk v0.5.2
github.com/stretchr/testify v1.8.3
github.com/zclconf/go-cty v1.13.3
golang.org/x/mod v0.13.0
)

require (
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,8 @@ golang.org/x/mobile v0.0.0-20210901025245-1fde1d6c3ca1 h1:t3ZHqovedSY8DEAUmZA99f
golang.org/x/mobile v0.0.0-20210901025245-1fde1d6c3ca1/go.mod h1:jFTmtFYCV0MFtXBU+J5V/+5AUeVS0ON/0WkE/KSrl6E=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down

0 comments on commit 51cdce6

Please sign in to comment.