191 lines
4.8 KiB
Go
191 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
golog "log"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"git.coolaj86.com/coolaj86/go-telebitd/log"
|
|
"git.coolaj86.com/coolaj86/go-telebitd/relay"
|
|
"git.coolaj86.com/coolaj86/go-telebitd/relay/api"
|
|
"git.coolaj86.com/coolaj86/go-telebitd/relay/mplexy"
|
|
|
|
jwt "github.com/dgrijalva/jwt-go"
|
|
"github.com/spf13/viper"
|
|
lumberjack "gopkg.in/natefinch/lumberjack.v2"
|
|
|
|
_ "github.com/joho/godotenv/autoload"
|
|
)
|
|
|
|
var Loginfo = log.Loginfo
|
|
var Logdebug = log.Logdebug
|
|
|
|
func init() {
|
|
log.LogFlags = golog.Ldate | golog.Lmicroseconds | golog.Lshortfile
|
|
}
|
|
|
|
var (
|
|
logfile = "stdout"
|
|
configPath = "./"
|
|
configFile = "telebit-relay"
|
|
|
|
argWssClientListener string
|
|
tcpPort int
|
|
argServerBinding string
|
|
argServerAdminBinding string
|
|
argServerExternalBinding string
|
|
argDeadTime int
|
|
connectionTable *api.Table
|
|
secretKey string
|
|
wssHostName = "localhost.rootprojects.org"
|
|
adminHostName string
|
|
idle int
|
|
dwell int
|
|
cancelcheck int
|
|
lbDefaultMethod string
|
|
nickname string
|
|
)
|
|
|
|
func init() {
|
|
flag.StringVar(&logfile, "log", logfile, "Log file (or stdout/stderr; empty for none)")
|
|
flag.StringVar(&configPath, "config-path", configPath, "Configuration File Path")
|
|
flag.StringVar(&secretKey, "secret", "", "a >= 16-character random string for JWT key signing")
|
|
}
|
|
|
|
var logoutput io.Writer
|
|
|
|
//Main -- main entry point
|
|
func main() {
|
|
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 {
|
|
case "stdout":
|
|
logoutput = os.Stdout
|
|
case "stderr":
|
|
logoutput = os.Stderr
|
|
case "":
|
|
logoutput = ioutil.Discard
|
|
default:
|
|
logoutput = &lumberjack.Logger{
|
|
Filename: logfile,
|
|
MaxSize: 100,
|
|
MaxAge: 120,
|
|
MaxBackups: 10,
|
|
}
|
|
}
|
|
|
|
// send the output io.Writing to the other packages
|
|
log.InitLogging(logoutput)
|
|
|
|
viper.SetConfigName(configFile)
|
|
viper.AddConfigPath(configPath)
|
|
viper.AddConfigPath("./")
|
|
err := viper.ReadInConfig()
|
|
if err != nil {
|
|
panic(fmt.Errorf("fatal error config file: %s", err))
|
|
}
|
|
|
|
flag.IntVar(&argDeadTime, "dead-time-counter", 5, "deadtime counter in seconds")
|
|
|
|
wssHostName = viper.Get("rvpn.wssdomain").(string)
|
|
adminHostName = viper.Get("rvpn.admindomain").(string)
|
|
tcpPort = viper.GetInt("rvpn.port")
|
|
deadtime := viper.Get("rvpn.deadtime").(map[string]interface{})
|
|
idle = deadtime["idle"].(int)
|
|
dwell = deadtime["dwell"].(int)
|
|
cancelcheck = deadtime["cancelcheck"].(int)
|
|
lbDefaultMethod = viper.Get("rvpn.loadbalancing.defaultmethod").(string)
|
|
nickname = viper.Get("rvpn.serverName").(string)
|
|
|
|
Loginfo.Println("startup")
|
|
|
|
ctx, cancelContext := context.WithCancel(context.Background())
|
|
defer cancelContext()
|
|
|
|
serverStatus := api.NewStatus(ctx)
|
|
serverStatus.AdminDomain = adminHostName
|
|
serverStatus.WssDomain = wssHostName
|
|
serverStatus.Name = nickname
|
|
serverStatus.DeadTime = api.NewStatusDeadTime(dwell, idle, cancelcheck)
|
|
serverStatus.LoadbalanceDefaultMethod = lbDefaultMethod
|
|
|
|
connectionTable := api.NewTable(dwell, idle, lbDefaultMethod)
|
|
|
|
tlsConfig := &tls.Config{
|
|
GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
|
|
// 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
|
|
},
|
|
}
|
|
|
|
authorizer := func(r *http.Request) (*mplexy.Authz, error) {
|
|
// do we have a valid wss_client?
|
|
|
|
var tokenString string
|
|
if auth := strings.Split(r.Header.Get("Authorization"), " "); len(auth) > 1 {
|
|
// TODO handle Basic auth tokens as well
|
|
tokenString = auth[1]
|
|
}
|
|
if "" == tokenString {
|
|
tokenString = r.URL.Query().Get("access_token")
|
|
}
|
|
|
|
_, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
|
return []byte(secretKey), nil
|
|
})
|
|
if nil != err {
|
|
return nil, err
|
|
}
|
|
|
|
authz := &mplexy.Authz{
|
|
Domains: []string{
|
|
"target.rootprojects.org",
|
|
},
|
|
}
|
|
return authz, err
|
|
|
|
/*
|
|
tokenString := r.URL.Query().Get("access_token")
|
|
result, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
|
return []byte(secretKey), nil
|
|
})
|
|
|
|
if err != nil || !result.Valid {
|
|
w.WriteHeader(http.StatusForbidden)
|
|
w.Write([]byte("Not Authorized"))
|
|
Loginfo.Println("access_token invalid...closing connection")
|
|
return
|
|
}
|
|
|
|
// TODO
|
|
claims := result.Claims.(jwt.MapClaims)
|
|
domains, ok := claims["domains"].([]interface{})
|
|
*/
|
|
}
|
|
|
|
r := relay.New(ctx, tlsConfig, authorizer, serverStatus, connectionTable)
|
|
r.ListenAndServe(tcpPort)
|
|
}
|