Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

preflight: Ensure crc network is accessible from session libvirt #709

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions cmd/crc/cmd/config/config_linux.go
Original file line number Diff line number Diff line change
@@ -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})
zeenix marked this conversation as resolved.
Show resolved Hide resolved
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})
)
2 changes: 1 addition & 1 deletion pkg/crc/machine/libvirt/templates_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const (
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='crc' stp='on' delay='0'/>
<bridge name='{{ .NetworkName }}' stp='on' delay='0'/>
<mac address='52:54:00:fd:be:d0'/>
<ip family='ipv4' address='192.168.130.1' prefix='24'>
<dhcp>
Expand Down
90 changes: 90 additions & 0 deletions pkg/crc/preflight/preflight_checks_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
14 changes: 14 additions & 0 deletions pkg/crc/preflight/preflight_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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 {
Expand Down
47 changes: 46 additions & 1 deletion pkg/os/util_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -20,3 +34,34 @@ func WriteToFileAsRoot(reason, content, filepath string) error {
}
return nil
}

func GetFirstExistentPath(paths []string) (string, error) {
readablePath := ""
zeenix marked this conversation as resolved.
Show resolved Hide resolved
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
}