Skip to content

Commit

Permalink
Merge pull request #25 from cashshuffle/tor
Browse files Browse the repository at this point in the history
[Feature] Add option to spin up TCP listeners for tor in parallel
  • Loading branch information
zquestz committed Mar 1, 2019
2 parents 636b65c + a72b48a commit a8fa37b
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 73 deletions.
46 changes: 35 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,43 @@ Usage:
cashshuffle [flags]
Flags:
-a, --auto-cert string register hostname with LetsEncrypt
-b, --bind-ip string IP address to bind to
-c, --cert string path to server.crt for TLS
-d, --debug debug mode
-h, --help help for cashshuffle
-k, --key string path to server.key for TLS
-s, --pool-size int pool size (default 5)
-p, --port int server port (default 1337)
-z, --stats-port int stats server port (default 8080)
-v, --version display version
-w, --websocket-port int websocket port (default 1338)
-a, --auto-cert string register hostname with LetsEncrypt
-b, --bind-ip string IP address to bind to
-c, --cert string path to server.crt for TLS
-d, --debug debug mode
-h, --help help for cashshuffle
-k, --key string path to server.key for TLS
-s, --pool-size int pool size (default 5)
-p, --port int server port (default 1337)
-z, --stats-port int stats server port (default 8080)
-t, --tor enable secondary listener for tor connections
--tor-bind-ip string IP address to bind to for tor (default "127.0.0.1")
--tor-port int tor server port (default 1339)
--tor-stats-port int tor stats server port (default 8081)
--tor-websocket-port int tor websocket port (default 1340)
-v, --version display version
-w, --websocket-port int websocket port (default 1338)
```

## Tor

To run a server on the public internet with SSL and also support Tor just use the `--tor` flag.

```
cashshuffle -s 5 -c <cert> -k <key> --tor
```

Now edit your `torrc` and add the following. Then restart Tor for the configuration to take effect.

```
HiddenServiceDir /var/lib/tor/cashshuffle
HiddenServicePort 1339 127.0.0.1:1339
HiddenServicePort 1340 127.0.0.1:1340
HiddenServicePort 8081 127.0.0.1:8081
```

For more docs on setting up onion services you can check out https://www.torproject.org/docs/tor-onion-service.html.en.

## License

cashshuffle is released under the MIT license.
Expand Down
25 changes: 15 additions & 10 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,21 @@ import (

// Config stores all the application configuration.
type Config struct {
DisplayVersion bool `json:"-"`
Port int `json:"port,string"`
StatsPort int `json:"stats_port,string"`
WebSocketPort int `json:"websocket_port,string"`
Cert string `json:"cert"`
Key string `json:"key"`
PoolSize int `json:"pool_size,string"`
Debug bool `json:"debug,string"`
AutoCert string `json:"auto_cert"`
BindIP string `json:"bind_ip"`
DisplayVersion bool `json:"-"`
Port int `json:"port,string"`
StatsPort int `json:"stats_port,string"`
WebSocketPort int `json:"websocket_port,string"`
Cert string `json:"cert"`
Key string `json:"key"`
PoolSize int `json:"pool_size,string"`
Debug bool `json:"debug,string"`
AutoCert string `json:"auto_cert"`
BindIP string `json:"bind_ip"`
Tor bool `json:"tor,string"`
TorBindIP string `json:"tor_bind_ip"`
TorPort int `json:"tor_port,string"`
TorStatsPort int `json:"tor_stats_port,string"`
TorWebSocketPort int `json:"tor_websocket_port,string"`
}

// Load reads the configuration from ~/.cashshuffle/config and loads it into the Config struct.
Expand Down
63 changes: 53 additions & 10 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ import (
)

const (
appName = "cashshuffle"
version = "0.6.0"
defaultPort = 1337
defaultWebSocketPort = 1338
defaultStatsPort = 8080
defaultPoolSize = 5
appName = "cashshuffle"
version = "0.6.1"
defaultPort = 1337
defaultWebSocketPort = 1338
defaultTorPort = 1339
defaultTorWebSocketPort = 1340
defaultStatsPort = 8080
defaultTorStatsPort = 8081
defaultPoolSize = 5
defaultTorBindIP = "127.0.0.1"
)

// Stores configuration data.
Expand Down Expand Up @@ -65,6 +69,22 @@ func prepareFlags() {
config.StatsPort = defaultStatsPort
}

if config.TorBindIP == "" {
config.TorBindIP = defaultTorBindIP
}

if config.TorPort == 0 {
config.TorPort = defaultTorPort
}

if config.TorWebSocketPort == 0 {
config.TorWebSocketPort = defaultTorWebSocketPort
}

if config.TorStatsPort == 0 {
config.TorStatsPort = defaultTorStatsPort
}

if config.PoolSize == 0 {
config.PoolSize = defaultPoolSize
}
Expand All @@ -89,6 +109,16 @@ func prepareFlags() {
&config.AutoCert, "auto-cert", "a", config.AutoCert, "register hostname with LetsEncrypt")
MainCmd.PersistentFlags().StringVarP(
&config.BindIP, "bind-ip", "b", config.BindIP, "IP address to bind to")
MainCmd.PersistentFlags().BoolVarP(
&config.Tor, "tor", "t", config.Tor, "enable secondary listener for tor connections")
MainCmd.PersistentFlags().StringVarP(
&config.TorBindIP, "tor-bind-ip", "", config.TorBindIP, "IP address to bind to for tor")
MainCmd.PersistentFlags().IntVarP(
&config.TorPort, "tor-port", "", config.TorPort, "tor server port")
MainCmd.PersistentFlags().IntVarP(
&config.TorWebSocketPort, "tor-websocket-port", "", config.TorWebSocketPort, "tor websocket port")
MainCmd.PersistentFlags().IntVarP(
&config.TorStatsPort, "tor-stats-port", "", config.TorStatsPort, "tor stats server port")
}

// Where all the work happens.
Expand All @@ -102,7 +132,7 @@ func performCommand(cmd *cobra.Command, args []string) error {
return errors.New("can't specify auto-cert and key/cert")
}

t := server.NewTracker(config.PoolSize, config.Port, config.WebSocketPort)
t := server.NewTracker(config.PoolSize, config.Port, config.WebSocketPort, config.TorPort, config.TorWebSocketPort)

m, err := getLetsEncryptManager()
if err != nil {
Expand All @@ -111,15 +141,28 @@ func performCommand(cmd *cobra.Command, args []string) error {

// enable stats if port specified
if config.StatsPort > 0 {
go server.StartStatsServer(config.BindIP, config.StatsPort, config.Cert, config.Key, t, m)
go server.StartStatsServer(config.BindIP, config.StatsPort, config.Cert, config.Key, t, m, false)
}

if config.Tor && config.TorStatsPort > 0 {
go server.StartStatsServer(config.TorBindIP, config.TorStatsPort, "", "", t, nil, true)
}

// enable websocket port if specified.
if config.WebSocketPort > 0 {
go server.StartWebsocket(config.BindIP, config.WebSocketPort, config.Cert, config.Key, config.Debug, t, m)
go server.StartWebsocket(config.BindIP, config.WebSocketPort, config.Cert, config.Key, config.Debug, t, m, false)
}

if config.Tor && config.TorWebSocketPort > 0 {
go server.StartWebsocket(config.TorBindIP, config.TorWebSocketPort, "", "", config.Debug, t, nil, true)
}

// enable tor server if specified.
if config.Tor {
go server.Start(config.TorBindIP, config.TorPort, "", "", config.Debug, t, nil, true)
}

return server.Start(config.BindIP, config.Port, config.Cert, config.Key, config.Debug, t, m)
return server.Start(config.BindIP, config.Port, config.Cert, config.Key, config.Debug, t, m, false)
}

func getLetsEncryptManager() (*autocert.Manager, error) {
Expand Down
18 changes: 14 additions & 4 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
var debugMode bool

// Start brings up the TCP server.
func Start(ip string, port int, cert string, key string, debug bool, t *Tracker, m *autocert.Manager) (err error) {
func Start(ip string, port int, cert string, key string, debug bool, t *Tracker, m *autocert.Manager, tor bool) (err error) {
var listener net.Listener

debugMode = debug
Expand All @@ -36,7 +36,12 @@ func Start(ip string, port int, cert string, key string, debug bool, t *Tracker,
packetInfoChan := make(chan *packetInfo)
go startPacketInfoChan(packetInfoChan)

fmt.Printf("Shuffle Listening on TCP %s:%d (pool size: %d)\n", ip, port, t.poolSize)
torStr := ""
if tor {
torStr = "Tor"
}

fmt.Printf("%sShuffle Listening on TCP %s:%d (pool size: %d)\n", torStr, ip, port, t.poolSize)
for {
conn, err := listener.Accept()
if err != nil {
Expand All @@ -48,7 +53,7 @@ func Start(ip string, port int, cert string, key string, debug bool, t *Tracker,
}

// StartWebsocket brings up the websocket server.
func StartWebsocket(ip string, port int, cert string, key string, debug bool, t *Tracker, m *autocert.Manager) (err error) {
func StartWebsocket(ip string, port int, cert string, key string, debug bool, t *Tracker, m *autocert.Manager, tor bool) (err error) {
packetInfoChan := make(chan *packetInfo)
go startPacketInfoChan(packetInfoChan)

Expand Down Expand Up @@ -78,7 +83,12 @@ func StartWebsocket(ip string, port int, cert string, key string, debug bool, t
srv.TLSConfig.GetCertificate = m.GetCertificate
}

fmt.Printf("Shuffle Listening via Websockets on %s:%d\n", ip, port)
torStr := ""
if tor {
torStr = "Tor"
}

fmt.Printf("%sShuffle Listening via Websockets on %s:%d\n", torStr, ip, port)

if tlsEnabled(cert, key, m) {
err = srv.ListenAndServeTLS(cert, key)
Expand Down
18 changes: 14 additions & 4 deletions server/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package server

// StatsInformer defines an interface that exposes tracker stats
type StatsInformer interface {
Stats(string) *TrackerStats
Stats(string, bool) *TrackerStats
}

// TrackerStats represents a snapshot of the trackers statistics
Expand All @@ -26,7 +26,7 @@ type PoolStats struct {
}

// Stats returns the tracker stats.
func (t *Tracker) Stats(ip string) *TrackerStats {
func (t *Tracker) Stats(ip string, tor bool) *TrackerStats {
t.mutex.RLock()
defer t.mutex.RUnlock()

Expand All @@ -41,14 +41,24 @@ func (t *Tracker) Stats(ip string) *TrackerStats {
}
}

sp := t.shufflePort
if tor {
sp = t.torShufflePort
}

wssp := t.shuffleWebSocketPort
if tor {
wssp = t.torShuffleWebSocketPort
}

ts := &TrackerStats{
BanScore: banScore,
Banned: banned,
Connections: len(t.connections),
PoolSize: t.poolSize,
Pools: make([]PoolStats, 0),
ShufflePort: t.shufflePort,
ShuffleWebSocketPort: t.shuffleWebSocketPort,
ShufflePort: sp,
ShuffleWebSocketPort: wssp,
}

for k, p := range t.pools {
Expand Down
16 changes: 11 additions & 5 deletions server/stats_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,28 @@ import (
)

// StartStatsServer creates a new server to serve stats
func StartStatsServer(ip string, port int, cert string, key string, si StatsInformer, m *autocert.Manager) error {
func StartStatsServer(ip string, port int, cert string, key string, si StatsInformer, m *autocert.Manager, tor bool) error {
mux := http.NewServeMux()
mux.HandleFunc("/stats", statsJSON(si))
mux.HandleFunc("/stats", statsJSON(si, tor))
s := newStatsServer(fmt.Sprintf("%s:%d", ip, port), mux, m)
tls := tlsEnabled(cert, key, m)
fmt.Printf("Stats Listening on TCP %s:%d (tls: %v)\n", ip, port, tls)

torStr := ""
if tor {
torStr = "Tor"
}

fmt.Printf("%sStats Listening on TCP %s:%d (tls: %v)\n", torStr, ip, port, tls)
if tls {
return s.ListenAndServeTLS(cert, key)
}
return s.ListenAndServe()
}

func statsJSON(si StatsInformer) func(http.ResponseWriter, *http.Request) {
func statsJSON(si StatsInformer, tor bool) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
b, _ := json.Marshal(si.Stats(ip))
b, _ := json.Marshal(si.Stats(ip, tor))
w.Header().Set("Content-Type", "application/json")
w.Write(b)
}
Expand Down
15 changes: 10 additions & 5 deletions server/stats_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,11 @@ func TestTrackStats(t *testing.T) {
fullPools: map[int]interface{}{
1: nil,
},
poolSize: 5,
shufflePort: 3000,
poolSize: 5,
shufflePort: 3000,
shuffleWebSocketPort: 3001,
torShufflePort: 3002,
torShuffleWebSocketPort: 3003,
bannedIPs: map[string]*banData{
"8.8.8.8": {
score: maxBanScore,
Expand All @@ -63,14 +66,15 @@ func TestTrackStats(t *testing.T) {
}

// Test with ban.
stats := tracker.Stats("8.8.8.8")
stats := tracker.Stats("8.8.8.8", false)

assert.Equal(t, uint32(3), stats.BanScore)
assert.Equal(t, true, stats.Banned)
assert.Equal(t, 8, stats.Connections)
assert.Equal(t, 5, stats.PoolSize)
assert.Equal(t, 2, len(stats.Pools))
assert.Equal(t, 3000, stats.ShufflePort)
assert.Equal(t, 3001, stats.ShuffleWebSocketPort)
assert.Contains(t, stats.Pools,
PoolStats{
Members: 5,
Expand All @@ -89,14 +93,15 @@ func TestTrackStats(t *testing.T) {
)

// Test without ban.
stats2 := tracker.Stats("8.8.4.4")
stats2 := tracker.Stats("8.8.4.4", true)

assert.Equal(t, uint32(2), stats2.BanScore)
assert.Equal(t, false, stats2.Banned)
assert.Equal(t, 8, stats2.Connections)
assert.Equal(t, 5, stats2.PoolSize)
assert.Equal(t, 2, len(stats2.Pools))
assert.Equal(t, 3000, stats2.ShufflePort)
assert.Equal(t, 3002, stats2.ShufflePort)
assert.Equal(t, 3003, stats2.ShuffleWebSocketPort)
assert.Contains(t, stats2.Pools,
PoolStats{
Members: 5,
Expand Down

0 comments on commit a8fa37b

Please sign in to comment.