Repository: code-ready/crc
Pull request #1722 (2020-11-25 09:26:11)

Use admin-helper instead of goodhosts
**Fixes:** Issue #1328

It uses https://github.com/code-ready/admin-helper instead of goodhosts CLI.
I introduced a new command called clean that remove all entries by a domain suffix. It is enough to clean up what was added by crc.

openshift-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/code-ready/crc/pull/1722#" title="Author self-approved">guillaumerose</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=code-ready%2Fcrc).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/code-ready/crc/blob/master/OWNERS)~~ [guillaumerose]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
openshift-merge-robot: @guillaumerose: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e-crc | c2f12462633c30a0910dc652fae595cbe9bff7ca | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/code-ready_crc/1722/pull-ci-code-ready-crc-master-e2e-crc/1331529612911448064) | `/test e2e-crc`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=code-ready&repo=crc&pr=1722). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:guillaumerose).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-ci-robot: @guillaumerose: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
gbraad: Renamed to WIP as scope of Admin-helper is of yet not clearly defined
guillaumerose: The scope of this PR is really limited. It is just an upgrade of goodhosts CLI + a new sub command. It doesn't engage us too much. It's not WIP and the user benefit is great: no more extra entries in the hosts file after crc cleanup.
praveenkumar: /hold
guillaumerose: /unhold

Rebased! PTAL.
praveenkumar: /lgtm
guillaumerose: retest this please

Patch:
diff --git a/cmd/crc-embedder/cmd/embed.go b/cmd/crc-embedder/cmd/embed.go
index 8175c1f68..9df174faf 100644
--- a/cmd/crc-embedder/cmd/embed.go
+++ b/cmd/crc-embedder/cmd/embed.go
@@ -90,14 +90,14 @@ var (
 			hyperkit.MachineDriverDownloadURL,
 			hyperkit.HyperKitDownloadURL,
 			constants.GetCRCMacTrayDownloadURL(),
-			constants.GetGoodhostsURLForOs("darwin"),
+			constants.GetAdminHelperURLForOs("darwin"),
 		},
 		"linux": {
 			libvirt.MachineDriverDownloadURL,
-			constants.GetGoodhostsURLForOs("linux"),
+			constants.GetAdminHelperURLForOs("linux"),
 		},
 		"windows": {
-			constants.GetGoodhostsURLForOs("windows"),
+			constants.GetAdminHelperURLForOs("windows"),
 			constants.GetCRCWindowsTrayDownloadURL(),
 		},
 	}
diff --git a/pkg/crc/goodhosts/goodhosts.go b/pkg/crc/adminhelper/hosts.go
similarity index 78%
rename from pkg/crc/goodhosts/goodhosts.go
rename to pkg/crc/adminhelper/hosts.go
index 18b67b9cc..e0cda1d7c 100644
--- a/pkg/crc/goodhosts/goodhosts.go
+++ b/pkg/crc/adminhelper/hosts.go
@@ -1,4 +1,4 @@
-package goodhosts
+package adminhelper
 
 import (
 	"path/filepath"
@@ -7,7 +7,7 @@ import (
 )
 
 var (
-	goodhostPath = filepath.Join(constants.CrcBinDir, constants.GoodhostsExecutableName)
+	goodhostPath = filepath.Join(constants.CrcBinDir, constants.AdminHelperExecutableName)
 )
 
 // UpdateHostsFile updates the host's /etc/hosts file with Instance IP.
@@ -32,3 +32,7 @@ func RemoveFromHostsFile(instanceIP string, hostnames ...string) error {
 	}
 	return execute(append([]string{"rm"}, hostnames...)...)
 }
+
+func CleanHostsFile() error {
+	return execute([]string{"clean", constants.ClusterDomain, constants.AppsDomain}...)
+}
diff --git a/pkg/crc/goodhosts/goodhosts_nowin.go b/pkg/crc/adminhelper/hosts_nowin.go
similarity index 90%
rename from pkg/crc/goodhosts/goodhosts_nowin.go
rename to pkg/crc/adminhelper/hosts_nowin.go
index 2d4dade0b..fc561b28e 100644
--- a/pkg/crc/goodhosts/goodhosts_nowin.go
+++ b/pkg/crc/adminhelper/hosts_nowin.go
@@ -1,6 +1,6 @@
 // +build !windows
 
