Set machine docker.sock according to rootful flag

Signed-off-by: Jason T. Greene <jason.greene@redhat.com>
This commit is contained in:
Jason T. Greene
2023-05-12 13:35:49 -05:00
parent 2783651005
commit 5a176f09c2
12 changed files with 265 additions and 50 deletions

View File

@ -8,6 +8,7 @@ import (
"github.com/containers/common/pkg/config" "github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/pkg/machine" "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/containers/podman/v4/pkg/machine/qemu"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -30,6 +31,8 @@ func GetSystemProvider() (machine.VirtProvider, error) {
switch resolvedVMType { switch resolvedVMType {
case machine.QemuVirt: case machine.QemuVirt:
return qemu.GetVirtualizationProvider(), nil return qemu.GetVirtualizationProvider(), nil
case machine.AppleHvVirt:
return applehv.GetVirtualizationProvider(), nil
default: default:
return nil, fmt.Errorf("unsupported virtualization provider: `%s`", resolvedVMType.String()) return nil, fmt.Errorf("unsupported virtualization provider: `%s`", resolvedVMType.String())
} }

View File

@ -193,6 +193,7 @@ func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
TimeZone: opts.TimeZone, TimeZone: opts.TimeZone,
WritePath: m.IgnitionFile.GetPath(), WritePath: m.IgnitionFile.GetPath(),
UID: m.UID, UID: m.UID,
Rootful: m.Rootful,
} }
if err := ign.GenerateIgnitionConfig(); err != nil { if err := ign.GenerateIgnitionConfig(); err != nil {
@ -278,10 +279,10 @@ func (m *MacMachine) Remove(name string, opts machine.RemoveOptions) (string, fu
logrus.Error(err) logrus.Error(err)
} }
} }
if err := machine.RemoveConnection(m.Name); err != nil { if err := machine.RemoveConnections(m.Name); err != nil {
logrus.Error(err) logrus.Error(err)
} }
if err := machine.RemoveConnection(m.Name + "-root"); err != nil { if err := machine.RemoveConnections(m.Name + "-root"); err != nil {
logrus.Error(err) logrus.Error(err)
} }

View File

@ -359,6 +359,8 @@ type HostUser struct {
Rootful bool Rootful bool
// UID is the numerical id of the user that called machine // UID is the numerical id of the user that called machine
UID int 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 // SSHConfig contains remote access information for SSH

View File

@ -27,7 +27,7 @@ type initMachine struct {
memory *uint memory *uint
now bool now bool
timezone string timezone string
rootful bool //nolint:unused rootful bool
volumes []string volumes []string
cmd []string cmd []string
@ -62,6 +62,9 @@ func (i *initMachine) buildCmd(m *machineTestBuilder) []string {
if i.now { if i.now {
cmd = append(cmd, "--now") cmd = append(cmd, "--now")
} }
if i.rootful {
cmd = append(cmd, "--rootful")
}
cmd = append(cmd, m.name) cmd = append(cmd, m.name)
i.cmd = cmd i.cmd = cmd
return cmd return cmd
@ -110,3 +113,8 @@ func (i *initMachine) withVolume(v string) *initMachine {
i.volumes = append(i.volumes, v) i.volumes = append(i.volumes, v)
return i return i
} }
func (i *initMachine) withRootful(r bool) *initMachine {
i.rootful = r
return i
}

View File

@ -8,6 +8,7 @@ type setMachine struct {
cpus *uint cpus *uint
diskSize *uint diskSize *uint
memory *uint memory *uint
rootful bool
cmd []string cmd []string
} }
@ -23,6 +24,9 @@ func (i *setMachine) buildCmd(m *machineTestBuilder) []string {
if i.memory != nil { if i.memory != nil {
cmd = append(cmd, "--memory", strconv.Itoa(int(*i.memory))) cmd = append(cmd, "--memory", strconv.Itoa(int(*i.memory)))
} }
if i.rootful {
cmd = append(cmd, "--rootful")
}
cmd = append(cmd, m.name) cmd = append(cmd, m.name)
i.cmd = cmd i.cmd = cmd
return cmd return cmd
@ -41,3 +45,8 @@ func (i *setMachine) withMemory(num uint) *setMachine {
i.memory = &num i.memory = &num
return i return i
} }
func (i *setMachine) withRootful(r bool) *setMachine {
i.rootful = r
return i
}

View File

@ -3,6 +3,7 @@ package e2e_test
import ( import (
"os" "os"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
@ -162,4 +163,46 @@ var _ = Describe("podman machine init", func() {
Expect(sshSession2.outputToString()).To(ContainSubstring("example")) 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"))
})
}) })

