add wiretrustee LOGIN command (#90)

* feature: add wiretrustee LOGIN command

* chore: add management initial connection timeout

* test: add login cmd test

* test: validate generated config in login cmd

* test: add up command test

* chore: add timeout to signal client creation method

* test: close wireguard interface once test finished
This commit is contained in:
Mikhail Bragin 2021-08-18 13:35:42 +02:00 committed by GitHub
parent f7e51e7453
commit 1dfa99d07c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 557 additions and 244 deletions

152
client/cmd/login.go Normal file
View File

@ -0,0 +1,152 @@
package cmd
import (
"bufio"
"context"
"fmt"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/wiretrustee/wiretrustee/client/internal"
mgm "github.com/wiretrustee/wiretrustee/management/client"
mgmProto "github.com/wiretrustee/wiretrustee/management/proto"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"os"
)
var (
setupKey string
loginCmd = &cobra.Command{
Use: "login",
Short: "login to the Wiretrustee Management Service (first run)",
RunE: func(cmd *cobra.Command, args []string) error {
InitLog(logLevel)
config, err := internal.GetConfig(managementURL, configPath)
if err != nil {
log.Errorf("failed getting config %s %v", configPath, err)
//os.Exit(ExitSetupFailed)
return err
}
//validate our peer's Wireguard PRIVATE key
myPrivateKey, err := wgtypes.ParseKey(config.PrivateKey)
if err != nil {
log.Errorf("failed parsing Wireguard key %s: [%s]", config.PrivateKey, err.Error())
//os.Exit(ExitSetupFailed)
return err
}
ctx := context.Background()
mgmTlsEnabled := false
if config.ManagementURL.Scheme == "https" {
mgmTlsEnabled = true
}
log.Debugf("connecting to Management Service %s", config.ManagementURL.String())
mgmClient, err := mgm.NewClient(ctx, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled)
if err != nil {
log.Errorf("failed connecting to Management Service %s %v", config.ManagementURL.String(), err)
//os.Exit(ExitSetupFailed)
return err
}
log.Debugf("connected to anagement Service %s", config.ManagementURL.String())
serverKey, err := mgmClient.GetServerPublicKey()
if err != nil {
log.Errorf("failed while getting Management Service public key: %v", err)
//os.Exit(ExitSetupFailed)
return err
}
_, err = loginPeer(*serverKey, mgmClient, setupKey)
if err != nil {
log.Errorf("failed logging-in peer on Management Service : %v", err)
//os.Exit(ExitSetupFailed)
return err
}
err = mgmClient.Close()
if err != nil {
log.Errorf("failed closing Management Service client: %v", err)
//os.Exit(ExitSetupFailed)
return err
}
return nil
},
}
)
// loginPeer attempts to login to Management Service. If peer wasn't registered, tries the registration flow.
func loginPeer(serverPublicKey wgtypes.Key, client *mgm.Client, setupKey string) (*mgmProto.LoginResponse, error) {
loginResp, err := client.Login(serverPublicKey)
if err != nil {
if s, ok := status.FromError(err); ok && s.Code() == codes.PermissionDenied {
log.Debugf("peer registration required")
return registerPeer(serverPublicKey, client, setupKey)
} else {
return nil, err
}
}
log.Info("peer has successfully logged-in to Management Service")
return loginResp, nil
}
// registerPeer checks whether setupKey was provided via cmd line and if not then it prompts user to enter a key.
// Otherwise tries to register with the provided setupKey via command line.
func registerPeer(serverPublicKey wgtypes.Key, client *mgm.Client, setupKey string) (*mgmProto.LoginResponse, error) {
var err error
if setupKey == "" {
setupKey, err = promptPeerSetupKey()
if err != nil {
log.Errorf("failed getting setup key from user: %s", err)
return nil, err
}
}
validSetupKey, err := uuid.Parse(setupKey)
if err != nil {
return nil, err
}
log.Debugf("sending peer registration request to Management Service")
loginResp, err := client.Register(serverPublicKey, validSetupKey.String())
if err != nil {
log.Errorf("failed registering peer %v", err)
return nil, err
}
log.Infof("peer has been successfully registered on Management Service")
return loginResp, nil
}
// promptPeerSetupKey prompts user to enter Setup Key
func promptPeerSetupKey() (string, error) {
fmt.Print("Enter setup key: ")
s := bufio.NewScanner(os.Stdin)
for s.Scan() {
input := s.Text()
if input != "" {
return input, nil
}
fmt.Println("Specified key is empty, try again:")
}
return "", s.Err()
}
func init() {
loginCmd.PersistentFlags().StringVar(&setupKey, "setup-key", "", "Setup key obtained from the Management Service Dashboard (used to register peer)")
}

68
client/cmd/login_test.go Normal file
View File

@ -0,0 +1,68 @@
package cmd
import (
"fmt"
"github.com/wiretrustee/wiretrustee/client/internal"
"github.com/wiretrustee/wiretrustee/iface"
mgmt "github.com/wiretrustee/wiretrustee/management/server"
"github.com/wiretrustee/wiretrustee/util"
"path/filepath"
"testing"
)
var mgmAddr string
func TestLogin_Start(t *testing.T) {
config := &mgmt.Config{}
_, err := util.ReadJson("../testdata/management.json", config)
if err != nil {
t.Fatal(err)
}
testDir := t.TempDir()
config.Datadir = testDir
err = util.CopyFileContents("../testdata/store.json", filepath.Join(testDir, "store.json"))
if err != nil {
t.Fatal(err)
}
_, listener := startManagement(config, t)
mgmAddr = listener.Addr().String()
}
func TestLogin(t *testing.T) {
tempDir := t.TempDir()
confPath := tempDir + "/config.json"
mgmtURL := fmt.Sprintf("http://%s", mgmAddr)
rootCmd.SetArgs([]string{
"login",
"--config",
confPath,
"--setup-key",
"a2c8e62b-38f5-4553-b31e-dd66c696cebb",
"--management-url",
mgmtURL,
})
err := rootCmd.Execute()
if err != nil {
t.Fatal(err)
}
// validate generated config
actualConf := &internal.Config{}
_, err = util.ReadJson(confPath, actualConf)
if err != nil {
t.Errorf("expected proper config file written, got broken %v", err)
}
if actualConf.ManagementURL.String() != mgmtURL {
t.Errorf("expected management URL %s got %s", mgmtURL, actualConf.ManagementURL.String())
}
if actualConf.WgIface != iface.WgInterfaceDefault {
t.Errorf("expected WgIface %s got %s", iface.WgInterfaceDefault, actualConf.WgIface)
}
if len(actualConf.PrivateKey) == 0 {
t.Errorf("expected non empty Private key, got empty")
}
}

View File

@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"github.com/wiretrustee/wiretrustee/client/internal"
"os"
"os/signal"
"runtime"
@ -20,6 +21,7 @@ var (
configPath string
defaultConfigPath string
logLevel string
managementURL string
rootCmd = &cobra.Command{
Use: "wiretrustee",
@ -43,10 +45,13 @@ func init() {
if runtime.GOOS == "windows" {
defaultConfigPath = os.Getenv("PROGRAMDATA") + "\\Wiretrustee\\" + "config.json"
}
rootCmd.PersistentFlags().StringVar(&configPath, "config", defaultConfigPath, "Wiretrustee config file location to write new config to")
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "")
rootCmd.PersistentFlags().StringVar(&managementURL, "management-url", "", fmt.Sprintf("Management Service URL [http|https]://[host]:[port] (default \"%s\")", internal.ManagementURLDefault().String()))
rootCmd.PersistentFlags().StringVar(&configPath, "config", defaultConfigPath, "Wiretrustee config file location")
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "sets Wiretrustee log level")
rootCmd.AddCommand(serviceCmd)
rootCmd.AddCommand(upCmd)
rootCmd.AddCommand(loginCmd)
serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd) // service control commands are subcommands of service
serviceCmd.AddCommand(installCmd, uninstallCmd) // service installer commands are subcommands of service
}

