bugfix relay and update client
This commit is contained in:
parent
7fdb393696
commit
5a68908e66
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
SECRET=xxxxyyyyssss8347
|
||||||
|
RELAY=wss://example.com:8443
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue