diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/machine/config.go | 9 | ||||
-rw-r--r-- | pkg/machine/connection.go | 25 | ||||
-rw-r--r-- | pkg/machine/qemu/config.go | 2 | ||||
-rw-r--r-- | pkg/machine/qemu/machine.go | 122 | ||||
-rw-r--r-- | pkg/machine/wsl/machine.go | 70 |
5 files changed, 189 insertions, 39 deletions
diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 97237f5e5..efb1eda15 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -27,6 +27,7 @@ type InitOptions struct { URI url.URL Username string ReExec bool + Rootful bool } type QemuMachineStatus = string @@ -35,7 +36,8 @@ const ( // Running indicates the qemu vm is running Running QemuMachineStatus = "running" // Stopped indicates the vm has stopped - Stopped QemuMachineStatus = "stopped" + Stopped QemuMachineStatus = "stopped" + DefaultMachineName string = "podman-machine-default" ) type Provider interface { @@ -89,6 +91,10 @@ type ListResponse struct { IdentityPath string } +type SetOptions struct { + Rootful bool +} + type SSHOptions struct { Username string Args []string @@ -107,6 +113,7 @@ type RemoveOptions struct { type VM interface { Init(opts InitOptions) (bool, error) Remove(name string, opts RemoveOptions) (string, func() error, error) + Set(name string, opts SetOptions) error SSH(name string, opts SSHOptions) error Start(name string, opts StartOptions) error Stop(name string, opts StopOptions) error diff --git a/pkg/machine/connection.go b/pkg/machine/connection.go index d28ffcef1..841b2afa6 100644 --- a/pkg/machine/connection.go +++ b/pkg/machine/connection.go @@ -39,6 +39,31 @@ func AddConnection(uri fmt.Stringer, name, identity string, isDefault bool) erro return cfg.Write() } +func AnyConnectionDefault(name ...string) (bool, error) { + cfg, err := config.ReadCustomConfig() + if err != nil { + return false, err + } + for _, n := range name { + if n == cfg.Engine.ActiveService { + return true, nil + } + } + + return false, nil +} + +func ChangeDefault(name string) error { + cfg, err := config.ReadCustomConfig() + if err != nil { + return err + } + + cfg.Engine.ActiveService = name + + return cfg.Write() +} + func RemoveConnection(name string) error { cfg, err := config.ReadCustomConfig() if err != nil { diff --git a/pkg/machine/qemu/config.go b/pkg/machine/qemu/config.go index e76509bb1..c619b7dd4 100644 --- a/pkg/machine/qemu/config.go +++ b/pkg/machine/qemu/config.go @@ -33,6 +33,8 @@ type MachineVM struct { QMPMonitor Monitor // RemoteUsername of the vm user RemoteUsername string + // Whether this machine should run in a rootful or rootless manner + Rootful bool } type Mount struct { diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 81a6b4935..9beec2173 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -5,14 +5,15 @@ package qemu import ( "bufio" - "encoding/base64" "context" + "encoding/base64" "encoding/json" "fmt" "io/fs" "io/ioutil" "net" "net/http" + "net/url" "os" "os/exec" "path/filepath" @@ -166,14 +167,8 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { key string ) sshDir := filepath.Join(homedir.Get(), ".ssh") - // GetConfDir creates the directory so no need to check for - // its existence - vmConfigDir, err := machine.GetConfDir(vmtype) - if err != nil { - return false, err - } - jsonFile := filepath.Join(vmConfigDir, v.Name) + ".json" v.IdentityPath = filepath.Join(sshDir, v.Name) + v.Rootful = opts.Rootful switch opts.ImagePath { case "testing", "next", "stable", "": @@ -256,29 +251,33 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { // This kind of stinks but no other way around this r/n if len(opts.IgnitionPath) < 1 { uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/user/1000/podman/podman.sock", strconv.Itoa(v.Port), v.RemoteUsername) - if err := machine.AddConnection(&uri, v.Name, filepath.Join(sshDir, v.Name), opts.IsDefault); err != nil { - return false, err + uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/podman/podman.sock", strconv.Itoa(v.Port), "root") + identity := filepath.Join(sshDir, v.Name) + + uris := []url.URL{uri, uriRoot} + names := []string{v.Name, v.Name + "-root"} + + // The first connection defined when connections is empty will become the default + // regardless of IsDefault, so order according to rootful + if opts.Rootful { + uris[0], names[0], uris[1], names[1] = uris[1], names[1], uris[0], names[0] } - uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/podman/podman.sock", strconv.Itoa(v.Port), "root") - if err := machine.AddConnection(&uriRoot, v.Name+"-root", filepath.Join(sshDir, v.Name), opts.IsDefault); err != nil { - return false, err + for i := 0; i < 2; i++ { + if err := machine.AddConnection(&uris[i], names[i], identity, opts.IsDefault && i == 0); err != nil { + return false, err + } } } else { fmt.Println("An ignition path was provided. No SSH connection was added to Podman") } // Write the JSON file - b, err := json.MarshalIndent(v, "", " ") - if err != nil { - return false, err - } - if err := ioutil.WriteFile(jsonFile, b, 0644); err != nil { - return false, err - } + v.writeConfig() // User has provided ignition file so keygen // will be skipped. if len(opts.IgnitionPath) < 1 { + var err error key, err = machine.CreateSSHKeys(v.IdentityPath) if err != nil { return false, err @@ -325,6 +324,30 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { return err == nil, err } +func (v *MachineVM) Set(name string, opts machine.SetOptions) error { + if v.Rootful == opts.Rootful { + return nil + } + + changeCon, err := machine.AnyConnectionDefault(v.Name, v.Name+"-root") + if err != nil { + return err + } + + if changeCon { + newDefault := v.Name + if opts.Rootful { + newDefault += "-root" + } + if err := machine.ChangeDefault(newDefault); err != nil { + return err + } + } + + v.Rootful = opts.Rootful + return v.writeConfig() +} + // Start executes the qemu command line and forks it func (v *MachineVM) Start(name string, _ machine.StartOptions) error { var ( @@ -457,7 +480,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { } } - printAPIForwardInstructions(forwardState, forwardSock) + waitAPIAndPrintInfo(forwardState, forwardSock, v.Rootful, v.Name) return nil } @@ -929,9 +952,17 @@ func (v *MachineVM) setupAPIForwarding(cmd []string) ([]string, string, apiForwa return cmd, "", noForwarding } + destSock := "/run/user/1000/podman/podman.sock" + forwardUser := "core" + + if v.Rootful { + destSock = "/run/podman/podman.sock" + forwardUser = "root" + } + cmd = append(cmd, []string{"-forward-sock", socket}...) - cmd = append(cmd, []string{"-forward-dest", "/run/podman/podman.sock"}...) - cmd = append(cmd, []string{"-forward-user", "root"}...) + cmd = append(cmd, []string{"-forward-dest", destSock}...) + cmd = append(cmd, []string{"-forward-user", forwardUser}...) cmd = append(cmd, []string{"-forward-identity", v.IdentityPath}...) link := filepath.Join(filepath.Dir(filepath.Dir(socket)), "podman.sock") @@ -1031,19 +1062,32 @@ func waitAndPingAPI(sock string) { } } -func printAPIForwardInstructions(forwardState apiForwardingState, forwardSock string) { +func waitAPIAndPrintInfo(forwardState apiForwardingState, forwardSock string, rootFul bool, name string) { if forwardState != noForwarding { waitAndPingAPI(forwardSock) + if !rootFul { + fmt.Printf("\nThis machine is currently configured in rootless mode. If your containers\n") + fmt.Printf("require root permissions (e.g. ports < 1024), or if you run into compatibility\n") + fmt.Printf("issues with non-podman clients, you can switch using the following command: \n") + + suffix := "" + if name != machine.DefaultMachineName { + suffix = " " + name + } + fmt.Printf("\n\tpodman machine set --rootful%s\n\n", suffix) + } + fmt.Printf("API forwarding listening on: %s\n", forwardSock) if forwardState == dockerGlobal { - fmt.Printf("\nDocker API clients default to this address. You do not need to set DOCKER_HOST.\n\n") + fmt.Printf("Docker API clients default to this address. You do not need to set DOCKER_HOST.\n\n") } else { stillString := "still " switch forwardState { case notInstalled: - fmt.Printf("\nThe system helper service is not installed; the default Docker API socket address can't be used by podman.\n") + fmt.Printf("\nThe system helper service is not installed; the default Docker API socket\n") + fmt.Printf("address can't be used by podman. ") if helper := findClaimHelper(); len(helper) > 0 { - fmt.Printf("If you would like to install it run the following command:\n") + fmt.Printf("If you would like to install it run the\nfollowing command:\n") fmt.Printf("\n\tsudo %s install\n\n", helper) } case machineLocal: @@ -1053,9 +1097,31 @@ func printAPIForwardInstructions(forwardState apiForwardingState, forwardSock st default: stillString = "" } - fmt.Printf("You can %sconnect Docker API clients by setting DOCKER HOST using the\n", stillString) + + fmt.Printf("You can %sconnect Docker API clients by setting DOCKER_HOST using the\n", stillString) fmt.Printf("following command in your terminal session:\n") fmt.Printf("\n\texport DOCKER_HOST='unix://%s'\n\n", forwardSock) } } } + +func (v *MachineVM) writeConfig() error { + // GetConfDir creates the directory so no need to check for + // its existence + vmConfigDir, err := machine.GetConfDir(vmtype) + if err != nil { + return err + } + + jsonFile := filepath.Join(vmConfigDir, v.Name) + ".json" + // Write the JSON file + b, err := json.MarshalIndent(v, "", " ") + if err != nil { + return err + } + if err := ioutil.WriteFile(jsonFile, b, 0644); err != nil { + return err + } + + return nil +} diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go index 3edf3ddf6..6fe79863f 100644 --- a/pkg/machine/wsl/machine.go +++ b/pkg/machine/wsl/machine.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package wsl @@ -8,6 +9,7 @@ import ( "fmt" "io" "io/ioutil" + "net/url" "os" "os/exec" "path/filepath" @@ -35,9 +37,6 @@ const ( ErrorSuccessRebootRequired = 3010 ) -// Usermode networking avoids potential nftables compatibility issues between the distro -// and the WSL Kernel. Additionally it avoids fw rule conflicts between distros, since -// all instances run under the same Kernel at runtime const containersConf = `[containers] [engine] @@ -162,6 +161,8 @@ type MachineVM struct { Port int // RemoteUsername of the vm user RemoteUsername string + // Whether this machine should run in a rootful or rootless manner + Rootful bool } type ExitCodeError struct { @@ -227,12 +228,13 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { homeDir := homedir.Get() sshDir := filepath.Join(homeDir, ".ssh") v.IdentityPath = filepath.Join(sshDir, v.Name) + v.Rootful = opts.Rootful if err := downloadDistro(v, opts); err != nil { return false, err } - if err := writeJSON(v); err != nil { + if err := v.writeConfig(); err != nil { return false, err } @@ -282,7 +284,7 @@ func downloadDistro(v *MachineVM, opts machine.InitOptions) error { return machine.DownloadImage(dd) } -func writeJSON(v *MachineVM) error { +func (v *MachineVM) writeConfig() error { vmConfigDir, err := machine.GetConfDir(vmtype) if err != nil { return err @@ -302,14 +304,26 @@ func writeJSON(v *MachineVM) 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") - if err := machine.AddConnection(&uriRoot, v.Name+"-root", filepath.Join(sshDir, v.Name), opts.IsDefault); err != nil { - return err + identity := filepath.Join(sshDir, v.Name) + + uris := []url.URL{uri, uriRoot} + names := []string{v.Name, v.Name + "-root"} + + // The first connection defined when connections is empty will become the default + // regardless of IsDefault, so order according to rootful + if opts.Rootful { + uris[0], names[0], uris[1], names[1] = uris[1], names[1], uris[0], names[0] + } + + for i := 0; i < 2; i++ { + if err := machine.AddConnection(&uris[i], names[i], identity, opts.IsDefault && i == 0); err != nil { + return err + } } - user := opts.Username - uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", withUser("/run/[USER]/1000/podman/podman.sock", user), strconv.Itoa(v.Port), v.RemoteUsername) - return machine.AddConnection(&uri, v.Name, filepath.Join(sshDir, v.Name), opts.IsDefault) + return nil } func provisionWSLDist(v *MachineVM) (string, error) { @@ -704,6 +718,30 @@ func pipeCmdPassThrough(name string, input string, arg ...string) error { return cmd.Run() } +func (v *MachineVM) Set(name string, opts machine.SetOptions) error { + if v.Rootful == opts.Rootful { + return nil + } + + changeCon, err := machine.AnyConnectionDefault(v.Name, v.Name+"-root") + if err != nil { + return err + } + + if changeCon { + newDefault := v.Name + if opts.Rootful { + newDefault += "-root" + } + if err := machine.ChangeDefault(newDefault); err != nil { + return err + } + } + + v.Rootful = opts.Rootful + return v.writeConfig() +} + func (v *MachineVM) Start(name string, _ machine.StartOptions) error { if v.isRunning() { return errors.Errorf("%q is already running", name) @@ -716,6 +754,18 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { return errors.Wrap(err, "WSL bootstrap script failed") } + if !v.Rootful { + fmt.Printf("\nThis machine is currently configured in rootless mode. If your containers\n") + fmt.Printf("require root permissions (e.g. ports < 1024), or if you run into compatibility\n") + fmt.Printf("issues with non-podman clients, you can switch using the following command: \n") + + suffix := "" + if name != machine.DefaultMachineName { + suffix = " " + name + } + fmt.Printf("\n\tpodman machine set --rootful%s\n\n", suffix) + } + globalName, pipeName, err := launchWinProxy(v) if err != nil { fmt.Fprintln(os.Stderr, "API forwarding for Docker API clients is not available due to the following startup failures.") |