diff options
author | Unknwon <u@gogs.io> | 2015-11-08 16:59:56 -0500 |
---|---|---|
committer | Unknwon <u@gogs.io> | 2015-11-08 16:59:56 -0500 |
commit | 18c841050b4c843ef723e788d4c0a4c29c67ffe3 (patch) | |
tree | 05fa3bde8473045add17f4efe9a0ba141b178bf7 /modules/ssh | |
parent | b55499d039c5e35130057b8af16401c558e79e79 (diff) | |
download | gitea-18c841050b4c843ef723e788d4c0a4c29c67ffe3.tar.gz gitea-18c841050b4c843ef723e788d4c0a4c29c67ffe3.zip |
fix 1540 and experimental SSH server support
Diffstat (limited to 'modules/ssh')
-rw-r--r-- | modules/ssh/ssh.go | 114 | ||||
-rw-r--r-- | modules/ssh/ssh_1.3.go | 7 |
2 files changed, 82 insertions, 39 deletions
diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go index 557f08ffeb..5e3761ca19 100644 --- a/modules/ssh/ssh.go +++ b/modules/ssh/ssh.go @@ -1,80 +1,110 @@ +// +build go1.4 + // Copyright 2014 The Gogs Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -// Prototype, git client looks like do not recognize req.Reply. package ssh import ( - "fmt" + "io" "io/ioutil" "net" "os" "os/exec" + "path/filepath" "strings" "github.com/Unknwon/com" + "golang.org/x/crypto/ssh" - "github.com/gogits/gogs/modules/crypto/ssh" + "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/setting" ) -func handleServerConn(keyId string, chans <-chan ssh.NewChannel) { +func cleanCommand(cmd string) string { + i := strings.Index(cmd, "git") + if i == -1 { + return cmd + } + return cmd[i:] +} + +func handleServerConn(keyID string, chans <-chan ssh.NewChannel) { for newChan := range chans { if newChan.ChannelType() != "session" { newChan.Reject(ssh.UnknownChannelType, "unknown channel type") continue } - channel, requests, err := newChan.Accept() + + ch, reqs, err := newChan.Accept() if err != nil { - log.Error(3, "Could not accept channel: %v", err) + log.Error(3, "Error accepting channel: %v", err) continue } go func(in <-chan *ssh.Request) { - defer channel.Close() + defer ch.Close() for req := range in { - ok, payload := false, strings.TrimLeft(string(req.Payload), "\x00&") - fmt.Println("Request:", req.Type, req.WantReply, payload) - if req.WantReply { - fmt.Println(req.Reply(true, nil)) - } + payload := cleanCommand(string(req.Payload)) switch req.Type { case "env": args := strings.Split(strings.Replace(payload, "\x00", "", -1), "\v") if len(args) != 2 { - break + return } args[0] = strings.TrimLeft(args[0], "\x04") _, _, err := com.ExecCmdBytes("env", args[0]+"="+args[1]) if err != nil { log.Error(3, "env: %v", err) - channel.Stderr().Write([]byte(err.Error())) - break + return } - ok = true case "exec": - os.Setenv("SSH_ORIGINAL_COMMAND", strings.TrimLeft(payload, "'(")) - log.Info("Payload: %v", strings.TrimLeft(payload, "'(")) - cmd := exec.Command("/Users/jiahuachen/Applications/Go/src/github.com/gogits/gogs/gogs", "serv", "key-"+keyId) - cmd.Stdout = channel - cmd.Stdin = channel - cmd.Stderr = channel.Stderr() - if err := cmd.Run(); err != nil { - log.Error(3, "exec: %v", err) - } else { - ok = true + cmdName := strings.TrimLeft(payload, "'()") + os.Setenv("SSH_ORIGINAL_COMMAND", cmdName) + log.Trace("Payload: %v", cmdName) + cmd := exec.Command(setting.AppPath, "serv", "key-"+keyID) + + stdout, err := cmd.StdoutPipe() + if err != nil { + log.Error(3, "StdoutPipe: %v", err) + return + } + stderr, err := cmd.StderrPipe() + if err != nil { + log.Error(3, "StderrPipe: %v", err) + return + } + input, err := cmd.StdinPipe() + if err != nil { + log.Error(3, "StdinPipe: %v", err) + return } + + go io.Copy(ch, stdout) + go io.Copy(ch.Stderr(), stderr) + go io.Copy(input, ch) + + if err = cmd.Start(); err != nil { + log.Error(3, "Start: %v", err) + return + } else if err = cmd.Wait(); err != nil { + log.Error(3, "Wait: %v", err) + return + } + + ch.SendRequest("exit-status", false, []byte{0, 0, 0, 0}) + return + default: } - fmt.Println("Done:", ok) } - fmt.Println("Done!!!") - }(requests) + }(reqs) } } -func listen(config *ssh.ServerConfig, port string) { - listener, err := net.Listen("tcp", "0.0.0.0:"+port) +func listen(config *ssh.ServerConfig, port int) { + listener, err := net.Listen("tcp", "0.0.0.0:"+com.ToStr(port)) if err != nil { panic(err) } @@ -82,15 +112,17 @@ func listen(config *ssh.ServerConfig, port string) { // Once a ServerConfig has been configured, connections can be accepted. conn, err := listener.Accept() if err != nil { - log.Error(3, "Fail to accept incoming connection: %v", err) + log.Error(3, "Error accepting incoming connection: %v", err) continue } // Before use, a handshake must be performed on the incoming net.Conn. sConn, chans, reqs, err := ssh.NewServerConn(conn, config) if err != nil { - log.Error(3, "Fail to handshake: %v", err) + log.Error(3, "Error on handshaking: %v", err) continue } + + log.Trace("Connection from %s (%s)", sConn.RemoteAddr(), sConn.ClientVersion()) // The incoming Request channel must be serviced. go ssh.DiscardRequests(reqs) go handleServerConn(sConn.Permissions.Extensions["key-id"], chans) @@ -98,21 +130,25 @@ func listen(config *ssh.ServerConfig, port string) { } // Listen starts a SSH server listens on given port. -func Listen(port string) { +func Listen(port int) { config := &ssh.ServerConfig{ PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { - // keyCache[string(ssh.MarshalAuthorizedKey(key))] = 2 - return &ssh.Permissions{Extensions: map[string]string{"key-id": "1"}}, nil + pkey, err := models.SearchPublicKeyByContent(strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key)))) + if err != nil { + log.Error(3, "SearchPublicKeyByContent: %v", err) + return nil, err + } + return &ssh.Permissions{Extensions: map[string]string{"key-id": com.ToStr(pkey.ID)}}, nil }, } - privateBytes, err := ioutil.ReadFile("/Users/jiahuachen/.ssh/id_rsa") + privateBytes, err := ioutil.ReadFile(filepath.Join(models.SSHPath, "id_rsa")) if err != nil { - panic("failed to load private key") + panic("Fail to load private key") } private, err := ssh.ParsePrivateKey(privateBytes) if err != nil { - panic("failed to parse private key") + panic("Fail to parse private key") } config.AddHostKey(private) diff --git a/modules/ssh/ssh_1.3.go b/modules/ssh/ssh_1.3.go new file mode 100644 index 0000000000..62275ee6ae --- /dev/null +++ b/modules/ssh/ssh_1.3.go @@ -0,0 +1,7 @@ +// +build !go1.4 + +package ssh + +func Listen(port int) { + panic("Gogs requires Go 1.4 for starting a SSH server") +} |