-package goodhosts
+package adminhelper
 
 import (
 	crcos "github.com/code-ready/crc/pkg/os"
diff --git a/pkg/crc/goodhosts/goodhosts_windows.go b/pkg/crc/adminhelper/hosts_windows.go
similarity index 92%
rename from pkg/crc/goodhosts/goodhosts_windows.go
rename to pkg/crc/adminhelper/hosts_windows.go
index ff77cc1bc..ba6a7fb1e 100644
--- a/pkg/crc/goodhosts/goodhosts_windows.go
+++ b/pkg/crc/adminhelper/hosts_windows.go
@@ -1,4 +1,4 @@
-package goodhosts
+package adminhelper
 
 import (
 	"strings"
diff --git a/pkg/crc/cache/cache.go b/pkg/crc/cache/cache.go
index d903e8908..8b6ca6051 100644
--- a/pkg/crc/cache/cache.go
+++ b/pkg/crc/cache/cache.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"io/ioutil"
 	"os"
+	"path"
 	"path/filepath"
 	"strings"
 
@@ -54,19 +55,36 @@ func (c *Cache) GetExecutableName() string {
  */
 func getVersionGeneric(executablePath string, args ...string) (string, error) { //nolint:deadcode,unused
 	stdOut, _, err := crcos.RunWithDefaultLocale(executablePath, args...)
+	if err != nil {
+		return "", err
+	}
 	parsedOutput := strings.Split(stdOut, ":")
 	if len(parsedOutput) < 2 {
 		return "", fmt.Errorf("Unable to parse the version information of %s", executablePath)
 	}
-	return strings.TrimSpace(parsedOutput[1]), err
+	return strings.TrimSpace(parsedOutput[1]), nil
 }
 
 func NewPodmanCache() *Cache {
 	return New(constants.PodmanExecutableName, constants.GetPodmanURL(), constants.CrcBinDir, "", nil)
 }
 
-func NewGoodhostsCache() *Cache {
-	return New(constants.GoodhostsExecutableName, constants.GetGoodhostsURL(), constants.CrcBinDir, "", nil)
+func NewAdminHelperCache() *Cache {
+	url := constants.GetAdminHelperURL()
+	version := path.Base(path.Dir(url))
+	return New(constants.AdminHelperExecutableName,
+		url,
+		constants.CrcBinDir,
+		version,
+		func(executable string) (string, error) {
+			out, _, err := crcos.RunWithDefaultLocale(executable, "--version")
+			if err != nil {
+				return "", err
+			}
+			split := strings.Split(out, " ")
+			return strings.TrimSpace(split[len(split)-1]), nil
+		},
+	)
 }
 
 func (c *Cache) IsCached() bool {
@@ -78,10 +96,7 @@ func (c *Cache) IsCached() bool {
 
 func (c *Cache) EnsureIsCached() error {
 	if !c.IsCached() || c.CheckVersion() != nil {
-		err := c.CacheExecutable()
-		if err != nil {
-			return err
-		}
+		return c.CacheExecutable()
 	}
 	return nil
 }
diff --git a/pkg/crc/constants/constants.go b/pkg/crc/constants/constants.go
index 4bfe916e9..0eb651cbc 100644
--- a/pkg/crc/constants/constants.go
+++ b/pkg/crc/constants/constants.go
@@ -29,7 +29,7 @@ const (
 	DaemonLogFile             = "crcd.log"
 	CrcLandingPageURL         = "https://cloud.redhat.com/openshift/install/crc/installer-provisioned" // #nosec G101
 	DefaultPodmanURLBase      = "https://storage.googleapis.com/libpod-master-releases"
-	DefaultGoodhostsCliBase   = "https://github.com/code-ready/goodhosts-cli/releases/download/v1.0.0"
+	DefaultAdminHelperCliBase = "https://github.com/code-ready/admin-helper/releases/download/0.0.1"
 	CRCMacTrayDownloadURL     = "https://github.com/code-ready/tray-macos/releases/download/v%s/crc-tray-macos.tar.gz"
 	CRCWindowsTrayDownloadURL = "https://github.com/code-ready/tray-windows/releases/download/v%s/crc-tray-windows.zip"
 	DefaultContext            = "admin"
@@ -38,6 +38,9 @@ const (
 	VsockSSHPort = 2222
 
 	OkdPullSecret = `{"auths":{"fake":{"auth": "Zm9vOmJhcgo="}}}` // #nosec G101
+
+	ClusterDomain = ".crc.testing"
+	AppsDomain    = ".apps-crc.testing"
 )
 
 var podmanURLForOs = map[string]string{
@@ -54,18 +57,18 @@ func GetPodmanURL() string {
 	return podmanURLForOs[runtime.GOOS]
 }
 
-var goodhostsURLForOs = map[string]string{
-	"darwin":  fmt.Sprintf("%s/%s", DefaultGoodhostsCliBase, "goodhosts-cli-macos-amd64.tar.xz"),
-	"linux":   fmt.Sprintf("%s/%s", DefaultGoodhostsCliBase, "goodhosts-cli-linux-amd64.tar.xz"),
-	"windows": fmt.Sprintf("%s/%s", DefaultGoodhostsCliBase, "goodhosts-cli-windows-amd64.tar.xz"),
+var adminHelperURLForOs = map[string]string{
+	"darwin":  fmt.Sprintf("%s/%s", DefaultAdminHelperCliBase, "admin-helper-macos-amd64.tar.xz"),
+	"linux":   fmt.Sprintf("%s/%s", DefaultAdminHelperCliBase, "admin-helper-linux-amd64.tar.xz"),
+	"windows": fmt.Sprintf("%s/%s", DefaultAdminHelperCliBase, "admin-helper-windows-amd64.tar.xz"),
 }
 
-func GetGoodhostsURLForOs(os string) string {
-	return goodhostsURLForOs[os]
+func GetAdminHelperURLForOs(os string) string {
+	return adminHelperURLForOs[os]
 }
 
-func GetGoodhostsURL() string {
-	return goodhostsURLForOs[runtime.GOOS]
+func GetAdminHelperURL() string {
+	return adminHelperURLForOs[runtime.GOOS]
 }
 
 var defaultBundleForOs = map[string]string{
@@ -144,10 +147,15 @@ func contains(arr []string, str string) bool {
 }
 
 func GetPublicKeyPath() string {
-	return filepath.Join(MachineInstanceDir, DefaultName, "id_rsa.pub")
+	return filepath.Join(MachineInstanceDir, DefaultName, "id_ecdsa.pub")
 }
 
 func GetPrivateKeyPath() string {
+	return filepath.Join(MachineInstanceDir, DefaultName, "id_ecdsa")
+}
+
+// For backward compatibility to v 1.20.0
+func GetRsaPrivateKeyPath() string {
 	return filepath.Join(MachineInstanceDir, DefaultName, "id_rsa")
 }
 
diff --git a/pkg/crc/constants/constants_darwin.go b/pkg/crc/constants/constants_darwin.go
index 5ea0a5b50..54af654e6 100644
--- a/pkg/crc/constants/constants_darwin.go
+++ b/pkg/crc/constants/constants_darwin.go
@@ -3,10 +3,10 @@ package constants
 import "path/filepath"
 
 const (
-	OcExecutableName        = "oc"
-	PodmanExecutableName    = "podman"
-	TrayExecutableName      = "CodeReady Containers.app"
-	GoodhostsExecutableName = "goodhosts"
+	OcExecutableName          = "oc"
+	PodmanExecutableName      = "podman"
+	TrayExecutableName        = "CodeReady Containers.app"
+	AdminHelperExecutableName = "admin-helper"
 )
 
 var (
diff --git a/pkg/crc/constants/constants_linux.go b/pkg/crc/constants/constants_linux.go
index 029e41a6f..4c701d651 100644
--- a/pkg/crc/constants/constants_linux.go
+++ b/pkg/crc/constants/constants_linux.go
@@ -1,7 +1,7 @@
 package constants
 
 const (
-	OcExecutableName        = "oc"
-	PodmanExecutableName    = "podman"
-	GoodhostsExecutableName = "goodhosts"
+	OcExecutableName          = "oc"
+	PodmanExecutableName      = "podman"
+	AdminHelperExecutableName = "admin-helper"
 )
diff --git a/pkg/crc/constants/constants_windows.go b/pkg/crc/constants/constants_windows.go
index 70c644b84..f773f6df0 100644
--- a/pkg/crc/constants/constants_windows.go
+++ b/pkg/crc/constants/constants_windows.go
@@ -3,12 +3,12 @@ package constants
 import "path/filepath"
 
 const (
-	OcExecutableName        = "oc.exe"
-	PodmanExecutableName    = "podman.exe"
-	GoodhostsExecutableName = "goodhosts.exe"
-	TrayExecutableName      = "tray-windows.exe"
-	DaemonServiceName       = "CodeReady Containers"
-	TrayShortcutName        = "tray-windows.lnk"
+	OcExecutableName          = "oc.exe"
+	PodmanExecutableName      = "podman.exe"
+	AdminHelperExecutableName = "admin-helper.exe"
+	TrayExecutableName        = "tray-windows.exe"
+	DaemonServiceName         = "CodeReady Containers"
+	TrayShortcutName          = "tray-windows.lnk"
 )
 
 var (
diff --git a/pkg/crc/machine/bundle/repository.go b/pkg/crc/machine/bundle/repository.go
index 8ab66fb26..059c8b61e 100644
--- a/pkg/crc/machine/bundle/repository.go
+++ b/pkg/crc/machine/bundle/repository.go
@@ -68,6 +68,12 @@ func (repo *Repository) Use(bundleName string) (*CrcBundleInfo, error) {
 	if err != nil {
 		return nil, err
 	}
+	if fmt.Sprintf(".%s", bundleInfo.ClusterInfo.AppsDomain) != constants.AppsDomain {
+		return nil, fmt.Errorf("unexpected bundle, it must have %s apps domain", constants.AppsDomain)
+	}
+	if bundleInfo.GetAPIHostname() != fmt.Sprintf("api%s", constants.ClusterDomain) {
+		return nil, fmt.Errorf("unexpected bundle, it must have %s base domain", constants.ClusterDomain)
+	}
 	if err := bundleInfo.createSymlinkOrCopyOpenShiftClient(repo.OcBinDir); err != nil {
 		return nil, err
 	}
diff --git a/pkg/crc/machine/machine.go b/pkg/crc/machine/machine.go
index 09e601f75..899c0dfa0 100644
--- a/pkg/crc/machine/machine.go
+++ b/pkg/crc/machine/machine.go
@@ -269,7 +269,7 @@ func (client *client) Start(startConfig StartConfig) (*StartResult, error) {
 	if err != nil {
 		return nil, errors.Wrap(err, "Error getting the IP")
 	}
-	sshRunner, err := crcssh.CreateRunner(instanceIP, getSSHPort(client.useVSock()), crcBundleMetadata.GetSSHKeyPath(), constants.GetPrivateKeyPath())
+	sshRunner, err := crcssh.CreateRunner(instanceIP, getSSHPort(client.useVSock()), crcBundleMetadata.GetSSHKeyPath(), constants.GetPrivateKeyPath(), constants.GetRsaPrivateKeyPath())
 	if err != nil {
 		return nil, errors.Wrap(err, "Error creating the ssh client")
 	}
@@ -608,7 +608,7 @@ func (client *client) Status() (*ClusterStatusResult, error) {
 	if err != nil {
 		return nil, errors.Wrap(err, "Error getting ip")
 	}
-	sshRunner, err := crcssh.CreateRunner(ip, getSSHPort(client.useVSock()), constants.GetPrivateKeyPath())
+	sshRunner, err := crcssh.CreateRunner(ip, getSSHPort(client.useVSock()), constants.GetPrivateKeyPath(), constants.GetRsaPrivateKeyPath())
 	if err != nil {
 		return nil, errors.Wrap(err, "Error creating the ssh client")
 	}
diff --git a/pkg/crc/preflight/preflight_checks_common.go b/pkg/crc/preflight/preflight_checks_common.go
index 5a85313ab..76db469f1 100644
--- a/pkg/crc/preflight/preflight_checks_common.go
+++ b/pkg/crc/preflight/preflight_checks_common.go
@@ -1,7 +1,6 @@
 package preflight
 
 import (
-	"errors"
 	"fmt"
 	"os"
 	"path/filepath"
@@ -13,6 +12,7 @@ import (
 	"github.com/code-ready/crc/pkg/crc/validation"
 	"github.com/code-ready/crc/pkg/embed"
 	"github.com/docker/go-units"
+	"github.com/pkg/errors"
 )
 
 var genericPreflightChecks = [...]Check{
@@ -24,11 +24,11 @@ var genericPreflightChecks = [...]Check{
 		fix:              fixPodmanExecutableCached,
 	},
 	{
-		configKeySuffix:  "check-goodhosts-cached",
-		checkDescription: "Checking if goodhosts executable is cached",
-		check:            checkGoodhostsExecutableCached,
-		fixDescription:   "Caching goodhosts executable",
-		fix:              fixGoodhostsExecutableCached,
+		configKeySuffix:  "check-admin-helper-cached",
+		checkDescription: "Checking if admin-helper executable is cached",
+		check:            checkAdminHelperExecutableCached,
+		fixDescription:   "Caching admin-helper executable",
+		fix:              fixAdminHelperExecutableCached,
 	},
 	{
 		configKeySuffix:  "check-bundle-extracted",
@@ -102,23 +102,26 @@ func fixPodmanExecutableCached() error {
 	return nil
 }
 
-// Check if goodhost executable is cached or not
-func checkGoodhostsExecutableCached() error {
-	goodhost := cache.NewGoodhostsCache()
-	if !goodhost.IsCached() {
-		return errors.New("goodhost executable is not cached")
+// Check if helper executable is cached or not
+func checkAdminHelperExecutableCached() error {
+	helper := cache.NewAdminHelperCache()
+	if !helper.IsCached() {
+		return errors.New("admin-helper executable is not cached")
 	}
-	logging.Debug("goodhost executable already cached")
-	return checkSuid(goodhost.GetExecutablePath())
+	if err := helper.CheckVersion(); err != nil {
+		return errors.Wrap(err, "unexpected version of the admin-helper executable")
+	}
+	logging.Debug("admin-helper executable already cached")
+	return checkSuid(helper.GetExecutablePath())
 }
 
-func fixGoodhostsExecutableCached() error {
-	goodhost := cache.NewGoodhostsCache()
-	if err := goodhost.EnsureIsCached(); err != nil {
-		return fmt.Errorf("Unable to download goodhost executable %v", err)
+func fixAdminHelperExecutableCached() error {
+	helper := cache.NewAdminHelperCache()
+	if err := helper.EnsureIsCached(); err != nil {
+		return errors.Wrap(err, "Unable to download admin-helper executable")
 	}
-	logging.Debug("goodhost executable cached")
-	return setSuid(goodhost.GetExecutablePath())
+	logging.Debug("admin-helper executable cached")
+	return setSuid(helper.GetExecutablePath())
 }
 
 func removeCRCMachinesDir() error {
diff --git a/pkg/crc/preflight/preflight_checks_nonwin.go b/pkg/crc/preflight/preflight_checks_nonwin.go
index d5f51dbb9..ac786b6cf 100644
--- a/pkg/crc/preflight/preflight_checks_nonwin.go
+++ b/pkg/crc/preflight/preflight_checks_nonwin.go
@@ -7,6 +7,7 @@ import (
 	"os"
 	"syscall"
 
+	"github.com/code-ready/crc/pkg/crc/adminhelper"
 	"github.com/code-ready/crc/pkg/crc/logging"
 	crcos "github.com/code-ready/crc/pkg/os"
 )
@@ -19,6 +20,11 @@ var nonWinPreflightChecks = [...]Check{
 		fixDescription:   "crc should be ran as a normal user",
 		flags:            NoFix,
 	},
+	{
+		cleanupDescription: "Removing hosts file records added by CRC",
+		cleanup:            adminhelper.CleanHostsFile,
+		flags:              CleanUpOnly,
+	},
 }
 
 func checkIfRunningAsNormalUser() error {
diff --git a/pkg/crc/services/dns/dns.go b/pkg/crc/services/dns/dns.go
index b3e41396b..4b9d8a80d 100644
--- a/pkg/crc/services/dns/dns.go
+++ b/pkg/crc/services/dns/dns.go
@@ -4,9 +4,9 @@ import (
 	"fmt"
 	"time"
 
+	"github.com/code-ready/crc/pkg/crc/adminhelper"
 	"github.com/code-ready/crc/pkg/crc/constants"
 	"github.com/code-ready/crc/pkg/crc/errors"
-	"github.com/code-ready/crc/pkg/crc/goodhosts"
 	"github.com/code-ready/crc/pkg/crc/network"
 	"github.com/code-ready/crc/pkg/crc/services"
 )
@@ -121,7 +121,7 @@ func CheckCRCPublicDNSReachable(serviceConfig services.ServicePostStartConfig) (
 }
 
 func addOpenShiftHosts(serviceConfig services.ServicePostStartConfig) error {
-	return goodhosts.UpdateHostsFile(serviceConfig.IP, serviceConfig.BundleMetadata.GetAPIHostname(),
+	return adminhelper.UpdateHostsFile(serviceConfig.IP, serviceConfig.BundleMetadata.GetAPIHostname(),
 		serviceConfig.BundleMetadata.GetAppHostname("oauth-openshift"),
 		serviceConfig.BundleMetadata.GetAppHostname("console-openshift-console"),
 		serviceConfig.BundleMetadata.GetAppHostname("default-route-openshift-image-registry"))
diff --git a/pkg/crc/ssh/keys.go b/pkg/crc/ssh/keys.go
index efa5bde8c..f6df1b8eb 100644
--- a/pkg/crc/ssh/keys.go
+++ b/pkg/crc/ssh/keys.go
@@ -1,8 +1,9 @@
 package ssh
 
 import (
+	"crypto/ecdsa"
+	"crypto/elliptic"
 	"crypto/rand"
-	"crypto/rsa"
 	"crypto/x509"
 	"errors"
 	"fmt"
@@ -13,7 +14,7 @@ import (
 
 var (
 	ErrKeyGeneration     = errors.New("Unable to generate key")
-	ErrValidation        = errors.New("Unable to validate key")
+	ErrPrivateKey        = errors.New("Unable to marshal private key")
 	ErrPublicKey         = errors.New("Unable to convert public key")
 	ErrUnableToWriteFile = errors.New("Unable to write file")
 )
@@ -26,17 +27,17 @@ type KeyPair struct {
 // NewKeyPair generates a new SSH keypair
 // This will return a private & public key encoded as DER.
 func NewKeyPair() (keyPair *KeyPair, err error) {
-	priv, err := rsa.GenerateKey(rand.Reader, 2048)
+
+	priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
 	if err != nil {
 		return nil, ErrKeyGeneration
 	}
 
-	if err := priv.Validate(); err != nil {
-		return nil, ErrValidation
+	privDer, err := x509.MarshalPKCS8PrivateKey(priv)
+	if err != nil {
+		return nil, ErrPrivateKey
 	}
 
-	privDer := x509.MarshalPKCS1PrivateKey(priv)
-
 	pubSSH, err := gossh.NewPublicKey(&priv.PublicKey)
 	if err != nil {
 		return nil, ErrPublicKey
diff --git a/pkg/crc/ssh/keys_unix.go b/pkg/crc/ssh/keys_unix.go
index 5c03347fb..7bb43e86b 100644
--- a/pkg/crc/ssh/keys_unix.go
+++ b/pkg/crc/ssh/keys_unix.go
@@ -16,7 +16,7 @@ func (kp *KeyPair) WriteToFile(privateKeyPath string, publicKeyPath string) erro
 	}{
 		{
 			File:  privateKeyPath,
-			Value: pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Headers: nil, Bytes: kp.PrivateKey}),
+			Value: pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Headers: nil, Bytes: kp.PrivateKey}),
 		},
 		{
 			File:  publicKeyPath,
diff --git a/pkg/crc/ssh/keys_windows.go b/pkg/crc/ssh/keys_windows.go
index 95d5e6b01..92a08933e 100644
--- a/pkg/crc/ssh/keys_windows.go
+++ b/pkg/crc/ssh/keys_windows.go
@@ -16,7 +16,7 @@ func (kp *KeyPair) WriteToFile(privateKeyPath string, publicKeyPath string) erro
 	}{
 		{
 			File:  privateKeyPath,
-			Value: pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Headers: nil, Bytes: kp.PrivateKey}),
+			Value: pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Headers: nil, Bytes: kp.PrivateKey}),
 		},
 		{
 			File:  publicKeyPath,
diff --git a/pkg/download/download.go b/pkg/download/download.go
index 149c6076e..494397098 100644
--- a/pkg/download/download.go
+++ b/pkg/download/download.go
@@ -1,35 +1,29 @@
 package download
 
 import (
-	"fmt"
 	"os"
 
 	"github.com/cavaliercoder/grab"
 	"github.com/code-ready/crc/pkg/crc/logging"
+	"github.com/pkg/errors"
 )
 
 func Download(uri, destination string, mode os.FileMode) (string, error) {
-	// create client
 	logging.Debugf("Downloading %s to %s", uri, destination)
+
 	client := grab.NewClient()
 	req, err := grab.NewRequest(destination, uri)
 	if err != nil {
-		return "", fmt.Errorf("Unable to get response from %s: %v", uri, err)
+		return "", errors.Wrapf(err, "unable to get response from %s", uri)
 	}
-	defer func() {
-		if err != nil {
-			os.Remove(destination)
-		}
-	}()
+
 	resp := client.Do(req)
-	// check for errors
 	if err := resp.Err(); err != nil {
-		return "", fmt.Errorf("Download failed: %v", err)
+		return "", errors.Wrapf(err, "download of %s failed", uri)
 	}
 
-	err = os.Chmod(resp.Filename, mode)
-	if err != nil {
-		os.Remove(resp.Filename)
+	if err := os.Chmod(resp.Filename, mode); err != nil {
+		_ = os.Remove(resp.Filename)
 		return "", err
 	}
 


Test patch:
diff --git a/pkg/crc/machine/bundle/metadata_test.go b/pkg/crc/machine/bundle/metadata_test.go
index 208341377..f83882ff1 100644
--- a/pkg/crc/machine/bundle/metadata_test.go
+++ b/pkg/crc/machine/bundle/metadata_test.go
@@ -20,7 +20,7 @@ const reference = `{
     "clusterName": "crc",
     "baseDomain": "testing",
     "appsDomain": "apps-crc.testing",
-    "sshPrivateKeyFile": "id_rsa_crc",
+    "sshPrivateKeyFile": "id_ecdsa_crc",
     "kubeConfig": "kubeconfig",
     "kubeadminPasswordFile": "kubeadmin-password"
   },
@@ -66,7 +66,7 @@ func TestUnmarshalMarshal(t *testing.T) {
 			ClusterName:           "crc",
 			BaseDomain:            "testing",
 			AppsDomain:            "apps-crc.testing",
-			SSHPrivateKeyFile:     "id_rsa_crc",
+			SSHPrivateKeyFile:     "id_ecdsa_crc",
 			KubeConfig:            "kubeconfig",
 			KubeadminPasswordFile: "kubeadmin-password",
 		}, Nodes: []Node{
diff --git a/pkg/crc/preflight/preflight_darwin_test.go b/pkg/crc/preflight/preflight_darwin_test.go
index 417275452..9b0b840c6 100644
--- a/pkg/crc/preflight/preflight_darwin_test.go
+++ b/pkg/crc/preflight/preflight_darwin_test.go
@@ -16,9 +16,9 @@ func TestCountConfigurationOptions(t *testing.T) {
 }
 
 func TestCountPreflights(t *testing.T) {
-	assert.Len(t, getPreflightChecks(false, network.DefaultMode), 11)
-	assert.Len(t, getPreflightChecks(true, network.DefaultMode), 17)
+	assert.Len(t, getPreflightChecks(false, network.DefaultMode), 12)
+	assert.Len(t, getPreflightChecks(true, network.DefaultMode), 18)
 
-	assert.Len(t, getPreflightChecks(false, network.VSockMode), 10)
-	assert.Len(t, getPreflightChecks(true, network.VSockMode), 16)
+	assert.Len(t, getPreflightChecks(false, network.VSockMode), 11)
+	assert.Len(t, getPreflightChecks(true, network.VSockMode), 17)
 }
diff --git a/pkg/crc/preflight/preflight_linux_test.go b/pkg/crc/preflight/preflight_linux_test.go
index ad6571aae..c0cd327ac 100644
--- a/pkg/crc/preflight/preflight_linux_test.go
+++ b/pkg/crc/preflight/preflight_linux_test.go
@@ -5,6 +5,8 @@ import (
 	"runtime"
 	"testing"
 
+	"github.com/code-ready/crc/pkg/crc/adminhelper"
+
 	"github.com/code-ready/crc/pkg/crc/config"
 	"github.com/code-ready/crc/pkg/crc/network"
 	crcos "github.com/code-ready/crc/pkg/os/linux"
@@ -60,11 +62,12 @@ var checkListForDistros = []checkListForDistro{
 		networkMode: network.DefaultMode,
 		checks: []Check{
 			{check: checkPodmanExecutableCached},
-			{check: checkGoodhostsExecutableCached},
+			{check: checkAdminHelperExecutableCached},
 			{check: checkBundleExtracted},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{check: checkIfRunningAsNormalUser},
+			{cleanup: adminhelper.CleanHostsFile},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -88,11 +91,12 @@ var checkListForDistros = []checkListForDistro{
 		networkMode: network.VSockMode,
 		checks: []Check{
 			{check: checkPodmanExecutableCached},
-			{check: checkGoodhostsExecutableCached},
+			{check: checkAdminHelperExecutableCached},
 			{check: checkBundleExtracted},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{check: checkIfRunningAsNormalUser},
+			{cleanup: adminhelper.CleanHostsFile},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -109,11 +113,12 @@ var checkListForDistros = []checkListForDistro{
 		networkMode: network.DefaultMode,
 		checks: []Check{
 			{check: checkPodmanExecutableCached},
-			{check: checkGoodhostsExecutableCached},
+			{check: checkAdminHelperExecutableCached},
 			{check: checkBundleExtracted},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{check: checkIfRunningAsNormalUser},
+			{cleanup: adminhelper.CleanHostsFile},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -136,11 +141,12 @@ var checkListForDistros = []checkListForDistro{
 		networkMode: network.VSockMode,
 		checks: []Check{
 			{check: checkPodmanExecutableCached},
-			{check: checkGoodhostsExecutableCached},
+			{check: checkAdminHelperExecutableCached},
 			{check: checkBundleExtracted},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{check: checkIfRunningAsNormalUser},
+			{cleanup: adminhelper.CleanHostsFile},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -157,11 +163,12 @@ var checkListForDistros = []checkListForDistro{
 		networkMode: network.DefaultMode,
 		checks: []Check{
 			{check: checkPodmanExecutableCached},
-			{check: checkGoodhostsExecutableCached},
+			{check: checkAdminHelperExecutableCached},
 			{check: checkBundleExtracted},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{check: checkIfRunningAsNormalUser},
+			{cleanup: adminhelper.CleanHostsFile},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -184,11 +191,12 @@ var checkListForDistros = []checkListForDistro{
 		networkMode: network.VSockMode,
 		checks: []Check{
 			{check: checkPodmanExecutableCached},
-			{check: checkGoodhostsExecutableCached},
+			{check: checkAdminHelperExecutableCached},
 			{check: checkBundleExtracted},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{check: checkIfRunningAsNormalUser},
+			{cleanup: adminhelper.CleanHostsFile},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -205,11 +213,12 @@ var checkListForDistros = []checkListForDistro{
 		networkMode: network.DefaultMode,
 		checks: []Check{
 			{check: checkPodmanExecutableCached},
-			{check: checkGoodhostsExecutableCached},
+			{check: checkAdminHelperExecutableCached},
 			{check: checkBundleExtracted},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{check: checkIfRunningAsNormalUser},
+			{cleanup: adminhelper.CleanHostsFile},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -234,11 +243,12 @@ var checkListForDistros = []checkListForDistro{
 		networkMode: network.VSockMode,
 		checks: []Check{
 			{check: checkPodmanExecutableCached},
-			{check: checkGoodhostsExecutableCached},
+			{check: checkAdminHelperExecutableCached},
 			{check: checkBundleExtracted},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{check: checkIfRunningAsNormalUser},
+			{cleanup: adminhelper.CleanHostsFile},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
diff --git a/pkg/crc/preflight/preflight_ubuntu_linux_test.go b/pkg/crc/preflight/preflight_ubuntu_linux_test.go
index b268443ab..5ff3b38e7 100644
--- a/pkg/crc/preflight/preflight_ubuntu_linux_test.go
+++ b/pkg/crc/preflight/preflight_ubuntu_linux_test.go
@@ -8,7 +8,6 @@ import (
 	"testing"
 
 	"github.com/code-ready/crc/pkg/crc/constants"
-
 	"github.com/stretchr/testify/assert"
 )
 
diff --git a/pkg/crc/ssh/keys_test.go b/pkg/crc/ssh/keys_test.go
index 717275482..afa0b2a8c 100644
--- a/pkg/crc/ssh/keys_test.go
+++ b/pkg/crc/ssh/keys_test.go
@@ -11,7 +11,7 @@ func TestNewKeyPair(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	if privPem := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Headers: nil, Bytes: pair.PrivateKey}); len(privPem) == 0 {
+	if privPem := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Headers: nil, Bytes: pair.PrivateKey}); len(privPem) == 0 {
 		t.Fatal("No PEM returned")
 	}
 }
diff --git a/pkg/crc/ssh/ssh_test.go b/pkg/crc/ssh/ssh_test.go
index cbfb2292b..c5332327a 100644
--- a/pkg/crc/ssh/ssh_test.go
+++ b/pkg/crc/ssh/ssh_test.go
@@ -2,8 +2,9 @@ package ssh
 
 import (
 	"bytes"
+	"crypto/ecdsa"
+	"crypto/elliptic"
 	"crypto/rand"
-	"crypto/rsa"
 	"crypto/x509"
 	"encoding/pem"
 	"fmt"
@@ -26,7 +27,7 @@ func TestRunner(t *testing.T) {
 	require.NoError(t, err)
 	defer os.RemoveAll(dir)
 
-	clientKey, err := rsa.GenerateKey(rand.Reader, 2048)
+	clientKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
 	require.NoError(t, err)
 
 	clientKeyFile := filepath.Join(dir, "private.key")
@@ -60,7 +61,7 @@ func TestRunner(t *testing.T) {
 	assert.Equal(t, 1, *totalConn)
 }
 
-func createSSHServer(t *testing.T, listener net.Listener, clientKey *rsa.PrivateKey, fun func(string) (byte, string)) *int {
+func createSSHServer(t *testing.T, listener net.Listener, clientKey *ecdsa.PrivateKey, fun func(string) (byte, string)) *int {
 	totalConn := 0
 	config := &ssh.ServerConfig{
 		PublicKeyCallback: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
@@ -79,7 +80,7 @@ func createSSHServer(t *testing.T, listener net.Listener, clientKey *rsa.Private
 		},
 	}
 
-	serverKey, err := rsa.GenerateKey(rand.Reader, 2048)
+	serverKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
 	require.NoError(t, err)
 	signer, err := ssh.NewSignerFromKey(serverKey)
 	require.NoError(t, err)
@@ -129,13 +130,14 @@ func createSSHServer(t *testing.T, listener net.Listener, clientKey *rsa.Private
 	return &totalConn
 }
 
-func writePrivateKey(t *testing.T, clientKeyFile string, clientKey *rsa.PrivateKey) {
+func writePrivateKey(t *testing.T, clientKeyFile string, clientKey *ecdsa.PrivateKey) {
 	privateKeyFile, err := os.OpenFile(clientKeyFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
 	require.NoError(t, err)
 	defer privateKeyFile.Close()
+	bytes, _ := x509.MarshalPKCS8PrivateKey(clientKey)
 	require.NoError(t, pem.Encode(privateKeyFile, &pem.Block{
-		Type:  "RSA PRIVATE KEY",
-		Bytes: x509.MarshalPKCS1PrivateKey(clientKey),
+		Type:  "PRIVATE KEY",
+		Bytes: bytes,
 	}))
 }