2017-09-30 19:12:43 +08:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2017-10-31 23:22:40 +08:00
|
|
|
"fmt"
|
2017-09-30 19:12:43 +08:00
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
|
|
|
|
"github.com/gliderlabs/ssh"
|
|
|
|
gossh "golang.org/x/crypto/ssh"
|
|
|
|
)
|
|
|
|
|
2017-12-02 05:17:44 +08:00
|
|
|
func proxy(s ssh.Session, host *Host, hk gossh.HostKeyCallback) error {
|
2017-12-04 01:18:17 +08:00
|
|
|
config, err := host.clientConfig(s, hk)
|
2017-10-31 16:24:18 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
rconn, err := gossh.Dial("tcp", host.Addr, config)
|
2017-09-30 19:12:43 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-12-04 01:18:17 +08:00
|
|
|
defer func() { _ = rconn.Close() }()
|
2017-09-30 19:12:43 +08:00
|
|
|
|
|
|
|
rch, rreqs, err := rconn.OpenChannel("session", []byte{})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-12-02 05:17:44 +08:00
|
|
|
log.Println("SSH Connection established")
|
2017-10-30 19:30:34 +08:00
|
|
|
return pipe(s.MaskedReqs(), rreqs, s, rch)
|
2017-09-30 19:12:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func pipe(lreqs, rreqs <-chan *gossh.Request, lch, rch gossh.Channel) error {
|
|
|
|
defer func() {
|
2017-12-04 01:18:17 +08:00
|
|
|
_ = lch.Close()
|
|
|
|
_ = rch.Close()
|
2017-09-30 19:12:43 +08:00
|
|
|
}()
|
|
|
|
|
|
|
|
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")
|
|
|
|
}()
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2017-12-04 01:18:17 +08:00
|
|
|
if err2 := req.Reply(b, nil); err2 != nil {
|
|
|
|
return err2
|
|
|
|
}
|
2017-09-30 19:12:43 +08:00
|
|
|
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
|
|
|
|
}
|
2017-12-04 01:18:17 +08:00
|
|
|
if err2 := req.Reply(b, nil); err2 != nil {
|
|
|
|
return err2
|
|
|
|
}
|
2017-09-30 19:12:43 +08:00
|
|
|
case err := <-errch:
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-10-31 23:22:40 +08:00
|
|
|
|
2017-12-04 01:18:17 +08:00
|
|
|
func (host *Host) clientConfig(_ ssh.Session, hk gossh.HostKeyCallback) (*gossh.ClientConfig, error) {
|
2017-10-31 23:22:40 +08:00
|
|
|
config := gossh.ClientConfig{
|
|
|
|
User: host.User,
|
2017-12-02 05:17:44 +08:00
|
|
|
HostKeyCallback: hk,
|
2017-10-31 23:22:40 +08:00
|
|
|
Auth: []gossh.AuthMethod{},
|
|
|
|
}
|
2017-11-02 06:42:17 +08:00
|
|
|
if host.SSHKey != nil {
|
|
|
|
signer, err := gossh.ParsePrivateKey([]byte(host.SSHKey.PrivKey))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
config.Auth = append(config.Auth, gossh.PublicKeys(signer))
|
|
|
|
}
|
2017-10-31 23:22:40 +08:00
|
|
|
if host.Password != "" {
|
|
|
|
config.Auth = append(config.Auth, gossh.Password(host.Password))
|
|
|
|
}
|
|
|
|
if len(config.Auth) == 0 {
|
|
|
|
return nil, fmt.Errorf("no valid authentication method for host %q", host.Name)
|
|
|
|
}
|
|
|
|
return &config, nil
|
|
|
|
}
|