dashboard,cmd/buildlet,cmd/coordinator: public key auth on windows

Enable public key auth for windows buildlets. Previously the
username/password was hardcoded.

This change enables the `/connect-ssh` http endpoint on the buildlet to setup
authorized_keys and uses the coordinator to proxy ssh traffic to the
buildlet.

Validation:
- run local coordinator (with patches to dev env to act like GCE) with
  changes
- build windows buildlet, post to own bucket
- execute:
  gomote -localdev create windows-amd64-2012
  ssh -p 2222 user-jrjohnson-windows-amd64-2012-0@127.0.0.1

Fixes golang/go#21247

Change-Id: If7adc7e6296a071d50412d9fa9d46ecd9e6cc3f3
Reviewed-on: https://go-review.googlesource.com/52350
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
diff --git a/cmd/buildlet/buildlet.go b/cmd/buildlet/buildlet.go
index a1ed73c..03f5ddb 100644
--- a/cmd/buildlet/buildlet.go
+++ b/cmd/buildlet/buildlet.go
@@ -1615,8 +1615,10 @@
 	switch runtime.GOOS {
 	case "darwin":
 		homeRoot = "/Users"
-	case "windows", "plan9":
+	case "plan9":
 		return fmt.Errorf("ssh not supported on %v", runtime.GOOS)
+	case "windows":
+		homeRoot = `C:\Users`
 	default:
 		homeRoot = "/home"
 		if runtime.GOOS == "freebsd" {
@@ -1657,5 +1659,10 @@
 	if runtime.GOOS == "freebsd" {
 		exec.Command("/usr/sbin/chown", "-R", sshUser, sshDir).Run()
 	}
+	if runtime.GOOS == "windows" {
+		if res, err := exec.Command("icacls.exe", authFile, "/grant", `NT SERVICE\sshd:(R)`).CombinedOutput(); err != nil {
+			return fmt.Errorf("setting permissions on authorized_keys with: %v\n%s.", err, res)
+		}
+	}
 	return nil
 }
diff --git a/cmd/coordinator/remote.go b/cmd/coordinator/remote.go
index ff4ad37..d216b61 100644
--- a/cmd/coordinator/remote.go
+++ b/cmd/coordinator/remote.go
@@ -491,8 +491,8 @@
 	go rb.renew(ctx)
 
 	sshUser := hostConf.SSHUsername
-	needsSSHProxyPport := bconf.GOOS() != "plan9" && bconf.GOOS() != "windows"
-	if sshUser == "" && needsSSHProxyPport {
+	useLocalSSHProxy := bconf.GOOS() != "plan9"
+	if sshUser == "" && useLocalSSHProxy {
 		fmt.Fprintf(s, "instance %q host type %q does not have SSH configured\n", inst, hostType)
 		return
 	}
@@ -512,7 +512,7 @@
 	fmt.Fprintf(s, "#\n")
 
 	var localProxyPort int
-	if needsSSHProxyPport {
+	if useLocalSSHProxy {
 		sshConn, err := rb.buildlet.ConnectSSH(sshUser, pubKey)
 		log.Printf("buildlet(%q).ConnectSSH = %T, %v", inst, sshConn, err)
 		if err != nil {
@@ -579,18 +579,6 @@
 			"-o", "StrictHostKeyChecking=no",
 			"-i", sshPrivateKeyFile,
 			sshUser+"@localhost")
-	case "windows":
-		// TODO(jrjohnson,bradfitz): figure out SSH public key auth on Windows (ACL issues?)
-		// and make this path more like the default case agbove.
-		fmt.Fprintf(s, "# Windows user/pass: gopher/gopher\n")
-		if ipErr != nil {
-			fmt.Fprintf(s, "# Failed to get IP out of %q: %v\n", rb.buildlet.IPPort(), err)
-			return
-		}
-		cmd = exec.Command("ssh",
-			"-o", "UserKnownHostsFile=/dev/null",
-			"-o", "StrictHostKeyChecking=no",
-			"gopher@"+ip)
 	case "plan9":
 		fmt.Fprintf(s, "# Plan9 user/pass: glenda/glenda123\n")
 		if ipErr != nil {
diff --git a/dashboard/builders.go b/dashboard/builders.go
index ba26b41..b10283e 100644
--- a/dashboard/builders.go
+++ b/dashboard/builders.go
@@ -212,18 +212,21 @@
 		machineType:        "n1-highcpu-4",
 		buildletURLTmpl:    "http://storage.googleapis.com/$BUCKET/buildlet.windows-amd64",
 		goBootstrapURLTmpl: "https://storage.googleapis.com/$BUCKET/go1.4-windows-amd64.tar.gz",
+		SSHUsername:        "gopher",
 	},
 	"host-windows-amd64-2012": &HostConfig{
 		VMImage:            "windows-amd64-server-2012r2-v4",
 		machineType:        "n1-highcpu-4",
 		buildletURLTmpl:    "http://storage.googleapis.com/$BUCKET/buildlet.windows-amd64",
 		goBootstrapURLTmpl: "https://storage.googleapis.com/$BUCKET/go1.4-windows-amd64.tar.gz",
+		SSHUsername:        "gopher",
 	},
 	"host-windows-amd64-2016": &HostConfig{
 		VMImage:            "windows-amd64-server-2016-v4",
 		machineType:        "n1-highcpu-4",
 		buildletURLTmpl:    "http://storage.googleapis.com/$BUCKET/buildlet.windows-amd64",
 		goBootstrapURLTmpl: "https://storage.googleapis.com/$BUCKET/go1.4-windows-amd64.tar.gz",
+		SSHUsername:        "gopher",
 	},
 	"host-darwin-10_8": &HostConfig{
 		IsReverse: true,