Skip to content

Commit

Permalink
merge max-retry-interval, add max-retry-count
Browse files Browse the repository at this point in the history
  • Loading branch information
jpillora committed Oct 20, 2018
2 parents 78c9ff5 + 36d5e41 commit fa71a68
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 56 deletions.
77 changes: 40 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@

[![GoDoc](https://godoc.org/github.com/jpillora/chisel?status.svg)](https://godoc.org/github.com/jpillora/chisel)

Chisel is a fast TCP tunnel, transported over HTTP, secured via SSH. Single executable including both client and server. Written in Go (Golang). Chisel is mainly useful for passing through firewalls, though it can also be used to provide a secure endpoint into your network. Chisel is very similar to [crowbar](https://github.com/q3k/crowbar) though achieves **much** higher [performance](#performance).
Chisel is a fast TCP tunnel, transported over HTTP, secured via SSH. Single executable including both client and server. Written in Go (golang). Chisel is mainly useful for passing through firewalls, though it can also be used to provide a secure endpoint into your network. Chisel is very similar to [crowbar](https://github.com/q3k/crowbar) though achieves **much** higher [performance](#performance).

![overview](https://docs.google.com/drawings/d/1p53VWxzGNfy8rjr-mW8pvisJmhkoLl82vAgctO_6f1w/pub?w=960&h=720)

### Features

* Easy to use
* [Performant](#performance)*
* [Encrypted connections](#security) using the SSH protocol (via `crypto/ssh`)
* [Authenticated connections](#authentication); authenticated client connections with a users config file, authenticated server connections with fingerprint matching.
* Client auto-reconnects with [exponential backoff](https://github.com/jpillora/backoff)
* Client can create multiple tunnel endpoints over one TCP connection
* Client can optionally pass through HTTP CONNECT proxies
* Server optionally doubles as a [reverse proxy](http://golang.org/pkg/net/http/httputil/#NewSingleHostReverseProxy)
* Server optionally allows [SOCKS5](https://en.wikipedia.org/wiki/SOCKS) connections (See [guide below](#socks5-guide))
- Easy to use
- [Performant](#performance)\*
- [Encrypted connections](#security) using the SSH protocol (via `crypto/ssh`)
- [Authenticated connections](#authentication); authenticated client connections with a users config file, authenticated server connections with fingerprint matching.
- Client auto-reconnects with [exponential backoff](https://github.com/jpillora/backoff)
- Client can create multiple tunnel endpoints over one TCP connection
- Client can optionally pass through HTTP CONNECT proxies
- Server optionally doubles as a [reverse proxy](http://golang.org/pkg/net/http/httputil/#NewSingleHostReverseProxy)
- Server optionally allows [SOCKS5](https://en.wikipedia.org/wiki/SOCKS) connections (See [guide below](#socks5-guide))

### Install

Expand All @@ -36,22 +36,22 @@ docker run --rm -it jpillora/chisel --help

**Source**

``` sh
```sh
$ go get -v github.com/jpillora/chisel
```

### Demo

A [demo app](https://chisel-demo.herokuapp.com) on Heroku is running this `chisel server`:

``` sh
```sh
$ chisel server --port $PORT --proxy http://example.com
# listens on $PORT, proxy web requests to 'http://example.com'
```

This demo app is also running a [simple file server](https://www.npmjs.com/package/serve) on `:3000`, which is normally inaccessible due to Heroku's firewall. However, if we tunnel in with:

``` sh
```sh
$ chisel client https://chisel-demo.herokuapp.com 3000
# connects to 'https://chisel-demo.herokuapp.com',
# tunnels your localhost:3000 to the server's localhost:3000
Expand All @@ -74,7 +74,6 @@ $ chisel --help
Read more:
https://github.com/jpillora/chisel
```

```
Expand Down Expand Up @@ -128,7 +127,6 @@ $ chisel server --help
Read more:
https://github.com/jpillora/chisel
```

```
Expand Down Expand Up @@ -185,6 +183,12 @@ $ chisel client --help
the chisel server. Authentication can be specified inside the URL.
For example, http://admin:password@my-server.com:8081
--max-retry-count, Maximum number of times to retry before exiting.
Defaults to unlimited.
--max-retry-interval, Maximum wait time before retrying after a
disconnection. Defaults to 5 minutes.
--pid Generate pid file in current directory
-v, Enable verbose logging
Expand All @@ -196,7 +200,6 @@ $ chisel client --help
Read more:
https://github.com/jpillora/chisel
```

### Security
Expand All @@ -207,7 +210,7 @@ Encryption is always enabled. When you start up a chisel server, it will generat

Using the `--authfile` option, the server may optionally provide a `user.json` configuration file to create a list of accepted users. The client then authenticates using the `--auth` option. See [users.json](example/users.json) for an example authentication configuration file. See the `--help` above for more information.

Internally, this is done using the *Password* authentication method provided by SSH. Learn more about `crypto/ssh` here http://blog.gopheracademy.com/go-and-ssh/.
Internally, this is done using the _Password_ authentication method provided by SSH. Learn more about `crypto/ssh` here http://blog.gopheracademy.com/go-and-ssh/.

### SOCKS5 Guide

Expand Down Expand Up @@ -252,7 +255,7 @@ request--->client:2001--->server:2002---->fileserver:3000

Note, we're using an in-memory "file" server on localhost for these tests

*direct*
_direct_

```
:3000 => 1 bytes in 1.291417ms
Expand Down Expand Up @@ -302,34 +305,34 @@ See more [test/](test/)

### Known Issues

* WebSockets support is required
* IaaS providers all will support WebSockets
* Unless an unsupporting HTTP proxy has been forced in front of you, in which case I'd argue that you've been downgraded to PaaS.
* PaaS providers vary in their support for WebSockets
* Heroku has full support
* Openshift has full support though connections are only accepted on ports 8443 and 8080
* Google App Engine has **no** support (Track this on [their repo](https://code.google.com/p/googleappengine/issues/detail?id=2535))
- WebSockets support is required
_ IaaS providers all will support WebSockets
_ Unless an unsupporting HTTP proxy has been forced in front of you, in which case I'd argue that you've been downgraded to PaaS.
_ PaaS providers vary in their support for WebSockets
_ Heroku has full support
_ Openshift has full support though connections are only accepted on ports 8443 and 8080
_ Google App Engine has **no** support (Track this on [their repo](https://code.google.com/p/googleappengine/issues/detail?id=2535))

### Contributing

* http://golang.org/doc/code.html
* http://golang.org/doc/effective_go.html
* `github.com/jpillora/chisel/share` contains the shared package
* `github.com/jpillora/chisel/server` contains the server package
* `github.com/jpillora/chisel/client` contains the client package
- http://golang.org/doc/code.html
- http://golang.org/doc/effective_go.html
- `github.com/jpillora/chisel/share` contains the shared package
- `github.com/jpillora/chisel/server` contains the server package
- `github.com/jpillora/chisel/client` contains the client package

### Changelog

* `1.0.0` - Initial release
* `1.1.0` - Swapped out simple symmetric encryption for ECDSA SSH
* `1.2.0` - Added SOCKS5 (server) and HTTP CONNECT (client) support
- `1.0.0` - Initial release
- `1.1.0` - Swapped out simple symmetric encryption for ECDSA SSH
- `1.2.0` - Added SOCKS5 (server) and HTTP CONNECT (client) support

### Todo

* Allow clients to act as an indirect tunnel endpoint for other clients
* Better, faster tests
* Expose a stats page for proxy throughput
* Treat client stdin/stdout as a socket
- Allow clients to act as an indirect tunnel endpoint for other clients
- Better, faster tests
- Expose a stats page for proxy throughput
- Treat client stdin/stdout as a socket

#### MIT License

Expand Down
46 changes: 33 additions & 13 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ import (

//Config represents a client configuration
type Config struct {
shared *chshare.Config
Fingerprint string
Auth string
KeepAlive time.Duration
Server string
HTTPProxy string
Remotes []string
shared *chshare.Config
Fingerprint string
Auth string
KeepAlive time.Duration
MaxRetryCount int
MaxRetryInterval time.Duration
Server string
HTTPProxy string
Remotes []string
}

//Client represents a client instance
Expand All @@ -46,6 +48,9 @@ func NewClient(config *Config) (*Client, error) {
if !strings.HasPrefix(config.Server, "http") {
config.Server = "http://" + config.Server
}
if config.MaxRetryInterval < time.Second {
config.MaxRetryInterval = 5 * time.Minute
}
u, err := url.Parse(config.Server)
if err != nil {
return nil, err
Expand Down Expand Up @@ -150,23 +155,38 @@ func (c *Client) loop() {
}
//connection loop!
var connerr error
b := &backoff.Backoff{Max: 5 * time.Minute}
b := &backoff.Backoff{Max: c.config.MaxRetryInterval}
for {
//NOTE: break == dont retry on handshake failures
if !c.running {
break
}
if connerr != nil {
attempt := int(b.Attempt())
maxAttempt := c.config.MaxRetryCount
d := b.Duration()
c.Debugf("Connection error: %s", connerr)
//show error and attempt counts
msg := fmt.Sprintf("Connection error: %s", connerr)
if attempt > 0 {
msg += fmt.Sprintf(" (Attempt: %d", attempt)
if maxAttempt > 0 {
msg += fmt.Sprintf("/%d", maxAttempt)
}
msg += ")"
}
c.Debugf(msg)
//give up?
if maxAttempt >= 0 && attempt >= maxAttempt {
break
}
c.Infof("Retrying in %s...", d)
connerr = nil
chshare.SleepSignal(d)
}
d := websocket.Dialer{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
Subprotocols: []string{chshare.ProtocolVersion},
ReadBufferSize: 1024,
WriteBufferSize: 1024,
HandshakeTimeout: 45 * time.Second,
Subprotocols: []string{chshare.ProtocolVersion},
}
//optionally CONNECT proxy
if c.httpProxyURL != nil {
Expand Down
22 changes: 16 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,12 @@ var clientHelp = `
specify a time with a unit, for example '30s' or '2m'. Defaults
to '0s' (disabled).
--max-retry-count, Maximum number of times to retry before exiting.
Defaults to unlimited.
--max-retry-interval, Maximum wait time before retrying after a
disconnection. Defaults to 5 minutes.
--proxy, An optional HTTP CONNECT proxy which will be used reach
the chisel server. Authentication can be specified inside the URL.
For example, http://admin:password@my-server.com:8081
Expand All @@ -240,6 +246,8 @@ func client(args []string) {
fingerprint := flags.String("fingerprint", "", "")
auth := flags.String("auth", "", "")
keepalive := flags.Duration("keepalive", 0, "")
maxRetryCount := flags.Int("max-retry-count", -1, "")
maxRetryInterval := flags.Duration("max-retry-interval", 0, "")
proxy := flags.String("proxy", "", "")
pid := flags.Bool("pid", false, "")
verbose := flags.Bool("v", false, "")
Expand All @@ -259,12 +267,14 @@ func client(args []string) {
}

c, err := chclient.NewClient(&chclient.Config{
Fingerprint: *fingerprint,
Auth: *auth,
KeepAlive: *keepalive,
HTTPProxy: *proxy,
Server: args[0],
Remotes: args[1:],
Fingerprint: *fingerprint,
Auth: *auth,
KeepAlive: *keepalive,
MaxRetryCount: *maxRetryCount,
MaxRetryInterval: *maxRetryInterval,
HTTPProxy: *proxy,
Server: args[0],
Remotes: args[1:],
})
if err != nil {
log.Fatal(err)
Expand Down

0 comments on commit fa71a68

Please sign in to comment.