From d7abfb0d267da4ea0ed5e657977a4334bc87ebd7 Mon Sep 17 00:00:00 2001 From: Dingjun Date: Fri, 15 Sep 2017 11:26:44 +0800 Subject: [PATCH] remove obfsucation, use tls instead --- README.md | 50 ++++--- client.go | 16 +-- conf.go | 8 -- conn.go | 261 ------------------------------------ conn_test.go | 74 ---------- doc.go | 2 +- obfscp/scp.go | 49 +++---- obfssh/config.go | 31 +++-- obfssh/ssh.go | 45 +++---- obfsshd/config.go | 17 ++- obfsshd/config_example.yaml | 30 ++--- obfsshd/server.go | 69 ++++++---- server.go | 20 +-- 13 files changed, 146 insertions(+), 526 deletions(-) diff --git a/README.md b/README.md index 968d0b7..9e54b10 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,7 @@ obfssh ===== -obfssh is wrapper for ssh protocol, use AES or RC4 to encrypt the transport data, -ssh is a good designed protocol and with the good encryption, but the protocol has a especially figerprint, -the firewall can easily identify the protocol and block it or QOS it, especial when we use its port forward function to escape from the state censorship. - -obfssh encrypt the ssh protocol and hide the figerprint, the firewall can not identify the protocol. - -We borrow the idea from https://github.com/brl/obfuscated-openssh, but not compatible with it, -beause the limitions of golang ssh library. - - +obfssh is wrapper for golang.org/x/crypto/ssh protocol, add support for listen or connect ssh via TLS server usage example @@ -19,11 +10,6 @@ server usage example import "github.com/fangdingjun/obfssh" import "golang.org/x/crypto/ssh" - // key for encryption - obfs_key := "some keyword" - - // encrypt method - obfs_method := "rc4" config := &ssh.ServerConfig{ // add ssh server configure here @@ -31,10 +17,18 @@ server usage example ... } - l, err := net.Listen(":2022") + var l net.Listener + var err error + if useTLS{ + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + l, err = tls.Listen("tcp", ":2022", &tls.Config{ + Certificates: []tls.Certificate{cert}, + }else{ + l, err = net.Listen(":2022") + } c, err := l.Accept() - sc, err := obfssh.NewServer(c, config, obfs_method, obfs_key) + sc, err := obfssh.NewServer(c, config, &obfssh.Conf{}) sc.Run() @@ -48,22 +42,25 @@ client usage example addr := "localhost:2022" - // key for encryption - obfs_key := "some keyword" - - // encrypt method - obfs_method := "rc4" - config := ssh.ClientConfig{ // add ssh client config here // for example auth method ... } - - c, err := net.Dial("tcp", addr) + + var c net.Conn + var err error + if useTLS{ + c, err = tls.Dial("tcp", addr, &tls.Config{ + ServerName: "localhost", + InsecureSkipVerify: true, + } + }else{ + c, err = net.Dial("tcp", addr) + } // create connection - client, err := obfssh.NewClient(c, config, addr, obfs_method, obfs_key) + client, err := obfssh.NewClient(c, config, addr, &obfssh.Conf{}) // local to remote port forward client.AddLocalForward(":2234:10.0.0.1:3221") @@ -83,7 +80,6 @@ limitions now, the server side only implements the port forward function, start shell or execute a command is not suppurted - if set the `obfs_method` to `none`, obfssh is compatible with standard ssh server/client(OpenSSH) License ======= diff --git a/client.go b/client.go index c3f69fb..74800ea 100644 --- a/client.go +++ b/client.go @@ -26,30 +26,16 @@ type Client struct { // // addr is server address // -// method is obfs encrypt method, value is rc4, aes or none or "" -// -// 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 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 - } + obfsConn := &TimedOutConn{c, conf.Timeout} sshConn, newch, reqs, err := ssh.NewClientConn(obfsConn, addr, config) 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, diff --git a/conf.go b/conf.go index ff69c14..77b649b 100644 --- a/conf.go +++ b/conf.go @@ -6,18 +6,10 @@ import ( // 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 diff --git a/conn.go b/conn.go index d406472..36d35f3 100644 --- a/conn.go +++ b/conn.go @@ -1,271 +1,10 @@ package obfssh import ( - "crypto/aes" - "crypto/cipher" - "crypto/md5" - "crypto/rand" - "crypto/rc4" - "crypto/sha1" - "crypto/sha512" - "encoding/binary" - "errors" - "io" - //"log" - "math/big" "net" - "strings" "time" ) -const ( - keyLength = 16 - seedLength = 16 - maxPadding = 1024 - magicValue uint32 = 0x0BF5CA7E - loopCount = 10 -) - -// ObfsConn implement the net.Conn interface which enrytp/decrpt -// the data automatic -type ObfsConn struct { - net.Conn - key []byte - cipherRead cipher.Stream - cipherWrite cipher.Stream - cipherDisabled bool - method string - writeBuf []byte - writeBufLen int - //isServer bool -} - -// NewObfsConn initial a ObfsConn -// after new return, seed handshake is done -func NewObfsConn(c net.Conn, method, key string, isServer bool) (*ObfsConn, error) { - - wc := &ObfsConn{ - Conn: c, - key: []byte(key), - cipherDisabled: false, - method: method, - writeBuf: make([]byte, 8192), - writeBufLen: 8192, - // isServer: isServer, - } - - // do not initial chiper when encrypt method is empty or none - if method == "" || method == "none" { - wc.DisableObfs() - return wc, nil - } - - if isServer { - if err := wc.readSeed(); err != nil { - buf := make([]byte, 1024) - Log(DEBUG, "read forever") - // read forever - for { - if _, err1 := wc.Conn.Read(buf); err1 != nil { - return nil, err - } - } - } - } else { - if err := wc.writeSeed(); err != nil { - return nil, err - } - } - return wc, nil -} - -func generateKey(seed, keyword, iv []byte) []byte { - buf := make([]byte, seedLength+len(keyword)+len(iv)) - - copy(buf[0:], seed) - - // user key - if keyword != nil { - copy(buf[seedLength:], keyword) - } - - copy(buf[seedLength+len(keyword):], iv) - - o := sha512.Sum512(buf[0:]) - - for i := 0; i < loopCount; i++ { - o = sha512.Sum512(o[0:]) - } - - return o[0:keyLength] -} - -// EnableObfs enable the encryption -func (wc *ObfsConn) EnableObfs() { - Log(DEBUG, "enable the encryption") - wc.cipherDisabled = false -} - -// DisableObfs disable the encryption -func (wc *ObfsConn) DisableObfs() { - Log(DEBUG, "disable the encryption") - wc.cipherDisabled = true -} - -func (wc *ObfsConn) writeSeed() error { - Log(DEBUG, "begin to write the seed") - - ii, err := rand.Int(rand.Reader, big.NewInt(int64(maxPadding))) - if err != nil { - //Log(ERROR, "initial the random seed failed: %s", err.Error()) - return err - } - i := ii.Int64() - Log(DEBUG, "use padding data length %d\n", int(i)) - buf := make([]byte, seedLength+8+int(i)) - - // generate seed - rand.Read(buf[0:seedLength]) - - // put magic value - binary.BigEndian.PutUint32(buf[seedLength:seedLength+4], magicValue) - - // put padding length - binary.BigEndian.PutUint32(buf[seedLength+4:seedLength+8], uint32(i)) - - // generate padding data - rand.Read(buf[24:]) - - // generate the key - keyToServer := generateKey(buf[0:seedLength], wc.key, []byte("client_to_server")) - keyToClient := generateKey(buf[0:seedLength], wc.key, []byte("server_to_client")) - - var r, w cipher.Stream - - // initial the cipher - switch strings.ToLower(wc.method) { - case "aes": - w, r = newAESCipher(keyToServer, keyToClient) - case "rc4": - w, r = newRC4Cipher(keyToServer, keyToClient) - default: - return errors.New("unknown cipher type") - } - - wc.cipherWrite = w - wc.cipherRead = r - - // encrypt the data, except the seed - wc.cipherWrite.XORKeyStream(buf[seedLength:], buf[seedLength:]) - - _, err = wc.Conn.Write(buf[0:]) - if err != nil { - return err - } - - Log(DEBUG, "write seed done") - return nil -} - -func (wc *ObfsConn) readSeed() error { - Log(DEBUG, "begin to read the seed") - buf := make([]byte, seedLength+8) - - // read the data except padding - _, err := io.ReadFull(wc.Conn, buf) - if err != nil { - return err - } - - // generate the key - keyToServer := generateKey(buf[0:seedLength], wc.key, []byte("client_to_server")) - keyToClient := generateKey(buf[0:seedLength], wc.key, []byte("server_to_client")) - - var w, r cipher.Stream - switch strings.ToLower(wc.method) { - case "aes": - w, r = newAESCipher(keyToClient, keyToServer) - case "rc4": - w, r = newRC4Cipher(keyToClient, keyToServer) - } - - wc.cipherWrite = w - wc.cipherRead = r - - // decrypt the magic and padding length - wc.cipherRead.XORKeyStream(buf[seedLength:seedLength+8], buf[seedLength:seedLength+8]) - - // check magic value - magic := binary.BigEndian.Uint32(buf[seedLength : seedLength+4]) - if magic != magicValue { - Log(ERROR, "magic %x check failed from %s", magic, wc.Conn.RemoteAddr()) - return errors.New("wrong magic value") - } - - // read the padding data - padLen := binary.BigEndian.Uint32(buf[seedLength+4 : seedLength+8]) - - Log(DEBUG, "padding %d", padLen) - - buf = make([]byte, padLen) - if _, err := io.ReadFull(wc, buf[0:]); err != nil { - return err - } - - Log(DEBUG, "read seed done") - return nil -} - -// Read read the data from underlying connection -// if encryption enabled, decrypt the data and return to plain data to upstream -func (wc *ObfsConn) Read(buf []byte) (int, error) { - n, err := wc.Conn.Read(buf) - if err != nil { - return 0, err - } - if !wc.cipherDisabled { - wc.cipherRead.XORKeyStream(buf[0:n], buf[0:n]) - } - //log.Printf("%+q", buf[0:n]) - return n, err -} - -// Write write the data to underlying connection -// if encryption enabled, encrypt it before write -func (wc *ObfsConn) Write(buf []byte) (int, error) { - if !wc.cipherDisabled { - bufLen := len(buf) - if bufLen > wc.writeBufLen { - wc.writeBufLen = bufLen + 8192 - wc.writeBuf = make([]byte, wc.writeBufLen) - } - wc.cipherWrite.XORKeyStream(wc.writeBuf[0:bufLen], buf[0:bufLen]) - return wc.Conn.Write(wc.writeBuf[0:bufLen]) - } - return wc.Conn.Write(buf[0:]) -} - -func newAESCipher(key1, key2 []byte) (cipher.Stream, cipher.Stream) { - b1, _ := aes.NewCipher(key1) - b2, _ := aes.NewCipher(key2) - - m1 := sha1.Sum(key1) - iv1 := md5.Sum(m1[0:]) - - m2 := sha1.Sum(key2) - iv2 := md5.Sum(m2[0:]) - - w := cipher.NewCFBEncrypter(b1, iv1[0:]) - r := cipher.NewCFBDecrypter(b2, iv2[0:]) - return w, r -} - -func newRC4Cipher(key1, key2 []byte) (cipher.Stream, cipher.Stream) { - w, _ := rc4.NewCipher(key1) - r, _ := rc4.NewCipher(key2) - return w, r -} - // TimedOutConn is a net.Conn with read/write timeout set type TimedOutConn struct { net.Conn diff --git a/conn_test.go b/conn_test.go index 2091aec..bac3640 100644 --- a/conn_test.go +++ b/conn_test.go @@ -7,80 +7,6 @@ import ( "time" ) -func TestObfsConn(t *testing.T) { - obfsMethod := "rc4" - obfsKey := "hello" - - // test rc4 - testObfsConn(t, obfsMethod, obfsKey) - - obfsMethod = "aes" - - // test aes - testObfsConn(t, obfsMethod, obfsKey) -} - -func testObfsConn(t *testing.T, obfsMethod, obfsKey string) { - l, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatalf("listen socket failed: %s", err) - } - - defer l.Close() - - addr := l.Addr() - - go func() { - // server - s, err := l.Accept() - if err != nil { - t.Fatalf("acceept failed: %s", err) - } - - defer s.Close() - - sConn, err := NewObfsConn(s, obfsMethod, obfsKey, true) - if err != nil { - t.Fatalf("create obfsconn failed: %s", err) - } - - buf := make([]byte, 100) - n, err := sConn.Read(buf) - if err != nil { - t.Fatalf("server read failed: %s", err) - } - - sConn.Write(buf[:n]) - }() - - c, err := net.Dial("tcp", addr.String()) - if err != nil { - t.Fatalf("dail failed: %s", err) - } - - defer c.Close() - - cConn, err := NewObfsConn(c, obfsMethod, obfsKey, false) - if err != nil { - t.Fatalf("create client obfsconn failed: %s", err) - } - - str := "hello, world" - cConn.Write([]byte(str)) - - buf := make([]byte, 100) - n, err := cConn.Read(buf) - - if str != string(buf[:n]) { - t.Errorf("data transport failed") - } -} - -func TestTimedOutConn(t *testing.T) { - testTimedOutConn(t, false) - testTimedOutConn(t, true) -} - func testTimedOutConn(t *testing.T, timeout bool) { l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { diff --git a/doc.go b/doc.go index 9ae4f1b..e8c39c2 100644 --- a/doc.go +++ b/doc.go @@ -1,7 +1,7 @@ package obfssh /* -obfssh is wrapper for ssh protocol, use AES or RC4 to encrypt the transport data, +Package obfssh is wrapper for ssh protocol, use AES or RC4 to encrypt the transport data, ssh is a good designed protocol and with the good encryption, but the protocol has a especially figerprint, the firewall can easily identify the protocol and block it or QOS it, especial when we use its port forward function to escape from the state censorship. diff --git a/obfscp/scp.go b/obfscp/scp.go index 24f15be..8eb5e05 100644 --- a/obfscp/scp.go +++ b/obfscp/scp.go @@ -1,6 +1,7 @@ package main import ( + "crypto/tls" "errors" "flag" "fmt" @@ -26,6 +27,8 @@ type options struct { Port int User string Passwd string + TLS bool + TLSInsecure bool Recursive bool ObfsMethod string ObfsKey string @@ -40,12 +43,11 @@ func main() { flag.BoolVar(&cfg.Debug, "d", false, "verbose mode") flag.IntVar(&cfg.Port, "p", 22, "port") flag.StringVar(&cfg.User, "l", os.Getenv("USER"), "user") + flag.BoolVar(&cfg.TLS, "tls", false, "use tls or not") + flag.BoolVar(&cfg.TLSInsecure, "tls-insecure", false, "insecure tls connection") flag.StringVar(&cfg.Passwd, "pw", "", "password") flag.StringVar(&cfg.PrivateKey, "i", "", "private key") flag.BoolVar(&cfg.Recursive, "r", false, "recursively copy entries") - flag.StringVar(&cfg.ObfsMethod, "obfs_method", "none", "obfs encrypt method, rc4, aes or none") - flag.StringVar(&cfg.ObfsKey, "obfs_key", "", "obfs encrypt key") - flag.BoolVar(&cfg.DisableObfsAfterHandshake, "disable_obfs_after_handshake", false, "disable obfs after handshake") flag.Parse() if cfg.Debug { @@ -146,23 +148,33 @@ func createSFTPConn(host, user string, cfg *options) (*sftp.Client, error) { User: user, Auth: auths, Timeout: 5 * time.Second, + HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { + return nil + }, } rhost := net.JoinHostPort(host, fmt.Sprintf("%d", cfg.Port)) - c, err := net.Dial("tcp", rhost) + var c net.Conn + var err error + if cfg.TLS { + c, err = tls.Dial("tcp", rhost, &tls.Config{ + ServerName: host, + InsecureSkipVerify: cfg.TLSInsecure, + }) + } else { + c, err = net.Dial("tcp", rhost) + } + if err != nil { //log.Fatal(err) return nil, err } conf := &obfssh.Conf{ - ObfsMethod: cfg.ObfsMethod, - ObfsKey: cfg.ObfsKey, - Timeout: 10 * time.Second, - KeepAliveInterval: 10 * time.Second, - KeepAliveMax: 5, - DisableObfsAfterHandshake: cfg.DisableObfsAfterHandshake, + Timeout: 10 * time.Second, + KeepAliveInterval: 10 * time.Second, + KeepAliveMax: 5, } conn, err := obfssh.NewClient(c, config, rhost, conf) @@ -599,23 +611,6 @@ Options: Specifies the password for log in remote machine -r recursively copy the directories - -Options for obfuscation: - -obfs_method method - Specifies the encryption method. - when this option is specified, the entire connection - will be encrypted. - when set to none, the encryption is disabled. - Avaliable methods: rc4, aes, none(default) - - -obfs_key key - Specifies the key to encrypt the connection, - if the server enable the obfs, only known the - right key can connect to the server. - - -disable_obfs_after_handshake - when this option is specified, only encrypt the - ssh handshake message. ` fmt.Printf("%s", usageStr) os.Exit(1) diff --git a/obfssh/config.go b/obfssh/config.go index 60aae64..2d4afa6 100644 --- a/obfssh/config.go +++ b/obfssh/config.go @@ -44,22 +44,21 @@ func (lf *stringSlice) String() string { } type config struct { - Host string `yaml:"host"` - Port int `yaml:"port"` - PrivateKey string `yaml:"private_key"` - ObfsMethod string `yaml:"obfs_method"` - ObfsKey string `yaml:"obfs_key"` - Username string `yaml:"username"` - Password string `yaml:"password"` - KeepaliveInterval int `yaml:"keepalive_interval"` - KeepaliveMax int `yaml:"keepalive_max"` - Debug bool `yaml:"debug"` - DisableObfsAfterHandshake bool `yaml:"disable_obfs_after_handshake"` - NotRunCmd bool `yaml:"not_run_cmd"` - LocalForwards stringSlice `yaml:"local_forward"` - RemoteForwards stringSlice `yaml:"remote_forward"` - DynamicForwards stringSlice `yaml:"dynamic_forward"` - Proxy proxy + Host string `yaml:"host"` + Port int `yaml:"port"` + TLS bool `yaml:"tls"` + TLSInsecure bool `yaml:"tls-insecure"` + PrivateKey string `yaml:"private_key"` + Username string `yaml:"username"` + Password string `yaml:"password"` + KeepaliveInterval int `yaml:"keepalive_interval"` + KeepaliveMax int `yaml:"keepalive_max"` + Debug bool `yaml:"debug"` + NotRunCmd bool `yaml:"not_run_cmd"` + LocalForwards stringSlice `yaml:"local_forward"` + RemoteForwards stringSlice `yaml:"remote_forward"` + DynamicForwards stringSlice `yaml:"dynamic_forward"` + Proxy proxy } type proxy struct { diff --git a/obfssh/ssh.go b/obfssh/ssh.go index 5da95cd..8fe1d06 100644 --- a/obfssh/ssh.go +++ b/obfssh/ssh.go @@ -1,6 +1,7 @@ package main import ( + "crypto/tls" "flag" "fmt" "github.com/bgentry/speakeasy" @@ -25,16 +26,15 @@ func main() { flag.StringVar(&cfg.Password, "pw", "", "ssh password") flag.IntVar(&cfg.Port, "p", 22, "remote port") flag.StringVar(&cfg.PrivateKey, "i", "", "private key file") + flag.BoolVar(&cfg.TLS, "tls", false, "use tls or not") + flag.BoolVar(&cfg.TLSInsecure, "tls-insecure", false, "insecure tls connnection") flag.Var(&cfg.LocalForwards, "L", "forward local port to remote, format [local_host:]local_port:remote_host:remote_port") flag.Var(&cfg.RemoteForwards, "R", "forward remote port to local, format [remote_host:]remote_port:local_host:local_port") flag.BoolVar(&cfg.NotRunCmd, "N", false, "not run remote command, useful when do port forward") flag.Var(&cfg.DynamicForwards, "D", "enable dynamic forward, format [local_host:]local_port") - flag.StringVar(&cfg.ObfsMethod, "obfs_method", "", "transport encrypt method, avaliable: rc4, aes, empty means disable encrypt") - flag.StringVar(&cfg.ObfsKey, "obfs_key", "", "transport encrypt key") flag.BoolVar(&cfg.Debug, "d", false, "verbose mode") flag.IntVar(&cfg.KeepaliveInterval, "keepalive_interval", 10, "keep alive interval") flag.IntVar(&cfg.KeepaliveMax, "keepalive_max", 5, "keep alive max") - flag.BoolVar(&cfg.DisableObfsAfterHandshake, "disable_obfs_after_handshake", false, "disable obfs after handshake") flag.Usage = usage flag.Parse() @@ -192,16 +192,24 @@ func main() { log.Fatal(err) } + tlsConn := c + if cfg.TLS { + tlsConn = tls.Client(c, &tls.Config{ + ServerName: host, + InsecureSkipVerify: cfg.TLSInsecure, + }) + if err := tlsConn.(*tls.Conn).Handshake(); err != nil { + log.Fatal(err) + } + } + conf := &obfssh.Conf{ - ObfsMethod: cfg.ObfsMethod, - ObfsKey: cfg.ObfsKey, - Timeout: time.Duration(cfg.KeepaliveInterval*2) * time.Second, - KeepAliveInterval: time.Duration(cfg.KeepaliveInterval) * time.Second, - KeepAliveMax: cfg.KeepaliveMax, - DisableObfsAfterHandshake: cfg.DisableObfsAfterHandshake, + Timeout: time.Duration(cfg.KeepaliveInterval*2) * time.Second, + KeepAliveInterval: time.Duration(cfg.KeepaliveInterval) * time.Second, + KeepAliveMax: cfg.KeepaliveMax, } - client, err := obfssh.NewClient(c, config, rhost, conf) + client, err := obfssh.NewClient(tlsConn, config, rhost, conf) if err != nil { log.Fatal(err) } @@ -382,23 +390,6 @@ Options: Specifies the max error count for keep alive, when the count reach the max, the connection will be abort. - -Options for obfuscation: - -obfs_method method - Specifies the encryption method. - when this option is specified, the entire connection - will be encrypted. - when set to none, the encryption is disabled. - Avaliable methods: rc4, aes, none(default) - - -obfs_key key - Specifies the key to encrypt the connection, - if the server enable the obfs, only known the - right key can connect to the server. - - -disable_obfs_after_handshake - when this option is specified, only encrypt the - ssh handshake message. ` fmt.Printf("%s", usageStr) os.Exit(1) diff --git a/obfsshd/config.go b/obfsshd/config.go index b1b5097..dd64079 100644 --- a/obfsshd/config.go +++ b/obfsshd/config.go @@ -8,14 +8,17 @@ import ( "log" ) +type listen struct { + Port int + Key string + Cert string +} + 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"` - DisableObfsAfterHandshake bool `yaml:"disable_obfs_after_handshake"` - Users []serverUser `yaml:"users"` + Listen []listen `yaml:"listen"` + Debug bool `yaml:"debug"` + HostKey string `yaml:"host_key_file"` + Users []serverUser `yaml:"users"` } type serverUser struct { diff --git a/obfsshd/config_example.yaml b/obfsshd/config_example.yaml index 75c9ad9..3dba7be 100644 --- a/obfsshd/config_example.yaml +++ b/obfsshd/config_example.yaml @@ -4,31 +4,19 @@ # port # the ssh port listen on -port: 2022 - -# obfs_key -# -# Specifies the key to encrypt the connection, -# if obfs enabled, only client known this key -# can connect -obfs_key: some_keyword +listen: + - + port: 2022 + key: + cert: + - + port: 2023 + key: server.key + cert: server.crt # ssh host key file host_key_file: ./ssh_host_rsa_key -# obfs_method -# -# Specifies the encryption method. -# when this option is specified, the entire connection -# will be encrypted. -# when set to none, the encryption is disabled. -# Avaliable methods: rc4, aes, none(default) -# -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/obfsshd/server.go b/obfsshd/server.go index ab6eda3..252b96e 100644 --- a/obfsshd/server.go +++ b/obfsshd/server.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "crypto/tls" "flag" "fmt" "github.com/fangdingjun/obfssh" @@ -28,11 +29,7 @@ func main() { obfssh.SSHLogLevel = obfssh.DEBUG } - sconf := &obfssh.Conf{ - ObfsMethod: conf.Method, - ObfsKey: conf.Key, - DisableObfsAfterHandshake: conf.DisableObfsAfterHandshake, - } + sconf := &obfssh.Conf{} config := &ssh.ServerConfig{ PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { @@ -77,29 +74,49 @@ func main() { } config.AddHostKey(private) - l, err := net.Listen("tcp", fmt.Sprintf(":%d", conf.Port)) - if err != nil { - log.Fatal(err) - } - defer l.Close() - for { - c, err := l.Accept() - if err != nil { - fmt.Println(err) - return - } - - obfssh.Log(obfssh.DEBUG, "accept tcp connection from %s", c.RemoteAddr()) - - go func(c net.Conn) { - sc, err := obfssh.NewServer(c, config, sconf) + for _, lst := range conf.Listen { + go func(lst listen) { + var l net.Listener + var err error + if lst.Key == "" || lst.Cert == "" { + l, err = net.Listen("tcp", fmt.Sprintf(":%d", lst.Port)) + } else { + cert, err := tls.LoadX509KeyPair(lst.Cert, lst.Key) + if err != nil { + log.Fatal(err) + } + l, err = tls.Listen("tcp", fmt.Sprintf(":%d", lst.Port), &tls.Config{ + Certificates: []tls.Certificate{cert}, + }) + } + if err != nil { - c.Close() - obfssh.Log(obfssh.ERROR, "%s", err.Error()) - return + log.Fatal(err) + } + defer l.Close() + + for { + c, err := l.Accept() + if err != nil { + fmt.Println(err) + return + } + + obfssh.Log(obfssh.DEBUG, "accept tcp connection from %s", c.RemoteAddr()) + + go func(c net.Conn) { + defer c.Close() + sc, err := obfssh.NewServer(c, config, sconf) + if err != nil { + c.Close() + obfssh.Log(obfssh.ERROR, "%s", err.Error()) + return + } + sc.Run() + }(c) } - sc.Run() - }(c) + }(lst) } + select {} } diff --git a/server.go b/server.go index 6b41280..9e2e15b 100644 --- a/server.go +++ b/server.go @@ -5,6 +5,7 @@ import ( "github.com/pkg/sftp" "golang.org/x/crypto/ssh" "net" + "time" ) // Server is server connection @@ -21,30 +22,17 @@ type Server struct { // // config is &ssh.ServerConfig // -// method is obfs encrypt method, value is rc4, aes or none or "" -// -// 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, conf *Conf) (*Server, error) { - wc, err := NewObfsConn(c, conf.ObfsMethod, conf.ObfsKey, true) + sshConn, ch, req, err := ssh.NewServerConn(&TimedOutConn{c, 15 * 60 * time.Second}, config) if err != nil { return nil, err } - sshConn, ch, req, err := ssh.NewServerConn(wc, config) - if err != nil { - return nil, err - } - - if conf.DisableObfsAfterHandshake { - wc.DisableObfs() - } - sc := &Server{conn: c, + sc := &Server{ + conn: c, sshConn: sshConn, forwardedPorts: map[string]net.Listener{}, exitCh: make(chan struct{})}