package main import ( "context" "crypto/tls" "flag" "fmt" "io" "io/ioutil" "log" "net/http" "os" 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" lumberjack "gopkg.in/natefinch/lumberjack.v2" _ "github.com/joho/godotenv/autoload" ) var ( logfile = "stdout" configPath = "./" configFile = "telebit-relay" loginfo *log.Logger logdebug *log.Logger logFlags = log.Ldate | log.Lmicroseconds | log.Lshortfile argWssClientListener string tcpPort int argServerBinding string argServerAdminBinding string argServerExternalBinding string argDeadTime int connectionTable *server.Table secretKey string wssHostName = "localhost.rootprojects.org" adminHostName = telebit.InvalidAdminDomain 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 server.InitLogging(logoutput) loginfo = log.New(logoutput, "INFO: main: ", logFlags) logdebug = log.New(logoutput, "DEBUG: main:", logFlags) 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 := server.NewStatus(ctx) serverStatus.AdminDomain = adminHostName serverStatus.WssDomain = wssHostName serverStatus.Name = nickname serverStatus.DeadTime = server.NewStatusDeadTime(dwell, idle, cancelcheck) serverStatus.LoadbalanceDefaultMethod = lbDefaultMethod connectionTable := server.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) (*server.Authz, error) { // do we have a valid wss_client? tokenString := r.URL.Query().Get("access_token") _, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { return []byte(secretKey), nil }) return nil, 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) }