From 084630dfd3b729240598000d2daac5a1344c341f Mon Sep 17 00:00:00 2001 From: Steven Powell Date: Mon, 26 Jun 2023 14:13:53 -0700 Subject: [PATCH 1/3] Unblock bootpd if detected blocked --- cmd/minikube/cmd/start.go | 65 +++-------------------- pkg/drivers/qemu/qemu.go | 17 ++++-- pkg/minikube/firewall/firewall.go | 87 +++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 64 deletions(-) create mode 100644 pkg/minikube/firewall/firewall.go diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index a248c95135..14e98ee80c 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -47,6 +47,7 @@ import ( "golang.org/x/text/cases" "golang.org/x/text/language" "k8s.io/minikube/pkg/minikube/command" + "k8s.io/minikube/pkg/minikube/firewall" netutil "k8s.io/minikube/pkg/network" "k8s.io/klog/v2" @@ -339,7 +340,11 @@ func provisionWithDriver(cmd *cobra.Command, ds registry.DriverState, existing * return node.Starter{}, errors.Wrap(err, "Failed to generate config") } - unblockBootpdFirewall(cc) + if firewall.IsBootpdBlocked(cc) { + if err := firewall.UnblockBootpd(); err != nil { + klog.Warningf("failed unblocking bootpd from firewall: %v", err) + } + } if driver.IsVM(cc.Driver) && runtime.GOARCH == "arm64" && cc.KubernetesConfig.ContainerRuntime == "crio" { exit.Message(reason.Unimplemented, "arm64 VM drivers do not currently support the crio container runtime. See https://github.com/kubernetes/minikube/issues/14146 for details.") @@ -416,64 +421,6 @@ func vmwareUnsupported(driverName string) { `) } -// isBootpdBlocked returns true if the built-in macOS firewall is on and bootpd is not unblocked -func isBootpdBlocked(cc config.ClusterConfig) bool { - // only applies to qemu, on macOS, with socket_vmnet - if cc.Driver != driver.QEMU2 || runtime.GOOS != "darwin" || cc.Network != "socket_vmnet" { - return false - } - out, err := exec.Command("/usr/libexec/ApplicationFirewall/socketfilterfw", "--getglobalstate").Output() - if err != nil { - klog.Warningf("failed to get firewall state: %v", err) - return false - } - if regexp.MustCompile(`Firewall is disabled`).Match(out) { - return false - } - out, err = exec.Command("/usr/libexec/ApplicationFirewall/socketfilterfw", "--listapps").Output() - if err != nil { - klog.Warningf("failed to list firewall apps: %v", err) - return false - } - return !regexp.MustCompile(`\/usr\/libexec\/bootpd.*\n.*\( Allow`).Match(out) -} - -// unblockBootpdFirewall adds bootpd to the built-in macOS firewall and then unblocks it -func unblockBootpdFirewall(cc config.ClusterConfig) { - if !isBootpdBlocked(cc) { - return - } - - cmds := []*exec.Cmd{ - exec.Command("sudo", "/usr/libexec/ApplicationFirewall/socketfilterfw", "--add", "/usr/libexec/bootpd"), - exec.Command("sudo", "/usr/libexec/ApplicationFirewall/socketfilterfw", "--unblock", "/usr/libexec/bootpd"), - } - - var cmdString strings.Builder - for _, c := range cmds { - cmdString.WriteString(fmt.Sprintf(" $ %s \n", strings.Join(c.Args, " "))) - } - - out.Styled(style.Permissions, "Your firewall is blocking bootpd which is required for socket_vmnet. The following commands will be executed to unblock bootpd:\n\n{{.commands}}\n", out.V{"commands": cmdString.String()}) - - for _, c := range cmds { - testArgs := append([]string{"-n"}, c.Args[1:]...) - test := exec.Command("sudo", testArgs...) - klog.Infof("testing: %s", test.Args) - if err := test.Run(); err != nil { - klog.Infof("%v may require a password: %v", c.Args, err) - if !viper.GetBool("interactive") { - klog.Warningf("%s requires a password, and --interactive=false", c.Args) - } - } - klog.Infof("running: %s", c.Args) - err := c.Run() - if err != nil { - klog.Warningf("running %s failed: %v", c.Args, err) - } - } -} - func validateBuiltImageVersion(r command.Runner, driverName string) { if driver.IsNone(driverName) { return diff --git a/pkg/drivers/qemu/qemu.go b/pkg/drivers/qemu/qemu.go index 0bd533ab96..3a5beaa540 100644 --- a/pkg/drivers/qemu/qemu.go +++ b/pkg/drivers/qemu/qemu.go @@ -40,8 +40,10 @@ import ( "github.com/docker/machine/libmachine/state" "github.com/pkg/errors" + "k8s.io/klog/v2" pkgdrivers "k8s.io/minikube/pkg/drivers" "k8s.io/minikube/pkg/minikube/exit" + "k8s.io/minikube/pkg/minikube/firewall" "k8s.io/minikube/pkg/minikube/reason" "k8s.io/minikube/pkg/network" ) @@ -515,13 +517,18 @@ func (d *Driver) Start() error { time.Sleep(2 * time.Second) } - if err != nil { - if isBootpdError(err) { - exit.Error(reason.IfBootpdFirewall, "ip not found", err) - } + if err == nil { + log.Debugf("IP: %s", d.IPAddress) + break + } + if !isBootpdError(err) { return errors.Wrap(err, "IP address never found in dhcp leases file") } - log.Debugf("IP: %s", d.IPAddress) + if unblockErr := firewall.UnblockBootpd(); unblockErr != nil { + klog.Errorf("failed unblocking bootpd from firewall: %v", unblockErr) + exit.Error(reason.IfBootpdFirewall, "ip not found", err) + } + return fmt.Errorf("bootpd process is unblocked, will retry") } log.Infof("Waiting for VM to start (ssh -p %d docker@%s)...", d.SSHPort, d.IPAddress) diff --git a/pkg/minikube/firewall/firewall.go b/pkg/minikube/firewall/firewall.go new file mode 100644 index 0000000000..f62b311996 --- /dev/null +++ b/pkg/minikube/firewall/firewall.go @@ -0,0 +1,87 @@ +/* +Copyright 2023 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package firewall + +import ( + "fmt" + "os/exec" + "regexp" + "runtime" + "strings" + + "github.com/spf13/viper" + "k8s.io/klog/v2" + "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/driver" + "k8s.io/minikube/pkg/minikube/out" + "k8s.io/minikube/pkg/minikube/style" +) + +// IsBootpdBlocked checks if the bootpd process is blocked by the macOS builtin firewall +func IsBootpdBlocked(cc config.ClusterConfig) bool { + // only applies to qemu, on macOS, with socket_vmnet + if cc.Driver != driver.QEMU2 || runtime.GOOS != "darwin" || cc.Network != "socket_vmnet" { + return false + } + out, err := exec.Command("/usr/libexec/ApplicationFirewall/socketfilterfw", "--getglobalstate").Output() + if err != nil { + klog.Warningf("failed to get firewall state: %v", err) + return false + } + if regexp.MustCompile(`Firewall is disabled`).Match(out) { + return false + } + out, err = exec.Command("/usr/libexec/ApplicationFirewall/socketfilterfw", "--listapps").Output() + if err != nil { + klog.Warningf("failed to list firewall apps: %v", err) + return false + } + return !regexp.MustCompile(`\/usr\/libexec\/bootpd.*\n.*\( Allow`).Match(out) +} + +// UnblockBootpd adds bootpd to the built-in macOS firewall and then unblocks it +func UnblockBootpd() error { + cmds := []*exec.Cmd{ + exec.Command("sudo", "/usr/libexec/ApplicationFirewall/socketfilterfw", "--add", "/usr/libexec/bootpd"), + exec.Command("sudo", "/usr/libexec/ApplicationFirewall/socketfilterfw", "--unblock", "/usr/libexec/bootpd"), + } + + var cmdString strings.Builder + for _, c := range cmds { + cmdString.WriteString(fmt.Sprintf(" $ %s \n", strings.Join(c.Args, " "))) + } + + out.Styled(style.Permissions, "Your firewall is blocking bootpd which is required for socket_vmnet. The following commands will be executed to unblock bootpd:\n\n{{.commands}}\n", out.V{"commands": cmdString.String()}) + + for _, c := range cmds { + testArgs := append([]string{"-n"}, c.Args[1:]...) + test := exec.Command("sudo", testArgs...) + klog.Infof("testing: %s", test.Args) + if err := test.Run(); err != nil { + klog.Infof("%v may require a password: %v", c.Args, err) + if !viper.GetBool("interactive") { + klog.Warningf("%s requires a password, and --interactive=false", c.Args) + } + } + klog.Infof("running: %s", c.Args) + err := c.Run() + if err != nil { + return fmt.Errorf("running %s failed: %v", c.Args, err) + } + } + return nil +} From 86dbac0bed092c0fed343a0f376604e48365ca7c Mon Sep 17 00:00:00 2001 From: Steven Powell Date: Wed, 28 Jun 2023 14:19:43 -0700 Subject: [PATCH 2/3] improved messaging --- pkg/drivers/qemu/qemu.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/drivers/qemu/qemu.go b/pkg/drivers/qemu/qemu.go index 3a5beaa540..b14a8ea9c2 100644 --- a/pkg/drivers/qemu/qemu.go +++ b/pkg/drivers/qemu/qemu.go @@ -44,7 +44,9 @@ import ( pkgdrivers "k8s.io/minikube/pkg/drivers" "k8s.io/minikube/pkg/minikube/exit" "k8s.io/minikube/pkg/minikube/firewall" + "k8s.io/minikube/pkg/minikube/out" "k8s.io/minikube/pkg/minikube/reason" + "k8s.io/minikube/pkg/minikube/style" "k8s.io/minikube/pkg/network" ) @@ -528,7 +530,8 @@ func (d *Driver) Start() error { klog.Errorf("failed unblocking bootpd from firewall: %v", unblockErr) exit.Error(reason.IfBootpdFirewall, "ip not found", err) } - return fmt.Errorf("bootpd process is unblocked, will retry") + out.Styled(style.Restarting, "Sucessfully unblocked bootpd process from firewall, retrying") + return fmt.Errorf("ip not found: %v", err) } log.Infof("Waiting for VM to start (ssh -p %d docker@%s)...", d.SSHPort, d.IPAddress) From 233e5b6d6ce97b51ccfbc6729efd63948a0610f4 Mon Sep 17 00:00:00 2001 From: Steven Powell Date: Wed, 28 Jun 2023 14:30:18 -0700 Subject: [PATCH 3/3] fix typo --- pkg/drivers/qemu/qemu.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/drivers/qemu/qemu.go b/pkg/drivers/qemu/qemu.go index b14a8ea9c2..f9c1059f88 100644 --- a/pkg/drivers/qemu/qemu.go +++ b/pkg/drivers/qemu/qemu.go @@ -530,7 +530,7 @@ func (d *Driver) Start() error { klog.Errorf("failed unblocking bootpd from firewall: %v", unblockErr) exit.Error(reason.IfBootpdFirewall, "ip not found", err) } - out.Styled(style.Restarting, "Sucessfully unblocked bootpd process from firewall, retrying") + out.Styled(style.Restarting, "Successfully unblocked bootpd process from firewall, retrying") return fmt.Errorf("ip not found: %v", err) }