remove obfsucation, use tls instead

tls
Dingjun 7 years ago
parent 15bb6de9b9
commit d7abfb0d26

@ -1,16 +1,7 @@
obfssh obfssh
===== =====
obfssh is wrapper for ssh protocol, use AES or RC4 to encrypt the transport data, obfssh is wrapper for golang.org/x/crypto/ssh protocol, add support for listen or connect ssh via TLS
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.
server usage example server usage example
@ -19,11 +10,6 @@ server usage example
import "github.com/fangdingjun/obfssh" import "github.com/fangdingjun/obfssh"
import "golang.org/x/crypto/ssh" import "golang.org/x/crypto/ssh"
// key for encryption
obfs_key := "some keyword"
// encrypt method
obfs_method := "rc4"
config := &ssh.ServerConfig{ config := &ssh.ServerConfig{
// add ssh server configure here // 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() c, err := l.Accept()
sc, err := obfssh.NewServer(c, config, obfs_method, obfs_key) sc, err := obfssh.NewServer(c, config, &obfssh.Conf{})
sc.Run() sc.Run()
@ -48,22 +42,25 @@ client usage example
addr := "localhost:2022" addr := "localhost:2022"
// key for encryption
obfs_key := "some keyword"
// encrypt method
obfs_method := "rc4"
config := ssh.ClientConfig{ config := ssh.ClientConfig{
// add ssh client config here // add ssh client config here
// for example auth method // 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 // 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 // local to remote port forward
client.AddLocalForward(":2234:10.0.0.1:3221") 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 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 License
======= =======

@ -26,30 +26,16 @@ type Client struct {
// //
// addr is server address // 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 // 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) { 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 := &TimedOutConn{c, conf.Timeout}
obfsConn, err := NewObfsConn(&TimedOutConn{c, conf.Timeout}, conf.ObfsMethod, conf.ObfsKey, false)
if err != nil {
return nil, err
}
sshConn, newch, reqs, err := ssh.NewClientConn(obfsConn, addr, config) sshConn, newch, reqs, err := ssh.NewClientConn(obfsConn, addr, config)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if conf.DisableObfsAfterHandshake {
obfsConn.DisableObfs()
}
sshClient := ssh.NewClient(sshConn, newch, reqs) sshClient := ssh.NewClient(sshConn, newch, reqs)
client := &Client{ client := &Client{
conn: c, sshConn: sshConn, client: sshClient, conn: c, sshConn: sshConn, client: sshClient,

@ -6,18 +6,10 @@ import (
// Conf keeps the configure of server or client // Conf keeps the configure of server or client
type Conf struct { 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 is the socket timeout on read/write
Timeout time.Duration Timeout time.Duration
// DisableObfsAfterHandShake disable the obfs encryption after ssh handshake done
DisableObfsAfterHandshake bool
// KeepAliveInterval the keep alive interval // KeepAliveInterval the keep alive interval
KeepAliveInterval time.Duration KeepAliveInterval time.Duration

@ -1,271 +1,10 @@
package obfssh package obfssh
import ( import (
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"crypto/rand"
"crypto/rc4"
"crypto/sha1"
"crypto/sha512"
"encoding/binary"
"errors"
"io"
//"log"
"math/big"
"net" "net"
"strings"
"time" "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 // TimedOutConn is a net.Conn with read/write timeout set
type TimedOutConn struct { type TimedOutConn struct {
net.Conn net.Conn

@ -7,80 +7,6 @@ import (
"time" "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) { func testTimedOutConn(t *testing.T, timeout bool) {
l, err := net.Listen("tcp", "127.0.0.1:0") l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil { if err != nil {

@ -1,7 +1,7 @@
package obfssh 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, 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. 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.

@ -1,6 +1,7 @@
package main package main
import ( import (
"crypto/tls"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
@ -26,6 +27,8 @@ type options struct {
Port int Port int
User string User string
Passwd string Passwd string
TLS bool
TLSInsecure bool
Recursive bool Recursive bool
ObfsMethod string ObfsMethod string
ObfsKey string ObfsKey string
@ -40,12 +43,11 @@ func main() {
flag.BoolVar(&cfg.Debug, "d", false, "verbose mode") flag.BoolVar(&cfg.Debug, "d", false, "verbose mode")
flag.IntVar(&cfg.Port, "p", 22, "port") flag.IntVar(&cfg.Port, "p", 22, "port")
flag.StringVar(&cfg.User, "l", os.Getenv("USER"), "user") 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.Passwd, "pw", "", "password")
flag.StringVar(&cfg.PrivateKey, "i", "", "private key") flag.StringVar(&cfg.PrivateKey, "i", "", "private key")
flag.BoolVar(&cfg.Recursive, "r", false, "recursively copy entries") 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() flag.Parse()
if cfg.Debug { if cfg.Debug {
@ -146,23 +148,33 @@ func createSFTPConn(host, user string, cfg *options) (*sftp.Client, error) {
User: user, User: user,
Auth: auths, Auth: auths,
Timeout: 5 * time.Second, 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)) 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 { if err != nil {
//log.Fatal(err) //log.Fatal(err)
return nil, err return nil, err
} }
conf := &obfssh.Conf{ conf := &obfssh.Conf{
ObfsMethod: cfg.ObfsMethod,
ObfsKey: cfg.ObfsKey,
Timeout: 10 * time.Second, Timeout: 10 * time.Second,
KeepAliveInterval: 10 * time.Second, KeepAliveInterval: 10 * time.Second,
KeepAliveMax: 5, KeepAliveMax: 5,
DisableObfsAfterHandshake: cfg.DisableObfsAfterHandshake,
} }
conn, err := obfssh.NewClient(c, config, rhost, conf) conn, err := obfssh.NewClient(c, config, rhost, conf)
@ -599,23 +611,6 @@ Options:
Specifies the password for log in remote machine Specifies the password for log in remote machine
-r recursively copy the directories -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) fmt.Printf("%s", usageStr)
os.Exit(1) os.Exit(1)

@ -46,15 +46,14 @@ func (lf *stringSlice) String() string {
type config struct { type config struct {
Host string `yaml:"host"` Host string `yaml:"host"`
Port int `yaml:"port"` Port int `yaml:"port"`
TLS bool `yaml:"tls"`
TLSInsecure bool `yaml:"tls-insecure"`
PrivateKey string `yaml:"private_key"` PrivateKey string `yaml:"private_key"`
ObfsMethod string `yaml:"obfs_method"`
ObfsKey string `yaml:"obfs_key"`
Username string `yaml:"username"` Username string `yaml:"username"`
Password string `yaml:"password"` Password string `yaml:"password"`
KeepaliveInterval int `yaml:"keepalive_interval"` KeepaliveInterval int `yaml:"keepalive_interval"`
KeepaliveMax int `yaml:"keepalive_max"` KeepaliveMax int `yaml:"keepalive_max"`
Debug bool `yaml:"debug"` Debug bool `yaml:"debug"`
DisableObfsAfterHandshake bool `yaml:"disable_obfs_after_handshake"`
NotRunCmd bool `yaml:"not_run_cmd"` NotRunCmd bool `yaml:"not_run_cmd"`
LocalForwards stringSlice `yaml:"local_forward"` LocalForwards stringSlice `yaml:"local_forward"`
RemoteForwards stringSlice `yaml:"remote_forward"` RemoteForwards stringSlice `yaml:"remote_forward"`

@ -1,6 +1,7 @@
package main package main
import ( import (
"crypto/tls"
"flag" "flag"
"fmt" "fmt"
"github.com/bgentry/speakeasy" "github.com/bgentry/speakeasy"
@ -25,16 +26,15 @@ func main() {
flag.StringVar(&cfg.Password, "pw", "", "ssh password") flag.StringVar(&cfg.Password, "pw", "", "ssh password")
flag.IntVar(&cfg.Port, "p", 22, "remote port") flag.IntVar(&cfg.Port, "p", 22, "remote port")
flag.StringVar(&cfg.PrivateKey, "i", "", "private key file") 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.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.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.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.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.BoolVar(&cfg.Debug, "d", false, "verbose mode")
flag.IntVar(&cfg.KeepaliveInterval, "keepalive_interval", 10, "keep alive interval") flag.IntVar(&cfg.KeepaliveInterval, "keepalive_interval", 10, "keep alive interval")
flag.IntVar(&cfg.KeepaliveMax, "keepalive_max", 5, "keep alive max") 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.Usage = usage
flag.Parse() flag.Parse()
@ -192,16 +192,24 @@ func main() {
log.Fatal(err) 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{ conf := &obfssh.Conf{
ObfsMethod: cfg.ObfsMethod,
ObfsKey: cfg.ObfsKey,
Timeout: time.Duration(cfg.KeepaliveInterval*2) * time.Second, Timeout: time.Duration(cfg.KeepaliveInterval*2) * time.Second,
KeepAliveInterval: time.Duration(cfg.KeepaliveInterval) * time.Second, KeepAliveInterval: time.Duration(cfg.KeepaliveInterval) * time.Second,
KeepAliveMax: cfg.KeepaliveMax, KeepAliveMax: cfg.KeepaliveMax,
DisableObfsAfterHandshake: cfg.DisableObfsAfterHandshake,
} }
client, err := obfssh.NewClient(c, config, rhost, conf) client, err := obfssh.NewClient(tlsConn, config, rhost, conf)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -382,23 +390,6 @@ Options:
Specifies the max error count for keep alive, Specifies the max error count for keep alive,
when the count reach the max, the connection will when the count reach the max, the connection will
be abort. 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) fmt.Printf("%s", usageStr)
os.Exit(1) os.Exit(1)

@ -8,13 +8,16 @@ import (
"log" "log"
) )
type listen struct {
Port int
Key string
Cert string
}
type serverConfig struct { type serverConfig struct {
Port int `yaml:"port"` Listen []listen `yaml:"listen"`
Key string `yaml:"obfs_key"`
Debug bool `yaml:"debug"` Debug bool `yaml:"debug"`
HostKey string `yaml:"host_key_file"` HostKey string `yaml:"host_key_file"`
Method string `yaml:"obfs_method"`
DisableObfsAfterHandshake bool `yaml:"disable_obfs_after_handshake"`
Users []serverUser `yaml:"users"` Users []serverUser `yaml:"users"`
} }

@ -4,31 +4,19 @@
# port # port
# the ssh port listen on # the ssh port listen on
listen:
-
port: 2022 port: 2022
key:
# obfs_key cert:
# -
# Specifies the key to encrypt the connection, port: 2023
# if obfs enabled, only client known this key key: server.key
# can connect cert: server.crt
obfs_key: some_keyword
# ssh host key file # ssh host key file
host_key_file: ./ssh_host_rsa_key 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 # show more log message
# value true or false # value true or false
debug: true debug: true

@ -2,6 +2,7 @@ package main
import ( import (
"bytes" "bytes"
"crypto/tls"
"flag" "flag"
"fmt" "fmt"
"github.com/fangdingjun/obfssh" "github.com/fangdingjun/obfssh"
@ -28,11 +29,7 @@ func main() {
obfssh.SSHLogLevel = obfssh.DEBUG obfssh.SSHLogLevel = obfssh.DEBUG
} }
sconf := &obfssh.Conf{ sconf := &obfssh.Conf{}
ObfsMethod: conf.Method,
ObfsKey: conf.Key,
DisableObfsAfterHandshake: conf.DisableObfsAfterHandshake,
}
config := &ssh.ServerConfig{ config := &ssh.ServerConfig{
PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
@ -77,11 +74,27 @@ func main() {
} }
config.AddHostKey(private) config.AddHostKey(private)
l, err := net.Listen("tcp", fmt.Sprintf(":%d", conf.Port)) 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 { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
defer l.Close() defer l.Close()
for { for {
c, err := l.Accept() c, err := l.Accept()
if err != nil { if err != nil {
@ -92,6 +105,7 @@ func main() {
obfssh.Log(obfssh.DEBUG, "accept tcp connection from %s", c.RemoteAddr()) obfssh.Log(obfssh.DEBUG, "accept tcp connection from %s", c.RemoteAddr())
go func(c net.Conn) { go func(c net.Conn) {
defer c.Close()
sc, err := obfssh.NewServer(c, config, sconf) sc, err := obfssh.NewServer(c, config, sconf)
if err != nil { if err != nil {
c.Close() c.Close()
@ -101,5 +115,8 @@ func main() {
sc.Run() sc.Run()
}(c) }(c)
} }
}(lst)
}
select {}
} }

@ -5,6 +5,7 @@ import (
"github.com/pkg/sftp" "github.com/pkg/sftp"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
"net" "net"
"time"
) )
// Server is server connection // Server is server connection
@ -21,30 +22,17 @@ type Server struct {
// //
// config is &ssh.ServerConfig // 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 // 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) { 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 { if err != nil {
return nil, err 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, sshConn: sshConn,
forwardedPorts: map[string]net.Listener{}, forwardedPorts: map[string]net.Listener{},
exitCh: make(chan struct{})} exitCh: make(chan struct{})}

Loading…
Cancel
Save