bugfix relay and update client

This commit is contained in:
AJ ONeal 2020-05-05 00:44:21 -06:00
parent 7fdb393696
commit 5a68908e66
8 changed files with 134 additions and 63 deletions

View File

@ -16,7 +16,7 @@ type Config struct {
Server string Server string
Token string Token string
Insecure bool Insecure bool
Services map[string]map[string]int Services RouteMap
} }
// Run establishes a connection with the RVPN server specified in the config. If the first attempt // Run establishes a connection with the RVPN server specified in the config. If the first attempt

21
client/router.go Normal file
View File

@ -0,0 +1,21 @@
package client
// SchemeName is an alias for string (for readability)
type SchemeName = string
// DomainName is an alias for string (for readability)
type DomainName = string
// TerminalConfig indicates destination
type TerminalConfig struct {
// The localhost port to which to forward
Port int
// Whether or not to unwap the TLS
TerminateTLS bool
//Hostname string
XForward bool
// ... create react app...
}
// RouteMap is a map of scheme to domain to port
type RouteMap = map[SchemeName]map[DomainName]*TerminalConfig

View File

@ -24,7 +24,7 @@ type WsHandler struct {
lock sync.Mutex lock sync.Mutex
localConns map[string]net.Conn localConns map[string]net.Conn
servicePorts map[string]map[string]int servicePorts RouteMap
ctx context.Context ctx context.Context
dataChan chan *packer.Packer dataChan chan *packer.Packer
@ -32,7 +32,7 @@ type WsHandler struct {
// NewWsHandler creates a new handler ready to be given a websocket connection. The services // NewWsHandler creates a new handler ready to be given a websocket connection. The services
// argument specifies what port each service type should be directed to on the local interface. // argument specifies what port each service type should be directed to on the local interface.
func NewWsHandler(services map[string]map[string]int) *WsHandler { func NewWsHandler(services RouteMap) *WsHandler {
h := new(WsHandler) h := new(WsHandler)
h.servicePorts = services h.servicePorts = services
h.localConns = make(map[string]net.Conn) h.localConns = make(map[string]net.Conn)
@ -142,6 +142,7 @@ func (h *WsHandler) getLocalConn(p *packer.Packer) net.Conn {
if service == "http" { if service == "http" {
if match := hostRegexp.FindSubmatch(p.Data.Data()); match != nil { if match := hostRegexp.FindSubmatch(p.Data.Data()); match != nil {
hostname = strings.Split(string(match[1]), ":")[0] hostname = strings.Split(string(match[1]), ":")[0]
// TODO remove Hostname
} }
} else if service == "https" { } else if service == "https" {
hostname, _ = sni.GetHostname(p.Data.Data()) hostname, _ = sni.GetHostname(p.Data.Data())
@ -154,23 +155,29 @@ func (h *WsHandler) getLocalConn(p *packer.Packer) net.Conn {
} }
hostname = strings.ToLower(hostname) hostname = strings.ToLower(hostname)
port := portList[hostname] term := portList[hostname]
if port == 0 { fmt.Println("route to", hostname, term)
port = portList["*"] if term == nil {
portList[hostname] = portList["*"]
term = portList[hostname]
} }
if port == 0 { if term.Port == 0 {
portList[hostname] = portList["*"]
}
if term.Port == 0 {
loginfo.Println("unable to determine local port for", service, hostname) loginfo.Println("unable to determine local port for", service, hostname)
return nil return nil
} }
conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port)) // TODO allow jumping
conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", term.Port))
if err != nil { if err != nil {
loginfo.Println("unable to open local connection on port", port, err) loginfo.Println("unable to open local connection on port", term.Port, err)
return nil return nil
} }
h.localConns[key] = conn h.localConns[key] = conn
loginfo.Printf("new client %q for %s:%d (%d clients)\n", key, hostname, port, len(h.localConns)) loginfo.Printf("new client %q for %s:%d (%d clients)\n", key, hostname, term.Port, len(h.localConns))
go h.readLocal(key, &p.Header) go h.readLocal(key, &p.Header)
return conn return conn
} }

View File

@ -220,17 +220,21 @@ func main() {
tokenString = r.URL.Query().Get("access_token") tokenString = r.URL.Query().Get("access_token")
} }
_, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { tok, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte(secretKey), nil return []byte(secretKey), nil
}) })
if nil != err { if nil != err {
fmt.Println("return an error, do not go on")
return nil, err return nil, err
} }
fmt.Printf("client claims:\n%+v\n", tok.Claims)
domains := []string{}
for _, name := range tok.Claims.(jwt.MapClaims)["domains"].([]interface{}) {
domains = append(domains, name.(string))
}
authz := &mplexy.Authz{ authz := &mplexy.Authz{
Domains: []string{ Domains: domains,
"target.rootprojects.org",
},
} }
return authz, err return authz, err

View File

