mirror of
https://github.com/moul/sshportal.git
synced 2025-03-10 06:17:23 +08:00
POC
This commit is contained in:
commit
1a9a95c737
3 changed files with 182 additions and 0 deletions
29
config.go
Normal file
29
config.go
Normal file
|
@ -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
|
||||
}
|
75
main.go
Normal file
75
main.go
Normal file
|
@ -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...)
|
||||
}
|
78
proxy.go
Normal file
78
proxy.go
Normal file
|
@ -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
|
||||
}
|
Loading…
Reference in a new issue