in cmd/coordinator/remote.go [604:765]
func (ah *sshHandlers) handleIncomingSSHPostAuth(s ssh.Session) {
inst := s.User()
user := userFromGomoteInstanceName(inst)
requestedMutable := strings.HasPrefix(inst, "mutable-")
if requestedMutable {
inst = strings.TrimPrefix(inst, "mutable-")
}
ptyReq, winCh, isPty := s.Pty()
if !isPty {
fmt.Fprintf(s, "scp etc not yet supported; https://golang.org/issue/21140\n")
return
}
if ah.gomotePublicKey == "" {
fmt.Fprint(s, "invalid gomote-ssh-public-key")
return
}
remoteBuildlets.Lock()
rb, ok := remoteBuildlets.m[inst]
remoteBuildlets.Unlock()
if !ok {
fmt.Fprintf(s, "unknown instance %q", inst)
return
}
hostType := rb.HostType
hostConf, ok := dashboard.Hosts[hostType]
if !ok {
fmt.Fprintf(s, "instance %q has unknown host type %q\n", inst, hostType)
return
}
bconf, ok := dashboard.Builders[rb.BuilderType]
if !ok {
fmt.Fprintf(s, "instance %q has unknown builder type %q\n", inst, rb.BuilderType)
return
}
ctx, cancel := context.WithCancel(s.Context())
defer cancel()
go rb.renew(ctx)
sshUser := hostConf.SSHUsername
useLocalSSHProxy := bconf.GOOS() != "plan9"
if sshUser == "" && useLocalSSHProxy {
fmt.Fprintf(s, "instance %q host type %q does not have SSH configured\n", inst, hostType)
return
}
if !hostConf.IsHermetic() && !requestedMutable {
fmt.Fprintf(s, "WARNING: instance %q host type %q is not currently\n", inst, hostType)
fmt.Fprintf(s, "configured to have a hermetic filesystem per boot.\n")
fmt.Fprintf(s, "You must be careful not to modify machine state\n")
fmt.Fprintf(s, "that will affect future builds. Do you agree? If so,\n")
fmt.Fprintf(s, "run gomote ssh --i-will-not-break-the-host <INST>\n")
return
}
log.Printf("connecting to ssh to instance %q ...", inst)
fmt.Fprintf(s, "# Welcome to the gomote ssh proxy, %s.\n", user)
fmt.Fprintf(s, "# Connecting to/starting remote ssh...\n")
fmt.Fprintf(s, "#\n")
var localProxyPort int
if useLocalSSHProxy {
sshConn, err := rb.buildlet.ConnectSSH(sshUser, ah.gomotePublicKey)
log.Printf("buildlet(%q).ConnectSSH = %T, %v", inst, sshConn, err)
if err != nil {
fmt.Fprintf(s, "failed to connect to ssh on %s: %v\n", inst, err)
return
}
defer sshConn.Close()
// Now listen on some localhost port that we'll proxy to sshConn.
// The openssh ssh command line tool will connect to this IP.
ln, err := net.Listen("tcp", "localhost:0")
if err != nil {
fmt.Fprintf(s, "local listen error: %v\n", err)
return
}
localProxyPort = ln.Addr().(*net.TCPAddr).Port
log.Printf("ssh local proxy port for %s: %v", inst, localProxyPort)
var lnCloseOnce sync.Once
lnClose := func() { lnCloseOnce.Do(func() { ln.Close() }) }
defer lnClose()
// Accept at most one connection from localProxyPort and proxy
// it to sshConn.
go func() {
c, err := ln.Accept()
lnClose()
if err != nil {
return
}
defer c.Close()
errc := make(chan error, 1)
go func() {
_, err := io.Copy(c, sshConn)
errc <- err
}()
go func() {
_, err := io.Copy(sshConn, c)
errc <- err
}()
err = <-errc
}()
}
workDir, err := rb.buildlet.WorkDir(ctx)
if err != nil {
fmt.Fprintf(s, "Error getting WorkDir: %v\n", err)
return
}
ip, _, ipErr := net.SplitHostPort(rb.buildlet.IPPort())
fmt.Fprintf(s, "# `gomote push` and the builders use:\n")
fmt.Fprintf(s, "# - workdir: %s\n", workDir)
fmt.Fprintf(s, "# - GOROOT: %s/go\n", workDir)
fmt.Fprintf(s, "# - GOPATH: %s/gopath\n", workDir)
fmt.Fprintf(s, "# - env: %s\n", strings.Join(bconf.Env(), " ")) // TODO: shell quote?
fmt.Fprintf(s, "# Happy debugging.\n")
log.Printf("ssh to %s: starting ssh -p %d for %s@localhost", inst, localProxyPort, sshUser)
var cmd *exec.Cmd
switch bconf.GOOS() {
default:
cmd = exec.Command("ssh",
"-p", strconv.Itoa(localProxyPort),
"-o", "UserKnownHostsFile=/dev/null",
"-o", "StrictHostKeyChecking=no",
"-i", sshPrivateKeyFile,
sshUser+"@localhost")
case "plan9":
fmt.Fprintf(s, "# Plan9 user/pass: glenda/glenda123\n")
if ipErr != nil {
fmt.Fprintf(s, "# Failed to get IP out of %q: %v\n", rb.buildlet.IPPort(), err)
return
}
cmd = exec.Command("/usr/local/bin/drawterm",
"-a", ip, "-c", ip, "-u", "glenda", "-k", "user=glenda")
}
envutil.SetEnv(cmd, "TERM="+ptyReq.Term)
f, err := pty.Start(cmd)
if err != nil {
log.Printf("running ssh client to %s: %v", inst, err)
return
}
defer f.Close()
go func() {
for win := range winCh {
setWinsize(f, win.Width, win.Height)
}
}()
go func() {
io.Copy(f, s) // stdin
}()
io.Copy(s, f) // stdout
cmd.Process.Kill()
cmd.Wait()
}