@ -2,36 +2,39 @@ package main
import ( import (
"context" "context"
"flag"
"fmt" "fmt"
"log"
"os"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
jwt "github.com/dgrijalva/jwt-go"
flag "github.com/spf13/pflag"
"github.com/spf13/viper"
"git.coolaj86.com/coolaj86/go-telebitd/client" "git.coolaj86.com/coolaj86/go-telebitd/client"
jwt "github.com/dgrijalva/jwt-go"
_ "github.com/joho/godotenv/autoload"
) )
var httpRegexp = regexp.MustCompile(`(?i)^http`) var httpRegexp = regexp.MustCompile(`(?i)^http`)
var locals string
var domains string
var insecure bool
var relay string
var secret string
var token string
func init() { func init() {
flag.StringSlice("locals", []string{}, "comma separated list of <proto>:<port> or "+ flag.StringVar(&locals, "locals", "", "comma separated list of <proto>:<port> or "+
"<proto>:<hostname>:<port> to which matching incoming connections should forward. "+ "<proto>:<hostname>:<port> to which matching incoming connections should forward. "+
"Ex: smtps:8465,https:example.com:8443") "Ex: smtps:8465,https:example.com:8443")
flag.StringSlice("domains", []string{}, "comma separated list of domain names to set to the tunnel") flag.StringVar(&domains, "domains", "", "comma separated list of domain names to set to the tunnel")
viper.BindPFlag("locals", flag.Lookup("locals")) flag.BoolVar(&insecure, "insecure", false, "Allow TLS connections to telebit-relay without valid certs")
viper.BindPFlag("domains", flag.Lookup("domains")) flag.BoolVar(&insecure, "k", false, "alias of --insecure")
flag.StringVar(&relay, "relay", "", "the domain (or ip address) at which the relay server is running")
flag.BoolP("insecure", "k", false, "Allow TLS connections to telebit-relay without valid certs") flag.StringVar(&secret, "secret", "", "the same secret used by telebit-relay (used for JWT authentication)")
flag.String("relay", "", "the domain (or ip address) at which the RVPN server is running") flag.StringVar(&token, "token", "", "a pre-generated token to give the server (instead of generating one with --secret)")
flag.String("secret", "", "the same secret used by telebit-relay (used for JWT authentication)")
flag.String("token", "", "a pre-generated token to give the server (instead of generating one with --secret)")
viper.BindPFlag("raw.insecure", flag.Lookup("insecure"))
viper.BindPFlag("raw.relay", flag.Lookup("relay"))
viper.BindPFlag("raw.secret", flag.Lookup("secret"))
viper.BindPFlag("raw.token", flag.Lookup("token"))
} }
type proxy struct { type proxy struct {
@ -139,14 +142,14 @@ func addDomains(proxies []proxy, location string) ([]proxy, error) {
return proxies, nil return proxies, nil
} }
func extractServicePorts(proxies []proxy) map[string]map[string]int { func extractServicePorts(proxies []proxy) client.RouteMap {
result := make(map[string]map[string]int, 2) result := make(client.RouteMap, 2)
for _, p := range proxies { for _, p := range proxies {
if p.protocol != "" && p.port != 0 { if p.protocol != "" && p.port != 0 {
hostPorts := result[p.protocol] hostPorts := result[p.protocol]
if hostPorts == nil { if hostPorts == nil {
result[p.protocol] = make(map[string]int) result[p.protocol] = make(map[client.DomainName]*client.TerminalConfig)
hostPorts = result[p.protocol] hostPorts = result[p.protocol]
} }
@ -155,25 +158,33 @@ func extractServicePorts(proxies []proxy) map[string]map[string]int {
if !httpRegexp.MatchString(p.protocol) || p.hostname == "" { if !httpRegexp.MatchString(p.protocol) || p.hostname == "" {
p.hostname = "*" p.hostname = "*"
} }
if port, ok := hostPorts[p.hostname]; ok && port != p.port { if port, ok := hostPorts[p.hostname]; ok && port.Port != p.port {
panic(fmt.Sprintf("duplicate ports for %s://%s", p.protocol, p.hostname)) panic(fmt.Sprintf("duplicate ports for %s://%s", p.protocol, p.hostname))
} }
hostPorts[p.hostname] = p.port hostPorts[p.hostname] = &client.TerminalConfig{
Port: p.port,
}
} }
} }
// Make sure we have defaults for HTTPS and HTTP. // Make sure we have defaults for HTTPS and HTTP.
if result["https"] == nil { if result["https"] == nil {
result["https"] = make(map[string]int, 1) result["https"] = make(map[client.DomainName]*client.TerminalConfig, 1)
} }
if result["https"]["*"] == 0 { if result["https"]["*"] == nil {
result["https"]["*"] = 8443 result["https"]["*"] = &client.TerminalConfig{}
}
if result["https"]["*"].Port == 0 {
result["https"]["*"].Port = 8443
} }
if result["http"] == nil { if result["http"] == nil {
result["http"] = make(map[string]int, 1) result["http"] = make(map[client.DomainName]*client.TerminalConfig, 1)
} }
if result["http"]["*"] == 0 { if result["http"]["*"] == nil {
result["http"]["*"] = &client.TerminalConfig{}
}
if result["http"]["*"].Port == 0 {
result["http"]["*"] = result["https"]["*"] result["http"]["*"] = result["https"]["*"]
} }
@ -184,8 +195,13 @@ func main() {
flag.Parse() flag.Parse()
var err error var err error
if "" == locals {
locals = os.Getenv("LOCALS")
}
proxies := make([]proxy, 0) proxies := make([]proxy, 0)
for _, option := range viper.GetStringSlice("locals") { for _, option := range stringSlice(locals) {
for _, location := range strings.Split(option, ",") { for _, location := range strings.Split(option, ",") {
//fmt.Println("locals", location) //fmt.Println("locals", location)
proxies, err = addLocals(proxies, location) proxies, err = addLocals(proxies, location)
@ -194,9 +210,10 @@ func main() {
} }
} }
} }
//fmt.Println("proxies:") //fmt.Println("proxies:")
//fmt.Printf("%+v\n\n", proxies) //fmt.Printf("%+v\n\n", proxies)
for _, option := range viper.GetStringSlice("domains") { for _, option := range stringSlice(domains) {
for _, location := range strings.Split(option, ",") { for _, location := range strings.Split(option, ",") {
proxies, err = addDomains(proxies, location) proxies, err = addDomains(proxies, location)
if nil != err { if nil != err {
@ -213,39 +230,58 @@ func main() {
} }
} }
if viper.GetString("raw.relay") == "" { if relay == "" {
panic("must provide remote RVPN server to connect to") relay = os.Getenv("RELAY")
}
if relay == "" {
fmt.Fprintf(os.Stderr, "must provide remote relay server to connect to\n")
os.Exit(1)
} }
var token string if secret == "" {
if viper.GetString("raw.token") != "" { secret = os.Getenv("SECRET")
token = viper.GetString("raw.token") }
} else if viper.GetString("raw.secret") != "" {
if secret != "" {
domains := make([]string, 0, len(domainMap)) domains := make([]string, 0, len(domainMap))
for name := range domainMap { for name := range domainMap {
domains = append(domains, name) domains = append(domains, name)
} }
tokenData := jwt.MapClaims{"domains": domains} tokenData := jwt.MapClaims{"domains": domains}
secret := []byte(viper.GetString("raw.secret")) secret := []byte(secret)
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, tokenData) jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, tokenData)
if tokenStr, err := jwtToken.SignedString(secret); err != nil { if tokenStr, err := jwtToken.SignedString(secret); err != nil {
panic(err) panic(err)
} else { } else {
token = tokenStr token = tokenStr
} }
} else { } else if token != "" {
panic("must provide either token or secret") fmt.Fprintf(os.Stderr, "must provide either token or secret\n")
os.Exit(1)
} }
ctx, quit := context.WithCancel(context.Background()) ctx, quit := context.WithCancel(context.Background())
defer quit() defer quit()
config := client.Config{ config := client.Config{
Insecure: viper.GetBool("raw.insecure"), Insecure: insecure,
Server: viper.GetString("raw.relay"), Server: relay,
Services: servicePorts, Services: servicePorts,
Token: token, Token: token,
} }
panic(client.Run(ctx, &config))
fmt.Printf("config:\n%#v\n", config)
log.Fatal(client.Run(ctx, &config))
}
func stringSlice(csv string) []string {
list := []string{}
for _, item := range strings.Split(csv, ", ") {
if 0 == len(item) {
continue
}
list = append(list, item)
}
return list
} }

2
examples/client.env Normal file
View File

@ -0,0 +1,2 @@
SECRET=xxxxyyyyssss8347
RELAY=wss://example.com:8443

View File

@ -259,17 +259,18 @@ func (mx *MPlexy) acceptPlainStream(ctx context.Context, wConn *tunnel.WedgeConn
} }
if hostname == mx.adminHostName { if hostname == mx.wssHostName &&
loginfo.Println("admin") ("Upgrade" == r.Header.Get("Connection") || "WebSocket" == r.Header.Get("Upgrade")) {
// TODO mx.Admin.CheckRemoteIP(conn) here loginfo.Println("WebSocket Upgrade is in order...")
// handle admin path mx.AcceptTargetServer(wConn)
mx.AcceptAdminClient(wConn)
return return
} }
if "Upgrade" == r.Header.Get("Connection") || "WebSocket" == r.Header.Get("Upgrade") { if hostname == mx.adminHostName {
loginfo.Println("WebSocket Upgrade is in order...") loginfo.Println("matched admin hostname")
mx.AcceptTargetServer(wConn) // TODO mx.Admin.CheckRemoteIP(conn) here
// handle admin path
mx.AcceptAdminClient(wConn)
return return
} }