View File

@ -2,6 +2,7 @@ package e2e_test
import ( import (
"strconv" "strconv"
"strings"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
@ -100,4 +101,28 @@ var _ = Describe("podman machine set", func() {
Expect(sshSession3.outputToString()).To(ContainSubstring("100 GiB")) 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"))
})
}) })

View File

@ -20,6 +20,7 @@ import (
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/utils" "github.com/containers/podman/v4/utils"
"github.com/containers/storage/pkg/homedir" "github.com/containers/storage/pkg/homedir"
"github.com/containers/storage/pkg/ioutils"
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/sirupsen/logrus" "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 // Write the JSON file for the second time. First time was in NewMachine
b, err := json.MarshalIndent(m, "", " ") if err := m.writeConfig(); err != nil {
if err != nil {
return false, err
}
if err := os.WriteFile(m.ConfigPath.GetPath(), b, 0644); err != nil {
return false, err 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 { 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 { if opts.DiskSize != nil && m.DiskSize != *opts.DiskSize {
setErrors = append(setErrors, hypervctl.ErrNotImplemented) setErrors = append(setErrors, hypervctl.ErrNotImplemented)
@ -405,27 +406,8 @@ func (m *HyperVMachine) Set(name string, opts machine.SetOptions) ([]error, erro
memoryChanged = true memoryChanged = true
} }
if !cpuChanged && !memoryChanged { if cpuChanged || memoryChanged {
switch len(setErrors) { err := vm.UpdateProcessorMemSettings(func(ps *hypervctl.ProcessorSettings) {
case 0:
return nil, nil
case 1:
return nil, setErrors[0]
default:
return setErrors[1:], setErrors[0]
}
}
// 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
}
return setErrors, vm.UpdateProcessorMemSettings(func(ps *hypervctl.ProcessorSettings) {
if cpuChanged { if cpuChanged {
ps.VirtualQuantity = m.CPUs ps.VirtualQuantity = m.CPUs
} }
@ -437,6 +419,18 @@ func (m *HyperVMachine) Set(name string, opts machine.SetOptions) ([]error, erro
ms.Reservation = m.Memory ms.Reservation = m.Memory
} }
}) })
if err != nil {
setErrors = append(setErrors, err)
}
}
if len(setErrors) > 0 {
return setErrors, setErrors[0]
}
// 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 { 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 return err
} }
// Wait on notification from the guest // 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) { 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) 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
}

View File

