diff --git a/cmd/crc/cmd/config/config_linux.go b/cmd/crc/cmd/config/config_linux.go
new file mode 100644
index 0000000000..157360c227
--- /dev/null
+++ b/cmd/crc/cmd/config/config_linux.go
@@ -0,0 +1,43 @@
+package config
+
+import (
+ cfg "github.com/code-ready/crc/pkg/crc/config"
+)
+
+var (
+ // Preflight checks
+ SkipCheckRootUser = cfg.AddSetting("skip-check-root-user", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckRootUser = cfg.AddSetting("warn-check-root-user", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckVirtEnabled = cfg.AddSetting("skip-check-virt-enabled", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckVirtEnabled = cfg.AddSetting("warn-check-virt-enabled", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckKvmEnabled = cfg.AddSetting("skip-check-kvm-enabled", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckKvmEnabled = cfg.AddSetting("warn-check-kvm-enabled", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckLibvirtInstalled = cfg.AddSetting("skip-check-libvirt-installed", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckLibvirtInstalled = cfg.AddSetting("warn-check-libvirt-installed", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckLibvirtEnabled = cfg.AddSetting("skip-check-libvirt-enabled", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckLibvirtEnabled = cfg.AddSetting("warn-check-libvirt-enabled", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckLibvirtRunning = cfg.AddSetting("skip-check-libvirt-running", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckLibvirtVersionCheck = cfg.AddSetting("warn-check-libvirt-version", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckLibvirtVersionCheck = cfg.AddSetting("skip-check-libvirt-version", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckLibvirtRunning = cfg.AddSetting("warn-check-libvirt-running", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckUserInLibvirtGroup = cfg.AddSetting("skip-check-user-in-libvirt-group", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckUserInLibvirtGroup = cfg.AddSetting("warn-check-user-in-libvirt-group", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckLibvirtDriver = cfg.AddSetting("skip-check-libvirt-driver", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckLibvirtDriver = cfg.AddSetting("warn-check-libvirt-driver", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckCrcNetwork = cfg.AddSetting("skip-check-crc-network", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckCrcNetwork = cfg.AddSetting("warn-check-crc-network", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckCrcNetworkActive = cfg.AddSetting("skip-check-crc-network-active", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckCrcNetworkActive = cfg.AddSetting("warn-check-crc-network-active", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckCrcBridgePermissions = cfg.AddSetting("skip-check-crc-network-permissions", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckCrcBridgePermissions = cfg.AddSetting("warn-check-crc-network-permissions", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckCrcDnsmasqFile = cfg.AddSetting("skip-check-crc-dnsmasq-file", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckCrcDnsmasqFile = cfg.AddSetting("warn-check-crc-dnsmasq-file", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckCrcNetworkManagerConfig = cfg.AddSetting("skip-check-network-manager-config", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckCrcNetworkManagerConfig = cfg.AddSetting("warn-check-network-manager-config", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckNetworkManagerInstalled = cfg.AddSetting("warn-check-network-manager-installed", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckNetworkManagerInstalled = cfg.AddSetting("skip-check-network-manager-installed", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckNetworkManagerRunning = cfg.AddSetting("warn-check-network-manager-running", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckNetworkManagerRunning = cfg.AddSetting("skip-check-network-manager-running", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ WarnCheckSystemLibvirtVM = cfg.AddSetting("warn-check-system-libvirt-vm", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+ SkipCheckSystemLibvirtVM = cfg.AddSetting("skip-check-system-libvirt-vm", nil, []cfg.ValidationFnType{cfg.ValidateBool}, []cfg.SetFn{cfg.SuccessfullyApplied})
+)
diff --git a/pkg/crc/machine/libvirt/templates_linux.go b/pkg/crc/machine/libvirt/templates_linux.go
index 4e5522517b..09d95f197b 100644
--- a/pkg/crc/machine/libvirt/templates_linux.go
+++ b/pkg/crc/machine/libvirt/templates_linux.go
@@ -9,7 +9,7 @@ const (
-
+
diff --git a/pkg/crc/preflight/preflight_checks_linux.go b/pkg/crc/preflight/preflight_checks_linux.go
index e97bd4157c..43baf85f68 100644
--- a/pkg/crc/preflight/preflight_checks_linux.go
+++ b/pkg/crc/preflight/preflight_checks_linux.go
@@ -41,6 +41,11 @@ server=/crc.testing/192.168.130.11
crcNetworkManagerConfig = `[main]
dns=dnsmasq
`
+ qemuBridgeConfig = "allow crc"
+ qemuBridgeConfigPaths = []string{
+ "/etc/qemu/bridge.conf", // Upstream
+ "/etc/qemu-kvm/bridge.conf", // RHEL
+ }
)
func checkVirtualizationEnabled() error {
@@ -392,6 +397,44 @@ func fixLibvirtCrcNetworkActive() error {
return nil
}
+func checkLibvirtCrcBridgePermissions() error {
+ logging.Debug("Checking if 'crc' bridge has appropriate permissions setup")
+ configPath, err := crcos.GetFirstExistentPath(qemuBridgeConfigPaths[:])
+ if err != nil {
+ return fmt.Errorf("Failed to find Qemu bridge configuration file: %s", err)
+ }
+
+ config, err := ioutil.ReadFile(filepath.Clean(configPath))
+ if err != nil {
+ return fmt.Errorf("Failed to read %s: %v", configPath, err)
+ }
+ regex := regexp.MustCompile(fmt.Sprintf("(?m)(\n|^)[[:space:]]*%s[[:space:]]*(\n|$)", qemuBridgeConfig))
+ if !regex.Match(config) {
+ return fmt.Errorf("Unpriviledged access to crc network is not allowed")
+ }
+ logging.Debug("The 'crc' bridge can be used by qemu-bridge-helper/session libvirt")
+ return nil
+}
+
+func fixLibvirtCrcBridgePermissions() error {
+ logging.Debug("Fixing permissions for 'crc'")
+ configPath, err := crcos.GetFirstExistentPath(qemuBridgeConfigPaths[:])
+ if err != nil {
+ return fmt.Errorf("Failed to find Qemu bridge configuration file: %s", err)
+ }
+
+ err = crcos.AppendToFileAsRoot(
+ "Allow 'crc' network to be used from session libvirt",
+ fmt.Sprintf("%s\n", qemuBridgeConfig),
+ configPath,
+ )
+ if err != nil {
+ return fmt.Errorf("Failed to write to %s: %v", configPath, err)
+ }
+ logging.Debug("The 'crc' bridge can now be used by qemu-bridge-helper/session libvirt")
+ return nil
+}
+
func checkCrcDnsmasqConfigFile() error {
logging.Debug("Checking dnsmasq configuration")
c := []byte(crcDnsmasqConfig)
@@ -504,3 +547,50 @@ func CheckNetworkManagerIsRunning() error {
func fixNetworkManagerIsRunning() error {
return fmt.Errorf("NetworkManager is required. Please make sure it is installed and running manually")
}
+
+func checkSystemLibvirtVM() error {
+ logging.Debug("Checking for existing VM on libvirt's system connection")
+ stdOut, stdErr, err := crcos.RunWithDefaultLocale("virsh", "--connect", "qemu:///system", "list", "--all")
+ if err != nil {
+ return fmt.Errorf("%+v: %s", err, stdErr)
+ }
+ regex := regexp.MustCompile(`- +crc`)
+
+ outputSlice := strings.Split(stdOut, "\n")
+ for _, stdOut = range outputSlice {
+ if regex.Match([]byte(stdOut)) {
+ return errors.New("Existing VM on system libvirt connection")
+ }
+ }
+ logging.Debug("No existing existing VM on libvirt system connection")
+ return nil
+}
+
+func fixSystemLibvirtVM() error {
+ logging.Debug("Deleting existing VM from libvirt's system connection")
+ _, stdErr, err := crcos.RunWithDefaultLocale("virsh", "--connect", "qemu:///system", "delete", "crc")
+ if err != nil {
+ // VM is probably not running, that's OK.
+ logging.Debug("Failed to stop `crc` VM: %+v: %s", err, stdErr)
+ }
+ _, stdErr, err = crcos.RunWithDefaultLocale("virsh", "--connect", "qemu:///system", "undefine", "crc")
+ if err != nil {
+ return fmt.Errorf("%+v: %s", err, stdErr)
+ }
+
+ // Ensure disk is owned by current user
+
+ // FIXME: First check if owner isn't already correctly set, even though if this function is called, it likely means
+ // it's not.
+ currentUser, err := user.Current()
+ if err != nil {
+ return fmt.Errorf("Error fetching current user info: %+v: %s", err, stdErr)
+ }
+ diskPath := filepath.Join(constants.MachineBaseDir, "machines", constants.DefaultName, constants.DefaultName)
+ err = crcos.ChownAsRoot(currentUser, diskPath)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/pkg/crc/preflight/preflight_linux.go b/pkg/crc/preflight/preflight_linux.go
index aaa08f676f..9900022c13 100644
--- a/pkg/crc/preflight/preflight_linux.go
+++ b/pkg/crc/preflight/preflight_linux.go
@@ -80,6 +80,13 @@ var libvirtPreflightChecks = [...]PreflightCheck{
fixDescription: "Starting libvirt 'crc' network",
fix: fixLibvirtCrcNetworkActive,
},
+ {
+ configKeySuffix: "check-crc-network-permission",
+ checkDescription: "Checking for appropriate permissions on 'crc' network",
+ check: checkLibvirtCrcBridgePermissions,
+ fixDescription: "Setting appropriate permissions on 'crc' network",
+ fix: fixLibvirtCrcBridgePermissions,
+ },
{
configKeySuffix: "check-network-manager-installed",
checkDescription: "Checking if NetworkManager is installed",
@@ -108,6 +115,13 @@ var libvirtPreflightChecks = [...]PreflightCheck{
fixDescription: "Writing dnsmasq config for crc",
fix: fixCrcDnsmasqConfigFile,
},
+ {
+ configKeySuffix: "check-system-libvirt-vm",
+ checkDescription: "Checking for existing `crc` VM on system libvirt connection",
+ check: checkSystemLibvirtVM,
+ fixDescription: "Importing existing `crc` VM",
+ fix: fixSystemLibvirtVM,
+ },
}
func getPreflightChecks() []PreflightCheck {
diff --git a/pkg/os/util_linux.go b/pkg/os/util_linux.go
index 2da1dda191..33c392ccbf 100644
--- a/pkg/os/util_linux.go
+++ b/pkg/os/util_linux.go
@@ -3,15 +3,29 @@ package os
import (
"bytes"
"fmt"
+ "os"
"os/exec"
+ "os/user"
"strings"
"github.com/code-ready/crc/pkg/crc/logging"
)
func WriteToFileAsRoot(reason, content, filepath string) error {
+ return writeToFileAsRoot(reason, content, filepath, false)
+}
+
+func AppendToFileAsRoot(reason, content, filepath string) error {
+ return writeToFileAsRoot(reason, content, filepath, true)
+}
+
+func writeToFileAsRoot(reason, content, filepath string, append bool) error {
logging.Infof("Will use root access: %s", reason)
- cmd := exec.Command("sudo", "tee", filepath) // #nosec G204
+ append_option := ""
+ if append {
+ append_option = "-a"
+ }
+ cmd := exec.Command("sudo", "tee", append_option, filepath) // #nosec G204
cmd.Stdin = strings.NewReader(content)
buf := new(bytes.Buffer)
cmd.Stderr = buf
@@ -20,3 +34,34 @@ func WriteToFileAsRoot(reason, content, filepath string) error {
}
return nil
}
+
+func GetFirstExistentPath(paths []string) (string, error) {
+ readablePath := ""
+ for _, path := range paths {
+ logging.Debug(fmt.Sprintf("Trying %s..", path))
+ _, err := os.Stat(path)
+ if err != nil {
+ logging.Debug(fmt.Sprintf("Failed to open %s: %s", path, err))
+ } else {
+ readablePath = path
+
+ break
+ }
+ }
+ if readablePath == "" {
+ return "", fmt.Errorf("Failed to find a readable file on any of these paths: %s", strings.Join(paths, ", "))
+ }
+
+ return readablePath, nil
+}
+
+func ChownAsRoot(user *user.User, filepath string) error {
+ logging.Infof("Will use root access to change owner & group of file %s to %s", filepath, user.Username)
+ cmd := exec.Command("sudo", "chown", fmt.Sprintf("%s.%s", user.Uid, user.Gid), filepath) // #nosec G204
+ buf := new(bytes.Buffer)
+ cmd.Stderr = buf
+ if err := cmd.Run(); err != nil {
+ return fmt.Errorf("Failed to change owner & group of %s to %s: %s: %s: %v", filepath, user.Username, buf.String(), err)
+ }
+ return nil
+}