From 5a176f09c28ac3449a0f6f4f5dcfacbca58959a6 Mon Sep 17 00:00:00 2001 From: "Jason T. Greene" Date: Fri, 12 May 2023 13:35:49 -0500 Subject: [PATCH] Set machine docker.sock according to rootful flag Signed-off-by: Jason T. Greene --- cmd/podman/machine/platform_darwin.go | 3 + pkg/machine/applehv/machine.go | 5 +- pkg/machine/config.go | 2 + pkg/machine/e2e/config_init_test.go | 10 ++- pkg/machine/e2e/config_set_test.go | 9 ++ pkg/machine/e2e/init_test.go | 43 ++++++++++ pkg/machine/e2e/set_test.go | 25 ++++++ pkg/machine/hyperv/machine.go | 118 ++++++++++++++++++-------- pkg/machine/ignition.go | 27 ++++-- pkg/machine/qemu/machine.go | 12 +++ pkg/machine/update.go | 28 ++++++ pkg/machine/wsl/machine.go | 31 +++++-- 12 files changed, 264 insertions(+), 49 deletions(-) create mode 100644 pkg/machine/update.go diff --git a/cmd/podman/machine/platform_darwin.go b/cmd/podman/machine/platform_darwin.go index f6b512a04e..726cd8d12b 100644 --- a/cmd/podman/machine/platform_darwin.go +++ b/cmd/podman/machine/platform_darwin.go @@ -8,6 +8,7 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/podman/v4/pkg/machine" + "github.com/containers/podman/v4/pkg/machine/applehv" "github.com/containers/podman/v4/pkg/machine/qemu" "github.com/sirupsen/logrus" ) @@ -30,6 +31,8 @@ func GetSystemProvider() (machine.VirtProvider, error) { switch resolvedVMType { case machine.QemuVirt: return qemu.GetVirtualizationProvider(), nil + case machine.AppleHvVirt: + return applehv.GetVirtualizationProvider(), nil default: return nil, fmt.Errorf("unsupported virtualization provider: `%s`", resolvedVMType.String()) } diff --git a/pkg/machine/applehv/machine.go b/pkg/machine/applehv/machine.go index fe5f577f53..06abea8672 100644 --- a/pkg/machine/applehv/machine.go +++ b/pkg/machine/applehv/machine.go @@ -193,6 +193,7 @@ func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) { TimeZone: opts.TimeZone, WritePath: m.IgnitionFile.GetPath(), UID: m.UID, + Rootful: m.Rootful, } if err := ign.GenerateIgnitionConfig(); err != nil { @@ -278,10 +279,10 @@ func (m *MacMachine) Remove(name string, opts machine.RemoveOptions) (string, fu logrus.Error(err) } } - if err := machine.RemoveConnection(m.Name); err != nil { + if err := machine.RemoveConnections(m.Name); err != nil { logrus.Error(err) } - if err := machine.RemoveConnection(m.Name + "-root"); err != nil { + if err := machine.RemoveConnections(m.Name + "-root"); err != nil { logrus.Error(err) } diff --git a/pkg/machine/config.go b/pkg/machine/config.go index c6587b369f..f59288590e 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -359,6 +359,8 @@ type HostUser struct { Rootful bool // UID is the numerical id of the user that called machine UID int + // Whether one of these fields has changed and actions should be taken + Modified bool `json:"HostUserModified"` } // SSHConfig contains remote access information for SSH diff --git a/pkg/machine/e2e/config_init_test.go b/pkg/machine/e2e/config_init_test.go index 47657c8d4f..8339f39d6f 100644 --- a/pkg/machine/e2e/config_init_test.go +++ b/pkg/machine/e2e/config_init_test.go @@ -27,7 +27,7 @@ type initMachine struct { memory *uint now bool timezone string - rootful bool //nolint:unused + rootful bool volumes []string cmd []string @@ -62,6 +62,9 @@ func (i *initMachine) buildCmd(m *machineTestBuilder) []string { if i.now { cmd = append(cmd, "--now") } + if i.rootful { + cmd = append(cmd, "--rootful") + } cmd = append(cmd, m.name) i.cmd = cmd return cmd @@ -110,3 +113,8 @@ func (i *initMachine) withVolume(v string) *initMachine { i.volumes = append(i.volumes, v) return i } + +func (i *initMachine) withRootful(r bool) *initMachine { + i.rootful = r + return i +} diff --git a/pkg/machine/e2e/config_set_test.go b/pkg/machine/e2e/config_set_test.go index 3c773b970b..1ebd97d118 100644 --- a/pkg/machine/e2e/config_set_test.go +++ b/pkg/machine/e2e/config_set_test.go @@ -8,6 +8,7 @@ type setMachine struct { cpus *uint diskSize *uint memory *uint + rootful bool cmd []string } @@ -23,6 +24,9 @@ func (i *setMachine) buildCmd(m *machineTestBuilder) []string { if i.memory != nil { cmd = append(cmd, "--memory", strconv.Itoa(int(*i.memory))) } + if i.rootful { + cmd = append(cmd, "--rootful") + } cmd = append(cmd, m.name) i.cmd = cmd return cmd @@ -41,3 +45,8 @@ func (i *setMachine) withMemory(num uint) *setMachine { i.memory = &num return i } + +func (i *setMachine) withRootful(r bool) *setMachine { + i.rootful = r + return i +} diff --git a/pkg/machine/e2e/init_test.go b/pkg/machine/e2e/init_test.go index 5548d98beb..9cd3ffa0fd 100644 --- a/pkg/machine/e2e/init_test.go +++ b/pkg/machine/e2e/init_test.go @@ -3,6 +3,7 @@ package e2e_test import ( "os" "strconv" + "strings" "time" "github.com/containers/podman/v4/pkg/machine" @@ -162,4 +163,46 @@ var _ = Describe("podman machine init", func() { Expect(sshSession2.outputToString()).To(ContainSubstring("example")) }) + It("machine init rootless docker.sock check", func() { + i := initMachine{} + name := randomString() + session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run() + Expect(err).ToNot(HaveOccurred()) + Expect(session).To(Exit(0)) + + s := startMachine{} + ssession, err := mb.setCmd(s).setTimeout(time.Minute * 10).run() + Expect(err).ToNot(HaveOccurred()) + Expect(ssession).Should(Exit(0)) + + ssh2 := sshMachine{} + sshSession2, err := mb.setName(name).setCmd(ssh2.withSSHCommand([]string{"readlink /var/run/docker.sock"})).run() + Expect(err).ToNot(HaveOccurred()) + Expect(sshSession2).To(Exit(0)) + + output := strings.TrimSpace(sshSession2.outputToString()) + Expect(output).To(HavePrefix("/run/user")) + Expect(output).To(HaveSuffix("/podman/podman.sock")) + + }) + + It("machine init rootful docker.sock check", func() { + i := initMachine{} + name := randomString() + session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath).withRootful(true)).run() + Expect(err).ToNot(HaveOccurred()) + Expect(session).To(Exit(0)) + + s := startMachine{} + ssession, err := mb.setCmd(s).setTimeout(time.Minute * 10).run() + Expect(err).ToNot(HaveOccurred()) + Expect(ssession).Should(Exit(0)) + + ssh2 := sshMachine{} + sshSession2, err := mb.setName(name).setCmd(ssh2.withSSHCommand([]string{"readlink /var/run/docker.sock"})).run() + Expect(err).ToNot(HaveOccurred()) + Expect(sshSession2).To(Exit(0)) + output := strings.TrimSpace(sshSession2.outputToString()) + Expect(output).To(Equal("/run/podman/podman.sock")) + }) }) diff --git a/pkg/machine/e2e/set_test.go b/pkg/machine/e2e/set_test.go index bb3ab8d967..1dd34e9627 100644 --- a/pkg/machine/e2e/set_test.go +++ b/pkg/machine/e2e/set_test.go @@ -2,6 +2,7 @@ package e2e_test import ( "strconv" + "strings" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -100,4 +101,28 @@ var _ = Describe("podman machine set", func() { Expect(sshSession3.outputToString()).To(ContainSubstring("100 GiB")) }) + It("set rootful, docker sock change", func() { + name := randomString() + i := new(initMachine) + session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run() + Expect(err).ToNot(HaveOccurred()) + Expect(session).To(Exit(0)) + + set := setMachine{} + setSession, err := mb.setName(name).setCmd(set.withRootful(true)).run() + Expect(err).ToNot(HaveOccurred()) + Expect(setSession).To(Exit(0)) + + s := new(startMachine) + startSession, err := mb.setCmd(s).run() + Expect(err).ToNot(HaveOccurred()) + Expect(startSession).To(Exit(0)) + + ssh2 := sshMachine{} + sshSession2, err := mb.setName(name).setCmd(ssh2.withSSHCommand([]string{"readlink /var/run/docker.sock"})).run() + Expect(err).ToNot(HaveOccurred()) + Expect(sshSession2).To(Exit(0)) + output := strings.TrimSpace(sshSession2.outputToString()) + Expect(output).To(Equal("/run/podman/podman.sock")) + }) }) diff --git a/pkg/machine/hyperv/machine.go b/pkg/machine/hyperv/machine.go index 7eb737d5b3..8ee9390672 100644 --- a/pkg/machine/hyperv/machine.go +++ b/pkg/machine/hyperv/machine.go @@ -20,6 +20,7 @@ import ( "github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/utils" "github.com/containers/storage/pkg/homedir" + "github.com/containers/storage/pkg/ioutils" "github.com/docker/go-units" "github.com/sirupsen/logrus" ) @@ -154,11 +155,7 @@ func (m *HyperVMachine) Init(opts machine.InitOptions) (bool, error) { } // Write the JSON file for the second time. First time was in NewMachine - b, err := json.MarshalIndent(m, "", " ") - if err != nil { - return false, err - } - if err := os.WriteFile(m.ConfigPath.GetPath(), b, 0644); err != nil { + if err := m.writeConfig(); err != nil { return false, err } @@ -391,7 +388,11 @@ func (m *HyperVMachine) Set(name string, opts machine.SetOptions) ([]error, erro } if opts.Rootful != nil && m.Rootful != *opts.Rootful { - setErrors = append(setErrors, hypervctl.ErrNotImplemented) + if err := m.setRootful(*opts.Rootful); err != nil { + setErrors = append(setErrors, fmt.Errorf("failed to set rootful option: %w", err)) + } else { + m.Rootful = *opts.Rootful + } } if opts.DiskSize != nil && m.DiskSize != *opts.DiskSize { setErrors = append(setErrors, hypervctl.ErrNotImplemented) @@ -405,38 +406,31 @@ func (m *HyperVMachine) Set(name string, opts machine.SetOptions) ([]error, erro memoryChanged = true } - if !cpuChanged && !memoryChanged { - switch len(setErrors) { - case 0: - return nil, nil - case 1: - return nil, setErrors[0] - default: - return setErrors[1:], setErrors[0] + if cpuChanged || memoryChanged { + err := vm.UpdateProcessorMemSettings(func(ps *hypervctl.ProcessorSettings) { + if cpuChanged { + ps.VirtualQuantity = m.CPUs + } + }, func(ms *hypervctl.MemorySettings) { + if memoryChanged { + ms.DynamicMemoryEnabled = false + ms.VirtualQuantity = m.Memory + ms.Limit = m.Memory + ms.Reservation = m.Memory + } + }) + if err != nil { + setErrors = append(setErrors, err) } } - // Write the new JSON out - // considering this a hard return if we cannot write the JSON file. - b, err := json.MarshalIndent(m, "", " ") - if err != nil { - return setErrors, err - } - if err := os.WriteFile(m.ConfigPath.GetPath(), b, 0644); err != nil { - return setErrors, err + + if len(setErrors) > 0 { + return setErrors, setErrors[0] } - return setErrors, vm.UpdateProcessorMemSettings(func(ps *hypervctl.ProcessorSettings) { - if cpuChanged { - ps.VirtualQuantity = m.CPUs - } - }, func(ms *hypervctl.MemorySettings) { - if memoryChanged { - ms.DynamicMemoryEnabled = false - ms.VirtualQuantity = m.Memory - ms.Limit = m.Memory - ms.Reservation = m.Memory - } - }) + // Write the new JSON out + // considering this a hard return if we cannot write the JSON file. + return setErrors, m.writeConfig() } func (m *HyperVMachine) SSH(name string, opts machine.SSHOptions) error { @@ -472,7 +466,20 @@ func (m *HyperVMachine) Start(name string, opts machine.StartOptions) error { return err } // Wait on notification from the guest - return m.ReadyHVSock.Listen() + if err := m.ReadyHVSock.Listen(); err != nil { + return err + } + + if m.HostUser.Modified { + if machine.UpdatePodmanDockerSockService(m, name, m.UID, m.Rootful) == nil { + // Reset modification state if there are no errors, otherwise ignore errors + // which are already logged + m.HostUser.Modified = false + _ = m.writeConfig() + } + } + + return nil } func (m *HyperVMachine) State(_ bool) (machine.Status, error) { @@ -661,3 +668,44 @@ func (m *HyperVMachine) forwardSocketPath() (*machine.VMFile, error) { } return machine.NewMachineFile(filepath.Join(path, sockName), &sockName) } + +func (m *HyperVMachine) writeConfig() error { + // Write the JSON file + opts := &ioutils.AtomicFileWriterOptions{ExplicitCommit: true} + w, err := ioutils.NewAtomicFileWriterWithOpts(m.ConfigPath.GetPath(), 0644, opts) + if err != nil { + return err + } + defer w.Close() + + enc := json.NewEncoder(w) + enc.SetIndent("", " ") + + if err := enc.Encode(m); err != nil { + return err + } + + // Commit the changes to disk if no errors + return w.Commit() +} + +func (m *HyperVMachine) setRootful(rootful bool) error { + changeCon, err := machine.AnyConnectionDefault(m.Name, m.Name+"-root") + if err != nil { + return err + } + + if changeCon { + newDefault := m.Name + if rootful { + newDefault += "-root" + } + err := machine.ChangeDefault(newDefault) + if err != nil { + return err + } + } + + m.HostUser.Modified = true + return nil +} diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go index 6d39fa69b3..95059324a3 100644 --- a/pkg/machine/ignition.go +++ b/pkg/machine/ignition.go @@ -25,7 +25,8 @@ import ( */ const ( - UserCertsTargetPath = "/etc/containers/certs.d" + UserCertsTargetPath = "/etc/containers/certs.d" + PodmanDockerTmpConfPath = "/etc/tmpfiles.d/podman-docker.conf" ) // Convenience function to convert int to ptr @@ -60,6 +61,7 @@ type DynamicIgnition struct { VMType VMType WritePath string Cfg Config + Rootful bool } func (ign *DynamicIgnition) Write() error { @@ -95,7 +97,7 @@ func (ign *DynamicIgnition) GenerateIgnitionConfig() error { ignStorage := Storage{ Directories: getDirs(ign.Name), - Files: getFiles(ign.Name, ign.UID), + Files: getFiles(ign.Name, ign.UID, ign.Rootful), Links: getLinks(ign.Name), } @@ -285,7 +287,7 @@ func getDirs(usrName string) []Directory { return dirs } -func getFiles(usrName string, uid int) []File { +func getFiles(usrName string, uid int, rootful bool) []File { files := make([]File, 0) lingerExample := `[Unit] @@ -448,14 +450,13 @@ Delegate=memory pids cpu io files = append(files, File{ Node: Node{ - Path: "/etc/tmpfiles.d/podman-docker.conf", + Path: PodmanDockerTmpConfPath, }, FileEmbedded1: FileEmbedded1{ Append: nil, // Create a symlink from the docker socket to the podman socket. - // Taken from https://github.com/containers/podman/blob/main/contrib/systemd/system/podman-docker.conf Contents: Resource{ - Source: EncodeDataURLPtr("L+ /run/docker.sock - - - - /run/podman/podman.sock\n"), + Source: EncodeDataURLPtr(GetPodmanDockerTmpConfig(uid, rootful, true)), }, Mode: IntToPtr(0644), }, @@ -645,3 +646,17 @@ func getLinks(usrName string) []Link { func EncodeDataURLPtr(contents string) *string { return StrToPtr(fmt.Sprintf("data:,%s", url.PathEscape(contents))) } + +func GetPodmanDockerTmpConfig(uid int, rootful bool, newline bool) string { + // Derived from https://github.com/containers/podman/blob/main/contrib/systemd/system/podman-docker.conf + podmanSock := "/run/podman/podman.sock" + if !rootful { + podmanSock = fmt.Sprintf("/run/user/%d/podman/podman.sock", uid) + } + suffix := "" + if newline { + suffix = "\n" + } + + return fmt.Sprintf("L+ /run/docker.sock - - - - %s%s", podmanSock, suffix) +} diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 5d7e011fc5..634bf7220d 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -387,6 +387,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { TimeZone: opts.TimeZone, WritePath: v.getIgnitionFile(), UID: v.UID, + Rootful: v.Rootful, } if err := ign.GenerateIgnitionConfig(); err != nil { @@ -645,6 +646,15 @@ func (v *MachineVM) Start(name string, opts machine.StartOptions) error { if err != nil { return err } + + if v.HostUser.Modified { + if machine.UpdatePodmanDockerSockService(v, name, v.UID, v.Rootful) == nil { + // Reset modification state if there are no errors, otherwise ignore errors + // which are already logged + v.HostUser.Modified = false + _ = v.writeConfig() + } + } if len(v.Mounts) > 0 { state, err := v.State(true) if err != nil { @@ -1668,6 +1678,8 @@ func (v *MachineVM) setRootful(rootful bool) error { return err } } + + v.HostUser.Modified = true return nil } diff --git a/pkg/machine/update.go b/pkg/machine/update.go new file mode 100644 index 0000000000..9f34f3ab13 --- /dev/null +++ b/pkg/machine/update.go @@ -0,0 +1,28 @@ +//go:build amd64 || arm64 +// +build amd64 arm64 + +package machine + +import ( + "fmt" + + "github.com/sirupsen/logrus" +) + +func UpdatePodmanDockerSockService(vm VM, name string, uid int, rootful bool) error { + content := GetPodmanDockerTmpConfig(uid, rootful, false) + command := fmt.Sprintf("'echo %q > %s'", content, PodmanDockerTmpConfPath) + args := []string{"sudo", "bash", "-c", command} + if err := vm.SSH(name, SSHOptions{Args: args}); err != nil { + logrus.Warnf("Could not not update internal docker sock config") + return err + } + + args = []string{"sudo", "systemd-tmpfiles", "--create", "--prefix=/run/docker.sock"} + if err := vm.SSH(name, SSHOptions{Args: args}); err != nil { + logrus.Warnf("Could not create internal docker sock") + return err + } + + return nil +} diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go index 7e9aad4010..b52d1303d1 100644 --- a/pkg/machine/wsl/machine.go +++ b/pkg/machine/wsl/machine.go @@ -214,6 +214,8 @@ const ( pipePrefix = "npipe:////./pipe/" globalPipe = "docker_engine" userModeDist = "podman-net-usermode" + rootfulSock = "/run/podman/podman.sock" + rootlessSock = "/run/user/1000/podman/podman.sock" ) type Virtualization struct { @@ -501,8 +503,8 @@ func (v *MachineVM) writeConfig() error { } func setupConnections(v *MachineVM, opts machine.InitOptions, sshDir string) error { - uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/user/1000/podman/podman.sock", strconv.Itoa(v.Port), v.RemoteUsername) - uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/podman/podman.sock", strconv.Itoa(v.Port), "root") + uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", rootlessSock, strconv.Itoa(v.Port), v.RemoteUsername) + uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", rootfulSock, strconv.Itoa(v.Port), "root") identity := filepath.Join(sshDir, v.Name) uris := []url.URL{uri, uriRoot} @@ -620,6 +622,10 @@ func configureSystem(v *MachineVM, dist string) error { return err } + if err := v.setupPodmanDockerSock(dist, v.Rootful); err != nil { + return err + } + if err := wslInvoke(dist, "sh", "-c", "echo wsl > /etc/containers/podman-machine"); err != nil { return fmt.Errorf("could not create podman-machine file for guest OS: %w", err) } @@ -627,6 +633,16 @@ func configureSystem(v *MachineVM, dist string) error { return changeDistUserModeNetworking(dist, user, "", v.UserModeNetworking) } +func (v *MachineVM) setupPodmanDockerSock(dist string, rootful bool) error { + content := machine.GetPodmanDockerTmpConfig(1000, rootful, true) + + if err := wslPipe(content, dist, "sh", "-c", "cat > "+machine.PodmanDockerTmpConfPath); err != nil { + return fmt.Errorf("could not create internal docker sock conf: %w", err) + } + + return nil +} + func configureProxy(dist string, useProxy bool, quiet bool) error { if !useProxy { _ = wslInvoke(dist, "sh", "-c", clearProxySettings) @@ -1020,6 +1036,9 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) { if err != nil { setErrors = append(setErrors, fmt.Errorf("setting rootful option: %w", err)) } else { + if v.isRunning() { + logrus.Warn("restart is necessary for rootful change to go into effect") + } v.Rootful = *opts.Rootful } } @@ -1155,11 +1174,11 @@ func launchWinProxy(v *MachineVM) (bool, string, error) { return globalName, "", err } - destSock := "/run/user/1000/podman/podman.sock" + destSock := rootlessSock forwardUser := v.RemoteUsername if v.Rootful { - destSock = "/run/podman/podman.sock" + destSock = rootfulSock forwardUser = "root" } @@ -1660,7 +1679,9 @@ func (v *MachineVM) setRootful(rootful bool) error { return err } } - return nil + + dist := toDist(v.Name) + return v.setupPodmanDockerSock(dist, rootful) } // Inspect returns verbose detail about the machine