@ -26,6 +26,7 @@ import (
const ( 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 // Convenience function to convert int to ptr
@ -60,6 +61,7 @@ type DynamicIgnition struct {
VMType VMType VMType VMType
WritePath string WritePath string
Cfg Config Cfg Config
Rootful bool
} }
func (ign *DynamicIgnition) Write() error { func (ign *DynamicIgnition) Write() error {
@ -95,7 +97,7 @@ func (ign *DynamicIgnition) GenerateIgnitionConfig() error {
ignStorage := Storage{ ignStorage := Storage{
Directories: getDirs(ign.Name), Directories: getDirs(ign.Name),
Files: getFiles(ign.Name, ign.UID), Files: getFiles(ign.Name, ign.UID, ign.Rootful),
Links: getLinks(ign.Name), Links: getLinks(ign.Name),
} }
@ -285,7 +287,7 @@ func getDirs(usrName string) []Directory {
return dirs return dirs
} }
func getFiles(usrName string, uid int) []File { func getFiles(usrName string, uid int, rootful bool) []File {
files := make([]File, 0) files := make([]File, 0)
lingerExample := `[Unit] lingerExample := `[Unit]
@ -448,14 +450,13 @@ Delegate=memory pids cpu io
files = append(files, File{ files = append(files, File{
Node: Node{ Node: Node{
Path: "/etc/tmpfiles.d/podman-docker.conf", Path: PodmanDockerTmpConfPath,
}, },
FileEmbedded1: FileEmbedded1{ FileEmbedded1: FileEmbedded1{
Append: nil, Append: nil,
// Create a symlink from the docker socket to the podman socket. // 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{ Contents: Resource{
Source: EncodeDataURLPtr("L+ /run/docker.sock - - - - /run/podman/podman.sock\n"), Source: EncodeDataURLPtr(GetPodmanDockerTmpConfig(uid, rootful, true)),
}, },
Mode: IntToPtr(0644), Mode: IntToPtr(0644),
}, },
@ -645,3 +646,17 @@ func getLinks(usrName string) []Link {
func EncodeDataURLPtr(contents string) *string { func EncodeDataURLPtr(contents string) *string {
return StrToPtr(fmt.Sprintf("data:,%s", url.PathEscape(contents))) 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)
}

View File

@ -387,6 +387,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
TimeZone: opts.TimeZone, TimeZone: opts.TimeZone,
WritePath: v.getIgnitionFile(), WritePath: v.getIgnitionFile(),
UID: v.UID, UID: v.UID,
Rootful: v.Rootful,
} }
if err := ign.GenerateIgnitionConfig(); err != nil { if err := ign.GenerateIgnitionConfig(); err != nil {
@ -645,6 +646,15 @@ func (v *MachineVM) Start(name string, opts machine.StartOptions) error {
if err != nil { if err != nil {
return err 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 { if len(v.Mounts) > 0 {
state, err := v.State(true) state, err := v.State(true)
if err != nil { if err != nil {
@ -1668,6 +1678,8 @@ func (v *MachineVM) setRootful(rootful bool) error {
return err return err
} }
} }
v.HostUser.Modified = true
return nil return nil
} }

28
pkg/machine/update.go Normal file
View File

@ -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
}

View File

@ -214,6 +214,8 @@ const (
pipePrefix = "npipe:////./pipe/" pipePrefix = "npipe:////./pipe/"
globalPipe = "docker_engine" globalPipe = "docker_engine"
userModeDist = "podman-net-usermode" userModeDist = "podman-net-usermode"
rootfulSock = "/run/podman/podman.sock"
rootlessSock = "/run/user/1000/podman/podman.sock"
) )
type Virtualization struct { type Virtualization struct {
@ -501,8 +503,8 @@ func (v *MachineVM) writeConfig() error {
} }
func setupConnections(v *MachineVM, opts machine.InitOptions, sshDir string) 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) uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", rootlessSock, strconv.Itoa(v.Port), v.RemoteUsername)
uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/podman/podman.sock", strconv.Itoa(v.Port), "root") uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", rootfulSock, strconv.Itoa(v.Port), "root")
identity := filepath.Join(sshDir, v.Name) identity := filepath.Join(sshDir, v.Name)
uris := []url.URL{uri, uriRoot} uris := []url.URL{uri, uriRoot}
@ -620,6 +622,10 @@ func configureSystem(v *MachineVM, dist string) error {
return err 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 { 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) 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) 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 { func configureProxy(dist string, useProxy bool, quiet bool) error {
if !useProxy { if !useProxy {
_ = wslInvoke(dist, "sh", "-c", clearProxySettings) _ = wslInvoke(dist, "sh", "-c", clearProxySettings)
@ -1020,6 +1036,9 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) {
if err != nil { if err != nil {
setErrors = append(setErrors, fmt.Errorf("setting rootful option: %w", err)) setErrors = append(setErrors, fmt.Errorf("setting rootful option: %w", err))
} else { } else {
if v.isRunning() {
logrus.Warn("restart is necessary for rootful change to go into effect")
}
v.Rootful = *opts.Rootful v.Rootful = *opts.Rootful
} }
} }
@ -1155,11 +1174,11 @@ func launchWinProxy(v *MachineVM) (bool, string, error) {
return globalName, "", err return globalName, "", err
} }
destSock := "/run/user/1000/podman/podman.sock" destSock := rootlessSock
forwardUser := v.RemoteUsername forwardUser := v.RemoteUsername
if v.Rootful { if v.Rootful {
destSock = "/run/podman/podman.sock" destSock = rootfulSock
forwardUser = "root" forwardUser = "root"
} }
@ -1660,7 +1679,9 @@ func (v *MachineVM) setRootful(rootful bool) error {
return err return err
} }
} }
return nil
dist := toDist(v.Name)
return v.setupPodmanDockerSock(dist, rootful)
} }
// Inspect returns verbose detail about the machine // Inspect returns verbose detail about the machine