mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-30 19:08:37 +00:00 
			
		
		
		
	fix 1540 and experimental SSH server support
This commit is contained in:
		| @@ -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) | ||||
|  | ||||
|   | ||||
							
								
								
									
										7
									
								
								modules/ssh/ssh_1.3.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								modules/ssh/ssh_1.3.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| // +build !go1.4 | ||||
|  | ||||
| package ssh | ||||
|  | ||||
| func Listen(port int) { | ||||
| 	panic("Gogs requires Go 1.4 for starting a SSH server") | ||||
| } | ||||
		Reference in New Issue
	
	Block a user