Ejemplo n.º 1
0
func handleChannel(ch ssh.NewChannel) {
	if ch.ChannelType() != "session" {
		log.Printf("[ERROR] unknown channel type %q", ch.ChannelType())
		ch.Reject(ssh.UnknownChannelType, "unknown channel type")
		return
	}
	channel, requests, err := ch.Accept()
	if err != nil {
		log.Printf("[ERROR] could not accept channel %q", err)
	}
	defer channel.Close()

	var master, slave *os.File
	defer func() {
		if master != nil {
			master.Close()
		}
		if slave != nil {
			slave.Close()
		}
	}()

	var wg sync.WaitGroup
	wg.Add(1)

	go func(in <-chan *ssh.Request) {
		for req := range in {
			log.Printf("[INFO] got request %q", req.Type)
			switch req.Type {
			case "shell":
				if len(req.Payload) > 0 {
					// do not accept any extra commands for shell
					req.Reply(false, nil)
					return
				}
				req.Reply(true, nil)
			case "pty-req":
				pty, tty, err := pty.Open()
				if err != nil {
					log.Printf("[ERROR] failed to open a pty %q", err)
					return
				}
				master, slave = pty, tty

				wg.Done()
				// FIXME handle pty request
				req.Reply(true, nil)
			case "window-change":
				// FIXME handle window resize
				req.Reply(true, nil)
			}
		}
	}(requests)

	wg.Wait()
	go io.Copy(channel, master)
	go io.Copy(master, channel)
	s := bufio.NewScanner(slave)

	for {
		prompt(slave)
		if !s.Scan() {
			break
		}
		line := s.Text()
		// clear the terminal and continue
		if line == "cls" {
			fmt.Fprintf(slave, "\033[2J")
			fmt.Fprintf(slave, "\033[1;1H")
			continue
		}
		tokens := strings.Split(line, " ")
		cmd := exec.Command("docker", tokens...)
		cmd.Stdin = slave
		cmd.Stdout = slave
		cmd.Stderr = slave
		cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
		log.Println("command: %#v", cmd)
		if err := cmd.Start(); err != nil {
			fmt.Fprintf(master, "invalid docker cmd %q\n", line)
		}
		if err := cmd.Wait(); err != nil {
			fmt.Fprintf(master, "cmd %q failed\n", line)
		}
		fmt.Fprint(slave, "\r\n")
	}
}
Ejemplo n.º 2
0
func (chi *ChanInfo) Serve(conn ssh.Conn, newChan ssh.NewChannel) (err error) {
	log.Info("new channel: %s (len: %d)",
		newChan.ChannelType(), len(newChan.ExtraData()))

	err = chi.onType(newChan.ChannelType(), newChan.ExtraData())
	if err != nil {
		newChan.Reject(ssh.ResourceShortage, err.Error())
		log.Error("reject channel: %s", err.Error())
		return
	}

	chout, outreqs, err := conn.OpenChannel(
		newChan.ChannelType(), newChan.ExtraData())
	if err != nil {
		newChan.Reject(ssh.UnknownChannelType, err.Error())
		log.Error("reject channel: %s", err.Error())
		return
	}
	log.Debug("open channel ok.")

	chin, inreqs, err := newChan.Accept()
	if err != nil {
		log.Error("could not accept channel.")
		return
	}
	log.Debug("accept channel ok.")

	go chi.serveReqs(chin, outreqs)
	go chi.serveReqs(chout, inreqs)

	_, ok := <-chi.ch
	if !ok {
		return
	}

	switch chi.Type {
	case "local", "remote":
		go MultiCopyClose(chin, chout, &DebugStream{"out"})
		go MultiCopyClose(chout, chin, &DebugStream{"in"})
	case "sshagent":
		go MultiCopyClose(chin, chout, &DebugStream{"out"})
		go MultiCopyClose(chout, chin, &DebugStream{"in"})
	case "shell":
		l, err := chi.prepareFile("")
		if err != nil {
			return err
		}
		go MultiCopyClose(chin, chout, l.CreateSubLogger(byte(0x01)))
		go MultiCopyClose(chout, chin, l.CreateSubLogger(byte(0x02)))
	case "exec":
		l, err := chi.prepareFile(strings.Join(chi.ExecCmds, "\r"))
		if err != nil {
			return err
		}
		go MultiCopyClose(chin, chout, l.CreateSubLogger(byte(0x01)))
		go MultiCopyClose(chout, chin, l.CreateSubLogger(byte(0x02)))
	case "scpto":
		go MultiCopyClose(chin, chout, CreateScpStream(chi))
		go MultiCopyClose(chout, chin)
	case "scpfrom":
		go MultiCopyClose(chin, chout)
		go MultiCopyClose(chout, chin, CreateScpStream(chi))
	default:
		log.Warning("redirect before setup")
		chin.Close()
		chout.Close()
	}
	return
}
Ejemplo n.º 3
0
func handleChannel(conn *ssh.ServerConn, newChan ssh.NewChannel) {
	ch, reqs, err := newChan.Accept()
	if err != nil {
		log.Println("newChan.Accept failed:", err)
		return
	}
	defer ch.Close()
	for req := range reqs {
		switch req.Type {
		case "exec":
			fail := func(at string, err error) {
				log.Printf("%s failed: %s", at, err)
				ch.Stderr().Write([]byte("Internal error.\n"))
			}
			if req.WantReply {
				req.Reply(true, nil)
			}
			cmdline := string(req.Payload[4:])
			cmdargs, err := shlex.Split(cmdline)
			if err != nil || len(cmdargs) != 2 {
				ch.Stderr().Write([]byte("Invalid arguments.\n"))
				return
			}
			if cmdargs[0] != "git-receive-pack" {
				ch.Stderr().Write([]byte("Only `git push` is supported.\n"))
				return
			}
			cmdargs[1] = strings.TrimSuffix(strings.TrimPrefix(cmdargs[1], "/"), ".git")
			if strings.Contains(cmdargs[1], "..") {
				ch.Stderr().Write([]byte("Invalid repo.\n"))
				return
			}

			if err := ensureCacheRepo(cmdargs[1]); err != nil {
				fail("ensureCacheRepo", err)
				return
			}
			cmd := exec.Command("git-shell", "-c", cmdargs[0]+" '"+cmdargs[1]+"'")
			cmd.Dir = *repoPath
			cmd.Env = append(os.Environ(),
				"RECEIVE_USER="******"RECEIVE_REPO="+cmdargs[1],
			)
			done, err := attachCmd(cmd, ch, ch.Stderr(), ch)
			if err != nil {
				fail("attachCmd", err)
				return
			}
			if err := cmd.Start(); err != nil {
				fail("cmd.Start", err)
				return
			}
			done.Wait()
			status, err := exitStatus(cmd.Wait())
			if err != nil {
				fail("exitStatus", err)
				return
			}
			if _, err := ch.SendRequest("exit-status", false, ssh.Marshal(&status)); err != nil {
				fail("sendExit", err)
			}
			return
		case "env":
			if req.WantReply {
				req.Reply(true, nil)
			}
		}
	}
}
Ejemplo n.º 4
0
// handleChannel runs the needed commands on a given SSH server connection against the user
// that opened the communication channel.
func handleChannel(conn *ssh.ServerConn, newChan ssh.NewChannel) {
	ch, reqs, err := newChan.Accept()
	if err != nil {
		log.Println("newChan.Accept failed:", err)
		return
	}

	defer ch.Close()

	for req := range reqs {
		switch req.Type {
		case "exec":
			assert := func(at string, err error) bool {
				if err != nil {
					log.Printf("%s failed: %s", at, err)
					ch.Stderr().Write([]byte("Internal error.\n"))
					return true
				}
				return false
			}

			defer func() {
				log.Printf("Connection lost from %s", conn.RemoteAddr().String())
			}()

			if req.WantReply {
				req.Reply(true, nil)
			}

			cmdline := string(req.Payload[4:])

			cmdargs, err := shlex.Split(cmdline)
			if assert("shlex.Split", err) {
				return
			}

			if len(cmdargs) != 2 {
				ch.Stderr().Write([]byte("Invalid arguments.\n"))
				return
			}

			if cmdargs[0] != "git-receive-pack" {
				ch.Stderr().Write([]byte("Only `git push` is supported.\n"))
				return
			}

			user := conn.Permissions.Extensions["user"]
			reponame := strings.TrimSuffix(strings.TrimPrefix(cmdargs[1], "/"), ".git")

			log.Printf("Push from %s at %s", user, reponame)

			if err := makeGitRepo(reponame); err != nil {
				ch.Stderr().Write([]byte("Error: " + err.Error()))
				return
			}

			log.Printf("Writing hooks...")

			err = ioutil.WriteFile(reponame+"/hooks/pre-receive", []byte(`#!/bin/bash
strip_remote_prefix() {
	sed -u "s/^/"$'\e[1G'"/"
}

set -eo pipefail; while read oldrev newrev refname; do
	/app/cloudchaser -etcd-machine `+*etcduplink+` pre $newrev 2>&1 | strip_remote_prefix
done`), 0755)
			if err != nil {
				return
			}

			err = ioutil.WriteFile(reponame+"/hooks/post-receive", []byte(`#!/bin/bash

strip_remote_prefix() {
	sed -u "s/^/"$'\e[1G'"/"
}

set -eo pipefail; while read oldrev newrev refname; do
	/app/builder -etcd-host `+*etcduplink+` $REPO ${refname##*/} $newrev 2>&1 | strip_remote_prefix
done`), 0755)
			if err != nil {
				return
			}

			log.Printf("Doing git receive...")

			receive := exec.Command("git-receive-pack", reponame)
			if conn.Permissions.Extensions["environ"] != "" {
				receive.Env = append(receive.Env, strings.Split(conn.Permissions.Extensions["environ"], "\n")...)
			}

			receive.Env = append(receive.Env, "USER="******"user"])
			receive.Env = append(receive.Env, "REMOTE_HOST="+conn.RemoteAddr().String())
			receive.Env = append(receive.Env, "REPO="+reponame)

			done, err := attachCmd(receive, ch, ch.Stderr(), ch)
			if err != nil {
				ch.Stderr().Write([]byte("Error: " + err.Error()))
				return
			}

			if assert("receive.Start", receive.Start()) {
				return
			}

			done.Wait()

			log.Printf("Receive done")

			status, rcvErr := exitStatus(receive.Wait())
			if rcvErr != nil {
				ch.Stderr().Write([]byte("Error: " + rcvErr.Error()))
				return
			}

			_, err = ch.SendRequest("exit-status", false, ssh.Marshal(&status))
			assert("sendExit", err)

			return

		case "env":
			if req.WantReply {
				req.Reply(true, nil)
			}
		default:
			return
		}
	}
}