// 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 } } }
func newAttacker(conn *ssh.ServerConn, username string, password string) *Attacker { addr := conn.RemoteAddr().String() addr = ipAddrFromRemoteAddr(addr) return &Attacker{addr, username, password} }