View File

@ -11,8 +11,6 @@ type program struct {
args []string
}
var logger service.Logger
func newSVCConfig() *service.Config {
return &service.Config{
Name: "wiretrustee",
@ -27,11 +25,6 @@ func newSVC(prg *program, conf *service.Config) (service.Service, error) {
log.Fatal(err)
return nil, err
}
logger, err = s.Logger(nil)
if err != nil {
log.Fatal(err)
return nil, err
}
return s, nil
}
@ -39,8 +32,6 @@ var (
serviceCmd = &cobra.Command{
Use: "service",
Short: "manages wiretrustee service",
//Run: func(cmd *cobra.Command, args []string) {
//},
}
)

View File

@ -9,7 +9,13 @@ import (
func (p *program) Start(s service.Service) error {
// Start should not block. Do the actual work async.
log.Info("starting service") //nolint
go upCmd.Run(p.cmd, p.args)
go func() {
err := upCmd.RunE(p.cmd, p.args)
if err != nil {
return
}
}()
return nil
}

View File

@ -1,129 +0,0 @@
package cmd
import (
"bytes"
"io/ioutil"
"os"
"testing"
"github.com/kardianos/service"
)
func Test_ServiceInstallCMD(t *testing.T) {
b := bytes.NewBufferString("")
rootCmd.SetOut(b)
rootCmd.SetErr(b)
rootCmd.SetArgs([]string{
"service",
"install",
"--config",
"/tmp/config.json",
})
err := rootCmd.Execute()
if err != nil {
t.Fatal(err)
}
out, err := ioutil.ReadAll(b)
if err != nil {
t.Fatal(err)
}
expectedMSG := "Wiretrustee service has been installed"
if string(out) != expectedMSG {
t.Fatalf("expected \"%s\" got \"%s\"", expectedMSG, string(out))
}
}
func Test_ServiceStartCMD(t *testing.T) {
b := bytes.NewBufferString("")
rootCmd.SetOut(b)
rootCmd.SetErr(b)
rootCmd.SetArgs([]string{"service", "start"})
err := rootCmd.Execute()
if err != nil {
t.Fatal(err)
}
out, err := ioutil.ReadAll(b)
if err != nil {
t.Fatal(err)
}
expectedMSG := "Wiretrustee service has been started"
if string(out) != expectedMSG {
t.Fatalf("expected \"%s\" got \"%s\"", expectedMSG, string(out))
}
}
func Test_ServiceRunCMD(t *testing.T) {
configFilePath := "/tmp/config.json"
if _, err := os.Stat(configFilePath); err == nil {
e := os.Remove(configFilePath)
if e != nil {
t.Fatal(err)
}
}
rootCmd.SetArgs([]string{
"--config",
configFilePath,
})
err := rootCmd.Execute()
if err != nil {
t.Fatal(err)
}
rootCmd.ResetFlags()
rootCmd.SetArgs([]string{"service", "start"})
err = rootCmd.Execute()
if err != nil {
t.Fatal(err)
}
s, err := newSVC(&program{}, newSVCConfig())
if err != nil {
t.Fatal(err)
}
status, err := s.Status()
if err != nil {
t.Fatal(err)
}
if status != service.StatusRunning {
t.Fatalf("expected running status of \"%d\" got \"%d\"", service.StatusRunning, status)
}
}
/*func Test_ServiceStopCMD(t *testing.T) {
b := bytes.NewBufferString("")
rootCmd.SetOut(b)
rootCmd.SetErr(b)
rootCmd.SetArgs([]string{"service", "stop"})
err := rootCmd.Execute()
if err != nil {
t.Fatal(err)
}
out, err := ioutil.ReadAll(b)
if err != nil {
t.Fatal(err)
}
expectedMSG := "Wiretrustee service has been stopped"
if string(out) != expectedMSG {
t.Fatalf("expected \"%s\" got \"%s\"", expectedMSG, string(out))
}
}*/
func Test_ServiceUninstallCMD(t *testing.T) {
b := bytes.NewBufferString("")
rootCmd.SetOut(b)
rootCmd.SetErr(b)
rootCmd.SetArgs([]string{"service", "uninstall"})
err := rootCmd.Execute()
if err != nil {
t.Fatal(err)
}
out, err := ioutil.ReadAll(b)
if err != nil {
t.Fatal(err)
}
expectedMSG := "Wiretrustee has been uninstalled"
if string(out) != expectedMSG {
t.Fatalf("expected \"%s\" got \"%s\"", expectedMSG, string(out))
}
}

54
client/cmd/testutil.go Normal file
View File

@ -0,0 +1,54 @@
package cmd
import (
mgmtProto "github.com/wiretrustee/wiretrustee/management/proto"
mgmt "github.com/wiretrustee/wiretrustee/management/server"
sigProto "github.com/wiretrustee/wiretrustee/signal/proto"
sig "github.com/wiretrustee/wiretrustee/signal/server"
"google.golang.org/grpc"
"net"
"testing"
)
func startSignal(t *testing.T) (*grpc.Server, net.Listener) {
lis, err := net.Listen("tcp", ":0")
if err != nil {
t.Fatal(err)
}
s := grpc.NewServer()
sigProto.RegisterSignalExchangeServer(s, sig.NewServer())
go func() {
if err := s.Serve(lis); err != nil {
panic(err)
}
}()
return s, lis
}
func startManagement(config *mgmt.Config, t *testing.T) (*grpc.Server, net.Listener) {
lis, err := net.Listen("tcp", ":0")
if err != nil {
t.Fatal(err)
}
s := grpc.NewServer()
store, err := mgmt.NewStore(config.Datadir)
if err != nil {
t.Fatal(err)
}
accountManager := mgmt.NewManager(store)
mgmtServer, err := mgmt.NewServer(config, accountManager)
if err != nil {
t.Fatal(err)
}
mgmtProto.RegisterManagementServiceServer(s, mgmtServer)
go func() {
if err := s.Serve(lis); err != nil {
t.Error(err)
return
}
}()
return s, lis
}

View File

@ -1,9 +1,7 @@
package cmd
import (
"bufio"
"context"
"fmt"
"github.com/pion/ice/v2"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@ -15,64 +13,58 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"net/url"
"os"
)
var (
managementAddr string
upCmd = &cobra.Command{
Use: "up",
Short: "start wiretrustee",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
InitLog(logLevel)
config, err := internal.GetConfig(managementAddr, configPath)
config, err := internal.ReadConfig(managementURL, configPath)
if err != nil {
log.Errorf("failed getting config %s %v", configPath, err)
os.Exit(ExitSetupFailed)
log.Errorf("failed reading config %s %v", configPath, err)
//os.Exit(ExitSetupFailed)
return err
}
//validate our peer's Wireguard PRIVATE key
myPrivateKey, err := wgtypes.ParseKey(config.PrivateKey)
if err != nil {
log.Errorf("failed parsing Wireguard key %s: [%s]", config.PrivateKey, err.Error())
os.Exit(ExitSetupFailed)
//os.Exit(ExitSetupFailed)
return err
}
ctx := context.Background()
managementURL, err := url.Parse(config.ManagementURL)
if err != nil {
log.Errorf("failed parsing managemtn URL%s: [%s]", config.ManagementURL, err.Error())
os.Exit(ExitSetupFailed)
}
mgmTlsEnabled := false
if managementURL.Scheme == "https" {
if config.ManagementURL.Scheme == "https" {
mgmTlsEnabled = true
}
// connect (just a connection, no stream yet) and login to Management Service to get an initial global Wiretrustee config
mgmClient, loginResp, err := connectToManagement(ctx, managementURL.Host, myPrivateKey, mgmTlsEnabled)
mgmClient, loginResp, err := connectToManagement(ctx, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled)
if err != nil {
log.Error(err)
os.Exit(ExitSetupFailed)
log.Warn(err)
//os.Exit(ExitSetupFailed)
return err
}
// with the global Wiretrustee config in hand connect (just a connection, no stream yet) Signal
signalClient, err := connectToSignal(ctx, loginResp.GetWiretrusteeConfig(), myPrivateKey)
if err != nil {
log.Error(err)
os.Exit(ExitSetupFailed)
//os.Exit(ExitSetupFailed)
return err
}
engineConfig, err := createEngineConfig(myPrivateKey, config, loginResp.GetWiretrusteeConfig(), loginResp.GetPeerConfig())
if err != nil {
log.Error(err)
os.Exit(ExitSetupFailed)
//os.Exit(ExitSetupFailed)
return err
}
// create start the Wiretrustee Engine that will connect to the Signal and Management streams and manage connections to remote peers.
@ -80,7 +72,8 @@ var (
err = engine.Start()
if err != nil {
log.Errorf("error while starting Wiretrustee Connection Engine: %s", err)
os.Exit(ExitSetupFailed)
//os.Exit(ExitSetupFailed)
return err
}
SetupCloseHandler()
@ -89,23 +82,30 @@ var (
err = mgmClient.Close()
if err != nil {
log.Errorf("failed closing Management Service client %v", err)
//os.Exit(ExitSetupFailed)
return err
}
err = signalClient.Close()
if err != nil {
log.Errorf("failed closing Signal Service client %v", err)
//os.Exit(ExitSetupFailed)
return err
}
log.Debugf("removing Wiretrustee interface %s", config.WgIface)
err = iface.Close()
if err != nil {
log.Errorf("failed closing Wiretrustee interface %s %v", config.WgIface, err)
//os.Exit(ExitSetupFailed)
return err
}
return nil
},
}
)
func init() {
upCmd.PersistentFlags().StringVar(&managementAddr, "management-addr", "", "Management Service address (e.g. app.wiretrustee.com")
}
// createEngineConfig converts configuration received from Management Service to EngineConfig
@ -161,6 +161,7 @@ func connectToSignal(ctx context.Context, wtConfig *mgmProto.WiretrusteeConfig,
} else {
sigTLSEnabled = false
}
signalClient, err := signal.NewClient(ctx, wtConfig.Signal.Uri, ourPrivateKey, sigTLSEnabled)
if err != nil {
log.Errorf("error while connecting to the Signal Exchange Service %s: %s", wtConfig.Signal.Uri, err)
@ -170,75 +171,31 @@ func connectToSignal(ctx context.Context, wtConfig *mgmProto.WiretrusteeConfig,
return signalClient, nil
}
// connectToManagement creates Management Services client, establishes a connection and gets a global Wiretrustee config (signal, turn, stun hosts, etc)
// connectToManagement creates Management Services client, establishes a connection, logs-in and gets a global Wiretrustee config (signal, turn, stun hosts, etc)
func connectToManagement(ctx context.Context, managementAddr string, ourPrivateKey wgtypes.Key, tlsEnabled bool) (*mgm.Client, *mgmProto.LoginResponse, error) {
log.Debugf("connecting to management server %s", managementAddr)
mgmClient, err := mgm.NewClient(ctx, managementAddr, ourPrivateKey, tlsEnabled)
client, err := mgm.NewClient(ctx, managementAddr, ourPrivateKey, tlsEnabled)
if err != nil {
return nil, nil, status.Errorf(codes.FailedPrecondition, "failed connecting to Management Service : %s", err)
}
log.Debugf("connected to management server %s", managementAddr)
serverKey, err := mgmClient.GetServerPublicKey()
serverPublicKey, err := client.GetServerPublicKey()
if err != nil {
return nil, nil, status.Errorf(codes.FailedPrecondition, "failed while getting Management Service public key: %s", err)
}
wtConfig, err := loginPeer(*serverKey, mgmClient)
if err != nil {
return nil, nil, status.Errorf(codes.FailedPrecondition, "failed logging-in peer on Management Service : %s", err)
}
log.Debugf("peer logged in to Management Service %s", wtConfig)
return mgmClient, wtConfig, nil
}
func registerPeer(serverPublicKey wgtypes.Key, client *mgm.Client) (*mgmProto.LoginResponse, error) {
setupKey, err := promptPeerSetupKey()
if err != nil {
log.Errorf("failed getting setup key: %s", err)
return nil, err
}
log.Debugf("sending peer registration request")
loginResp, err := client.Register(serverPublicKey, *setupKey)
if err != nil {
log.Errorf("failed registering peer %v", err)
return nil, err
}
return loginResp, nil
}
func loginPeer(serverPublicKey wgtypes.Key, client *mgm.Client) (*mgmProto.LoginResponse, error) {
loginResp, err := client.Login(serverPublicKey)
loginResp, err := client.Login(*serverPublicKey)
if err != nil {
if s, ok := status.FromError(err); ok && s.Code() == codes.PermissionDenied {
log.Debugf("peer registration required")
return registerPeer(serverPublicKey, client)
log.Error("peer registration required. Please run wiretrustee login command first")
return nil, nil, err
} else {
return nil, err
return nil, nil, err
}
}
return loginResp, nil
}
log.Infof("peer logged in to Management Service %s", managementAddr)
// promptPeerSetupKey prompts user to input a Setup Key
func promptPeerSetupKey() (*string, error) {
fmt.Print("Enter setup key: ")
s := bufio.NewScanner(os.Stdin)
for s.Scan() {
input := s.Text()
if input != "" {
return &input, nil
}
fmt.Println("Specified key is empty, try again:")
}
return nil, s.Err()
return client, loginResp, nil
}

110
client/cmd/up_test.go Normal file
View File

@ -0,0 +1,110 @@
package cmd
import (
"errors"
"fmt"
"github.com/wiretrustee/wiretrustee/iface"
mgmt "github.com/wiretrustee/wiretrustee/management/server"
"github.com/wiretrustee/wiretrustee/util"
"net/url"
"os"
"path/filepath"
"testing"
"time"
)
var signalAddr string
func TestUp_Start(t *testing.T) {
config := &mgmt.Config{}
_, err := util.ReadJson("../testdata/management.json", config)
if err != nil {
t.Fatal(err)
}
testDir := t.TempDir()
config.Datadir = testDir
err = util.CopyFileContents("../testdata/store.json", filepath.Join(testDir, "store.json"))
if err != nil {
t.Fatal(err)
}
_, signalLis := startSignal(t)
signalAddr = signalLis.Addr().String()
config.Signal.URI = signalAddr
_, mgmLis := startManagement(config, t)
mgmAddr = mgmLis.Addr().String()
}
func TestUp_ShouldFail_On_NoConfig(t *testing.T) {
tempDir := t.TempDir()
confPath := tempDir + "/config.json"
mgmtURL := fmt.Sprintf("http://%s", mgmAddr)
rootCmd.SetArgs([]string{
"up",
"--config",
confPath,
"--management-url",
mgmtURL,
})
err := rootCmd.Execute()
if err == nil || !errors.Is(err, os.ErrNotExist) {
t.Errorf("expecting login command to fail on absence of config")
}
}
func TestUp(t *testing.T) {
defer iface.Close()
tempDir := t.TempDir()
confPath := tempDir + "/config.json"
mgmtURL, err := url.Parse("http://" + mgmAddr)
if err != nil {
t.Fatal(err)
}
rootCmd.SetArgs([]string{
"login",
"--config",
confPath,
"--setup-key",
"a2c8e62b-38f5-4553-b31e-dd66c696cebb",
"--management-url",
mgmtURL.String(),
})
err = rootCmd.Execute()
if err != nil {
t.Fatal(err)
}
rootCmd.SetArgs([]string{
"up",
"--config",
confPath,
})
go func() {
err = rootCmd.Execute()
if err != nil {
t.Errorf("expected no error while running up command, got %v", err)
}
}()
exists := false
for start := time.Now(); time.Since(start) < 7*time.Second; {
e, err := iface.Exists(iface.WgInterfaceDefault)
if err != nil {
continue
}
if *e {
exists = true
break
}
}
if !exists {
t.Errorf("expected wireguard interface %s to be created", iface.WgInterfaceDefault)
}
}

View File

@ -1,20 +1,34 @@
package internal
import (
"fmt"
log "github.com/sirupsen/logrus"
"github.com/wiretrustee/wiretrustee/iface"
"github.com/wiretrustee/wiretrustee/util"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"net/url"
"os"
)
const ManagementAddrDefault = "https://app.wiretrustee.com"
var managementURLDefault *url.URL
func ManagementURLDefault() *url.URL {
return managementURLDefault
}
func init() {
managementURL, err := parseManagementURL("https://api.wiretrustee.com:33073")
if err != nil {
panic(err)
}
managementURLDefault = managementURL
}
// Config Configuration type
type Config struct {
// Wireguard private key of local peer
PrivateKey string
ManagementURL string
ManagementURL *url.URL
WgIface string
IFaceBlackList []string
}
@ -24,10 +38,16 @@ func createNewConfig(managementURL string, configPath string) (*Config, error) {
wgKey := generateKey()
config := &Config{PrivateKey: wgKey, WgIface: iface.WgInterfaceDefault, IFaceBlackList: []string{}}
if managementURL != "" {
config.ManagementURL = managementURL
} else {
config.ManagementURL = ManagementAddrDefault
URL, err := parseManagementURL(managementURL)
if err != nil {
return nil, err
}
config.ManagementURL = URL
} else {
config.ManagementURL = managementURLDefault
}
config.IFaceBlackList = []string{iface.WgInterfaceDefault, "tun0"}
err := util.WriteJson(configPath, config)
if err != nil {
@ -37,29 +57,50 @@ func createNewConfig(managementURL string, configPath string) (*Config, error) {
return config, nil
}
// GetConfig reads existing config or generates a new one
func GetConfig(managementURL string, configPath string) (*Config, error) {
func parseManagementURL(managementURL string) (*url.URL, error) {
var config *Config
if _, err := os.Stat(configPath); os.IsNotExist(err) {
log.Warnf("first run - generating new config %s", configPath)
config, err = createNewConfig(managementURL, configPath)
parsedMgmtURL, err := url.ParseRequestURI(managementURL)
if err != nil {
log.Errorf("failed parsing management URL %s: [%s]", managementURL, err.Error())
return nil, err
}
} else {
config = &Config{}
if !(parsedMgmtURL.Scheme == "https" || parsedMgmtURL.Scheme == "http") {
return nil, fmt.Errorf("invalid Management Service URL provided %s. Supported format [http|https]://[host]:[port]", managementURL)
}
return parsedMgmtURL, err
}
// ReadConfig reads existing config. In case provided managementURL is not empty overrides the read property
func ReadConfig(managementURL string, configPath string) (*Config, error) {
config := &Config{}
_, err := util.ReadJson(configPath, config)
if err != nil {
return nil, err
}
}
if managementURL != "" {
config.ManagementURL = managementURL
URL, err := parseManagementURL(managementURL)
if err != nil {
return nil, err
}
config.ManagementURL = URL
}
return config, nil
return config, err
}
// GetConfig reads existing config or generates a new one
func GetConfig(managementURL string, configPath string) (*Config, error) {
if _, err := os.Stat(configPath); os.IsNotExist(err) {
log.Infof("generating new config %s", configPath)
return createNewConfig(managementURL, configPath)
} else {
return ReadConfig(managementURL, configPath)
}
}
// generateKey generates a new Wireguard private key

33
client/testdata/management.json vendored Normal file
View File

@ -0,0 +1,33 @@
{
"Stuns": [
{
"Proto": "udp",
"URI": "stun:stun.wiretrustee.com:3468",
"Username": "",
"Password": null
}
],
"Turns": [
{
"Proto": "udp",
"URI": "turn:stun.wiretrustee.com:3468",
"Username": "some_user",
"Password": "c29tZV9wYXNzd29yZA=="
}
],
"Signal": {
"Proto": "http",
"URI": "signal.wiretrustee.com:10000",
"Username": "",
"Password": null
},
"DataDir": "",
"HttpConfig": {
"LetsEncryptDomain": "",
"Address": "0.0.0.0:3000",
"AuthDomain": "<PASTE YOUR AUTH0 DOMAIN HERE>",
"AuthClientId": "<PASTE YOUR AUTH0 CLIENT ID HERE>",
"AuthClientSecret": "<PASTE YOUR AUTH0 SECRET>",
"AuthCallback": "http://localhost:3000/callback"
}
}

21
client/testdata/store.json vendored Normal file
View File

@ -0,0 +1,21 @@
{
"Accounts": {
"bf1c8084-ba50-4ce7-9439-34653001fc3b": {
"Id": "bf1c8084-ba50-4ce7-9439-34653001fc3b",
"SetupKeys": {
"a2c8e62b-38f5-4553-b31e-dd66c696cebb": {
"Key": "a2c8e62b-38f5-4553-b31e-dd66c696cebb"
}
},
"Network": {
"Id": "af1c8024-ha40-4ce2-9418-34653101fc3c",
"Net": {
"IP": "100.64.0.0",
"Mask": "/8AAAA=="
},
"Dns": null
},
"Peers": {}
}
}
}

View File

@ -31,8 +31,10 @@ func NewClient(ctx context.Context, addr string, ourPrivateKey wgtypes.Key, tlsE
transportOption = grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))
}
mgmCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
conn, err := grpc.DialContext(
ctx,
mgmCtx,
addr,
transportOption,
grpc.WithBlock(),

View File

@ -48,8 +48,10 @@ func NewClient(ctx context.Context, addr string, key wgtypes.Key, tlsEnabled boo
transportOption = grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))
}
sigCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
conn, err := grpc.DialContext(
ctx,
sigCtx,
addr,
transportOption,
grpc.WithBlock(),