From ad396effa3df56a2d64a859f139d03c72b0ef32d Mon Sep 17 00:00:00 2001 From: fangdingjun Date: Fri, 2 Dec 2016 19:00:28 +0800 Subject: [PATCH] add more commandline options client add keepalive, timeout options all add disable obfs after handshake option --- client.go | 15 ++-- conf.go | 26 +++++++ obfssh_client/ssh.go | 113 +++++++++++++++++++++++------- obfssh_server/config.go | 13 ++-- obfssh_server/config_example.yaml | 3 + obfssh_server/server.go | 8 ++- server.go | 12 +++- 7 files changed, 149 insertions(+), 41 deletions(-) create mode 100644 conf.go diff --git a/client.go b/client.go index 725fef7..9801ba9 100644 --- a/client.go +++ b/client.go @@ -28,12 +28,14 @@ type Client struct { // // key is obfs encrypt key // +// conf is the client configure +// // if set method to none or "", means disable the obfs, // when the obfs is disabled, the client can connect to standard ssh server, like OpenSSH server // -func NewClient(c net.Conn, config *ssh.ClientConfig, addr, method, key string) (*Client, error) { - Log(DEBUG, "create obfs conn with method %s", method) - obfsConn, err := NewObfsConn(&TimedOutConn{c, 15 * time.Second}, method, key, false) +func NewClient(c net.Conn, config *ssh.ClientConfig, addr string, conf *Conf) (*Client, error) { + Log(DEBUG, "create obfs conn with method %s", conf.ObfsMethod) + obfsConn, err := NewObfsConn(&TimedOutConn{c, conf.Timeout}, conf.ObfsMethod, conf.ObfsKey, false) if err != nil { return nil, err } @@ -41,12 +43,17 @@ func NewClient(c net.Conn, config *ssh.ClientConfig, addr, method, key string) ( if err != nil { return nil, err } + + if conf.DisableObfsAfterHandshake { + obfsConn.DisableObfs() + } + sshClient := ssh.NewClient(sshConn, newch, reqs) client := &Client{ conn: c, sshConn: sshConn, client: sshClient, ch: make(chan int), } - go client.keepAlive(10*time.Second, 4) + go client.keepAlive(conf.KeepAliveInterval, conf.KeepAliveMax) return client, nil } diff --git a/conf.go b/conf.go new file mode 100644 index 0000000..ff69c14 --- /dev/null +++ b/conf.go @@ -0,0 +1,26 @@ +package obfssh + +import ( + "time" +) + +// Conf keeps the configure of server or client +type Conf struct { + // ObfsMethod is the encrpt method + ObfsMethod string + + // ObfsKey is key for encrypt + ObfsKey string + + // Timeout is the socket timeout on read/write + Timeout time.Duration + + // DisableObfsAfterHandShake disable the obfs encryption after ssh handshake done + DisableObfsAfterHandshake bool + + // KeepAliveInterval the keep alive interval + KeepAliveInterval time.Duration + + // KeepAliveMax the max times of keep alive error + KeepAliveMax int +} diff --git a/obfssh_client/ssh.go b/obfssh_client/ssh.go index cf04065..d5678ac 100644 --- a/obfssh_client/ssh.go +++ b/obfssh_client/ssh.go @@ -1,28 +1,21 @@ package main import ( - //"bytes" "flag" "fmt" + "github.com/bgentry/speakeasy" "github.com/fangdingjun/obfssh" "github.com/golang/crypto/ssh" "github.com/golang/crypto/ssh/agent" - //"github.com/golang/crypto/ssh/terminal" - "time" - //"io" "io/ioutil" "log" "net" "os" - //"os/signal" "path/filepath" "strings" - //"sync" - //"syscall" + "time" ) -var method, encryptKey string - type stringSlice []string func (lf *stringSlice) Set(val string) error { @@ -42,15 +35,17 @@ func (lf *stringSlice) String() string { return s } -var localForwards stringSlice -var remoteForwards stringSlice -var dynamicForwards stringSlice - func main() { var host, port, user, pass, key string - //var localForward, remoteForward, dynamicForward string + var method, encryptKey string var notRunCmd bool var debug bool + var disableObfsAfterHandshake bool + var keepAliveInterval, keepAliveMax int + + var localForwards stringSlice + var remoteForwards stringSlice + var dynamicForwards stringSlice flag.StringVar(&user, "l", os.Getenv("USER"), "ssh username") flag.StringVar(&pass, "pw", "", "ssh password") @@ -63,19 +58,23 @@ func main() { flag.StringVar(&method, "obfs_method", "", "transport encrypt method, avaliable: rc4, aes, empty means disable encrypt") flag.StringVar(&encryptKey, "obfs_key", "", "transport encrypt key") flag.BoolVar(&debug, "d", false, "verbose mode") + flag.IntVar(&keepAliveInterval, "keepalive_interval", 10, "keep alive interval") + flag.IntVar(&keepAliveMax, "keepalive_max", 5, "keep alive max") + flag.BoolVar(&disableObfsAfterHandshake, "disalbe_obfs_after_handshake", false, "disable obfs after handshake") flag.Parse() if debug { obfssh.SSHLogLevel = obfssh.DEBUG } + auth := []ssh.AuthMethod{} + var agentConn net.Conn + var err error + // read ssh agent and default auth key if pass == "" && key == "" { - if aconn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil { - obfssh.Log(obfssh.DEBUG, "add auth method with agent %s", os.Getenv("SSH_AUTH_SOCK")) - auth = append(auth, ssh.PublicKeysCallback(agent.NewClient(aconn).Signers)) - } + var pkeys []ssh.Signer home := os.Getenv("HOME") for _, f := range []string{ @@ -89,10 +88,26 @@ func main() { if pemBytes, err := ioutil.ReadFile(k1); err == nil { if priKey, err := ssh.ParsePrivateKey(pemBytes); err == nil { obfssh.Log(obfssh.DEBUG, "add private key: %s", k1) - auth = append(auth, ssh.PublicKeys(priKey)) + //auth = append(auth, ssh.PublicKeys(priKey)) + pkeys = append(pkeys, priKey) } } } + + if len(pkeys) != 0 { + obfssh.Log(obfssh.DEBUG, "private key length %d", len(pkeys)) + auth = append(auth, ssh.PublicKeys(pkeys...)) + } + + agentConn, err = net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")) + if err == nil { + defer agentConn.Close() + obfssh.Log(obfssh.DEBUG, "add auth method with agent %s", os.Getenv("SSH_AUTH_SOCK")) + agentClient := agent.NewClient(agentConn) + auth = append(auth, ssh.PublicKeysCallback(agentClient.Signers)) + } else { + obfssh.Log(obfssh.DEBUG, "connect to agent failed") + } } args := flag.Args() @@ -115,11 +130,7 @@ func main() { host = ss[1] } - if pass != "" { - obfssh.Log(obfssh.DEBUG, "add password auth method") - auth = append(auth, ssh.Password(pass)) - } - + // process user specified private key if key != "" { pemBytes, err := ioutil.ReadFile(key) if err != nil { @@ -133,23 +144,48 @@ func main() { auth = append(auth, ssh.PublicKeys(priKey)) } + if pass != "" { + obfssh.Log(obfssh.DEBUG, "add password auth method") + auth = append(auth, ssh.Password(pass)) + } else { + obfssh.Log(obfssh.DEBUG, "add keyboard interactive auth") + //auth = append(auth, + // ssh.RetryableAuthMethod(ssh.KeyboardInteractive(keyboardAuth), 3)) + auth = append(auth, + ssh.RetryableAuthMethod(ssh.PasswordCallback(passwordAuth), 3)) + } + config := &ssh.ClientConfig{ User: user, Auth: auth, Timeout: 10 * time.Second, } - h := net.JoinHostPort(host, port) - c, err := net.Dial("tcp", h) + rhost := net.JoinHostPort(host, port) + + c, err := net.Dial("tcp", rhost) if err != nil { log.Fatal(err) } - client, err := obfssh.NewClient(c, config, h, method, encryptKey) + conf := &obfssh.Conf{ + ObfsMethod: method, + ObfsKey: encryptKey, + Timeout: time.Duration(keepAliveInterval+5) * time.Second, + KeepAliveInterval: time.Duration(keepAliveInterval) * time.Second, + KeepAliveMax: keepAliveMax, + DisableObfsAfterHandshake: disableObfsAfterHandshake, + } + + client, err := obfssh.NewClient(c, config, rhost, conf) if err != nil { log.Fatal(err) } + var local, remote string + + // process port forward + for _, p := range localForwards { addr := parseForwardAddr(p) if len(addr) != 4 && len(addr) != 3 { @@ -226,3 +262,26 @@ func parseForwardAddr(s string) []string { }) return ss } + +/* +func keyboardAuth(user, instruction string, question []string, echos []bool) (answers []string, err error) { + if len(question) == 0 { + fmt.Printf("%s %s\n", user, instruction) + return nil, nil + } + r := bufio.NewReader(os.Stdin) + var s string + for i := range question { + fmt.Printf("%s ", question[i]) + s, err = r.ReadString('\n') + answers = append(answers, s) + } + return +} +*/ + +func passwordAuth() (string, error) { + // read password from console + s, err := speakeasy.Ask("Password: ") + return strings.Trim(s, " \r\n"), err +} diff --git a/obfssh_server/config.go b/obfssh_server/config.go index 75a9e90..e19bbf5 100644 --- a/obfssh_server/config.go +++ b/obfssh_server/config.go @@ -9,12 +9,13 @@ import ( ) type serverConfig struct { - Port int `yaml:"port"` - Key string `yaml:"obfs_key"` - Debug bool `yaml:"debug"` - HostKey string `yaml:"host_key_file"` - Method string `yaml:"obfs_method"` - Users []serverUser `yaml:"users"` + Port int `yaml:"port"` + Key string `yaml:"obfs_key"` + Debug bool `yaml:"debug"` + HostKey string `yaml:"host_key_file"` + Method string `yaml:"obfs_method"` + DisableObfsAfterHandshake bool `yaml:"disable_obfs_after_handshake"` + Users []serverUser `yaml:"users"` } type serverUser struct { diff --git a/obfssh_server/config_example.yaml b/obfssh_server/config_example.yaml index d389755..f792788 100644 --- a/obfssh_server/config_example.yaml +++ b/obfssh_server/config_example.yaml @@ -17,6 +17,9 @@ host_key_file: ssh_host_rsa_key #obfs_method: rc4 obfs_method: "rc4" +# when set to true, only the ssh handshake packet is encrypted +disable_obfs_after_handshake: true + # show more log message # value true or false debug: true diff --git a/obfssh_server/server.go b/obfssh_server/server.go index d63147d..e66b5a7 100644 --- a/obfssh_server/server.go +++ b/obfssh_server/server.go @@ -28,6 +28,12 @@ func main() { obfssh.SSHLogLevel = obfssh.DEBUG } + sconf := &obfssh.Conf{ + ObfsMethod: conf.Method, + ObfsKey: conf.Key, + DisableObfsAfterHandshake: conf.DisableObfsAfterHandshake, + } + config := &ssh.ServerConfig{ PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { if u, err := conf.getUser(c.User()); err == nil { @@ -86,7 +92,7 @@ func main() { obfssh.Log(obfssh.DEBUG, "accept tcp connection from %s", c.RemoteAddr()) go func(c net.Conn) { - sc, err := obfssh.NewServer(c, config, conf.Method, conf.Key) + sc, err := obfssh.NewServer(c, config, sconf) if err != nil { c.Close() obfssh.Log(obfssh.ERROR, "%s", err.Error()) diff --git a/server.go b/server.go index 6c232d1..6d09605 100644 --- a/server.go +++ b/server.go @@ -26,11 +26,13 @@ type Server struct { // // key is obfs encrypt key // +// conf is the server configure +// // if set method to none or "", means disable obfs encryption, when the obfs is disabled, // the server can accept connection from standard ssh client, like OpenSSH client // -func NewServer(c net.Conn, config *ssh.ServerConfig, method, key string) (*Server, error) { - wc, err := NewObfsConn(c, method, key, true) +func NewServer(c net.Conn, config *ssh.ServerConfig, conf *Conf) (*Server, error) { + wc, err := NewObfsConn(c, conf.ObfsMethod, conf.ObfsKey, true) if err != nil { return nil, err } @@ -38,7 +40,11 @@ func NewServer(c net.Conn, config *ssh.ServerConfig, method, key string) (*Serve if err != nil { return nil, err } - //wc.DisableObfs() + + if conf.DisableObfsAfterHandshake { + wc.DisableObfs() + } + sc := &Server{conn: c, sshConn: sshConn, forwardedPorts: map[string]net.Listener{},