Skip to content

Commit

Permalink
Support rootless Podman driver, take 2
Browse files Browse the repository at this point in the history
Usage:
```
minikube config set rootless true
minikube start --driver=podman --container-runtime=(cri-o|containerd)`
```

Tested on Podman 4.0.2, Ubuntu 21.10.

Needs cgroup v2 (as in Rootless Docker): https://rootlesscontaine.rs/getting-started/common/cgroup2/
See also `site/content/en/docs/drivers/includes/podman_usage.inc`

Fix issue 8719
Fix issue 12460
Replace PR 12901

Changes from PR 12901: `rootless` is now a config property.
In the previous PR, `--rootless` was implemented as a flag for `minikube start`

Signed-off-by: Akihiro Suda <[email protected]>
  • Loading branch information
AkihiroSuda committed Mar 26, 2022
1 parent e1745fa commit c9db704
Show file tree
Hide file tree
Showing 11 changed files with 83 additions and 10 deletions.
4 changes: 4 additions & 0 deletions cmd/minikube/cmd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ var settings = []Setting{
name: "native-ssh",
set: SetBool,
},
{
name: config.Rootless,
set: SetBool,
},
}

// ConfigCmd represents the config command
Expand Down
7 changes: 7 additions & 0 deletions cmd/minikube/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ var RootCmd = &cobra.Command{
out.WarningT("User name '{{.username}}' is not valid", out.V{"username": userName})
exit.Message(reason.Usage, "User name must be 60 chars or less.")
}
// viper maps $MINIKUBE_ROOTLESS to --rootless automatically, but it does not do vice versa,
// so we map --rootless to $MINIKUBE_ROOTLESS expliclity here.
// $MINIKUBE_ROOTLESS is referred by KIC runner, which is decoupled from viper.
if viper.GetBool(config.Rootless) {
os.Setenv(constants.MinikubeRootlessEnv, "true")
}
},
}

Expand Down Expand Up @@ -206,6 +212,7 @@ func init() {
RootCmd.PersistentFlags().StringP(config.ProfileName, "p", constants.DefaultClusterName, `The name of the minikube VM being used. This can be set to allow having multiple instances of minikube independently.`)
RootCmd.PersistentFlags().StringP(configCmd.Bootstrapper, "b", "kubeadm", "The name of the cluster bootstrapper that will set up the Kubernetes cluster.")
RootCmd.PersistentFlags().String(config.UserFlag, "", "Specifies the user executing the operation. Useful for auditing operations executed by 3rd party tools. Defaults to the operating system username.")
RootCmd.PersistentFlags().Bool(config.Rootless, false, "Force to use rootless driver (docker and podman driver only)")

groups := templates.CommandGroups{
{
Expand Down
10 changes: 10 additions & 0 deletions cmd/minikube/cmd/start_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,12 +552,22 @@ func generateNewConfigFromFlags(cmd *cobra.Command, k8sVersion string, rtime str
exit.Message(reason.Usage, "Ensure your {{.driver_name}} is running and is healthy.", out.V{"driver_name": driver.FullName(drvName)})
}
if si.Rootless {
out.Styled(style.Notice, "Using rootless {{.driver_name}} driver", out.V{"driver_name": driver.FullName(drvName)})
if cc.KubernetesConfig.ContainerRuntime == constants.Docker {
exit.Message(reason.Usage, "--container-runtime must be set to \"containerd\" or \"cri-o\" for rootless")
}
// KubeletInUserNamespace feature gate is essential for rootless driver.
// See https://kubernetes.io/docs/tasks/administer-cluster/kubelet-in-userns/
cc.KubernetesConfig.FeatureGates = addFeatureGate(cc.KubernetesConfig.FeatureGates, "KubeletInUserNamespace=true")
} else {
if oci.IsRootlessForced() {
if driver.IsDocker(drvName) {
exit.Message(reason.Usage, "Using rootless Docker driver was required, but the current Docker does not seem rootless. Try 'docker context use rootless' .")
} else {
exit.Message(reason.Usage, "Using rootless driver was required, but the current driver does not seem rootless")
}
}
out.Styled(style.Notice, "Using {{.driver_name}} driver with the root privilege", out.V{"driver_name": driver.FullName(drvName)})
}
if si.StorageDriver == "btrfs" {
klog.Info("auto-setting LocalStorageCapacityIsolation to false because using btrfs storage driver")
Expand Down
37 changes: 34 additions & 3 deletions pkg/drivers/kic/oci/cli_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (

"k8s.io/klog/v2"

"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
)
Expand Down Expand Up @@ -72,10 +73,40 @@ func (rr RunResult) Output() string {
return sb.String()
}

// IsRootlessForced returns whether rootless mode is explicitly required.
func IsRootlessForced() bool {
s := os.Getenv(constants.MinikubeRootlessEnv)
if s == "" {
return false
}
v, err := strconv.ParseBool(s)
if err != nil {
klog.ErrorS(err, "failed to parse", "env", constants.MinikubeRootlessEnv, "value", s)
return false
}
return v
}

type prefixCmdOptions struct {
sudoFlags []string
}

type PrefixCmdOption func(*prefixCmdOptions)

func WithSudoFlags(ss ...string) PrefixCmdOption {
return func(o *prefixCmdOptions) {
o.sudoFlags = ss
}
}

// PrefixCmd adds any needed prefix (such as sudo) to the command
func PrefixCmd(cmd *exec.Cmd) *exec.Cmd {
if cmd.Args[0] == Podman && runtime.GOOS == "linux" { // want sudo when not running podman-remote
cmdWithSudo := exec.Command("sudo", append([]string{"-n"}, cmd.Args...)...)
func PrefixCmd(cmd *exec.Cmd, opt ...PrefixCmdOption) *exec.Cmd {
var o prefixCmdOptions
for _, f := range opt {
f(&o)
}
if cmd.Args[0] == Podman && runtime.GOOS == "linux" && !IsRootlessForced() { // want sudo when not running podman-remote
cmdWithSudo := exec.Command("sudo", append(append([]string{"-n"}, o.sudoFlags...), cmd.Args...)...)
cmdWithSudo.Env = cmd.Env
cmdWithSudo.Dir = cmd.Dir
cmdWithSudo.Stdin = cmd.Stdin
Expand Down
8 changes: 5 additions & 3 deletions pkg/drivers/kic/oci/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func CachedDaemonInfo(ociBin string) (SysInfo, error) {
func DaemonInfo(ociBin string) (SysInfo, error) {
if ociBin == Podman {
p, err := podmanSystemInfo()
cachedSysInfo = &SysInfo{CPUs: p.Host.Cpus, TotalMemory: p.Host.MemTotal, OSType: p.Host.Os, Swarm: false, StorageDriver: p.Store.GraphDriverName}
cachedSysInfo = &SysInfo{CPUs: p.Host.Cpus, TotalMemory: p.Host.MemTotal, OSType: p.Host.Os, Swarm: false, Rootless: p.Host.Security.Rootless, StorageDriver: p.Store.GraphDriverName}
return *cachedSysInfo, err
}
d, err := dockerSystemInfo()
Expand Down Expand Up @@ -213,8 +213,10 @@ type podmanSysInfo struct {
Hostname string `json:"hostname"`
Kernel string `json:"kernel"`
Os string `json:"os"`
Rootless bool `json:"rootless"`
Uptime string `json:"uptime"`
Security struct {
Rootless bool `json:"rootless"`
} `json:"security"`
Uptime string `json:"uptime"`
} `json:"host"`
Registries struct {
Search []string `json:"search"`
Expand Down
4 changes: 2 additions & 2 deletions pkg/drivers/kic/oci/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ func createContainer(ociBin string, image string, opts ...createOpt) error {

// to run nested container from privileged container in podman https://bugzilla.redhat.com/show_bug.cgi?id=1687713
// only add when running locally (linux), when running remotely it needs to be configured on server in libpod.conf
if ociBin == Podman && runtime.GOOS == "linux" {
if ociBin == Podman && runtime.GOOS == "linux" && !IsRootlessForced() {
args = append(args, "--cgroup-manager", "cgroupfs")
}

Expand Down Expand Up @@ -341,7 +341,7 @@ func StartContainer(ociBin string, container string) error {

// to run nested container from privileged container in podman https://bugzilla.redhat.com/show_bug.cgi?id=1687713
// only add when running locally (linux), when running remotely it needs to be configured on server in libpod.conf
if ociBin == Podman && runtime.GOOS == "linux" {
if ociBin == Podman && runtime.GOOS == "linux" && !IsRootlessForced() {
args = append(args, "--cgroup-manager", "cgroupfs")
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/minikube/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ const (
ProfileName = "profile"
// UserFlag is the key for the global user flag (ex. --user=user1)
UserFlag = "user"
// Rootless is the key for the global rootless flag (ex. --rootless=true)
Rootless = "rootless"
// AddonImages stores custom addon images config
AddonImages = "addon-images"
// AddonRegistries stores custom addon images config
Expand Down
2 changes: 2 additions & 0 deletions pkg/minikube/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ const (
TestDiskUsedEnv = "MINIKUBE_TEST_STORAGE_CAPACITY"
// TestDiskAvailableEnv is used in integration tests for insufficient storage with 'minikube status' (in GiB)
TestDiskAvailableEnv = "MINIKUBE_TEST_AVAILABLE_STORAGE"
// MinikubeRootlessEnv is used to force Rootless Docker/Podman driver
MinikubeRootlessEnv = "MINIKUBE_ROOTLESS"

// scheduled stop constants

Expand Down
5 changes: 3 additions & 2 deletions pkg/minikube/registry/drvs/podman/podman.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ func status() registry.State {
cmd := exec.CommandContext(ctx, oci.Podman, "version", "--format", "{{.Server.Version}}")
// Run with sudo on linux (local), otherwise podman-remote (as podman)
if runtime.GOOS == "linux" {
cmd = exec.CommandContext(ctx, "sudo", "-k", "-n", oci.Podman, "version", "--format", "{{.Version}}")
cmd = exec.CommandContext(ctx, oci.Podman, "version", "--format", "{{.Version}}")
cmd = oci.PrefixCmd(cmd, oci.WithSudoFlags("-k"))
cmd.Env = append(os.Environ(), "LANG=C", "LC_ALL=C") // sudo is localized
}
o, err := cmd.Output()
Expand Down Expand Up @@ -150,7 +151,7 @@ func status() registry.State {
newErr := fmt.Errorf(`%q %v: %s`, strings.Join(cmd.Args, " "), exitErr, stderr)

if strings.Contains(stderr, "a password is required") && runtime.GOOS == "linux" {
return registry.State{Error: newErr, Installed: true, Healthy: false, Fix: fmt.Sprintf("Add your user to the 'sudoers' file: '%s ALL=(ALL) NOPASSWD: %s'", username, podman), Doc: "https://podman.io"}
return registry.State{Error: newErr, Installed: true, Healthy: false, Fix: fmt.Sprintf("Add your user to the 'sudoers' file: '%s ALL=(ALL) NOPASSWD: %s' , or specify '--rootless' to enable rootless mode", username, podman), Doc: "https://podman.io"}
}

// Typical low-level errors from running podman-remote:
Expand Down
3 changes: 3 additions & 0 deletions site/content/en/docs/drivers/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ docker context use rootless
minikube start --driver=docker --container-runtime=containerd
```

Unlike Podman driver, it is not necessary to set the `rootless` property of minikube (`minikube config set rootless true`).
When the `rootless` property is explicitly set but the current Docker host is not rootless, minikube fails with an error.

The `--container-runtime` flag must be set to "containerd" or "cri-o".
{{% /tab %}}
{{% /tabs %}}
Expand Down
11 changes: 11 additions & 0 deletions site/content/en/docs/drivers/includes/podman_usage.inc
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,14 @@ To make podman the default driver:
```shell
minikube config set driver podman
```
## Rootless Podman
By default, minikube executes Podman with `sudo`.
To use Podman without `sudo` (i.e., Rootless Podman), set the `rootless` property to `true`:
```shell
minikube config set rootless true
```
See the [Rootless Docker](https://minikube.sigs.k8s.io/docs/drivers/docker/#rootless-docker) section for the requirements and the restrictions.

0 comments on commit c9db704

Please sign in to comment.