Repository: code-ready/crc
Pull request #2008 (2021-02-17 15:18:08)

Use admin-helper daemon on Windows to avoid UAC prompts
Requires https://github.com/code-ready/admin-helper/pull/3

This change removes all UAC prompts (at crc start) on Windows by adding a small admin helper daemon. It accepts add/remove commands. 

Once the daemon PR is merged, I will do a new release and add a preflight check that install the daemon automatically. 

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

This pull-request has been approved by: *<a href="https://github.com/code-ready/crc/pull/2008#" 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":[]} -->
anjannath: @guillaumerose This is only for the vsock network mode right? for the default mode UAC prompt on `start` in windows is shown for configuring the dns server address in the vswitch.

for vsock network mode we need to keep the hosts file updated with the .apps routes, which i assume will be done by the route-controller? or do we need to update the hosts file during crc start as well?


guillaumerose: > @guillaumerose This is only for the vsock network mode right? for the default mode UAC prompt on start in windows is shown for configuring the dns server address in the vswitch.

Yes it is only for vsock

> for vsock network mode we need to keep the hosts file updated with the .apps routes, which i assume will be done by the route-controller? or do we need to update the hosts file during crc start as well?

The route controller talks to the unprivileged crc daemon (via http://gateway.crc.testing in the VM), then the daemon talks to the admin helper daemon (via named pipe crc-admin-helper). 

All routes (except ssh) will be added and updated by the route controller, yes. We can still help him at start and setup the default ones. 
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>
cfergeau: (rebased at https://github.com/cfergeau/crc/commits/daemonhelper )
openshift-ci[bot]: @guillaumerose: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/integration-crc | 00b910ecc1c860a43fb707882937ffb4c3be2180 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/code-ready_crc/2008/pull-ci-code-ready-crc-master-integration-crc/1382598588357414912) | `/test integration-crc`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=code-ready&repo=crc&pr=2008). [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 -->
gbraad: Is the executable correctly named?
openshift-ci[bot]: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/code-ready/crc/pull/2008#" 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-ci[bot]: @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>
openshift-ci[bot]: @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>
openshift-ci[bot]: @guillaumerose: The following test **failed**, say `/retest` to rerun all failed tests:

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

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=code-ready&repo=crc&pr=2008). [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 -->
anjannath: shouldn't "add preflights..." commit come before the "use admin helper.." commit? 😛
guillaumerose: I added a commit to merge somehow the action of adding the current user in Hyper-V group and crc-users group.
It avoids the user to reboot 2 times.
It seems to work well for me. Let me know for you!
anjannath: Tested, works as expected, initial and new routes are added in the hosts file👍🏽 

Patch:
diff --git a/go.mod b/go.mod
index ca15049bc..070580662 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
 	github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
 	github.com/cavaliercoder/grab v2.0.0+incompatible
 	github.com/cheggaaa/pb/v3 v3.0.8
-	github.com/code-ready/admin-helper v0.0.0-20210518083720-ee7100e9f7ae
+	github.com/code-ready/admin-helper v0.0.0-20210602075518-d22477037442
 	github.com/code-ready/clicumber v0.0.0-20210201104241-cecb794bdf9a
 	github.com/code-ready/gvisor-tap-vsock v0.0.0-20210308122700-d61f9aac135c
 	github.com/code-ready/machine v0.0.0-20210122113819-281ccfbb4566
diff --git a/go.sum b/go.sum
index ffdb0aa39..5fdbd7321 100644
--- a/go.sum
+++ b/go.sum
@@ -143,8 +143,8 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
 github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
 github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk=
 github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
-github.com/code-ready/admin-helper v0.0.0-20210518083720-ee7100e9f7ae h1:2oPNH50PMnFXA+bfa7bHcg8XtVOt/maFb0A7V8qkKbc=
-github.com/code-ready/admin-helper v0.0.0-20210518083720-ee7100e9f7ae/go.mod h1:4HSCLxIh62FmyT+XNNdPT7svzEcdcFvrlyzeUa1sqHY=
+github.com/code-ready/admin-helper v0.0.0-20210602075518-d22477037442 h1:uuCCqdNeZs2nETXs7qQ2i8wxthI8WgTZylQdyvTXcIA=
+github.com/code-ready/admin-helper v0.0.0-20210602075518-d22477037442/go.mod h1:JIRSIX+K3gN+A4DGJIcnG/opLnj5zOVG+H2h2eA9Wgo=
 github.com/code-ready/clicumber v0.0.0-20210201104241-cecb794bdf9a h1:VOQmCj9Y8pAPU4C0rIvF2ByUBuuqnaR9JSvb9C8uBQE=
 github.com/code-ready/clicumber v0.0.0-20210201104241-cecb794bdf9a/go.mod h1:ZPZUcYGpv/FQGnwakUuhiHP9x/IIcU2SKbn8xPe4ROc=
 github.com/code-ready/gvisor-tap-vsock v0.0.0-20210308122700-d61f9aac135c h1:UOg6w7Uv5hWvFVABjJpawCEmAK99vG96SozUaulqVCk=
@@ -548,6 +548,7 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
+github.com/kardianos/service v1.2.0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
@@ -1142,6 +1143,7 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201026133411-418715ba6fdd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/pkg/crc/adminhelper/hosts.go b/pkg/crc/adminhelper/hosts.go
index 1e901341a..2f6f7379b 100644
--- a/pkg/crc/adminhelper/hosts.go
+++ b/pkg/crc/adminhelper/hosts.go
@@ -4,11 +4,12 @@ import (
 	"path/filepath"
 
 	"github.com/code-ready/admin-helper/pkg/hosts"
+	"github.com/code-ready/admin-helper/pkg/types"
 	"github.com/code-ready/crc/pkg/crc/constants"
 )
 
 var (
-	adminHelperPath = filepath.Join(constants.BinDir(), constants.GetAdminHelperExecutable())
+	BinPath = filepath.Join(constants.BinDir(), constants.GetAdminHelperExecutable())
 )
 
 // UpdateHostsFile updates the host's /etc/hosts file with Instance IP.
@@ -36,13 +37,27 @@ func AddToHostsFile(instanceIP string, hostnames ...string) error {
 	if len(filtered) == 0 {
 		return nil
 	}
-	return execute(append([]string{"add", instanceIP}, hostnames...)...)
+
+	return instance().Add(&types.AddRequest{
+		IP:    instanceIP,
+		Hosts: filtered,
+	})
 }
 
 func RemoveFromHostsFile(hostnames ...string) error {
-	return execute(append([]string{"rm"}, hostnames...)...)
+	return instance().Remove(&types.RemoveRequest{
+		Hosts: hostnames,
+	})
 }
 
 func CleanHostsFile() error {
-	return execute("clean", constants.ClusterDomain, constants.AppsDomain)
+	return instance().Clean(&types.CleanRequest{
+		Domains: []string{constants.ClusterDomain, constants.AppsDomain},
+	})
+}
+
+type helper interface {
+	Add(req *types.AddRequest) error
+	Remove(req *types.RemoveRequest) error
+	Clean(req *types.CleanRequest) error
 }
diff --git a/pkg/crc/adminhelper/hosts_nowin.go b/pkg/crc/adminhelper/hosts_nowin.go
index feb2a2e2a..74a6878b4 100644
--- a/pkg/crc/adminhelper/hosts_nowin.go
+++ b/pkg/crc/adminhelper/hosts_nowin.go
@@ -3,10 +3,30 @@
 package adminhelper
 
 import (
+	"github.com/code-ready/admin-helper/pkg/types"
 	crcos "github.com/code-ready/crc/pkg/os"
 )
 
 func execute(args ...string) error {
-	_, _, err := crcos.RunWithDefaultLocale(adminHelperPath, args...)
+	_, _, err := crcos.RunWithDefaultLocale(BinPath, args...)
 	return err
 }
+
+func instance() helper {
+	return &cliHelper{}
+}
+
+type cliHelper struct {
+}
+
+func (c *cliHelper) Add(req *types.AddRequest) error {
+	return execute(append([]string{"add", req.IP}, req.Hosts...)...)
+}
+
+func (c *cliHelper) Remove(req *types.RemoveRequest) error {
+	return execute(append([]string{"rm"}, req.Hosts...)...)
+}
+
+func (c *cliHelper) Clean(req *types.CleanRequest) error {
+	return execute(append([]string{"clean"}, req.Domains...)...)
+}
diff --git a/pkg/crc/adminhelper/hosts_windows.go b/pkg/crc/adminhelper/hosts_windows.go
index 54fac5255..9c09d2741 100644
--- a/pkg/crc/adminhelper/hosts_windows.go
+++ b/pkg/crc/adminhelper/hosts_windows.go
@@ -1,12 +1,26 @@
 package adminhelper
 
 import (
-	"strings"
+	"context"
+	"net"
+	"net/http"
+	"time"
 
-	"github.com/code-ready/crc/pkg/os/windows/powershell"
+	"github.com/Microsoft/go-winio"
+	"github.com/code-ready/admin-helper/pkg/client"
 )
 
-func execute(args ...string) error {
-	_, _, err := powershell.ExecuteAsAdmin("modifying hosts file", strings.Join(append([]string{adminHelperPath}, args...), " "))
-	return err
+func instance() helper {
+	return Client()
+}
+
+func Client() *client.Client {
+	return client.New(&http.Client{
+		Transport: &http.Transport{
+			DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+				return winio.DialPipeContext(ctx, `\\.\pipe\crc-admin-helper`)
+			},
+		},
+		Timeout: 3 * time.Second,
+	}, "http://unix")
 }
diff --git a/pkg/crc/constants/constants.go b/pkg/crc/constants/constants.go
index 98a9d62b7..32f5c7053 100644
--- a/pkg/crc/constants/constants.go
+++ b/pkg/crc/constants/constants.go
@@ -27,7 +27,7 @@ const (
 	DaemonLogFile             = "crcd.log"
 	CrcLandingPageURL         = "https://cloud.redhat.com/openshift/create/local" // #nosec G101
 	DefaultPodmanURLBase      = "https://storage.googleapis.com/libpod-master-releases"
-	DefaultAdminHelperCliBase = "https://github.com/code-ready/admin-helper/releases/download/0.0.4"
+	DefaultAdminHelperCliBase = "https://github.com/code-ready/admin-helper/releases/download/0.0.6"
 	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"
diff --git a/pkg/crc/preflight/preflight_checks_common.go b/pkg/crc/preflight/preflight_checks_common.go
index b1724980e..548560392 100644
--- a/pkg/crc/preflight/preflight_checks_common.go
+++ b/pkg/crc/preflight/preflight_checks_common.go
@@ -76,13 +76,6 @@ var genericPreflightChecks = []Check{
 
 		labels: None,
 	},
-	{
-		cleanupDescription: "Removing hosts file records added by CRC",
-		cleanup:            removeHostsFileEntry,
-		flags:              CleanUpOnly,
-
-		labels: None,
-	},
 	{
 		cleanupDescription: "Removing older logs",
 		cleanup:            removeOldLogs,
@@ -99,6 +92,14 @@ var genericPreflightChecks = []Check{
 	},
 }
 
+var cleanUpHostsFile = Check{
+	cleanupDescription: "Removing hosts file records added by CRC",
+	cleanup:            removeHostsFileEntry,
+	flags:              CleanUpOnly,
+
+	labels: None,
+}
+
 func checkSupportedCPUArch() error {
 	if runtime.GOARCH != "amd64" {
 		logging.Debugf("GOARCH is %s", runtime.GOARCH)
diff --git a/pkg/crc/preflight/preflight_darwin.go b/pkg/crc/preflight/preflight_darwin.go
index a88e20cd5..11b041c24 100644
--- a/pkg/crc/preflight/preflight_darwin.go
+++ b/pkg/crc/preflight/preflight_darwin.go
@@ -134,6 +134,7 @@ func getChecks(mode network.Mode) []Check {
 
 	checks = append(checks, nonWinPreflightChecks...)
 	checks = append(checks, genericPreflightChecks...)
+	checks = append(checks, cleanUpHostsFile)
 	checks = append(checks, hyperkitPreflightChecks(mode)...)
 	checks = append(checks, daemonSetupChecks...)
 	checks = append(checks, resolverPreflightChecks...)
diff --git a/pkg/crc/preflight/preflight_linux.go b/pkg/crc/preflight/preflight_linux.go
index fe7ade6df..4f92a7be2 100644
--- a/pkg/crc/preflight/preflight_linux.go
+++ b/pkg/crc/preflight/preflight_linux.go
@@ -312,6 +312,7 @@ func getChecks(distro *linux.OsRelease) []Check {
 	checks = append(checks, nonWinPreflightChecks...)
 	checks = append(checks, wsl2PreflightCheck)
 	checks = append(checks, genericPreflightChecks...)
+	checks = append(checks, cleanUpHostsFile)
 	checks = append(checks, libvirtPreflightChecks(distro)...)
 	checks = append(checks, ubuntuPreflightChecks...)
 	checks = append(checks, nmPreflightChecks...)
diff --git a/pkg/crc/preflight/preflight_windows.go b/pkg/crc/preflight/preflight_windows.go
index 0ff7a2d1d..d8779baf9 100644
--- a/pkg/crc/preflight/preflight_windows.go
+++ b/pkg/crc/preflight/preflight_windows.go
@@ -3,8 +3,13 @@ package preflight
 import (
 	"errors"
 	"fmt"
+	"os"
+	"path/filepath"
 	"strings"
 
+	"github.com/code-ready/crc/pkg/crc/adminhelper"
+	"github.com/code-ready/crc/pkg/crc/constants"
+	crcerrors "github.com/code-ready/crc/pkg/crc/errors"
 	"github.com/code-ready/crc/pkg/crc/network"
 	"github.com/code-ready/crc/pkg/crc/version"
 	"github.com/code-ready/crc/pkg/os/windows/powershell"
@@ -47,13 +52,46 @@ var hypervPreflightChecks = []Check{
 
 		labels: labels{Os: Windows},
 	},
+	{
+		configKeySuffix:  "check-crc-users-group-exists",
+		checkDescription: "Checking if crc-users group exists",
+		check: func() error {
+			_, _, err := powershell.Execute("Get-LocalGroup -Name crc-users")
+			return err
+		},
+		fixDescription: "Creating crc-users group",
+		fix: func() error {
+			_, _, err := powershell.ExecuteAsAdmin("create crc-users group", "New-LocalGroup -Name crc-users")
+			return err
+		},
+		labels: labels{Os: Windows},
+	},
 	{
 		configKeySuffix:  "check-user-in-hyperv-group",
-		checkDescription: "Checking if user is a member of the Hyper-V Administrators group",
-		check:            checkIfUserPartOfHyperVAdmins,
-		fixDescription:   "Adding user to the Hyper-V Administrators group",
-		fix:              fixUserPartOfHyperVAdmins,
-
+		checkDescription: "Checking if current user is in Hyper-V group and crc-users group",
+		check: func() error {
+			if err := adminHelperGroup.check(); err != nil {
+				return err
+			}
+			return hypervGroup.check()
+		},
+		fixDescription: "Adding current user to group",
+		fix: func() error {
+			m := crcerrors.MultiError{}
+			if err := adminHelperGroup.check(); err != nil {
+				m.Collect(adminHelperGroup.fix())
+			}
+			if err := hypervGroup.check(); err != nil {
+				m.Collect(hypervGroup.fix())
+			}
+			if len(m.Errors) == 0 {
+				return nil
+			}
+			if m.Errors[0] == errReboot {
+				return errReboot
+			}
+			return m
+		},
 		labels: labels{Os: Windows},
 	},
 	{
@@ -141,6 +179,57 @@ var vsockChecks = []Check{
 	},
 }
 
+var errReboot = errors.New("Please reboot your system and run 'crc setup' to complete the setup process")
+
+var adminHelperGroup = Check{
+	check: func() error {
+		_, _, err := powershell.Execute(fmt.Sprintf("Get-LocalGroupMember -Name crc-users -Member '%s'", os.Getenv("USERNAME")))
+		return err
+	},
+	fix: func() error {
+		_, _, err := powershell.ExecuteAsAdmin("adding current user to crc-users group", fmt.Sprintf("Add-LocalGroupMember -Group crc-users -Member '%s'", os.Getenv("USERNAME")))
+		if err != nil {
+			return err
+		}
+		return errReboot
+	},
+}
+
+var hypervGroup = Check{
+	check: checkIfUserPartOfHyperVAdmins,
+	fix:   fixUserPartOfHyperVAdmins,
+}
+
+var adminHelperChecks = []Check{
+	{
+		configKeySuffix:  "check-admin-helper-daemon-installed",
+		checkDescription: "Checking if admin-helper daemon is installed",
+		check: func() error {
+			version, err := adminhelper.Client().Version()
+			if err != nil {
+				return err
+			}
+			expected := filepath.Base(constants.DefaultAdminHelperCliBase)
+			if version != expected {
+				return fmt.Errorf("unexpected admin-helper daemon version (expected: %s, got: %s)", expected, version)
+			}
+			return nil
+		},
+		fixDescription: "Installing admin-helper daemon",
+		fix: func() error {
+			_, _, err := powershell.ExecuteAsAdmin("install admin-helper daemon", fmt.Sprintf("& '%s' uninstall-daemon; & '%s' install-daemon", adminhelper.BinPath, adminhelper.BinPath))
+			return err
+		},
+		cleanupDescription: "Uninstalling admin-helper daemon",
+		cleanup: func() error {
+			_, _, err := powershell.ExecuteAsAdmin("uninstall admin-helper daemon", fmt.Sprintf("& '%s' uninstall-daemon", adminhelper.BinPath))
+			return err
+		},
+		labels: labels{Os: Windows},
+	},
+	cleanUpHostsFile,
+}
+
 const (
 	// This key is required to activate the vsock communication
 	registryDirectory = `HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\GuestCommunicationServices`
@@ -202,10 +291,10 @@ func getChecks() []Check {
 	checks := []Check{}
 	checks = append(checks, genericPreflightChecks...)
 	checks = append(checks, hypervPreflightChecks...)
+	checks = append(checks, adminHelperChecks...)
 	checks = append(checks, vsockChecks...)
 	checks = append(checks, traySetupChecks...)
 	checks = append(checks, bundleCheck)
-
 	return checks
 }
 
diff --git a/pkg/crc/version/version.go b/pkg/crc/version/version.go
index d73b49915..9fd1894fe 100644
--- a/pkg/crc/version/version.go
+++ b/pkg/crc/version/version.go
@@ -33,7 +33,7 @@ const (
 	// Tray version to be embedded in executable
 	crcMacTrayVersion = "1.0.9"
 	// Windows forms application version type major.minor.buildnumber.revesion
-	crcWindowsTrayVersion = "0.6.0.0"
+	crcWindowsTrayVersion = "0.7.0.0"
 )
 
 type CrcReleaseInfo struct {
diff --git a/pkg/os/windows/powershell/powershell_windows.go b/pkg/os/windows/powershell/powershell_windows.go
index 1499dc310..0a1d4c272 100644
--- a/pkg/os/windows/powershell/powershell_windows.go
+++ b/pkg/os/windows/powershell/powershell_windows.go
@@ -95,7 +95,13 @@ func runAsCmds(powershell string) []string {
 		`  $procInfo.WindowStyle = [Diagnostics.ProcessWindowStyle]::Hidden`,
 		`  $procInfo.Arguments = "-ExecutionPolicy RemoteSigned & '" + $script:MyInvocation.MyCommand.Path + "'"`,
 		`  $procInfo.Verb = "runas";`,
-		`  [System.Diagnostics.Process]::Start($procInfo);`,
+		`  $p = New-Object System.Diagnostics.Process`,
+		`  $p.StartInfo = $procInfo`,
+		`  $p.Start() | Out-Null`,
+		`  $p.WaitForExit()`,
+		`  if ($p.ExitCode -ne 0) {`,
+		`    throw "Unexpected failure";`,
+		`  }`,
 		`  Exit;`,
 		`}`,
 	}
diff --git a/vendor/github.com/code-ready/admin-helper/pkg/client/client.go b/vendor/github.com/code-ready/admin-helper/pkg/client/client.go
new file mode 100644
index 000000000..1cdf57ce4
--- /dev/null
+++ b/vendor/github.com/code-ready/admin-helper/pkg/client/client.go
@@ -0,0 +1,101 @@
+package client
+
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strings"
+
+	"github.com/code-ready/admin-helper/pkg/types"
+)
+
+type Client struct {
+	client *http.Client
+	base   string
+}
+
+func New(client *http.Client, base string) *Client {
+	return &Client{
+		client: client,
+		base:   base,
+	}
+}
+
+func (c *Client) Version() (string, error) {
+	res, err := c.client.Get(fmt.Sprintf("%s%s", c.base, "/version"))
+	if err != nil {
+		return "", err
+	}
+	defer res.Body.Close()
+	if res.StatusCode != http.StatusOK {
+		return "", fmt.Errorf("unexpected status: %d", res.StatusCode)
+	}
+	body, err := ioutil.ReadAll(res.Body)
+	if err != nil {
+		return "", err
+	}
+	return strings.TrimSpace(string(body)), nil
+}
+
+func (c *Client) Add(req *types.AddRequest) error {
+	bin, err := json.Marshal(req)
+	if err != nil {
+		return err
+	}
+	res, err := c.client.Post(fmt.Sprintf("%s%s", c.base, "/add"), "application/json", bytes.NewReader(bin))
+	if err != nil {
+		return err
+	}
+	defer res.Body.Close()
+	if res.StatusCode != http.StatusOK {
+		err, readErr := ioutil.ReadAll(res.Body)
+		if readErr != nil {
+			return fmt.Errorf("error while reading error message: %v", readErr)
+		}
+		return errors.New(strings.TrimSpace(string(err)))
+	}
+	return nil
+}
+
+func (c *Client) Remove(req *types.RemoveRequest) error {
+	bin, err := json.Marshal(req)
+	if err != nil {
+		return err
+	}
+	res, err := c.client.Post(fmt.Sprintf("%s%s", c.base, "/remove"), "application/json", bytes.NewReader(bin))
+	if err != nil {
+		return err
+	}
+	defer res.Body.Close()
+	if res.StatusCode != http.StatusOK {
+		err, readErr := ioutil.ReadAll(res.Body)
+		if readErr != nil {
+			return fmt.Errorf("error while reading error message: %v", readErr)
+		}
+		return errors.New(strings.TrimSpace(string(err)))
+	}
+	return nil
+}
+
+func (c *Client) Clean(req *types.CleanRequest) error {
+	bin, err := json.Marshal(req)
+	if err != nil {
+		return err
+	}
+	res, err := c.client.Post(fmt.Sprintf("%s%s", c.base, "/clean"), "application/json", bytes.NewReader(bin))
+	if err != nil {
+		return err
+	}
+	defer res.Body.Close()
+	if res.StatusCode != http.StatusOK {
+		err, readErr := ioutil.ReadAll(res.Body)
+		if readErr != nil {
+			return fmt.Errorf("error while reading error message: %v", readErr)
+		}
+		return errors.New(strings.TrimSpace(string(err)))
+	}
+	return nil
+}
diff --git a/vendor/github.com/code-ready/admin-helper/pkg/types/requests.go b/vendor/github.com/code-ready/admin-helper/pkg/types/requests.go
new file mode 100644
index 000000000..7b9ef40ec
--- /dev/null
+++ b/vendor/github.com/code-ready/admin-helper/pkg/types/requests.go
@@ -0,0 +1,14 @@
+package types
+
+type AddRequest struct {
+	IP    string   `json:"ip"`
+	Hosts []string `json:"hosts"`
+}
+
+type RemoveRequest struct {
+	Hosts []string `json:"hosts"`
+}
+
+type CleanRequest struct {
+	Domains []string `json:"domains"`
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 3259cc3bb..8114a70db 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -33,9 +33,11 @@ github.com/cavaliercoder/grab
 ## explicit
 github.com/cheggaaa/pb/v3
 github.com/cheggaaa/pb/v3/termutil
-# github.com/code-ready/admin-helper v0.0.0-20210518083720-ee7100e9f7ae
+# github.com/code-ready/admin-helper v0.0.0-20210602075518-d22477037442
 ## explicit
+github.com/code-ready/admin-helper/pkg/client
 github.com/code-ready/admin-helper/pkg/hosts
+github.com/code-ready/admin-helper/pkg/types
 # github.com/code-ready/clicumber v0.0.0-20210201104241-cecb794bdf9a
 ## explicit
 github.com/code-ready/clicumber/testsuite


Test patch:
diff --git a/pkg/crc/preflight/preflight_linux_test.go b/pkg/crc/preflight/preflight_linux_test.go
index 7cc456890..31952142f 100644
--- a/pkg/crc/preflight/preflight_linux_test.go
+++ b/pkg/crc/preflight/preflight_linux_test.go
@@ -69,9 +69,9 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
-			{cleanup: removeHostsFileEntry},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
+			{cleanup: removeHostsFileEntry},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -104,9 +104,9 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
-			{cleanup: removeHostsFileEntry},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
+			{cleanup: removeHostsFileEntry},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -138,9 +138,9 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
-			{cleanup: removeHostsFileEntry},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
+			{cleanup: removeHostsFileEntry},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -166,9 +166,9 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
-			{cleanup: removeHostsFileEntry},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
+			{cleanup: removeHostsFileEntry},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -201,9 +201,9 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
-			{cleanup: removeHostsFileEntry},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
+			{cleanup: removeHostsFileEntry},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -235,9 +235,9 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
-			{cleanup: removeHostsFileEntry},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
+			{cleanup: removeHostsFileEntry},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -263,9 +263,9 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
-			{cleanup: removeHostsFileEntry},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
+			{cleanup: removeHostsFileEntry},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -298,9 +298,9 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
-			{cleanup: removeHostsFileEntry},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
+			{cleanup: removeHostsFileEntry},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -332,9 +332,9 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
-			{cleanup: removeHostsFileEntry},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
+			{cleanup: removeHostsFileEntry},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -360,9 +360,9 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
-			{cleanup: removeHostsFileEntry},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
+			{cleanup: removeHostsFileEntry},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -396,9 +396,9 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
-			{cleanup: removeHostsFileEntry},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
+			{cleanup: removeHostsFileEntry},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -431,9 +431,9 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
-			{cleanup: removeHostsFileEntry},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
+			{cleanup: removeHostsFileEntry},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
diff --git a/pkg/crc/preflight/preflight_windows_test.go b/pkg/crc/preflight/preflight_windows_test.go
index 3feeb5cb8..0362822d3 100644
--- a/pkg/crc/preflight/preflight_windows_test.go
+++ b/pkg/crc/preflight/preflight_windows_test.go
@@ -11,13 +11,13 @@ import (
 func TestCountConfigurationOptions(t *testing.T) {
 	cfg := config.New(config.NewEmptyInMemoryStorage())
 	RegisterSettings(cfg)
-	assert.Len(t, cfg.AllConfigs(), 13)
+	assert.Len(t, cfg.AllConfigs(), 15)
 }
 
 func TestCountPreflights(t *testing.T) {
-	assert.Len(t, getPreflightChecks(false, false, network.SystemNetworkingMode), 18)
-	assert.Len(t, getPreflightChecks(true, true, network.SystemNetworkingMode), 18)
+	assert.Len(t, getPreflightChecks(false, false, network.SystemNetworkingMode), 20)
+	assert.Len(t, getPreflightChecks(true, true, network.SystemNetworkingMode), 20)
 
-	assert.Len(t, getPreflightChecks(false, false, network.UserNetworkingMode), 19)
-	assert.Len(t, getPreflightChecks(true, true, network.UserNetworkingMode), 19)
+	assert.Len(t, getPreflightChecks(false, false, network.UserNetworkingMode), 21)
+	assert.Len(t, getPreflightChecks(true, true, network.UserNetworkingMode), 21)
 }