Skip to content

Commit

Permalink
enhance: Add ssh transport & dialer (#8)
Browse files Browse the repository at this point in the history
Support ssh transport & enhance dialer
  • Loading branch information
josexy authored Jun 10, 2024
1 parent 922359a commit 624a3ab
Show file tree
Hide file tree
Showing 34 changed files with 928 additions and 113 deletions.
10 changes: 10 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var (
Quic: &config.QuicOption{},
Obfs: &config.ObfsOption{},
Grpc: &config.GrpcOption{},
Ssh: &config.SshOption{},
SSR: &config.SSROption{},
}},
Local: &config.LocalConfig{
Expand Down Expand Up @@ -80,6 +81,9 @@ func init() {
rootCmd.PersistentFlags().StringVar(&cfg.Server[0].Obfs.Host, "obfs-host", "www.baidu.com", "obfs host")
// quic options
rootCmd.PersistentFlags().IntVar(&cfg.Server[0].Quic.Conns, "quic-max-conn", 3, "maximum number of quic connections")
rootCmd.PersistentFlags().IntVar(&cfg.Server[0].Quic.KeepAlive, "quic-keepalive", 0, "quic connection keep alive")
rootCmd.PersistentFlags().IntVar(&cfg.Server[0].Quic.MaxIdleTimeout, "quic-max-idle-timeout", 0, "quic max idle timeout")
rootCmd.PersistentFlags().IntVar(&cfg.Server[0].Quic.HandshakeIdleTimeout, "quic-handshake-idle-timeout", 0, "quic handshake idle timeout")
rootCmd.PersistentFlags().StringVar(&cfg.Server[0].Quic.TLS.Mode, "quic-tls-mode", "", "quic tls mode (tls, mtls)")
rootCmd.PersistentFlags().StringVar(&cfg.Server[0].Quic.TLS.KeyPath, "quic-tls-key", "", "quic tls key path")
rootCmd.PersistentFlags().StringVar(&cfg.Server[0].Quic.TLS.CertPath, "quic-tls-cert", "", "quic tls cert path")
Expand All @@ -93,6 +97,12 @@ func init() {
rootCmd.PersistentFlags().StringVar(&cfg.Server[0].Grpc.TLS.CertPath, "grpc-tls-cert", "", "grpc tls cert path")
rootCmd.PersistentFlags().StringVar(&cfg.Server[0].Grpc.TLS.CAPath, "grpc-tls-ca", "", "grpc tls ca path")
rootCmd.PersistentFlags().StringVar(&cfg.Server[0].Grpc.TLS.Hostname, "grpc-tls-host", "", "grpc tls common name")
// ssh options
rootCmd.PersistentFlags().StringVar(&cfg.Server[0].Ssh.User, "ssh-user", "", "ssh user")
rootCmd.PersistentFlags().StringVar(&cfg.Server[0].Ssh.Password, "ssh-password", "", "ssh password")
rootCmd.PersistentFlags().StringVar(&cfg.Server[0].Ssh.PrivateKey, "ssh-prikey", "", "ssh private key")
rootCmd.PersistentFlags().StringVar(&cfg.Server[0].Ssh.PublicKey, "ssh-pubkey", "", "ssh public key (only used for client)")
rootCmd.PersistentFlags().StringVar(&cfg.Server[0].Ssh.AuthorizedKey, "ssh-authorizedkey", "", "ssh authorized key (only used for server)")
// interface
rootCmd.PersistentFlags().StringVar(&cfg.Iface, "iface", "", "bind outbound interface")
rootCmd.PersistentFlags().BoolVar(&cfg.AutoDetectIface, "auto-detect-iface", false, "enable auto-detect interface")
Expand Down
27 changes: 25 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"strings"
"time"

"github.com/josexy/mini-ss/options"
"github.com/josexy/mini-ss/rule"
Expand Down Expand Up @@ -33,8 +34,11 @@ type ObfsOption struct {
}

type QuicOption struct {
Conns int `yaml:"conns" json:"conns"`
TLS TlsOption `yaml:"tls,omitempty" json:"tls,omitempty"`
Conns int `yaml:"conns" json:"conns"`
KeepAlive int `yaml:"keep_alive" json:"keep_alive"`
MaxIdleTimeout int `yaml:"max_idle_timeout" json:"max_idle_timeout"`
HandshakeIdleTimeout int `yaml:"handshake_idle_timeout" json:"handshake_idle_timeout"`
TLS TlsOption `yaml:"tls,omitempty" json:"tls,omitempty"`
}

type GrpcOption struct {
Expand All @@ -43,6 +47,14 @@ type GrpcOption struct {
TLS TlsOption `yaml:"tls,omitempty" json:"tls,omitempty"`
}

type SshOption struct {
User string `yaml:"user" json:"user"`
Password string `yaml:"password" json:"password"`
PrivateKey string `yaml:"private_key" json:"private_key"`
PublicKey string `yaml:"public_key" json:"public_key"`
AuthorizedKey string `yaml:"authorized_key" json:"authorized_key"`
}

type SSROption struct {
Protocol string `yaml:"protocol" json:"protocol"`
ProtocolParam string `yaml:"protocol_param,omitempty" json:"protocol_param,omitempty"`
Expand All @@ -63,6 +75,7 @@ type ServerConfig struct {
Obfs *ObfsOption `yaml:"obfs,omitempty" json:"obfs,omitempty"`
Quic *QuicOption `yaml:"quic,omitempty" json:"quic,omitempty"`
Grpc *GrpcOption `yaml:"grpc,omitempty" json:"grpc,omitempty"`
Ssh *SshOption `yaml:"ssh,omitempty" json:"ssh,omitempty"`
SSR *SSROption `yaml:"ssr,omitempty" json:"ssr,omitempty"`
}

Expand Down Expand Up @@ -331,6 +344,9 @@ func (cfg *Config) BuildServerOptions() []ss.SSOption {
case "quic":
opts = append(opts, ss.WithQuicTransport())
opts = append(opts, ss.WithQuicConns(opt.Quic.Conns))
opts = append(opts, ss.WithQuicKeepAlivePeriod(time.Second*time.Duration(opt.Quic.KeepAlive)))
opts = append(opts, ss.WithQuicMaxIdleTimeout(time.Second*time.Duration(opt.Quic.MaxIdleTimeout)))
opts = append(opts, ss.WithQuicHandshakeIdleTimeout(time.Second*time.Duration(opt.Quic.HandshakeIdleTimeout)))
opts = append(opts, ss.WithQuicCertPath(opt.Quic.TLS.CertPath))
opts = append(opts, ss.WithQuicKeyPath(opt.Quic.TLS.KeyPath))
opts = append(opts, ss.WithQuicCAPath(opt.Quic.TLS.CAPath))
Expand All @@ -354,6 +370,13 @@ func (cfg *Config) BuildServerOptions() []ss.SSOption {
case "mtls":
opts = append(opts, ss.WithGrpcTLS(options.MTLS))
}
case "ssh":
opts = append(opts, ss.WithSshTransport())
opts = append(opts, ss.WithSshUser(opt.Ssh.User))
opts = append(opts, ss.WithSshPassword(opt.Ssh.Password))
opts = append(opts, ss.WithSshPrivateKey(opt.Ssh.PrivateKey))
opts = append(opts, ss.WithSshPublicKey(opt.Ssh.PublicKey))
opts = append(opts, ss.WithSshAuthorizedKey(opt.Ssh.AuthorizedKey))
case "default":
// whether to support ssr
if opt.Type == "ssr" {
Expand Down
2 changes: 2 additions & 0 deletions connection/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Generate golang code from proto buffer file:
--go-grpc_out=paths=source_relative:./proto ./proto/stream.proto
*/

var _ net.Conn = (*GrpcStreamConn)(nil)

type grpcStream interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Expand Down
2 changes: 2 additions & 0 deletions connection/obfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (

var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")

var _ net.Conn = (*ObfsConn)(nil)

type ObfsConn struct {
net.Conn
host string
Expand Down
2 changes: 2 additions & 0 deletions connection/quic.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"github.com/quic-go/quic-go"
)

var _ net.Conn = (*QuicConn)(nil)

type QuicConn struct {
quic.Stream
laddr net.Addr
Expand Down
39 changes: 39 additions & 0 deletions connection/ssh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package connection

import (
"net"
"time"

"golang.org/x/crypto/ssh"
)

var _ net.Conn = (*SshConn)(nil)

type SshConn struct {
ssh.Channel
laddr net.Addr
raddr net.Addr
}

func NewSshConn(channel ssh.Channel, laddr, raddr net.Addr) *SshConn {
return &SshConn{
Channel: channel,
laddr: laddr,
raddr: raddr,
}
}

func (c *SshConn) LocalAddr() net.Addr { return c.laddr }

func (c *SshConn) RemoteAddr() net.Addr { return c.raddr }

func (c *SshConn) SetReadDeadline(time.Time) error { return nil }

func (c *SshConn) SetWriteDeadline(time.Time) error { return nil }

func (c *SshConn) SetDeadline(t time.Time) error {
if err := c.SetReadDeadline(t); err != nil {
return err
}
return c.SetWriteDeadline(t)
}
2 changes: 2 additions & 0 deletions connection/ws.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/gorilla/websocket"
)

var _ net.Conn = (*WebsocketConn)(nil)

type WebsocketConn struct {
conn *websocket.Conn
rbuf []byte // remaining buffer data
Expand Down
1 change: 1 addition & 0 deletions example-configs/client-grpc-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ server:
local:
socks_addr: 127.0.0.1:10086
http_addr: 127.0.0.1:10087
mixed_addr: 127.0.0.1:10088
log:
color: true
log_level: info
Expand Down
4 changes: 4 additions & 0 deletions example-configs/client-quic-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ server:
transport: quic
quic:
conns: 3
keep_alive: 5
max_idle_timeout: 0
handshake_idle_timeout: 0
tls:
mode: "tls"
cert_path: "certs/client.crt"
Expand All @@ -15,6 +18,7 @@ server:
local:
socks_addr: 127.0.0.1:10086
http_addr: 127.0.0.1:10087
mixed_addr: 127.0.0.1:10088
log:
color: true
log_level: trace
Expand Down
28 changes: 28 additions & 0 deletions example-configs/client-ssh-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
server:
- name: ss
addr: 127.0.0.1:8388
password: "12345"
# method: chacha20-ietf-poly1305
method: none
transport: ssh
ssh:
user: root
# password: root
private_key: ssh-keys/test-key
public_key: ssh-keys/test-key.pub

local:
socks_addr: 127.0.0.1:10086
http_addr: 127.0.0.1:10087
mixed_addr: 127.0.0.1:10088
log:
color: true
log_level: trace
verbose_level: 2
iface: en5
auto_detect_iface: true
rules:
mode: global
global_to: 'ss'
direct_to: ''

1 change: 1 addition & 0 deletions example-configs/client-ws-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ server:
local:
socks_addr: 127.0.0.1:10086
http_addr: 127.0.0.1:10087
mixed_addr: 127.0.0.1:10088
log:
color: true
log_level: trace
Expand Down
3 changes: 2 additions & 1 deletion example-configs/server-quic-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ server:
method: chacha20-ietf-poly1305
transport: quic
quic:
conns: 3
# max_idle_timeout: 0
# handshake_idle_timeout: 0
tls:
mode: "tls"
cert_path: "certs/server.crt"
Expand Down
19 changes: 19 additions & 0 deletions example-configs/server-ssh-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
server:
- name: ss
addr: ':8388'
password: "12345"
# method: chacha20-ietf-poly1305
method: none
transport: ssh
ssh:
user: root
# password: root
private_key: ssh-keys/pri.key
authorized_key: ssh-keys/authorized_keys
log:
color: true
log_level: debug
verbose_level: 3

# iface: en5
# auto_detect_iface: true
14 changes: 9 additions & 5 deletions example/quic_client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,22 @@ import (

type echoSrv struct{}

// multiplexing quic dialer
var dialer = transport.NewDialer(transport.Quic, options.DefaultQuicOptions)
var dialer = transport.NewDialer(transport.Quic, &options.QuicOptions{
Conns: 3,
// TlsOptions: options.TlsOptions{
// Mode: options.TLS,
// Hostname: "127.0.0.1",
// CAFile: "certs/ca.crt",
// },
})

func (echoSrv) ServeTCP(conn net.Conn) {
log.Println(conn.RemoteAddr().String())

quicConn, err := dialer.Dial(context.Background(), "127.0.0.1:10001")
if err != nil {
log.Println(err)
return
}
// the Close() don't close the raw quic connection, it only closes the smux.Stream()
defer quicConn.Close()
relay.IoCopyBidirectionalForStream(conn, quicConn)
}
Expand All @@ -45,5 +49,5 @@ func main() {
<-interrupt

srv.Close()
time.Sleep(time.Second * 2)
time.Sleep(time.Millisecond * 200)
}
10 changes: 8 additions & 2 deletions example/quic_server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ func (customSrv) ServeQUIC(conn net.Conn) {
}

func main() {
srv := server.NewQuicServer(":10001", &customSrv{}, options.DefaultQuicOptions)
srv := server.NewQuicServer(":10001", &customSrv{}, &options.QuicOptions{
// TlsOptions: options.TlsOptions{
// Mode: options.TLS,
// KeyFile: "certs/server.key",
// CertFile: "certs/server.crt",
// },
})

go func() {
err := srv.Start(context.Background())
Expand All @@ -41,5 +47,5 @@ func main() {
<-interrupt

srv.Close()
time.Sleep(time.Second * 2)
time.Sleep(time.Millisecond * 200)
}
48 changes: 48 additions & 0 deletions example/ssh_client/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import (
"bytes"
"context"
"log"
"os"
"os/signal"
"syscall"
"time"

"github.com/josexy/mini-ss/options"
"github.com/josexy/mini-ss/transport"
)

func main() {
dialer := transport.NewDialer(transport.Ssh, &options.SshOptions{
User: "test",
Password: "test",
PrivateKey: "ssh-keys/test-key",
PublicKey: "ssh-keys/test-key.pub",
})

request := func() {
time.Sleep(time.Millisecond * 20)
conn, err := dialer.Dial(context.Background(), "127.0.0.1:10086")
if err != nil {
log.Fatalln(err)
}
defer conn.Close()
go func() {
conn.Write([]byte("GET / HTTP/1.1\r\n\r\n\r\n"))
}()
buf := &bytes.Buffer{}
buf.ReadFrom(conn)
log.Println(buf.Len())
}

for i := 0; i < 10; i++ {
go request()
time.Sleep(time.Millisecond * 20)
}

interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, syscall.SIGINT)
<-interrupt
time.Sleep(time.Millisecond * 200)
}
Loading

0 comments on commit 624a3ab

Please sign in to comment.