heavier refactoring
This commit is contained in:
parent
09d296df2a
commit
e740d2ca0f
|
@ -1,3 +1,5 @@
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
certs
|
certs
|
||||||
*.exe
|
*.exe
|
||||||
/telebitd
|
/telebitd
|
||||||
|
|
|
@ -32,7 +32,12 @@ tmp $ serve-https -p 8080 -d /tmp --servername hfc.rootprojects.org --agree-tos
|
||||||
### Start Tunnel Client
|
### Start Tunnel Client
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
node-tunnel-client $ bin/stunnel.js --locals http://hfc.rootprojects.org:8080,http://test1.hfc.rootprojects.org:8080 --stunneld wss://localhost.rootprojects.org:8443 --secret abc123
|
# For .env
|
||||||
|
TELEBIT_SECRET=abcdef1234567890
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node-tunnel-client $ bin/stunnel.js --locals http://hfc.rootprojects.org:8080,http://test1.hfc.rootprojects.org:8080 --stunneld wss://localhost.rootprojects.org:8443 --secret abcdef1234567890
|
||||||
```
|
```
|
||||||
|
|
||||||
### Execute RVPN
|
### Execute RVPN
|
||||||
|
|
|
@ -8,46 +8,49 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
|
|
||||||
|
telebit "git.coolaj86.com/coolaj86/go-telebitd"
|
||||||
|
"git.coolaj86.com/coolaj86/go-telebitd/relay"
|
||||||
|
"git.coolaj86.com/coolaj86/go-telebitd/server"
|
||||||
|
|
||||||
|
jwt "github.com/dgrijalva/jwt-go"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
lumberjack "gopkg.in/natefinch/lumberjack.v2"
|
lumberjack "gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
|
||||||
telebit "git.coolaj86.com/coolaj86/go-telebitd"
|
_ "github.com/joho/godotenv/autoload"
|
||||||
"git.coolaj86.com/coolaj86/go-telebitd/server"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logfile = "stdout"
|
logfile = "stdout"
|
||||||
configPath = "./"
|
configPath = "./"
|
||||||
configFile = "go-rvpn-server"
|
configFile = "telebit-relay"
|
||||||
|
|
||||||
loginfo *log.Logger
|
loginfo *log.Logger
|
||||||
logdebug *log.Logger
|
logdebug *log.Logger
|
||||||
logFlags = log.Ldate | log.Lmicroseconds | log.Lshortfile
|
logFlags = log.Ldate | log.Lmicroseconds | log.Lshortfile
|
||||||
argWssClientListener string
|
argWssClientListener string
|
||||||
argGenericBinding int
|
tcpPort int
|
||||||
argServerBinding string
|
argServerBinding string
|
||||||
argServerAdminBinding string
|
argServerAdminBinding string
|
||||||
argServerExternalBinding string
|
argServerExternalBinding string
|
||||||
argDeadTime int
|
argDeadTime int
|
||||||
connectionTable *server.Table
|
connectionTable *server.Table
|
||||||
secretKey = "abc123"
|
secretKey string
|
||||||
wssHostName = "localhost.rootprojects.org"
|
wssHostName = "localhost.rootprojects.org"
|
||||||
adminHostName = telebit.InvalidAdminDomain
|
adminHostName = telebit.InvalidAdminDomain
|
||||||
idle int
|
idle int
|
||||||
dwell int
|
dwell int
|
||||||
cancelcheck int
|
cancelcheck int
|
||||||
lbDefaultMethod string
|
lbDefaultMethod string
|
||||||
serverName string
|
nickname string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.StringVar(&logfile, "log", logfile, "Log file (or stdout/stderr; empty for none)")
|
flag.StringVar(&logfile, "log", logfile, "Log file (or stdout/stderr; empty for none)")
|
||||||
flag.StringVar(&configPath, "config-path", configPath, "Configuration File Path")
|
flag.StringVar(&configPath, "config-path", configPath, "Configuration File Path")
|
||||||
flag.StringVar(&configFile, "config-file", configFile, "Configuration File Name")
|
flag.StringVar(&secretKey, "secret", "", "a >= 16-character random string for JWT key signing")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var logoutput io.Writer
|
var logoutput io.Writer
|
||||||
|
@ -55,6 +58,15 @@ var logoutput io.Writer
|
||||||
//Main -- main entry point
|
//Main -- main entry point
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
if "" == secretKey {
|
||||||
|
secretKey = os.Getenv("TELEBIT_SECRET")
|
||||||
|
}
|
||||||
|
if len(secretKey) < 16 {
|
||||||
|
fmt.Fprintf(os.Stderr, "Invalid secret: %q. See --help for details.\n", secretKey)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
switch logfile {
|
switch logfile {
|
||||||
case "stdout":
|
case "stdout":
|
||||||
logoutput = os.Stdout
|
logoutput = os.Stdout
|
||||||
|
@ -89,54 +101,51 @@ func main() {
|
||||||
|
|
||||||
wssHostName = viper.Get("rvpn.wssdomain").(string)
|
wssHostName = viper.Get("rvpn.wssdomain").(string)
|
||||||
adminHostName = viper.Get("rvpn.admindomain").(string)
|
adminHostName = viper.Get("rvpn.admindomain").(string)
|
||||||
argGenericBinding = viper.GetInt("rvpn.genericlistener")
|
tcpPort = viper.GetInt("rvpn.port")
|
||||||
deadtime := viper.Get("rvpn.deadtime").(map[string]interface{})
|
deadtime := viper.Get("rvpn.deadtime").(map[string]interface{})
|
||||||
idle = deadtime["idle"].(int)
|
idle = deadtime["idle"].(int)
|
||||||
dwell = deadtime["dwell"].(int)
|
dwell = deadtime["dwell"].(int)
|
||||||
cancelcheck = deadtime["cancelcheck"].(int)
|
cancelcheck = deadtime["cancelcheck"].(int)
|
||||||
lbDefaultMethod = viper.Get("rvpn.loadbalancing.defaultmethod").(string)
|
lbDefaultMethod = viper.Get("rvpn.loadbalancing.defaultmethod").(string)
|
||||||
serverName = viper.Get("rvpn.serverName").(string)
|
nickname = viper.Get("rvpn.serverName").(string)
|
||||||
|
|
||||||
loginfo.Println("startup")
|
loginfo.Println("startup")
|
||||||
|
|
||||||
certbundle, err := tls.LoadX509KeyPair("certs/fullchain.pem", "certs/privkey.pem")
|
|
||||||
if err != nil {
|
|
||||||
loginfo.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancelContext := context.WithCancel(context.Background())
|
ctx, cancelContext := context.WithCancel(context.Background())
|
||||||
defer cancelContext()
|
defer cancelContext()
|
||||||
|
|
||||||
serverStatus := server.NewStatus(ctx)
|
serverStatus := server.NewStatus(ctx)
|
||||||
serverStatus.AdminDomain = adminHostName
|
serverStatus.AdminDomain = adminHostName
|
||||||
serverStatus.WssDomain = wssHostName
|
serverStatus.WssDomain = wssHostName
|
||||||
serverStatus.Name = serverName
|
serverStatus.Name = nickname
|
||||||
serverStatus.StartTime = time.Now()
|
|
||||||
serverStatus.DeadTime = server.NewStatusDeadTime(dwell, idle, cancelcheck)
|
serverStatus.DeadTime = server.NewStatusDeadTime(dwell, idle, cancelcheck)
|
||||||
serverStatus.LoadbalanceDefaultMethod = lbDefaultMethod
|
serverStatus.LoadbalanceDefaultMethod = lbDefaultMethod
|
||||||
|
|
||||||
// Setup for GenericListenServe.
|
connectionTable := server.NewTable(dwell, idle, lbDefaultMethod)
|
||||||
// - establish context for the generic listener
|
|
||||||
// - startup listener
|
|
||||||
// - accept with peek buffer.
|
|
||||||
// - peek at the 1st 30 bytes.
|
|
||||||
// - check for tls
|
|
||||||
// - if tls, establish, protocol peek buffer, else decrypted
|
|
||||||
// - match protocol
|
|
||||||
|
|
||||||
connectionTracking := server.NewTracking()
|
tlsConfig := &tls.Config{
|
||||||
serverStatus.ConnectionTracking = connectionTracking
|
GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
go connectionTracking.Run(ctx)
|
// TODO
|
||||||
|
// 1. call out to greenlock for validation
|
||||||
|
// 2. push challenges through http channel
|
||||||
|
// 3. receive certificates (or don't)
|
||||||
|
certbundle, err := tls.LoadX509KeyPair("certs/fullchain.pem", "certs/privkey.pem")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &certbundle, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
connectionTable = server.NewTable(dwell, idle)
|
authorizer := func(r *http.Request) (*server.Authz, error) {
|
||||||
serverStatus.ConnectionTable = connectionTable
|
// do we have a valid wss_client?
|
||||||
go connectionTable.Run(ctx, lbDefaultMethod)
|
tokenString := r.URL.Query().Get("access_token")
|
||||||
|
_, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
return []byte(secretKey), nil
|
||||||
|
})
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
genericListeners := server.NewGenerListeners(ctx, secretKey, certbundle, serverStatus)
|
r := relay.New(ctx, tlsConfig, authorizer, serverStatus, connectionTable)
|
||||||
//serverStatus.GenericListeners = genericListeners
|
r.ListenAndServe(tcpPort)
|
||||||
|
|
||||||
go genericListeners.Run(ctx, argGenericBinding)
|
|
||||||
|
|
||||||
select {}
|
|
||||||
}
|
}
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -6,6 +6,7 @@ require (
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
github.com/gorilla/mux v1.7.4
|
github.com/gorilla/mux v1.7.4
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
|
github.com/joho/godotenv v1.3.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/spf13/viper v1.6.3
|
github.com/spf13/viper v1.6.3
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -49,6 +49,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package relay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
|
||||||
|
"git.coolaj86.com/coolaj86/go-telebitd/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Relay struct {
|
||||||
|
ctx context.Context
|
||||||
|
status *server.Status
|
||||||
|
mx *server.MPlexy
|
||||||
|
table *server.Table
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context, tlsConfig *tls.Config, authz server.Authorizer, status *server.Status, table *server.Table) *Relay {
|
||||||
|
return &Relay{
|
||||||
|
ctx: ctx,
|
||||||
|
status: status,
|
||||||
|
table: table,
|
||||||
|
mx: server.New(ctx, tlsConfig, authz, status),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Relay) ListenAndServe(port int) error {
|
||||||
|
|
||||||
|
serverStatus := r.status
|
||||||
|
|
||||||
|
// Setup for GenericListenServe.
|
||||||
|
// - establish context for the generic listener
|
||||||
|
// - startup listener
|
||||||
|
// - accept with peek buffer.
|
||||||
|
// - peek at the 1st 30 bytes.
|
||||||
|
// - check for tls
|
||||||
|
// - if tls, establish, protocol peek buffer, else decrypted
|
||||||
|
// - match protocol
|
||||||
|
|
||||||
|
connectionTracking := server.NewTracking()
|
||||||
|
serverStatus.ConnectionTracking = connectionTracking
|
||||||
|
go connectionTracking.Run(r.ctx)
|
||||||
|
|
||||||
|
serverStatus.ConnectionTable = r.table
|
||||||
|
go serverStatus.ConnectionTable.Run(r.ctx)
|
||||||
|
|
||||||
|
//serverStatus.GenericListeners = genericListeners
|
||||||
|
|
||||||
|
// blocks until it can listen, which it can't until started
|
||||||
|
go r.mx.MultiListenAndServe(port)
|
||||||
|
|
||||||
|
return r.mx.Run()
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ Docker version 17.03.0-ce, build 60ccb22
|
||||||
cd $GOPATH/src/git.coolaj86.com/coolaj86
|
cd $GOPATH/src/git.coolaj86.com/coolaj86
|
||||||
git clone git@git.coolaj86.com:coolaj86/go-telebitd.git
|
git clone git@git.coolaj86.com:coolaj86/go-telebitd.git
|
||||||
|
|
||||||
cd go-rvpn-server
|
cd telebit-relay
|
||||||
go get
|
go get
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -36,7 +36,7 @@ Step 2/3 : LABEL maintainer "henry.f.camacho@gmail.com"
|
||||||
---> Running in 5cdffef8e33d
|
---> Running in 5cdffef8e33d
|
||||||
---> f7e09c097612
|
---> f7e09c097612
|
||||||
Removing intermediate container 5cdffef8e33d
|
Removing intermediate container 5cdffef8e33d
|
||||||
Step 3/3 : WORKDIR "/go-rvpn-server"
|
Step 3/3 : WORKDIR "/telebit-relay"
|
||||||
---> 182aa9c814f2
|
---> 182aa9c814f2
|
||||||
Removing intermediate container f136550d6d48
|
Removing intermediate container f136550d6d48
|
||||||
Successfully built 182aa9c814f2
|
Successfully built 182aa9c814f2
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
FROM golang:1.7.5
|
FROM golang:1.14
|
||||||
LABEL maintainer "henry.f.camacho@gmail.com"
|
LABEL maintainer "aj@therootcompany.com"
|
||||||
|
|
||||||
|
|
||||||
WORKDIR "/go-rvpn-server"
|
WORKDIR "/telebit-relay"
|
||||||
|
|
|
@ -21,10 +21,11 @@ type Table struct {
|
||||||
domainRevoke chan *DomainMapping
|
domainRevoke chan *DomainMapping
|
||||||
dwell int
|
dwell int
|
||||||
idle int
|
idle int
|
||||||
|
balanceMethod string
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewTable -- consructor
|
//NewTable -- consructor
|
||||||
func NewTable(dwell, idle int) (p *Table) {
|
func NewTable(dwell, idle int, balanceMethod string) (p *Table) {
|
||||||
p = new(Table)
|
p = new(Table)
|
||||||
p.connections = make(map[*Connection][]string)
|
p.connections = make(map[*Connection][]string)
|
||||||
p.domains = make(map[string]*DomainLoadBalance)
|
p.domains = make(map[string]*DomainLoadBalance)
|
||||||
|
@ -34,6 +35,7 @@ func NewTable(dwell, idle int) (p *Table) {
|
||||||
p.domainRevoke = make(chan *DomainMapping)
|
p.domainRevoke = make(chan *DomainMapping)
|
||||||
p.dwell = dwell
|
p.dwell = dwell
|
||||||
p.idle = idle
|
p.idle = idle
|
||||||
|
p.balanceMethod = balanceMethod
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +90,7 @@ func (c *Table) GetConnection(serverID int64) (*Connection, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//Run -- Execute
|
//Run -- Execute
|
||||||
func (c *Table) Run(ctx context.Context, defaultMethod string) {
|
func (c *Table) Run(ctx context.Context) {
|
||||||
loginfo.Println("ConnectionTable starting")
|
loginfo.Println("ConnectionTable starting")
|
||||||
|
|
||||||
go c.reaper(c.dwell, c.idle)
|
go c.reaper(c.dwell, c.idle)
|
||||||
|
@ -122,7 +124,7 @@ func (c *Table) Run(ctx context.Context, defaultMethod string) {
|
||||||
c.domains[newDomain].AddConnection(connection)
|
c.domains[newDomain].AddConnection(connection)
|
||||||
} else {
|
} else {
|
||||||
//if not, then add as the 1st to the list of connections
|
//if not, then add as the 1st to the list of connections
|
||||||
c.domains[newDomain] = NewDomainLoadBalance(defaultMethod)
|
c.domains[newDomain] = NewDomainLoadBalance(c.balanceMethod)
|
||||||
c.domains[newDomain].AddConnection(connection)
|
c.domains[newDomain].AddConnection(connection)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -31,7 +32,7 @@ const (
|
||||||
|
|
||||||
//ctxConnectionTable contextKey = "connectionTable"
|
//ctxConnectionTable contextKey = "connectionTable"
|
||||||
|
|
||||||
ctxConfig contextKey = "config"
|
ctxConfig contextKey = "tlsConfig"
|
||||||
ctxListenerRegistration contextKey = "listenerRegistration"
|
ctxListenerRegistration contextKey = "listenerRegistration"
|
||||||
ctxConnectionTrack contextKey = "connectionTrack"
|
ctxConnectionTrack contextKey = "connectionTrack"
|
||||||
ctxWssHostName contextKey = "wsshostname"
|
ctxWssHostName contextKey = "wsshostname"
|
||||||
|
@ -40,21 +41,26 @@ const (
|
||||||
ctxLoadbalanceDefaultMethod contextKey = "lbdefaultmethod"
|
ctxLoadbalanceDefaultMethod contextKey = "lbdefaultmethod"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO isn't this restriction in the TLS lib?
|
||||||
|
// or are we just pre-checking for remote hosts?
|
||||||
|
type tlsScheme int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
encryptNone int = iota
|
encryptNone tlsScheme = iota
|
||||||
encryptSSLV2
|
encryptSSLV2
|
||||||
encryptSSLV3
|
encryptSSLV3
|
||||||
encryptTLS10
|
encryptTLS10
|
||||||
encryptTLS11
|
encryptTLS11
|
||||||
encryptTLS12
|
encryptTLS12
|
||||||
|
encryptTLS13
|
||||||
)
|
)
|
||||||
|
|
||||||
//GenericListenAndServe -- used to lisen for any https traffic on 443 (8443)
|
// multiListenAndServe -- used to lisen for any https traffic on 443 (8443)
|
||||||
// - setup generic TCP listener, unencrypted TCP, with a Deadtime out
|
// - setup generic TCP listener, unencrypted TCP, with a Deadtime out
|
||||||
// - leaverage the wedgeConn to peek into the buffer.
|
// - leaverage the wedgeConn to peek into the buffer.
|
||||||
// - if TLS, consume connection with TLS certbundle, pass to request identifier
|
// - if TLS, consume connection with TLS certbundle, pass to request identifier
|
||||||
// - else, just pass to the request identififer
|
// - else, just pass to the request identififer
|
||||||
func GenericListenAndServe(ctx context.Context, listenerRegistration *ListenerRegistration) {
|
func (mx *MPlexy) multiListenAndServe(ctx context.Context, listenerRegistration *ListenerRegistration) {
|
||||||
loginfo.Println(":" + string(listenerRegistration.port))
|
loginfo.Println(":" + string(listenerRegistration.port))
|
||||||
cancelCheck := ctx.Value(ctxCancelCheck).(int)
|
cancelCheck := ctx.Value(ctxCancelCheck).(int)
|
||||||
|
|
||||||
|
@ -95,18 +101,24 @@ func GenericListenAndServe(ctx context.Context, listenerRegistration *ListenerRe
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println("New connection from %v on %v", conn.LocalAddr(), conn.RemoteAddr())
|
||||||
|
|
||||||
|
// TODO maybe put these into something like mx.newConnCh and have an mx.Accept()?
|
||||||
wedgeConn := NewWedgeConn(conn)
|
wedgeConn := NewWedgeConn(conn)
|
||||||
go acceptTCPOrTLS(ctx, wedgeConn)
|
go mx.accept(ctx, wedgeConn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//acceptTCPOrTLS -
|
//accept -
|
||||||
// - accept a wedgeConnection along with all the other required attritvues
|
// - accept a wedgeConnection along with all the other required attritvues
|
||||||
// - peek into the buffer, determine TLS or unencrypted
|
// - peek into the buffer, determine TLS or unencrypted
|
||||||
// - if TSL, then terminate with a TLS endpoint, pass to handleStream
|
// - if TSL, then terminate with a TLS endpoint, pass to handleStream
|
||||||
// - if clearText, pass to handleStream
|
// - if clearText, pass to handleStream
|
||||||
func acceptTCPOrTLS(ctx context.Context, wConn *WedgeConn) {
|
func (mx *MPlexy) accept(ctx context.Context, wConn *WedgeConn) {
|
||||||
|
// TODO shouldn't this responsibility fall elsewhere?
|
||||||
|
// (otherwise I think we're keeping this function in memory while something else fails to end)
|
||||||
|
// (i.e. something, somewhere is missing a `go doStuff()`
|
||||||
defer wConn.Close()
|
defer wConn.Close()
|
||||||
peekCnt := 10
|
peekCnt := 10
|
||||||
|
|
||||||
|
@ -136,20 +148,24 @@ func acceptTCPOrTLS(ctx context.Context, wConn *WedgeConn) {
|
||||||
|
|
||||||
} else if bytes.Contains(peek[0:3], []byte{0x16, 0x03, 0x03}) {
|
} else if bytes.Contains(peek[0:3], []byte{0x16, 0x03, 0x03}) {
|
||||||
encryptMode = encryptTLS12
|
encryptMode = encryptTLS12
|
||||||
|
|
||||||
|
} else if bytes.Contains(peek[0:3], []byte{0x16, 0x03, 0x04}) {
|
||||||
|
encryptMode = encryptTLS13
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
oneConn := &oneConnListener{wConn}
|
oneConn := &oneConnListener{wConn}
|
||||||
config := ctx.Value(ctxConfig).(*tls.Config)
|
tlsConfig := ctx.Value(ctxConfig).(*tls.Config)
|
||||||
|
|
||||||
if encryptMode == encryptSSLV2 {
|
if encryptMode == encryptSSLV2 {
|
||||||
loginfo.Println("SSLv2 is not accepted")
|
loginfo.Println("<= SSLv2 is not accepted")
|
||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if encryptMode == encryptNone {
|
if encryptMode == encryptNone {
|
||||||
loginfo.Println("Handle Unencrypted")
|
loginfo.Println("Handle Unencrypted")
|
||||||
handleStream(ctx, wConn)
|
mx.handleStream(ctx, wConn)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,9 +193,10 @@ func acceptTCPOrTLS(ctx context.Context, wConn *WedgeConn) {
|
||||||
|
|
||||||
loginfo.Println("sni:", sniHostName)
|
loginfo.Println("sni:", sniHostName)
|
||||||
|
|
||||||
|
// This is where a target device connects to receive traffic
|
||||||
if sniHostName == wssHostName {
|
if sniHostName == wssHostName {
|
||||||
//handle WSS Path
|
//handle WSS Path
|
||||||
tlsListener := tls.NewListener(oneConn, config)
|
tlsListener := tls.NewListener(oneConn, tlsConfig)
|
||||||
|
|
||||||
conn, err := tlsListener.Accept()
|
conn, err := tlsListener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -188,30 +205,31 @@ func acceptTCPOrTLS(ctx context.Context, wConn *WedgeConn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsWedgeConn := NewWedgeConn(conn)
|
tlsWedgeConn := NewWedgeConn(conn)
|
||||||
handleStream(ctx, tlsWedgeConn)
|
mx.handleStream(ctx, tlsWedgeConn)
|
||||||
return
|
return
|
||||||
|
|
||||||
} else if sniHostName == adminHostName {
|
|
||||||
// handle admin path
|
|
||||||
tlsListener := tls.NewListener(oneConn, config)
|
|
||||||
|
|
||||||
conn, err := tlsListener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
loginfo.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsWedgeConn := NewWedgeConn(conn)
|
|
||||||
handleStream(ctx, tlsWedgeConn)
|
|
||||||
return
|
|
||||||
|
|
||||||
} else {
|
|
||||||
//traffic not terminating on the rvpn do not decrypt
|
|
||||||
loginfo.Println("processing non terminating traffic", wssHostName, sniHostName)
|
|
||||||
handleExternalHTTPRequest(ctx, wConn, sniHostName, "https")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
// This is where an admin of the relay manages it
|
||||||
|
if sniHostName == adminHostName {
|
||||||
|
// TODO mx.Admin.CheckRemoteIP(conn) here
|
||||||
|
|
||||||
|
// handle admin path
|
||||||
|
tlsListener := tls.NewListener(oneConn, tlsConfig)
|
||||||
|
|
||||||
|
conn, err := tlsListener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
loginfo.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsWedgeConn := NewWedgeConn(conn)
|
||||||
|
mx.handleStream(ctx, tlsWedgeConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//traffic not terminating on the rvpn do not decrypt
|
||||||
|
loginfo.Println("processing non terminating traffic", wssHostName, sniHostName)
|
||||||
|
handleExternalHTTPRequest(ctx, wConn, sniHostName, "https")
|
||||||
}
|
}
|
||||||
|
|
||||||
//handleStream --
|
//handleStream --
|
||||||
|
@ -222,7 +240,7 @@ func acceptTCPOrTLS(ctx context.Context, wConn *WedgeConn) {
|
||||||
// - attempt to identify as ADMIN/API session
|
// - attempt to identify as ADMIN/API session
|
||||||
// - else handle as raw http
|
// - else handle as raw http
|
||||||
// - handle other?
|
// - handle other?
|
||||||
func handleStream(ctx context.Context, wConn *WedgeConn) {
|
func (mx *MPlexy) handleStream(ctx context.Context, wConn *WedgeConn) {
|
||||||
loginfo.Println("handle Stream")
|
loginfo.Println("handle Stream")
|
||||||
loginfo.Println("conn", wConn.LocalAddr().String(), wConn.RemoteAddr().String())
|
loginfo.Println("conn", wConn.LocalAddr().String(), wConn.RemoteAddr().String())
|
||||||
|
|
||||||
|
@ -234,6 +252,9 @@ func handleStream(ctx context.Context, wConn *WedgeConn) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO handle by TCP port as well
|
||||||
|
// (which needs a short read timeout since servers expect clients to say hello)
|
||||||
|
|
||||||
// HTTP Identifcation // CRLF
|
// HTTP Identifcation // CRLF
|
||||||
if !bytes.Contains(peek[:], []byte{0x0d, 0x0a}) {
|
if !bytes.Contains(peek[:], []byte{0x0d, 0x0a}) {
|
||||||
return
|
return
|
||||||
|
@ -252,14 +273,11 @@ func handleStream(ctx context.Context, wConn *WedgeConn) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// do we have a valid wss_client?
|
// TODO add newtypes
|
||||||
secretKey := ctx.Value(ctxSecretKey).(string)
|
// TODO check if this is a websocket
|
||||||
tokenString := r.URL.Query().Get("access_token")
|
_, err = mx.authorize(r)
|
||||||
result, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
|
||||||
return []byte(secretKey), nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err == nil && result.Valid {
|
if err == nil {
|
||||||
loginfo.Println("Valid WSS dected...sending to handler")
|
loginfo.Println("Valid WSS dected...sending to handler")
|
||||||
oneConn := &oneConnListener{wConn}
|
oneConn := &oneConnListener{wConn}
|
||||||
handleWssClient(ctx, oneConn)
|
handleWssClient(ctx, oneConn)
|
||||||
|
@ -268,6 +286,7 @@ func handleStream(ctx context.Context, wConn *WedgeConn) {
|
||||||
//if yes, prep the oneConn and send it to the handler
|
//if yes, prep the oneConn and send it to the handler
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.Contains(r.Host, telebit.InvalidAdminDomain) {
|
if strings.Contains(r.Host, telebit.InvalidAdminDomain) {
|
||||||
loginfo.Println("admin")
|
loginfo.Println("admin")
|
||||||
oneConn := &oneConnListener{wConn}
|
oneConn := &oneConnListener{wConn}
|
||||||
|
|
|
@ -4,11 +4,28 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
//ListenerRegistrationStatus - post registration status
|
//ListenerRegistrationStatus - post registration status
|
||||||
type ListenerRegistrationStatus int
|
type ListenerRegistrationStatus int
|
||||||
|
|
||||||
|
// Authz represents grants or privileges of a client
|
||||||
|
// clientID
|
||||||
|
// domains that may be forwarded
|
||||||
|
// # of domains that may be forwarded
|
||||||
|
// ports that may be forwarded (i.e. allow special ports < 1024, exclude 443, 25, etc)
|
||||||
|
// # of ports that may be forwarded
|
||||||
|
// # of concurrent conections
|
||||||
|
// # bandwith rate (i.e. 5 mbps)
|
||||||
|
// # bandwith cap per time period (i.e. 100 MB / hour)
|
||||||
|
// # throttled rate (i.e. 0 (kill), or 1 mbps)
|
||||||
|
type Authz struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authorizer is called when a new client connects and we need to know something about it
|
||||||
|
type Authorizer func(*http.Request) (*Authz, error)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
listenerAdded ListenerRegistrationStatus = iota
|
listenerAdded ListenerRegistrationStatus = iota
|
||||||
listenerExists
|
listenerExists
|
||||||
|
@ -40,99 +57,107 @@ func NewListenerRegistration(port int) (p *ListenerRegistration) {
|
||||||
p = new(ListenerRegistration)
|
p = new(ListenerRegistration)
|
||||||
p.port = port
|
p.port = port
|
||||||
p.commCh = make(chan *ListenerRegistration)
|
p.commCh = make(chan *ListenerRegistration)
|
||||||
return
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// Servers -
|
// MPlexy -
|
||||||
type Servers struct {
|
type MPlexy struct {
|
||||||
listeners map[*net.Listener]int
|
listeners map[*net.Listener]int
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
connnectionTable *Table
|
connnectionTable *Table
|
||||||
connectionTracking *Tracking
|
connectionTracking *Tracking
|
||||||
secretKey string
|
authorize Authorizer
|
||||||
certbundle tls.Certificate
|
tlsConfig *tls.Config
|
||||||
register chan *ListenerRegistration
|
register chan *ListenerRegistration
|
||||||
servers *Servers
|
|
||||||
wssHostName string
|
wssHostName string
|
||||||
adminHostName string
|
adminHostName string
|
||||||
cancelCheck int
|
cancelCheck int
|
||||||
lbDefaultMethod string
|
lbDefaultMethod string
|
||||||
serverStatus *Status
|
serverStatus *Status
|
||||||
|
//xservers *MPlexy
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGenerListeners creates tcp (and https and wss?) listeners
|
// New creates tcp (and https and wss?) listeners
|
||||||
func NewGenerListeners(ctx context.Context, secretKey string, certbundle tls.Certificate, serverStatus *Status) (p *Servers) {
|
func New(ctx context.Context, tlsConfig *tls.Config, authz Authorizer, serverStatus *Status) (mx *MPlexy) {
|
||||||
p = &Servers{}
|
mx = &MPlexy{
|
||||||
p.listeners = make(map[*net.Listener]int)
|
listeners: make(map[*net.Listener]int),
|
||||||
p.ctx = ctx
|
ctx: ctx,
|
||||||
p.connnectionTable = serverStatus.ConnectionTable
|
connnectionTable: serverStatus.ConnectionTable,
|
||||||
p.connectionTracking = serverStatus.ConnectionTracking
|
connectionTracking: serverStatus.ConnectionTracking,
|
||||||
p.secretKey = secretKey
|
authorize: authz,
|
||||||
p.certbundle = certbundle
|
tlsConfig: tlsConfig,
|
||||||
p.register = make(chan *ListenerRegistration)
|
register: make(chan *ListenerRegistration),
|
||||||
p.wssHostName = serverStatus.WssDomain
|
wssHostName: serverStatus.WssDomain,
|
||||||
p.adminHostName = serverStatus.AdminDomain
|
adminHostName: serverStatus.AdminDomain,
|
||||||
p.cancelCheck = serverStatus.DeadTime.cancelcheck
|
cancelCheck: serverStatus.DeadTime.cancelcheck,
|
||||||
p.lbDefaultMethod = serverStatus.LoadbalanceDefaultMethod
|
lbDefaultMethod: serverStatus.LoadbalanceDefaultMethod,
|
||||||
p.serverStatus = serverStatus
|
serverStatus: serverStatus,
|
||||||
return
|
}
|
||||||
|
return mx
|
||||||
}
|
}
|
||||||
|
|
||||||
//Run -- Execute
|
//Run -- Execute
|
||||||
// - execute the GenericLister
|
// - execute the GenericLister
|
||||||
// - pass initial port, we'll announce that
|
// - pass initial port, we'll announce that
|
||||||
func (gl *Servers) Run(ctx context.Context, initialPort int) {
|
func (mx *MPlexy) Run() error {
|
||||||
loginfo.Println("ConnectionTable starting")
|
loginfo.Println("ConnectionTable starting")
|
||||||
|
|
||||||
config := &tls.Config{Certificates: []tls.Certificate{gl.certbundle}}
|
loginfo.Println(mx.connectionTracking)
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, ctxSecretKey, gl.secretKey)
|
ctx := mx.ctx
|
||||||
|
|
||||||
loginfo.Println(gl.connectionTracking)
|
// For just this bit
|
||||||
|
ctx = context.WithValue(ctx, ctxConnectionTrack, mx.connectionTracking)
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, ctxConnectionTrack, gl.connectionTracking)
|
// For all Listeners
|
||||||
ctx = context.WithValue(ctx, ctxConfig, config)
|
ctx = context.WithValue(ctx, ctxConfig, mx.tlsConfig)
|
||||||
ctx = context.WithValue(ctx, ctxListenerRegistration, gl.register)
|
ctx = context.WithValue(ctx, ctxListenerRegistration, mx.register)
|
||||||
ctx = context.WithValue(ctx, ctxWssHostName, gl.wssHostName)
|
ctx = context.WithValue(ctx, ctxWssHostName, mx.wssHostName)
|
||||||
ctx = context.WithValue(ctx, ctxAdminHostName, gl.adminHostName)
|
ctx = context.WithValue(ctx, ctxAdminHostName, mx.adminHostName)
|
||||||
ctx = context.WithValue(ctx, ctxCancelCheck, gl.cancelCheck)
|
ctx = context.WithValue(ctx, ctxCancelCheck, mx.cancelCheck)
|
||||||
ctx = context.WithValue(ctx, ctxLoadbalanceDefaultMethod, gl.lbDefaultMethod)
|
ctx = context.WithValue(ctx, ctxLoadbalanceDefaultMethod, mx.lbDefaultMethod)
|
||||||
ctx = context.WithValue(ctx, ctxServerStatus, gl.serverStatus)
|
ctx = context.WithValue(ctx, ctxServerStatus, mx.serverStatus)
|
||||||
|
|
||||||
go func(ctx context.Context) {
|
for {
|
||||||
for {
|
select {
|
||||||
select {
|
|
||||||
|
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
loginfo.Println("Cancel signal hit")
|
loginfo.Println("Cancel signal hit")
|
||||||
return
|
return nil
|
||||||
|
|
||||||
case registration := <-gl.register:
|
case registration := <-mx.register:
|
||||||
loginfo.Println("register fired", registration.port)
|
loginfo.Println("register fired", registration.port)
|
||||||
|
|
||||||
// check to see if port is already running
|
// check to see if port is already running
|
||||||
for listener := range gl.listeners {
|
for listener := range mx.listeners {
|
||||||
if gl.listeners[listener] == registration.port {
|
if mx.listeners[listener] == registration.port {
|
||||||
loginfo.Println("listener already running", registration.port)
|
loginfo.Println("listener already running", registration.port)
|
||||||
registration.status = listenerExists
|
registration.status = listenerExists
|
||||||
registration.commCh <- registration
|
registration.commCh <- registration
|
||||||
}
|
|
||||||
}
|
|
||||||
loginfo.Println("listener starting up ", registration.port)
|
|
||||||
loginfo.Println(ctx.Value(ctxConnectionTrack).(*Tracking))
|
|
||||||
go GenericListenAndServe(ctx, registration)
|
|
||||||
|
|
||||||
status := <-registration.commCh
|
|
||||||
if status.status == listenerAdded {
|
|
||||||
gl.listeners[status.listener] = status.port
|
|
||||||
} else if status.status == listenerFault {
|
|
||||||
loginfo.Println("Unable to create a new listerer", registration.port)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
loginfo.Println("listener starting up ", registration.port)
|
||||||
|
loginfo.Println(ctx.Value(ctxConnectionTrack).(*Tracking))
|
||||||
|
go mx.multiListenAndServe(ctx, registration)
|
||||||
|
|
||||||
|
status := <-registration.commCh
|
||||||
|
if status.status == listenerAdded {
|
||||||
|
mx.listeners[status.listener] = status.port
|
||||||
|
} else if status.status == listenerFault {
|
||||||
|
loginfo.Println("Unable to create a new listerer", registration.port)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}(ctx)
|
return nil
|
||||||
|
}
|
||||||
newListener := NewListenerRegistration(initialPort)
|
|
||||||
gl.register <- newListener
|
func (mx *MPlexy) Start() {
|
||||||
|
go mx.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiListenAndServe starts another listener (to the same application) on a new port
|
||||||
|
func (mx *MPlexy) MultiListenAndServe(port int) {
|
||||||
|
// TODO how to associate a listening device with a given plain port
|
||||||
|
mx.register <- NewListenerRegistration(port)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,13 @@ type Status struct {
|
||||||
DeadTime *StatusDeadTime
|
DeadTime *StatusDeadTime
|
||||||
ConnectionTracking *Tracking
|
ConnectionTracking *Tracking
|
||||||
ConnectionTable *Table
|
ConnectionTable *Table
|
||||||
servers *Servers
|
|
||||||
LoadbalanceDefaultMethod string
|
LoadbalanceDefaultMethod string
|
||||||
AdminStats *TrafficStats
|
AdminStats *TrafficStats
|
||||||
AdminReqTyoe *AdminReqType
|
AdminReqTyoe *AdminReqType
|
||||||
TrafficStats *TrafficStats
|
TrafficStats *TrafficStats
|
||||||
ExtConnections *ConnectionStats
|
ExtConnections *ConnectionStats
|
||||||
WSSConnections *ConnectionStats
|
WSSConnections *ConnectionStats
|
||||||
|
//servers *MPlexy
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewStatus --
|
//NewStatus --
|
||||||
|
@ -32,7 +32,9 @@ func NewStatus(ctx context.Context) (p *Status) {
|
||||||
p.TrafficStats = new(TrafficStats)
|
p.TrafficStats = new(TrafficStats)
|
||||||
p.ExtConnections = new(ConnectionStats)
|
p.ExtConnections = new(ConnectionStats)
|
||||||
p.WSSConnections = new(ConnectionStats)
|
p.WSSConnections = new(ConnectionStats)
|
||||||
return
|
// TODO any reason not to set StartTime like this?
|
||||||
|
p.StartTime = time.Now()
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// South Facing Functions
|
// South Facing Functions
|
||||||
|
|
|
@ -2,4 +2,7 @@ package telebit
|
||||||
|
|
||||||
// InvalidAdminDomain is a domain that can only be accessed by Domain Fronting
|
// InvalidAdminDomain is a domain that can only be accessed by Domain Fronting
|
||||||
// (i.e. trixy clients sending fake headers), not browsers
|
// (i.e. trixy clients sending fake headers), not browsers
|
||||||
var InvalidAdminDomain = "rvpn.rootprojects.invalid"
|
var InvalidAdminDomain = "chilly-bobcat-15.telebit.io"
|
||||||
|
|
||||||
|
//var InvalidAdminDomain = "invalid.rootprojects.org"
|
||||||
|
//var InvalidAdminDomain = "rvpn.rootprojects.invalid"
|
||||||
|
|
Loading…
Reference in New Issue