Skip to content

Commit 68eb79a

Browse files
committed
progressing to users and remote address whitelists
2 parents d6b070e + 9b52e16 commit 68eb79a

File tree

14 files changed

+602
-336
lines changed

14 files changed

+602
-336
lines changed

client/client.go

Lines changed: 69 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
11
package chclient
22

33
import (
4-
"errors"
54
"fmt"
5+
"io"
66
"net"
77
"net/url"
88
"regexp"
99
"strings"
1010
"time"
1111

12-
"github.com/hashicorp/yamux"
1312
"github.com/jpillora/backoff"
1413
"github.com/jpillora/chisel/share"
15-
"github.com/jpillora/conncrypt"
14+
"golang.org/x/crypto/ssh"
1615
"golang.org/x/net/websocket"
1716
)
1817

1918
type Client struct {
2019
*chshare.Logger
2120
config *chshare.Config
22-
encconfig []byte
23-
key, server string
21+
sshConfig *ssh.ClientConfig
2422
proxies []*Proxy
25-
session *yamux.Session
23+
sshConn ssh.Conn
24+
fingerprint string
25+
server string
26+
keyPrefix string
2627
running bool
2728
runningc chan error
2829
}
2930

30-
func NewClient(key, server string, remotes ...string) (*Client, error) {
31+
func NewClient(keyPrefix, auth, server string, remotes ...string) (*Client, error) {
3132

3233
//apply default scheme
3334
if !strings.HasPrefix(server, "http") {
@@ -60,20 +61,27 @@ func NewClient(key, server string, remotes ...string) (*Client, error) {
6061
config.Remotes = append(config.Remotes, r)
6162
}
6263

63-
encconfig, err := chshare.EncodeConfig(config)
64-
if err != nil {
65-
return nil, fmt.Errorf("Failed to encode config: %s", err)
66-
}
67-
68-
return &Client{
64+
c := &Client{
6965
Logger: chshare.NewLogger("client"),
7066
config: config,
71-
encconfig: encconfig,
72-
key: key,
7367
server: u.String(),
68+
keyPrefix: keyPrefix,
7469
running: true,
7570
runningc: make(chan error, 1),
76-
}, nil
71+
}
72+
73+
c.sshConfig = &ssh.ClientConfig{
74+
ClientVersion: chshare.ProtocolVersion + "-client",
75+
HostKeyCallback: c.verifyServer,
76+
}
77+
78+
user, pass := chshare.ParseAuth(auth)
79+
if user != "" {
80+
c.sshConfig.User = user
81+
c.sshConfig.Auth = []ssh.AuthMethod{ssh.Password(pass)}
82+
}
83+
84+
return c, nil
7785
}
7886

7987
//Start then Wait
@@ -82,6 +90,15 @@ func (c *Client) Run() error {
8290
return c.Wait()
8391
}
8492

93+
func (c *Client) verifyServer(hostname string, remote net.Addr, key ssh.PublicKey) error {
94+
f := chshare.FingerprintKey(key)
95+
if c.keyPrefix != "" && !strings.HasPrefix(f, c.keyPrefix) {
96+
return fmt.Errorf("Invalid fingerprint (Got %s)", f)
97+
}
98+
c.fingerprint = f
99+
return nil
100+
}
101+
85102
//Starts the client
86103
func (c *Client) Start() {
87104
go c.start()
@@ -90,37 +107,24 @@ func (c *Client) Start() {
90107
func (c *Client) start() {
91108
c.Infof("Connecting to %s\n", c.server)
92109

93-
//proxies all use this function
94-
openStream := func() (net.Conn, error) {
95-
if c.session == nil || c.session.IsClosed() {
96-
return nil, c.Errorf("no session available")
97-
}
98-
stream, err := c.session.Open()
99-
if err != nil {
100-
return nil, err
101-
}
102-
return stream, nil
103-
}
104-
105110
//prepare proxies
106111
for id, r := range c.config.Remotes {
107-
proxy := NewProxy(c, id, r, openStream)
112+
proxy := NewProxy(c, id, r)
108113
go proxy.start()
109114
c.proxies = append(c.proxies, proxy)
110115
}
111116

117+
//connection loop!
112118
var connerr error
113-
b := &backoff.Backoff{Max: 15 * time.Second}
119+
b := &backoff.Backoff{Max: 5 * time.Minute}
114120

115-
//connection loop!
116121
for {
117122
if !c.running {
118123
break
119124
}
120125
if connerr != nil {
121126
d := b.Duration()
122-
c.Infof("Connerr: %v", connerr)
123-
c.Infof("Retrying in %s...", d)
127+
c.Infof("Retrying in %s...\n", d)
124128
connerr = nil
125129
time.Sleep(d)
126130
}
@@ -131,58 +135,43 @@ func (c *Client) start() {
131135
continue
132136
}
133137

134-
conn := net.Conn(ws)
135-
136-
if c.key != "" {
137-
conn = conncrypt.New(conn, &conncrypt.Config{Password: c.key})
138+
sshConn, chans, reqs, err := ssh.NewClientConn(ws, "", c.sshConfig)
139+
//NOTE break -> dont retry on handshake failures
140+
if err != nil {
141+
if strings.Contains(err.Error(), "unable to authenticate") {
142+
c.Infof("Authentication failed")
143+
} else {
144+
c.Infof(err.Error())
145+
}
146+
break
138147
}
139-
140-
//write config, read result
141-
chshare.SizeWrite(conn, c.encconfig)
142-
143-
resp := chshare.SizeRead(conn)
144-
if string(resp) != "Handshake Success" {
145-
//no point in retrying
146-
c.runningc <- errors.New("Handshake failed")
147-
conn.Close()
148+
conf, _ := chshare.EncodeConfig(c.config)
149+
_, conerr, err := sshConn.SendRequest("config", true, conf)
150+
if err != nil {
151+
c.Infof("Config verification failed", c.fingerprint)
152+
break
153+
}
154+
if len(conerr) > 0 {
155+
c.Infof(string(conerr))
148156
break
149157
}
150158

151-
// Setup client side of yamux
152-
c.session, err = yamux.Client(conn, nil)
153-
if err != nil {
159+
c.Infof("Connected (%s)", c.fingerprint)
160+
//connected
161+
b.Reset()
162+
163+
c.sshConn = sshConn
164+
go ssh.DiscardRequests(reqs)
165+
go chshare.RejectStreams(chans)
166+
err = sshConn.Wait()
167+
//disconnected
168+
c.sshConn = nil
169+
if err != nil && err != io.EOF {
154170
connerr = err
171+
c.Infof("Disconnection error: %s", err)
155172
continue
156173
}
157-
b.Reset()
158-
159-
//check latency
160-
go func() {
161-
d, err := c.session.Ping()
162-
if err == nil {
163-
c.Infof("Connected (Latency: %s)\n", d)
164-
} else {
165-
c.Infof("Connected\n")
166-
}
167-
}()
168-
169-
//signal is connected
170-
connected := make(chan bool)
171-
172-
//poll websocket state
173-
go func() {
174-
for {
175-
if c.session.IsClosed() {
176-
connerr = c.Errorf("disconnected")
177-
c.Infof("Disconnected\n")
178-
close(connected)
179-
break
180-
}
181-
time.Sleep(100 * time.Millisecond)
182-
}
183-
}()
184-
//block!
185-
<-connected
174+
c.Infof("Disconnected\n")
186175
}
187176
close(c.runningc)
188177
}
@@ -195,8 +184,8 @@ func (c *Client) Wait() error {
195184
//Close manual stops the client
196185
func (c *Client) Close() error {
197186
c.running = false
198-
if c.session == nil {
187+
if c.sshConn == nil {
199188
return nil
200189
}
201-
return c.session.Close()
190+
return c.sshConn.Close()
202191
}

client/proxy.go

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
package chclient
22

33
import (
4-
"encoding/binary"
4+
"io"
55
"net"
66

77
"github.com/jpillora/chisel/share"
88
)
99

1010
type Proxy struct {
1111
*chshare.Logger
12-
id int
13-
count int
14-
remote *chshare.Remote
15-
openStream func() (net.Conn, error)
12+
client *Client
13+
id int
14+
count int
15+
remote *chshare.Remote
1616
}
1717

18-
func NewProxy(c *Client, id int, remote *chshare.Remote, openStream func() (net.Conn, error)) *Proxy {
18+
func NewProxy(c *Client, id int, remote *chshare.Remote) *Proxy {
1919
return &Proxy{
20-
Logger: c.Logger.Fork("%s:%s#%d", remote.RemoteHost, remote.RemotePort, id+1),
21-
id: id,
22-
remote: remote,
23-
openStream: openStream,
20+
Logger: c.Logger.Fork("%s:%s#%d", remote.RemoteHost, remote.RemotePort, id+1),
21+
client: c,
22+
id: id,
23+
remote: remote,
2424
}
2525
}
2626

@@ -43,27 +43,28 @@ func (p *Proxy) start() {
4343
}
4444
}
4545

46-
func (p *Proxy) accept(src net.Conn) {
46+
func (p *Proxy) accept(src io.ReadWriteCloser) {
4747
p.count++
4848
cid := p.count
49-
clog := p.Fork("conn#%d", cid)
49+
l := p.Fork("conn#%d", cid)
5050

51-
clog.Debugf("Open")
51+
l.Debugf("Open")
5252

53-
dst, err := p.openStream()
54-
if err != nil {
55-
clog.Debugf("Stream error: %s", err)
53+
if p.client.sshConn == nil {
54+
l.Debugf("No server connection")
5655
src.Close()
5756
return
5857
}
5958

60-
//write endpoint id
61-
b := make([]byte, 2)
62-
binary.BigEndian.PutUint16(b, uint16(p.id))
63-
dst.Write(b)
59+
remoteAddr := p.remote.RemoteHost + ":" + p.remote.RemotePort
60+
dst, err := chshare.OpenStream(p.client.sshConn, remoteAddr)
61+
if err != nil {
62+
l.Infof("Stream error: %s", err)
63+
src.Close()
64+
return
65+
}
6466

6567
//then pipe
6668
s, r := chshare.Pipe(src, dst)
67-
68-
clog.Debugf("Close (sent %d received %d)", s, r)
69+
l.Debugf("Close (sent %d received %d)", s, r)
6970
}

example/users.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"foo:bar": [
3+
"0.0.0.0:3000"
4+
],
5+
"ping:pong": [
6+
"0.0.0.0:[45]000",
7+
"example.com:80"
8+
]
9+
}

0 commit comments

Comments
 (0)