Skip to content

Commit

Permalink
Execute add/remove registry entries in elevated mode with hyperv
Browse files Browse the repository at this point in the history
Signed-off-by: lstocchi <[email protected]>
  • Loading branch information
lstocchi committed Dec 11, 2024
1 parent 7b35f4f commit 369163b
Show file tree
Hide file tree
Showing 8 changed files with 248 additions and 146 deletions.
4 changes: 2 additions & 2 deletions build_windows.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,8 @@ When `machine init` completes, run `machine start`:
.\bin\windows\podman.exe machine start
```

:information_source: If the virtualization provider is Hyperv-V, execute the
above commands in an administrator terminal.
:information_source: If the virtualization provider is Hyperv-V, you will be asked
to elevate privileges to add/remove specific Windows Registry podman settings

### Run a container using podman

Expand Down
42 changes: 23 additions & 19 deletions pkg/machine/hyperv/stubber.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,26 +55,31 @@ func (h HyperVStubber) CreateVM(opts define.CreateVMOpts, mc *vmconfigs.MachineC
Memory: uint64(mc.Resources.Memory),
}

networkHVSock, err := vsock.NewHVSockRegistryEntry(mc.Name, vsock.Network)
networkHVSock, err := vsock.CreateHVSockRegistryEntry(mc.Name, vsock.Network)
if err != nil {
return err
}

mc.HyperVHypervisor.NetworkVSock = *networkHVSock

// Add vsock port numbers to mounts
err = createShares(mc)
// Create vsock port numbers to mounts
sharesVsock, err := createShares(mc)
if err != nil {
return err
}

removeShareCallBack := func() error {
return removeShares(mc)
// Add all vsock
err = vsock.AddHVSockRegistryEntries(append([]vsock.HVSockRegistryEntry{
mc.HyperVHypervisor.ReadyVsock,
mc.HyperVHypervisor.NetworkVSock,
}, sharesVsock...))
if err != nil {
return err
}
callbackFuncs.Add(removeShareCallBack)

removeRegistrySockets := func() error {
removeNetworkAndReadySocketsFromRegistry(mc)
sockets := getVsockShares(mc)
removeSocketsFromRegistry(mc, sockets)
return nil
}
callbackFuncs.Add(removeRegistrySockets)
Expand Down Expand Up @@ -144,7 +149,7 @@ func (h HyperVStubber) Remove(mc *vmconfigs.MachineConfig) ([]string, func() err

rmFunc := func() error {
// Tear down vsocks
removeNetworkAndReadySocketsFromRegistry(mc)
removeSocketsFromRegistry(mc, []vsock.HVSockRegistryEntry{})

// Remove ignition registry entries - not a fatal error
// for vm removal
Expand Down Expand Up @@ -358,7 +363,7 @@ func (h HyperVStubber) PrepareIgnition(mc *vmconfigs.MachineConfig, ignBuilder *
// simply be derived. So we create the HyperVConfig here.
mc.HyperVHypervisor = new(vmconfigs.HyperVConfig)
var ignOpts ignition.ReadyUnitOpts
readySock, err := vsock.NewHVSockRegistryEntry(mc.Name, vsock.Events)
readySock, err := vsock.CreateHVSockRegistryEntry(mc.Name, vsock.Events)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -461,17 +466,16 @@ func resizeDisk(newSize strongunits.GiB, imagePath *define.VMFile) error {
return nil
}

// removeNetworkAndReadySocketsFromRegistry removes the Network and Ready sockets
// removeSocketsFromRegistry removes the Network, Ready and others (passed by the caller) sockets
// from the Windows Registry
func removeNetworkAndReadySocketsFromRegistry(mc *vmconfigs.MachineConfig) {
// Remove the HVSOCK for networking
if err := mc.HyperVHypervisor.NetworkVSock.Remove(); err != nil {
logrus.Errorf("unable to remove registry entry for %s: %q", mc.HyperVHypervisor.NetworkVSock.KeyName, err)
}

// Remove the HVSOCK for events
if err := mc.HyperVHypervisor.ReadyVsock.Remove(); err != nil {
logrus.Errorf("unable to remove registry entry for %s: %q", mc.HyperVHypervisor.ReadyVsock.KeyName, err)
func removeSocketsFromRegistry(mc *vmconfigs.MachineConfig, others []vsock.HVSockRegistryEntry) {
// remove all sockets from registry
err := vsock.RemoveHVSockRegistryEntries(append([]vsock.HVSockRegistryEntry{
mc.HyperVHypervisor.ReadyVsock,
mc.HyperVHypervisor.NetworkVSock,
}, others...))
if err != nil {
logrus.Errorf("unable to remove registry entries: %q", err)
}
}

Expand Down
23 changes: 10 additions & 13 deletions pkg/machine/hyperv/volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import (
"github.com/sirupsen/logrus"
)

func removeShares(mc *vmconfigs.MachineConfig) error {
var removalErr error
func getVsockShares(mc *vmconfigs.MachineConfig) []vsock.HVSockRegistryEntry {
entries := []vsock.HVSockRegistryEntry{}

for _, mount := range mc.Mounts {
if mount.VSockNumber == nil {
Expand All @@ -29,15 +29,10 @@ func removeShares(mc *vmconfigs.MachineConfig) error {
continue
}

if err := vsockReg.Remove(); err != nil {
if removalErr != nil {
logrus.Errorf("Error removing vsock: %v", removalErr)
}
removalErr = fmt.Errorf("removing vsock %d for mountpoint %s: %w", *mount.VSockNumber, mount.Target, err)
}
entries = append(entries, *vsockReg)
}

return removalErr
return entries
}

func startShares(mc *vmconfigs.MachineConfig) error {
Expand Down Expand Up @@ -70,14 +65,16 @@ func startShares(mc *vmconfigs.MachineConfig) error {
return nil
}

func createShares(mc *vmconfigs.MachineConfig) (err error) {
func createShares(mc *vmconfigs.MachineConfig) ([]vsock.HVSockRegistryEntry, error) {
vsockEntries := []vsock.HVSockRegistryEntry{}
for _, mount := range mc.Mounts {
testVsock, err := vsock.NewHVSockRegistryEntry(mc.Name, vsock.Fileserver)
testVsock, err := vsock.CreateHVSockRegistryEntry(mc.Name, vsock.Fileserver)
if err != nil {
return err
return nil, err
}
vsockEntries = append(vsockEntries, *testVsock)
mount.VSockNumber = &testVsock.Port
logrus.Debugf("Going to share directory %s via 9p on vsock %d", mount.Source, testVsock.Port)
}
return nil
return vsockEntries, nil
}
102 changes: 97 additions & 5 deletions pkg/machine/hyperv/vsock/vsock.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import (
"fmt"
"io"
"net"
"os"
"os/exec"
"strings"

"github.com/Microsoft/go-winio"
"github.com/containers/podman/v5/pkg/machine/sockets"
"github.com/containers/podman/v5/pkg/machine/windows"
"github.com/containers/podman/v5/utils"
"github.com/sirupsen/logrus"
"golang.org/x/sys/windows/registry"
Expand Down Expand Up @@ -83,7 +86,7 @@ type HVSockRegistryEntry struct {
}

// Add creates a new Windows registry entry with string values from the
// HVSockRegistryEntry.
// HVSockRegistryEntry. Must have elevated rights.
func (hv *HVSockRegistryEntry) Add() error {
if err := hv.validate(); err != nil {
return err
Expand Down Expand Up @@ -186,9 +189,9 @@ func findOpenHVSockPort() (uint64, error) {
return 0, errors.New("unable to find a free port for hvsock use")
}

// NewHVSockRegistryEntry is a constructor to make a new registry entry in Windows. After making the new
// object, you must call the add() method to *actually* add it to the Windows registry.
func NewHVSockRegistryEntry(machineName string, purpose HVSockPurpose) (*HVSockRegistryEntry, error) {
// CreateHVSockRegistryEntry is a constructor to make an instance of a registry entry in Windows. After making the new
// object, you must call the add() method or AddHVSockRegistryEntries(...) to *actually* add it to the Windows registry.
func CreateHVSockRegistryEntry(machineName string, purpose HVSockPurpose) (*HVSockRegistryEntry, error) {
// a so-called wildcard entry ... everything from FACB -> 6D3 is MS special sauce
// for a " linux vm". this first segment is hexi for the hvsock port number
// 00000400-FACB-11E6-BD58-64006A7986D3
Expand All @@ -202,10 +205,99 @@ func NewHVSockRegistryEntry(machineName string, purpose HVSockPurpose) (*HVSockR
Port: port,
MachineName: machineName,
}

return &r, nil
}

// NewHVSockRegistryEntry is a constructor to make a new registry entry in Windows. After making the new
// object, it calls the add() method to *actually* add it to the Windows registry.
func NewHVSockRegistryEntry(machineName string, purpose HVSockPurpose) (*HVSockRegistryEntry, error) {
r, err := CreateHVSockRegistryEntry(machineName, purpose)
if err != nil {
return nil, err
}

if err := r.Add(); err != nil {
return nil, err
}
return &r, nil

return r, nil
}

// AddHVSockRegistryEntries allows to *actually* add multiple registry entries to the Windows registry
// As adding an entry to the HKLM path in the Registry requires elevated privileges, this func can be used for bulk insertion so to
// ask the user for elevated rights only once
func AddHVSockRegistryEntries(entries []HVSockRegistryEntry) error {
// create a script which will be executed with elevated rights
script := ""
for _, entry := range entries {
if err := entry.validate(); err != nil {
return err
}
exists, err := entry.exists()
if err != nil {
return err
}
if exists {
return fmt.Errorf("%q: %s", ErrVSockRegistryEntryExists, entry.KeyName)
}
parentKey, err := registry.OpenKey(registry.LOCAL_MACHINE, VsockRegistryPath, registry.QUERY_VALUE)
defer func() {
if err := parentKey.Close(); err != nil {
logrus.Error(err)
}
}()
if err != nil {
return err
}

// for each entry it adds a purpose and machineName property
registryPath := fmt.Sprintf("HKLM:\\%s", VsockRegistryPath)
keyPath := fmt.Sprintf("%s\\%s", registryPath, entry.KeyName)

createRegistryKeyCmd := fmt.Sprintf("New-Item -Path '%s' -Name '%s'", registryPath, entry.KeyName)
addPurposePropertyCmd := fmt.Sprintf("New-ItemProperty -Path '%s' -Name '%s' -Value '%s' -PropertyType String", keyPath, HvsockPurpose, entry.Purpose.string())
addMachinePropertyCmd := fmt.Sprintf("New-ItemProperty -Path '%s' -Name '%s' -Value '%s' -PropertyType String", keyPath, HvsockMachineName, entry.MachineName)

script += fmt.Sprintf("%s; %s; %s;", createRegistryKeyCmd, addPurposePropertyCmd, addMachinePropertyCmd)
}

// launch the script in elevated mode
return launchElevated(script)
}

// RemoveHVSockRegistryEntries allows to *actually* remove multiple registry entries from the Windows registry
// As removing an entry from the HKLM path in the Registry requires elevated privileges, this func can be used for bulk deletion so to
// ask the user for elevated rights only once
func RemoveHVSockRegistryEntries(entries []HVSockRegistryEntry) error {
// create a script which will be executed with elevated rights
script := ""
for _, entry := range entries {
// for each entry it calculate the path and the script to remove it
registryPath := fmt.Sprintf("HKLM:\\%s", VsockRegistryPath)
keyPath := fmt.Sprintf("%s\\%s", registryPath, entry.KeyName)

removeRegistryKeyCmd := fmt.Sprintf("Remove-Item -Path '%s' -Force -Recurse", keyPath)

script += fmt.Sprintf("%s;", removeRegistryKeyCmd)
}

// launch the script in elevated mode
return launchElevated(script)
}

func launchElevated(args string) error {
psPath, err := exec.LookPath("powershell.exe")
if err != nil {
return err
}

d, err := os.Getwd()
if err != nil {
return err
}

return windows.LaunchElevatedWait(psPath, d, args)
}

func portToKeyName(port uint64) string {
Expand Down
3 changes: 0 additions & 3 deletions pkg/machine/provider/platform_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ func Get() (vmconfigs.VMProvider, error) {
case define.WSLVirt:
return new(wsl.WSLStubber), nil
case define.HyperVVirt:
if !wsl.HasAdminRights() {
return nil, fmt.Errorf("hyperv machines require admin authority")
}
return new(hyperv.HyperVStubber), nil
default:
return nil, fmt.Errorf("unsupported virtualization provider: `%s`", resolvedVMType.String())
Expand Down
Loading

0 comments on commit 369163b

Please sign in to comment.