commit 1a9a95c737e4a0cf3d3b2bddc7369eebdd6c0026 Author: Manfred Touron Date: Sat Sep 30 13:12:43 2017 +0200 POC diff --git a/config.go b/config.go new file mode 100644 index 0000000..18957e1 --- /dev/null +++ b/config.go @@ -0,0 +1,29 @@ +package main + +import ( + "os" + + "github.com/gliderlabs/ssh" + gossh "golang.org/x/crypto/ssh" +) + +type Config struct { + clientConfig *gossh.ClientConfig + remoteAddr string +} + +func getConfig(s ssh.Session) (*Config, error) { + // TODO: get the config from a database + config := Config{ + remoteAddr: os.Getenv("SSH_ADDR"), + clientConfig: &gossh.ClientConfig{ + User: os.Getenv("SSH_USERNAME"), + HostKeyCallback: gossh.InsecureIgnoreHostKey(), // TODO: show the remote host to the client + store it in db if approved + Auth: []gossh.AuthMethod{ + gossh.Password(os.Getenv("SSH_PASSWORD")), + }, + }, + } + + return &config, nil +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..68acd43 --- /dev/null +++ b/main.go @@ -0,0 +1,75 @@ +package main + +import ( + "errors" + "fmt" + "io" + "log" + "os" + "path" + + "github.com/gliderlabs/ssh" + "github.com/urfave/cli" +) + +var banner = ` + + __________ _____ __ __ + / __/ __/ // / _ \___ ____/ /____ _/ / + _\ \_\ \/ _ / ___/ _ \/ __/ __/ _ '/ / + /___/___/_//_/_/ \___/_/ \__/\_,_/_/ + +` + +func main() { + app := cli.NewApp() + app.Name = path.Base(os.Args[0]) + app.Author = "Manfred Touron" + app.Email = "https://github.com/moul/sshportal" + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "bind-address, b", + EnvVar: "SSHPORTAL_BIND", + Value: ":2222", + Usage: "SSH server bind address", + }, + cli.BoolFlag{ + Name: "demo", + Usage: "*unsafe* - demo mode: accept all connections", + }, + // TODO: add verbose mode + // TODO: add web server + } + app.Action = server + app.Run(os.Args) +} + +func server(c *cli.Context) error { + ssh.Handle(func(s ssh.Session) { + log.Printf("New connection: user=%q remote=%q local=%q command=%q", s.User(), s.RemoteAddr(), s.LocalAddr(), s.Command()) + + switch s.User() { + case "config": + io.WriteString(s, banner) + io.WriteString(s, "Configuration menu not yet implemented.\n\n") + default: + config, err := getConfig(s) + if err != nil { + io.WriteString(s, fmt.Sprintf("error: %v\n", err)) + // FIXME: drop a menu shell? + return + } + if err := proxy(s, config); err != nil { + io.WriteString(s, fmt.Sprintf("error: %v\n", err)) + } + } + }) + + opts := []ssh.Option{} + if !c.Bool("demo") { + return errors.New("POC: real authentication is not yet implemented") + } + + log.Printf("SSH Server accepting connections on %s", c.String("bind-address")) + return ssh.ListenAndServe(c.String("bind-address"), nil, opts...) +} diff --git a/proxy.go b/proxy.go new file mode 100644 index 0000000..95eb954 --- /dev/null +++ b/proxy.go @@ -0,0 +1,78 @@ +package main + +import ( + "errors" + "io" + "log" + + "github.com/gliderlabs/ssh" + gossh "golang.org/x/crypto/ssh" +) + +func proxy(s ssh.Session, config *Config) error { + rconn, err := gossh.Dial("tcp", config.remoteAddr, config.clientConfig) + if err != nil { + return err + } + defer rconn.Close() + + rch, rreqs, err := rconn.OpenChannel("session", []byte{}) + if err != nil { + return err + } + + log.Println("SSH Connectin established") + lreqs := make(chan *gossh.Request, 1) + defer close(lreqs) + + return pipe(lreqs, rreqs, s, rch) +} + +func pipe(lreqs, rreqs <-chan *gossh.Request, lch, rch gossh.Channel) error { + defer func() { + lch.Close() + rch.Close() + }() + + errch := make(chan error, 1) + + go func() { + _, _ = io.Copy(lch, rch) + errch <- errors.New("lch closed the connection") + }() + + go func() { + _, _ = io.Copy(rch, lch) + errch <- errors.New("rch closed the connection") + }() + + go func() { + // FIXME: find a way to get the client requests + }() + + for { + select { + case req := <-lreqs: // forward ssh requests from local to remote + if req == nil { + return nil + } + b, err := rch.SendRequest(req.Type, req.WantReply, req.Payload) + if err != nil { + return err + } + req.Reply(b, nil) + case req := <-rreqs: // forward ssh requests from remote to local + if req == nil { + return nil + } + b, err := lch.SendRequest(req.Type, req.WantReply, req.Payload) + if err != nil { + return err + } + req.Reply(b, nil) + case err := <-errch: + return err + } + } + return nil +}