aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/machine/init.go5
-rw-r--r--cmd/podman/machine/set.go56
-rw-r--r--docs/source/markdown/podman-machine-init.1.md9
-rw-r--r--docs/source/markdown/podman-machine-set.1.md59
-rw-r--r--docs/source/markdown/podman-machine.1.md1
-rw-r--r--pkg/machine/config.go9
-rw-r--r--pkg/machine/connection.go25
-rw-r--r--pkg/machine/qemu/config.go2
-rw-r--r--pkg/machine/qemu/machine.go122
-rw-r--r--pkg/machine/wsl/machine.go70
10 files changed, 318 insertions, 40 deletions
diff --git a/cmd/podman/machine/init.go b/cmd/podman/machine/init.go
index 0834aa381..ab13d8651 100644
--- a/cmd/podman/machine/init.go
+++ b/cmd/podman/machine/init.go
@@ -26,7 +26,7 @@ var (
var (
initOpts = machine.InitOptions{}
- defaultMachineName = "podman-machine-default"
+ defaultMachineName = machine.DefaultMachineName
now bool
)
@@ -99,6 +99,9 @@ func init() {
IgnitionPathFlagName := "ignition-path"
flags.StringVar(&initOpts.IgnitionPath, IgnitionPathFlagName, "", "Path to ignition file")
_ = initCmd.RegisterFlagCompletionFunc(IgnitionPathFlagName, completion.AutocompleteDefault)
+
+ rootfulFlagName := "rootful"
+ flags.BoolVar(&initOpts.Rootful, rootfulFlagName, false, "Whether this machine should prefer rootful container exectution")
}
// TODO should we allow for a users to append to the qemu cmdline?
diff --git a/cmd/podman/machine/set.go b/cmd/podman/machine/set.go
new file mode 100644
index 000000000..c978206f0
--- /dev/null
+++ b/cmd/podman/machine/set.go
@@ -0,0 +1,56 @@
+// +build amd64 arm64
+
+package machine
+
+import (
+ "github.com/containers/common/pkg/completion"
+ "github.com/containers/podman/v4/cmd/podman/registry"
+ "github.com/containers/podman/v4/pkg/machine"
+ "github.com/spf13/cobra"
+)
+
+var (
+ setCmd = &cobra.Command{
+ Use: "set [options] [NAME]",
+ Short: "Sets a virtual machine setting",
+ Long: "Sets an updatable virtual machine setting",
+ RunE: setMachine,
+ Args: cobra.MaximumNArgs(1),
+ Example: `podman machine set --root=false`,
+ ValidArgsFunction: completion.AutocompleteNone,
+ }
+)
+
+var (
+ setOpts = machine.SetOptions{}
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Command: setCmd,
+ Parent: machineCmd,
+ })
+ flags := setCmd.Flags()
+
+ rootfulFlagName := "rootful"
+ flags.BoolVar(&setOpts.Rootful, rootfulFlagName, false, "Whether this machine should prefer rootful container execution")
+}
+
+func setMachine(cmd *cobra.Command, args []string) error {
+ var (
+ vm machine.VM
+ err error
+ )
+
+ vmName := defaultMachineName
+ if len(args) > 0 && len(args[0]) > 0 {
+ vmName = args[0]
+ }
+ provider := getSystemDefaultProvider()
+ vm, err = provider.LoadVMByName(vmName)
+ if err != nil {
+ return err
+ }
+
+ return vm.Set(vmName, setOpts)
+}
diff --git a/docs/source/markdown/podman-machine-init.1.md b/docs/source/markdown/podman-machine-init.1.md
index b515e8763..36db5b1cd 100644
--- a/docs/source/markdown/podman-machine-init.1.md
+++ b/docs/source/markdown/podman-machine-init.1.md
@@ -55,6 +55,14 @@ Memory (in MB).
Start the virtual machine immediately after it has been initialized.
+#### **--rootful**=*true|false*
+
+Whether this machine should prefer rootful (`true`) or rootless (`false`)
+container execution. This option will also determine the remote connection default
+if there is no existing remote connection configurations.
+
+API forwarding, if available, will follow this setting.
+
#### **--timezone**
Set the timezone for the machine and containers. Valid values are `local` or
@@ -84,6 +92,7 @@ Print usage statement.
```
$ podman machine init
$ podman machine init myvm
+$ podman machine init --rootful
$ podman machine init --disk-size 50
$ podman machine init --memory=1024 myvm
$ podman machine init -v /Users:/mnt/Users
diff --git a/docs/source/markdown/podman-machine-set.1.md b/docs/source/markdown/podman-machine-set.1.md
new file mode 100644
index 000000000..e69779564
--- /dev/null
+++ b/docs/source/markdown/podman-machine-set.1.md
@@ -0,0 +1,59 @@
+% podman-machine-set(1)
+
+## NAME
+podman\-machine\-set - Sets a virtual machine setting
+
+## SYNOPSIS
+**podman machine set** [*options*] [*name*]
+
+## DESCRIPTION
+
+Sets an updatable virtual machine setting.
+
+Options mirror values passed to `podman machine init`. Only a limited
+subset can be changed after machine initialization.
+
+## OPTIONS
+
+#### **--rootful**=*true|false*
+
+Whether this machine should prefer rootful (`true`) or rootless (`false`)
+container execution. This option will also update the current podman
+remote connection default if it is currently pointing at the specified
+machine name (or `podman-machine-default` if no name is specified).
+
+API forwarding, if available, will follow this setting.
+
+#### **--help**
+
+Print usage statement.
+
+## EXAMPLES
+
+To switch the default VM `podman-machine-default` from rootless to rootful:
+
+```
+$ podman machine set --rootful
+```
+
+or more explicitly:
+
+```
+$ podman machine set --rootful=true
+```
+
+To switch the default VM `podman-machine-default` from rootful to rootless:
+```
+$ podman machine set --rootful=false
+```
+
+To switch the VM `myvm` from rootless to rootful:
+```
+$ podman machine set --rootful myvm
+```
+
+## SEE ALSO
+**[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)**
+
+## HISTORY
+February 2022, Originally compiled by Jason Greene <jason.greene@redhat.com>
diff --git a/docs/source/markdown/podman-machine.1.md b/docs/source/markdown/podman-machine.1.md
index 8d9e77ea5..3bdfd0be9 100644
--- a/docs/source/markdown/podman-machine.1.md
+++ b/docs/source/markdown/podman-machine.1.md
@@ -16,6 +16,7 @@ podman\-machine - Manage Podman's virtual machine
| init | [podman-machine-init(1)](podman-machine-init.1.md) | Initialize a new virtual machine |
| list | [podman-machine-list(1)](podman-machine-list.1.md) | List virtual machines |
| rm | [podman-machine-rm(1)](podman-machine-rm.1.md) | Remove a virtual machine |
+| set | [podman-machine-set(1)](podman-machine-set.1.md) | Sets a virtual machine setting |
| ssh | [podman-machine-ssh(1)](podman-machine-ssh.1.md) | SSH into a virtual machine |
| start | [podman-machine-start(1)](podman-machine-start.1.md) | Start a virtual machine |
| stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine |
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.")