diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index bc27e9b86b..368815bc79 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -241,8 +241,10 @@ func runStart(cmd *cobra.Command, args []string) { fmt.Println(`=================== WARNING: IT IS RECOMMENDED NOT TO RUN THE NONE DRIVER ON PERSONAL WORKSTATIONS The 'none' driver will run an insecure kubernetes apiserver as root that may leave the host vulnerable to CSRF attacks +`) -When using the none driver, the kubectl config and credentials generated will be root owned and will appear in the root home directory. + if os.Getenv("CHANGE_MINIKUBE_NONE_USER") != "" { + fmt.Println(`When using the none driver, the kubectl config and credentials generated will be root owned and will appear in the root home directory. You will need to move the files to the appropriate location and then set the correct permissions. An example of this is below: sudo mv /root/.kube $HOME/.kube # this will overwrite any config you have. You may have to append the file contents manually sudo chown -R $USER $HOME/.kube @@ -252,6 +254,12 @@ You will need to move the files to the appropriate location and then set the cor sudo chown -R $USER $HOME/.minikube sudo chgrp -R $USER $HOME/.minikube This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true`) + } + if err := cmdUtil.MaybeChownDirRecursiveToMinikubeUser(constants.GetMinipath()); err != nil { + glog.Errorf("Error recursively changing ownership of directory %s: %s", + constants.GetMinipath(), err) + cmdUtil.MaybeReportErrorAndExit(err) + } } } diff --git a/cmd/util/util.go b/cmd/util/util.go index fa046ab89d..cd169c277b 100644 --- a/cmd/util/util.go +++ b/cmd/util/util.go @@ -27,6 +27,7 @@ import ( "net/http" "os" "os/exec" + "os/user" "path/filepath" "runtime" "strings" @@ -246,3 +247,34 @@ func KillMountProcess() error { } return mountProc.Kill() } + +func MaybeChownDirRecursiveToMinikubeUser(dir string) error { + if os.Getenv("CHANGE_MINIKUBE_NONE_USER") != "" && os.Getenv("SUDO_USER") != "" { + username := os.Getenv("SUDO_USER") + usr, err := user.Lookup(username) + if err != nil { + return errors.Wrap(err, "Error looking up user") + } + uid, err := strconv.Atoi(usr.Uid) + if err != nil { + return errors.Wrapf(err, "Error parsing uid for user: %s", username) + } + gid, err := strconv.Atoi(usr.Gid) + if err != nil { + return errors.Wrapf(err, "Error parsing gid for user: %s", username) + } + if err := ChownR(dir, uid, gid); err != nil { + return errors.Wrapf(err, "Error changing ownership for: %s", dir) + } + } + return nil +} + +func ChownR(path string, uid, gid int) error { + return filepath.Walk(path, func(name string, info os.FileInfo, err error) error { + if err == nil { + err = os.Chown(name, uid, gid) + } + return err + }) +} diff --git a/pkg/minikube/assets/vm_assets.go b/pkg/minikube/assets/vm_assets.go index e4f53c9c51..9a53b44373 100644 --- a/pkg/minikube/assets/vm_assets.go +++ b/pkg/minikube/assets/vm_assets.go @@ -20,7 +20,6 @@ import ( "bytes" "io" "os" - "os/user" "path/filepath" "strconv" @@ -157,29 +156,7 @@ func CopyFileLocal(f CopyableFile) error { _, err = io.Copy(target, f) if err != nil { - return errors.Wrap(err, "Error copying file to target location") - } - - if os.Getenv("CHANGE_MINIKUBE_NONE_USER") != "" { - username := os.Getenv("SUDO_USER") - if username == "" { - return nil - } - usr, err := user.Lookup(username) - if err != nil { - return errors.Wrap(err, "Error looking up user") - } - uid, err := strconv.Atoi(usr.Uid) - if err != nil { - return errors.Wrapf(err, "Error parsing uid for user: %s", username) - } - gid, err := strconv.Atoi(usr.Gid) - if err != nil { - return errors.Wrapf(err, "Error parsing gid for user: %s", username) - } - if err := os.Chown(targetPath, uid, gid); err != nil { - return errors.Wrapf(err, "Error changing ownership for: %s", targetPath) - } + return errors.Wrap(err, "Error copying file to target location, do you have the correct permissions?") } return nil } diff --git a/pkg/minikube/cluster/cluster.go b/pkg/minikube/cluster/cluster.go index 18a2fd1b4b..884f025347 100644 --- a/pkg/minikube/cluster/cluster.go +++ b/pkg/minikube/cluster/cluster.go @@ -153,10 +153,7 @@ func GetLocalkubeStatus(api libmachine.API) (string, error) { if err != nil { return "", err } - - statusCmd := localkubeStatusCommand - - s, err := RunCommand(h, statusCmd, false) + s, err := RunCommand(h, localkubeStatusCommand, false) if err != nil { return "", err } @@ -446,7 +443,7 @@ func GetHostLogs(api libmachine.API, follow bool) (string, error) { s, err := RunCommand(h, logsCommand, false) if err != nil { - return "", err + return s, err } return s, nil } @@ -577,15 +574,20 @@ func EnsureMinikubeRunningOrExit(api libmachine.API, exitStatus int) { // RunCommand executes commands for both the local and driver implementations func RunCommand(h *host.Host, command string, sudo bool) (string, error) { if h.Driver.DriverName() == "none" { - cmd := exec.Command("/bin/sh", "-c", command) + cmd := exec.Command("/bin/bash", "-c", command) if sudo { - cmd = exec.Command("sudo", "/bin/sh", "-c", command) + cmd = exec.Command("sudo", "/bin/bash", "-c", command) } out, err := cmd.CombinedOutput() if err != nil { - return "", err + return "", errors.Wrap(err, string(out)) } - return string(out), nil + return string(out), err } - return h.RunSSHCommand(command) + out, err := h.RunSSHCommand(command) + if err != nil { + return "", errors.Wrap(err, string(out)) + } + return string(out), err + } diff --git a/pkg/minikube/cluster/commands.go b/pkg/minikube/cluster/commands.go index 45401bfef9..b19f181917 100644 --- a/pkg/minikube/cluster/commands.go +++ b/pkg/minikube/cluster/commands.go @@ -23,13 +23,20 @@ import ( "net" "strings" - "k8s.io/minikube/pkg/minikube/constants" "text/template" + + "k8s.io/minikube/pkg/minikube/constants" ) // Kill any running instances. + var localkubeStartCmdTemplate = "/usr/local/bin/localkube {{.Flags}} --generate-certs=false --logtostderr=true --enable-dns=false" +var startCommandNoSystemdTemplate = ` +# Run with nohup so it stays up. Redirect logs to useful places. +sudo sh -c 'PATH=/usr/local/sbin:$PATH nohup {{.LocalkubeStartCmd}} > {{.Stdout}} 2> {{.Stderr}} < /dev/null & echo $! > {{.Pidfile}} &' +` + var localkubeSystemdTmpl = `[Unit] Description=Localkube Documentation=https://github.com/kubernetes/minikube/tree/master/pkg/localkube @@ -47,11 +54,15 @@ ExecReload=/bin/kill -s HUP $MAINPID WantedBy=multi-user.target ` -var startCommandTemplate = ` +var startCommandTemplate = "if [[ `systemctl` =~ -\\.mount ]] &>/dev/null;" + `then {{.StartCommandSystemd}} sudo systemctl daemon-reload sudo systemctl enable localkube.service sudo systemctl restart localkube.service || true +else + sudo killall localkube || true + {{.StartCommandNoSystemd}} +fi ` func GetStartCommand(kubernetesConfig KubernetesConfig) (string, error) { @@ -59,6 +70,10 @@ func GetStartCommand(kubernetesConfig KubernetesConfig) (string, error) { if err != nil { return "", err } + startCommandNoSystemd, err := GetStartCommandNoSystemd(kubernetesConfig, localkubeStartCommand) + if err != nil { + return "", err + } startCommandSystemd, err := GetStartCommandSystemd(kubernetesConfig, localkubeStartCommand) if err != nil { return "", err @@ -66,9 +81,31 @@ func GetStartCommand(kubernetesConfig KubernetesConfig) (string, error) { t := template.Must(template.New("startCommand").Parse(startCommandTemplate)) buf := bytes.Buffer{} data := struct { - StartCommandSystemd string + StartCommandNoSystemd string + StartCommandSystemd string }{ - StartCommandSystemd: startCommandSystemd, + StartCommandNoSystemd: startCommandNoSystemd, + StartCommandSystemd: startCommandSystemd, + } + if err := t.Execute(&buf, data); err != nil { + return "", err + } + return buf.String(), nil +} + +func GetStartCommandNoSystemd(kubernetesConfig KubernetesConfig, localkubeStartCmd string) (string, error) { + t := template.Must(template.New("startCommand").Parse(startCommandNoSystemdTemplate)) + buf := bytes.Buffer{} + data := struct { + LocalkubeStartCmd string + Stdout string + Stderr string + Pidfile string + }{ + LocalkubeStartCmd: localkubeStartCmd, + Stdout: constants.RemoteLocalKubeOutPath, + Stderr: constants.RemoteLocalKubeErrPath, + Pidfile: constants.LocalkubePIDPath, } if err := t.Execute(&buf, data); err != nil { return "", err @@ -130,6 +167,7 @@ func GenLocalkubeStartCmd(kubernetesConfig KubernetesConfig) (string, error) { flagVals = append(flagVals, fmt.Sprintf("--extra-config=%s", e.String())) } flags := strings.Join(flagVals, " ") + t := template.Must(template.New("localkubeStartCmd").Parse(localkubeStartCmdTemplate)) buf := bytes.Buffer{} data := struct { @@ -145,7 +183,12 @@ func GenLocalkubeStartCmd(kubernetesConfig KubernetesConfig) (string, error) { return buf.String(), nil } -const logsTemplate = "sudo journalctl {{.Flags}} -u localkube" +const logsTemplate = "if [[ `systemctl` =~ -\\.mount ]] &>/dev/null; " + `then + sudo journalctl {{.Flags}} -u localkube +else + tail -n +1 {{.Flags}} {{.RemoteLocalkubeErrPath}} {{.RemoteLocalkubeOutPath}} +fi +` func GetLogsCommand(follow bool) (string, error) { t, err := template.New("logsTemplate").Parse(logsTemplate) @@ -173,7 +216,16 @@ func GetLogsCommand(follow bool) (string, error) { return buf.String(), nil } -var localkubeStatusCommand = `sudo systemctl is-active localkube 2>&1 1>/dev/null && echo "Running" || echo "Stopped"` +var localkubeStatusCommand = fmt.Sprintf("if [[ `systemctl` =~ -\\.mount ]] &>/dev/null; "+`then + sudo systemctl is-active localkube &>/dev/null && echo "Running" || echo "Stopped" +else + if ps $(cat %s) &>/dev/null; then + echo "Running" + else + echo "Stopped" + fi +fi +`, constants.LocalkubePIDPath) func GetMountCleanupCommand(path string) string { return fmt.Sprintf("sudo umount %s;", path) diff --git a/pkg/minikube/kubeconfig/config.go b/pkg/minikube/kubeconfig/config.go index 32c8d98d03..cdb5986f8a 100644 --- a/pkg/minikube/kubeconfig/config.go +++ b/pkg/minikube/kubeconfig/config.go @@ -31,6 +31,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/clientcmd/api" "k8s.io/client-go/tools/clientcmd/api/latest" + cmdUtil "k8s.io/minikube/cmd/util" cfg "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" ) @@ -166,6 +167,10 @@ func WriteConfig(config *api.Config, filename string) error { if err := ioutil.WriteFile(filename, data, 0600); err != nil { return errors.Wrapf(err, "Error writing file %s", filename) } + if err := cmdUtil.MaybeChownDirRecursiveToMinikubeUser(dir); err != nil { + return errors.Wrapf(err, "Error recursively changing ownership for dir: %s", dir) + } + return nil } diff --git a/pkg/minikube/machine/drivers/none/none.go b/pkg/minikube/machine/drivers/none/none.go index 55611d2ff3..288396473a 100644 --- a/pkg/minikube/machine/drivers/none/none.go +++ b/pkg/minikube/machine/drivers/none/none.go @@ -17,17 +17,15 @@ limitations under the License. package none import ( - "errors" "fmt" - "io/ioutil" "os" "os/exec" - "path/filepath" "strings" "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/mcnflag" "github.com/docker/machine/libmachine/state" + "github.com/pkg/errors" "k8s.io/minikube/pkg/minikube/constants" ) @@ -50,10 +48,6 @@ func NewDriver(hostName, storePath string) *Driver { // PreCreateCheck checks that VBoxManage exists and works func (d *Driver) PreCreateCheck() error { - // check that systemd is installed as it is a requirement - if _, err := exec.LookPath("systemctl"); err != nil { - return errors.New("systemd is a requirement in order to use the none driver") - } return nil } @@ -96,17 +90,22 @@ func (d *Driver) GetURL() (string, error) { } func (d *Driver) GetState() (state.State, error) { - command := `sudo systemctl is-active localkube 2>&1 1>/dev/null && echo "Running" || echo "Stopped"` + var statuscmd = fmt.Sprintf("if [[ `systemctl` =~ -\\.mount ]] &>/dev/null; "+`then + sudo systemctl is-active localkube &>/dev/null && echo "Running" || echo "Stopped" +else + if ps $(cat %s) &>/dev/null; then + echo "Running" + else + echo "Stopped" + fi +fi +`, constants.LocalkubePIDPath) - path := filepath.Join(constants.GetMinipath(), "tmp-cmd") - ioutil.WriteFile(filepath.Join(constants.GetMinipath(), "tmp-cmd"), []byte(command), os.FileMode(0644)) - defer os.Remove(path) - cmd := exec.Command("sudo", "/bin/sh", path) - out, err := cmd.CombinedOutput() + out, err := runCommand(statuscmd, true) if err != nil { return state.None, err } - s := strings.TrimSpace(string(out)) + s := strings.TrimSpace(out) if state.Running.String() == s { return state.Running, nil } else if state.Stopped.String() == s { @@ -162,11 +161,16 @@ func (d *Driver) Start() error { } func (d *Driver) Stop() error { - cmd := exec.Command("sudo", "systemctl", "stop", "localkube.service") - if err := cmd.Start(); err != nil { + var stopcmd = fmt.Sprintf("if [[ `systemctl` =~ -\\.mount ]] &>/dev/null; "+`then + sudo systemctl stop localkube.service +else + sudo kill $(cat %s) +fi +`, constants.LocalkubePIDPath) + _, err := runCommand(stopcmd, true) + if err != nil { return err } - for { s, err := d.GetState() if err != nil { @@ -182,3 +186,15 @@ func (d *Driver) Stop() error { func (d *Driver) RunSSHCommandFromDriver() error { return fmt.Errorf("driver does not support ssh commands") } + +func runCommand(command string, sudo bool) (string, error) { + cmd := exec.Command("/bin/bash", "-c", command) + if sudo { + cmd = exec.Command("sudo", "/bin/bash", "-c", command) + } + out, err := cmd.CombinedOutput() + if err != nil { + return "", errors.Wrap(err, string(out)) + } + return string(out), nil +}