Compare commits

...

20 Commits

Author SHA1 Message Date
Mikhail Bragin
0b2c26847b fix: return ctx error when UP command exists (#140) 2021-10-26 21:49:05 +02:00
braginini
595ea0d4f8 chore: decrease log verbosity 2021-10-26 10:08:28 +02:00
Maycon Santos
f714868fdd remove arch if and replacement for debian packages (#138) 2021-10-23 10:29:49 +02:00
Mikhail Bragin
81821a1f39 docs: update diagram and Wireguard title (#137)
* docs: update diagram and Wireguard title
2021-10-21 10:06:29 +02:00
mlsmaycon
842b143a48 sync go.sum 2021-10-20 21:57:16 +02:00
Maycon Santos
1323a74db0 fix: avoid failing and extra error messages (#136)
* avoid failing and extra error messages

* avoid extra error messages when executed after pre_remove.sh

* remove extra output and avoid failure on minor errors

* ensure the steps will run only on remove
2021-10-20 11:51:32 +02:00
Mikhail Bragin
74485d3b13 fix: service hanging when error on startup has been encountered (#135) 2021-10-18 13:29:26 +02:00
Mikhail Bragin
bef3b3392b fix: graceful shutdown (#134)
* fix: graceful shutdown

* fix: windows graceful shutdown
2021-10-17 22:15:38 +02:00
Maycon Santos
fcea3c99d4 Enhance up command (#133)
* move setup-key to root command

* up will check login and start service

* update tests to reflect new UP capabilities

* display client IP

* removed unused argument

* install service if not installed

* update post-install and add pre remove script

* improve log messages

* handle service status failures and install service when needed

* removing unused files

* update documentation and description

* add version command

* update service lib version

* using lib constant for not installed services

* match version from goreleaser

* fix: graceful shutdown

* stop only if service is running

* add logs initialization to service controller commands

Co-authored-by: braginini <bangvalo@gmail.com>
2021-10-17 21:34:07 +02:00
Mikhail Bragin
96799a25b5 docs: fix gif size 2021-10-16 16:54:37 +02:00
Mikhail Bragin
07291cdb93 docs: update readme (#132)
* update readme
2021-10-16 16:53:39 +02:00
Mikhail Bragin
21139938c1 docs: highlight Slack channel 2021-10-12 14:37:49 +02:00
Maycon Santos
5cf2d0a6a9 add slack invitation link (#129) 2021-10-12 12:16:09 +02:00
Maycon Santos
8551afe04e enhancement: Support new architectures and auto upload packages to repo (#128)
* adding uploads

* adding uploads

* adding uploads

* adding uploads

* adding uploads

* adding uploads

* use https://pkgs.wiretrustee.com/

* use https://pkgs.wiretrustee.com/

* use https://pkgs.wiretrustee.com/

* set yum id

* secrets for goreleaser uploads

* ensure Github release is enabled
2021-10-12 12:15:45 +02:00
braginini
1685817171 docs: correct installation steps 2021-10-03 18:55:47 +02:00
Mikhail Bragin
e17f662683 docs: update intro (#125)
* docs: update intro
2021-10-03 18:21:41 +02:00
Mikhail Bragin
a764fb870c docs: move intro link up in readme 2021-09-27 09:23:19 +02:00
Mikhail Bragin
cabff941ac docs: add self-hosting video 2021-09-26 16:49:59 +02:00
Mikhail Bragin
b5f35dfb5e docs: replace beta with app.wiretrustee.com (#123)
* docs: replace beta with app.wiretrustee.com

* docs: add Signal port to the list of the open ports

* docs: minor corrections
2021-09-26 11:44:34 +02:00
braginini
1d426b7f81 docs: fix docker-compose management image 2021-09-25 20:17:01 +02:00
31 changed files with 583 additions and 345 deletions

View File

@@ -51,6 +51,8 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
UPLOAD_DEBIAN_SECRET: ${{ secrets.PKG_UPLOAD_SECRET }}
UPLOAD_YUM_SECRET: ${{ secrets.PKG_UPLOAD_SECRET }}
- -
name: Trigger Windows binaries sign pipeline name: Trigger Windows binaries sign pipeline

View File

@@ -13,13 +13,18 @@ builds:
- arm - arm
- amd64 - amd64
- arm64 - arm64
- mips
gomips:
- hardfloat
- softfloat
ignore: ignore:
- goos: darwin
goarch: arm64
- goos: windows - goos: windows
goarch: arm64 goarch: arm64
- goos: windows - goos: windows
goarch: arm goarch: arm
ldflags:
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
mod_timestamp: '{{ .CommitTimestamp }}'
tags: tags:
- load_wintun_from_rsrc - load_wintun_from_rsrc
@@ -32,6 +37,9 @@ builds:
goarch: goarch:
- amd64 - amd64
- arm64 - arm64
ldflags:
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
mod_timestamp: '{{ .CommitTimestamp }}'
- id: wiretrustee-signal - id: wiretrustee-signal
dir: signal dir: signal
@@ -42,28 +50,38 @@ builds:
goarch: goarch:
- amd64 - amd64
- arm64 - arm64
ldflags:
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
mod_timestamp: '{{ .CommitTimestamp }}'
archives: archives:
- builds: - builds:
- wiretrustee - wiretrustee
nfpms: nfpms:
- maintainer: Wiretrustee <wiretrustee@wiretrustee.com> - maintainer: Wiretrustee <dev@wiretrustee.com>
description: Wiretrustee project. description: Wiretrustee client.
homepage: https://wiretrustee.com/ homepage: https://wiretrustee.com/
id: deb
builds: builds:
- wiretrustee - wiretrustee
formats: formats:
- deb - deb
- rpm
contents:
- src: release_files/wiretrustee.service
dst: /lib/systemd/system/wiretrustee.service
- src: release_files/wiretrustee.json
dst: /etc/wiretrustee/wiretrustee.json
type: "config|noreplace"
scripts: scripts:
postinstall: "release_files/post_install.sh" postinstall: "release_files/post_install.sh"
preremove: "release_files/pre_remove.sh"
- maintainer: Wiretrustee <dev@wiretrustee.com>
description: Wiretrustee client.
homepage: https://wiretrustee.com/
id: rpm
builds:
- wiretrustee
formats:
- rpm
scripts:
postinstall: "release_files/post_install.sh"
preremove: "release_files/pre_remove.sh"
dockers: dockers:
- image_templates: - image_templates:
- wiretrustee/signal:{{ .Version }}-amd64 - wiretrustee/signal:{{ .Version }}-amd64
@@ -196,4 +214,20 @@ brews:
homepage: https://wiretrustee.com/ homepage: https://wiretrustee.com/
license: "BSD3" license: "BSD3"
test: | test: |
system "#{bin}/{{ .ProjectName }} -h" system "#{bin}/{{ .ProjectName }} -h"
uploads:
- name: debian
ids:
- deb
mode: archive
target: https://pkgs.wiretrustee.com/debian/pool/{{ .ArtifactName }};deb.distribution=stable;deb.component=main;deb.architecture={{ .Arch }}
username: dev@wiretrustee.com
method: PUT
- name: yum
ids:
- rpm
mode: archive
target: https://pkgs.wiretrustee.com/yum/{{ .Arch }}{{ if .Arm }}{{ .Arm }}{{ end }}
username: dev@wiretrustee.com
method: PUT

194
README.md
View File

@@ -1,18 +1,50 @@
# Wiretrustee <div align="center">
A WireGuard®-based mesh network that connects your devices into a single private network. <p align="center">
<img width="250" src="docs/media/logo-full.png"/>
</p>
<p>
<img src="https://img.shields.io/badge/license-BSD--3-blue" />
<img src="https://img.shields.io/docker/pulls/wiretrustee/management" />
<img src="https://badgen.net/badge/Open%20Source%3F/Yes%21/blue?icon=github" />
</p>
</div>
<p align="center">
<strong>
Start using Wiretrustee at <a href="https://app.wiretrustee.com/">app.wiretrustee.com</a>
<br/>
See <a href="docs/README.md">Documentation</a>
<br/>
Join our <a href="https://join.slack.com/t/wiretrustee/shared_invite/zt-vrahf41g-ik1v7fV8du6t0RwxSrJ96A">Slack channel</a>
<br/>
</strong>
</p>
<br>
**Wiretrustee is an open-source VPN platform built on top of WireGuard® making it easy to create secure private networks for your organization or home.**
It requires zero configuration effort leaving behind the hassle of opening ports, complex firewall rules, vpn gateways, and so forth.
There is no centralized VPN server with Wiretrustee - your computers, devices, machines, and servers connect to each other directly over a fast encrypted tunnel.
### Secure peer-to-peer VPN in minutes
<p float="left" align="middle">
<img src="docs/media/peerA.gif" width="400"/>
<img src="docs/media/peerB.gif" width="400"/>
</p>
**Note**: The `main` branch may be in an *unstable or even broken state* during development. For stable versions, see [releases](https://github.com/wiretrustee/wiretrustee/releases). **Note**: The `main` branch may be in an *unstable or even broken state* during development. For stable versions, see [releases](https://github.com/wiretrustee/wiretrustee/releases).
Hosted demo version: Hosted demo version:
[https://beta.wiretrustee.com/](https://beta.wiretrustee.com/peers). [https://app.wiretrustee.com/](https://app.wiretrustee.com/peers).
The number of peers is limited to 15.
Please don't use the hosted demonstration version for production purposes. We appreciate your usage of the demonstration version and will love to hear your feedback, please reach out via Issue or [Slack](https://wiretrustee.slack.com/).
[UI Dashboard Repo](https://github.com/wiretrustee/wiretrustee-dashboard) [UI Dashboard Repo](https://github.com/wiretrustee/wiretrustee-dashboard)
### Why using Wiretrustee? ### Why using Wiretrustee?
* Connect multiple devices to each other via a secure peer-to-peer Wireguard VPN tunnel. At home, the office, or anywhere else. * Connect multiple devices to each other via a secure peer-to-peer Wireguard VPN tunnel. At home, the office, or anywhere else.
@@ -28,8 +60,6 @@ Please don't use the hosted demonstration version for production purposes. We ap
* Works on ARM devices (e.g. Raspberry Pi). * Works on ARM devices (e.g. Raspberry Pi).
* Open-source (including Management Service) * Open-source (including Management Service)
### Secure peer-to-peer VPN in minutes
![animation](docs/media/peers.gif)
### A bit on Wiretrustee internals ### A bit on Wiretrustee internals
* Wiretrustee features a Management Service that offers peer IP management and network updates distribution (e.g. when new peer joins the network). * Wiretrustee features a Management Service that offers peer IP management and network updates distribution (e.g. when new peer joins the network).
@@ -45,116 +75,106 @@ Please don't use the hosted demonstration version for production purposes. We ap
- [Public Roadmap](https://github.com/wiretrustee/wiretrustee/projects/2) - [Public Roadmap](https://github.com/wiretrustee/wiretrustee/projects/2)
- [Public Roadmap Progress Tracking](https://github.com/wiretrustee/wiretrustee/projects/1) - [Public Roadmap Progress Tracking](https://github.com/wiretrustee/wiretrustee/projects/1)
### Getting started
See [Docs](docs/README.md) for managed and self-hosting guides.
### Client Installation ### Client Installation
#### Linux #### Linux
1. Checkout Wiretrustee [releases](https://github.com/wiretrustee/wiretrustee/releases)
2. Download the latest release (**Switch VERSION to the latest**):
**Debian packages** **APT/Debian**
```shell 1. Add the repository:
wget https://github.com/wiretrustee/wiretrustee/releases/download/v<VERSION>/wiretrustee_<VERSION>_linux_amd64.deb ```shell
``` sudo apt-get update
3. Install the package sudo apt-get install ca-certificates curl gnupg -y
```shell curl -L https://pkgs.wiretrustee.com/debian/public.key | sudo apt-key add -
sudo dpkg -i wiretrustee_<VERSION>_linux_amd64.deb echo 'deb https://pkgs.wiretrustee.com/debian stable main' | sudo tee /etc/apt/sources.list.d/wiretrustee.list
``` ```
**Fedora/Centos packages** 2. Install the package
```shell ```shell
wget https://github.com/wiretrustee/wiretrustee/releases/download/v<VERSION>/wiretrustee_<VERSION>_linux_amd64.rpm sudo apt-get update
``` sudo apt-get install wiretrustee
3. Install the package ```
```shell **RPM/Red hat**
sudo rpm -i wiretrustee_<VERSION>_linux_amd64.rpm 1. Add the repository:
``` ```shell
cat <<EOF | sudo tee /etc/yum.repos.d/wiretrustee.repo
[Wiretrustee]
name=Wiretrustee
baseurl=https://pkgs.wiretrustee.com/yum/
enabled=1
gpgcheck=0
gpgkey=https://pkgs.wiretrustee.com/yum/repodata/repomd.xml.key
repo_gpgcheck=1
EOF
```
2. Install the package
```shell
sudo yum install wiretrustee
```
#### MACOS #### MACOS
**Brew install** **Brew install**
1. Download and install Brew at https://brew.sh/ 1. Download and install Brew at https://brew.sh/
2. Install the client 2. Install the client
```shell ```shell
brew install wiretrustee/client/wiretrustee brew install wiretrustee/client/wiretrustee
``` ```
**Installation from binary** **Installation from binary**
1. Checkout Wiretrustee [releases](https://github.com/wiretrustee/wiretrustee/releases/latest) 1. Checkout Wiretrustee [releases](https://github.com/wiretrustee/wiretrustee/releases/latest)
2. Download the latest release (**Switch VERSION to the latest**): 2. Download the latest release (**Switch VERSION to the latest**):
```shell ```shell
curl -o ./wiretrustee_<VERSION>_darwin_amd64.tar.gz https://github.com/wiretrustee/wiretrustee/releases/download/v<VERSION>/wiretrustee_<VERSION>_darwin_amd64.tar.gz curl -o ./wiretrustee_<VERSION>_darwin_amd64.tar.gz https://github.com/wiretrustee/wiretrustee/releases/download/v<VERSION>/wiretrustee_<VERSION>_darwin_amd64.tar.gz
``` ```
3. Decompress 3. Decompress
```shell ```shell
tar xcf ./wiretrustee_<VERSION>_darwin_amd64.tar.gz tar xcf ./wiretrustee_<VERSION>_darwin_amd64.tar.gz
sudo mv wiretrusee /usr/local/bin/wiretrustee sudo mv wiretrusee /usr/local/bin/wiretrustee
chmod +x /usr/local/bin/wiretrustee chmod +x /usr/local/bin/wiretrustee
``` ```
After that you may need to add /usr/local/bin in your MAC's PATH environment variable: After that you may need to add /usr/local/bin in your MAC's PATH environment variable:
````shell ````shell
export PATH=$PATH:/usr/local/bin export PATH=$PATH:/usr/local/bin
```` ````
#### Windows #### Windows
1. Checkout Wiretrustee [releases](https://github.com/wiretrustee/wiretrustee/releases/latest) 1. Checkout Wiretrustee [releases](https://github.com/wiretrustee/wiretrustee/releases/latest)
2. Download the latest Windows release installer ```wiretrustee_installer_<VERSION>_windows_amd64.exe``` (**Switch VERSION to the latest**): 2. Download the latest Windows release installer ```wiretrustee_installer_<VERSION>_windows_amd64.exe``` (**Switch VERSION to the latest**):
3. Proceed with installation steps 3. Proceed with installation steps
4. This will install the client in the C:\\Program Files\\Wiretrustee and add the client service 4. This will install the client in the C:\\Program Files\\Wiretrustee and add the client service
5. After installing you can follow the [Client Configuration](#Client-Configuration) steps. 5. After installing, you can follow the [Client Configuration](#Client-Configuration) steps.
> To uninstall the client and service, you can use Add/Remove programs > To uninstall the client and service, you can use Add/Remove programs
### Client Configuration ### Client Configuration
1. Login to the Management Service. You need to have a `setup key` in hand (see ). 1. Login to the Management Service. You need to have a `setup key` in hand (see ).
For **Unix** systems: For **Unix** systems:
```shell ```shell
sudo wiretrustee login --setup-key <SETUP KEY> sudo wiretrustee up --setup-key <SETUP KEY>
``` ```
For **Windows** systems: For **Windows** systems, start powershell as administrator and:
```shell ```shell
.\wiretrustee.exe login --setup-key <SETUP KEY> wiretrustee up --setup-key <SETUP KEY>
``` ```
Alternatively, if you are hosting your own Management Service provide `--management-url` property pointing to your Management Service: Alternatively, if you are hosting your own Management Service provide `--management-url` property pointing to your Management Service:
```shell ```shell
sudo wiretrustee login --setup-key <SETUP KEY> --management-url https://localhost:33073 sudo wiretrustee up --setup-key <SETUP KEY> --management-url https://localhost:33073
``` ```
You could also omit `--setup-key` property. In this case the tool will prompt it the key. > You could also omit `--setup-key` property. In this case the tool will prompt it the key.
2. Start Wiretrustee:
For **MACOS** you will just start the service: 2. Check your IP:
````shell For **MACOS** you will just start the service:
sudo wiretrustee up ````shell
# or sudo ipconfig getifaddr utun100
sudo wiretrustee up & # to run it in background ````
````
For **Linux** systems: For **Linux** systems:
```shell ```shell
sudo systemctl restart wiretrustee.service ip addr show wt0
sudo systemctl status wiretrustee.service ```
```
For **Windows** systems: For **Windows** systems:
```shell ```shell
.\wiretrustee.exe service start netsh interface ip show config name="wt0"
``` ```
> You may need to run Powershell as Administrator
3. Check your IP: 3. Repeat on other machines.
For **MACOS** you will just start the service:
````shell
sudo ipconfig getifaddr utun100
````
For **Linux** systems:
```shell
ip addr show wt0
```
For **Windows** systems:
```shell
netsh interface ip show config name="wt0"
```
4. Repeat on other machines.
### Running Dashboard, Management, Signal and Coturn ### Running Dashboard, Management, Signal and Coturn
Wiretrustee uses [Auth0](https://auth0.com) for user authentication and authorization, therefore you will need to create a free account Wiretrustee uses [Auth0](https://auth0.com) for user authentication and authorization, therefore you will need to create a free account

View File

@@ -152,6 +152,5 @@ func promptPeerSetupKey() (string, error) {
return "", s.Err() return "", s.Err()
} }
func init() { //func init() {
loginCmd.PersistentFlags().StringVar(&setupKey, "setup-key", "", "Setup key obtained from the Management Service Dashboard (used to register peer)") //}
}

View File

@@ -2,11 +2,13 @@ package cmd
import ( import (
"fmt" "fmt"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/wiretrustee/wiretrustee/client/internal" "github.com/wiretrustee/wiretrustee/client/internal"
"os" "os"
"os/signal" "os/signal"
"runtime" "runtime"
"syscall"
) )
const ( const (
@@ -22,15 +24,15 @@ var (
defaultLogFile string defaultLogFile string
logFile string logFile string
managementURL string managementURL string
rootCmd = &cobra.Command{
rootCmd = &cobra.Command{
Use: "wiretrustee", Use: "wiretrustee",
Short: "", Short: "",
Long: "", Long: "",
} }
// Execution control channel for stopCh signal // Execution control channel for stopCh signal
stopCh chan int stopCh chan int
cleanupCh chan struct{}
) )
// Execute executes the root command. // Execute executes the root command.
@@ -40,6 +42,7 @@ func Execute() error {
func init() { func init() {
stopCh = make(chan int) stopCh = make(chan int)
cleanupCh = make(chan struct{})
defaultConfigPath = "/etc/wiretrustee/config.json" defaultConfigPath = "/etc/wiretrustee/config.json"
defaultLogFile = "/var/log/wiretrustee/client.log" defaultLogFile = "/var/log/wiretrustee/client.log"
@@ -52,9 +55,11 @@ func init() {
rootCmd.PersistentFlags().StringVar(&configPath, "config", defaultConfigPath, "Wiretrustee config file location") rootCmd.PersistentFlags().StringVar(&configPath, "config", defaultConfigPath, "Wiretrustee config file location")
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "sets Wiretrustee log level") rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "sets Wiretrustee log level")
rootCmd.PersistentFlags().StringVar(&logFile, "log-file", defaultLogFile, "sets Wiretrustee log path. If console is specified the the log will be output to stdout") rootCmd.PersistentFlags().StringVar(&logFile, "log-file", defaultLogFile, "sets Wiretrustee log path. If console is specified the the log will be output to stdout")
rootCmd.PersistentFlags().StringVar(&setupKey, "setup-key", "", "Setup key obtained from the Management Service Dashboard (used to register peer)")
rootCmd.AddCommand(serviceCmd) rootCmd.AddCommand(serviceCmd)
rootCmd.AddCommand(upCmd) rootCmd.AddCommand(upCmd)
rootCmd.AddCommand(loginCmd) rootCmd.AddCommand(loginCmd)
rootCmd.AddCommand(versionCmd)
serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd) // service control commands are subcommands of service serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd) // service control commands are subcommands of service
serviceCmd.AddCommand(installCmd, uninstallCmd) // service installer commands are subcommands of service serviceCmd.AddCommand(installCmd, uninstallCmd) // service installer commands are subcommands of service
} }
@@ -62,10 +67,10 @@ func init() {
// SetupCloseHandler handles SIGTERM signal and exits with success // SetupCloseHandler handles SIGTERM signal and exits with success
func SetupCloseHandler() { func SetupCloseHandler() {
c := make(chan os.Signal, 1) c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt) signal.Notify(c, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
go func() { go func() {
for range c { for range c {
fmt.Println("\r- Ctrl+C pressed in Terminal") log.Info("shutdown signal received")
stopCh <- 0 stopCh <- 0
} }
}() }()

View File

@@ -1,26 +1,58 @@
package cmd package cmd
import ( import (
"github.com/cenkalti/backoff/v4"
"github.com/kardianos/service" "github.com/kardianos/service"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/wiretrustee/wiretrustee/util"
"time"
) )
func (p *program) Start(s service.Service) error { func (p *program) Start(s service.Service) error {
var backOff = &backoff.ExponentialBackOff{
InitialInterval: time.Second,
RandomizationFactor: backoff.DefaultRandomizationFactor,
Multiplier: backoff.DefaultMultiplier,
MaxInterval: 30 * time.Second,
MaxElapsedTime: 24 * 3 * time.Hour, //stop after 3 days trying
Stop: backoff.Stop,
Clock: backoff.SystemClock,
}
// Start should not block. Do the actual work async. // Start should not block. Do the actual work async.
log.Info("starting service") //nolint log.Info("starting service") //nolint
go func() { go func() {
err := upCmd.RunE(p.cmd, p.args) operation := func() error {
if err != nil { err := runClient()
return if err != nil {
log.Warnf("retrying Wiretrustee client app due to error: %v", err)
return err
}
return nil
} }
err := backoff.Retry(operation, backOff)
if err != nil {
log.Errorf("exiting client retry loop due to unrecoverable error: %s", err)
return
}
}() }()
return nil return nil
} }
func (p *program) Stop(s service.Service) error { func (p *program) Stop(s service.Service) error {
stopCh <- 1 go func() {
stopCh <- 1
}()
select {
case <-cleanupCh:
case <-time.After(time.Second * 10):
log.Warnf("failed waiting for service cleanup, terminating")
}
log.Info("stopped Wiretrustee service") //nolint
return nil return nil
} }
@@ -30,6 +62,14 @@ var (
Short: "runs wiretrustee as service", Short: "runs wiretrustee as service",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := util.InitLog(logLevel, logFile)
if err != nil {
log.Errorf("failed initializing log %v", err)
return
}
SetupCloseHandler()
prg := &program{ prg := &program{
cmd: cmd, cmd: cmd,
args: args, args: args,
@@ -54,19 +94,24 @@ var (
startCmd = &cobra.Command{ startCmd = &cobra.Command{
Use: "start", Use: "start",
Short: "starts wiretrustee service", Short: "starts wiretrustee service",
Run: func(cmd *cobra.Command, args []string) { RunE: func(cmd *cobra.Command, args []string) error {
err := util.InitLog(logLevel, logFile)
if err != nil {
log.Errorf("failed initializing log %v", err)
return err
}
s, err := newSVC(&program{}, newSVCConfig()) s, err := newSVC(&program{}, newSVCConfig())
if err != nil { if err != nil {
cmd.PrintErrln(err) cmd.PrintErrln(err)
return return err
} }
err = s.Start() err = s.Start()
if err != nil { if err != nil {
cmd.PrintErrln(err) cmd.PrintErrln(err)
return return err
} }
cmd.Printf("Wiretrustee service has been started") cmd.Println("Wiretrustee service has been started")
return nil
}, },
} }
) )
@@ -76,7 +121,10 @@ var (
Use: "stop", Use: "stop",
Short: "stops wiretrustee service", Short: "stops wiretrustee service",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := util.InitLog(logLevel, logFile)
if err != nil {
log.Errorf("failed initializing log %v", err)
}
s, err := newSVC(&program{}, newSVCConfig()) s, err := newSVC(&program{}, newSVCConfig())
if err != nil { if err != nil {
cmd.PrintErrln(err) cmd.PrintErrln(err)
@@ -87,7 +135,7 @@ var (
cmd.PrintErrln(err) cmd.PrintErrln(err)
return return
} }
cmd.Printf("Wiretrustee service has been stopped") cmd.Println("Wiretrustee service has been stopped")
}, },
} }
) )
@@ -97,7 +145,10 @@ var (
Use: "restart", Use: "restart",
Short: "restarts wiretrustee service", Short: "restarts wiretrustee service",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := util.InitLog(logLevel, logFile)
if err != nil {
log.Errorf("failed initializing log %v", err)
}
s, err := newSVC(&program{}, newSVCConfig()) s, err := newSVC(&program{}, newSVCConfig())
if err != nil { if err != nil {
cmd.PrintErrln(err) cmd.PrintErrln(err)
@@ -108,7 +159,7 @@ var (
cmd.PrintErrln(err) cmd.PrintErrln(err)
return return
} }
cmd.Printf("Wiretrustee service has been restarted") cmd.Println("Wiretrustee service has been restarted")
}, },
} }
) )

View File

@@ -9,7 +9,7 @@ var (
installCmd = &cobra.Command{ installCmd = &cobra.Command{
Use: "install", Use: "install",
Short: "installs wiretrustee service", Short: "installs wiretrustee service",
Run: func(cmd *cobra.Command, args []string) { RunE: func(cmd *cobra.Command, args []string) error {
svcConfig := newSVCConfig() svcConfig := newSVCConfig()
@@ -30,15 +30,16 @@ var (
s, err := newSVC(&program{}, svcConfig) s, err := newSVC(&program{}, svcConfig)
if err != nil { if err != nil {
cmd.PrintErrln(err) cmd.PrintErrln(err)
return return err
} }
err = s.Install() err = s.Install()
if err != nil { if err != nil {
cmd.PrintErrln(err) cmd.PrintErrln(err)
return return err
} }
cmd.Printf("Wiretrustee service has been installed") cmd.Println("Wiretrustee service has been installed")
return nil
}, },
} }
) )
@@ -60,7 +61,7 @@ var (
cmd.PrintErrln(err) cmd.PrintErrln(err)
return return
} }
cmd.Printf("Wiretrustee has been uninstalled") cmd.Println("Wiretrustee has been uninstalled")
}, },
} }
) )

View File

@@ -2,13 +2,13 @@ package cmd
import ( import (
"context" "context"
"github.com/kardianos/service"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/wiretrustee/wiretrustee/client/internal" "github.com/wiretrustee/wiretrustee/client/internal"
mgm "github.com/wiretrustee/wiretrustee/management/client" mgm "github.com/wiretrustee/wiretrustee/management/client"
mgmProto "github.com/wiretrustee/wiretrustee/management/proto" mgmProto "github.com/wiretrustee/wiretrustee/management/proto"
signal "github.com/wiretrustee/wiretrustee/signal/client" signal "github.com/wiretrustee/wiretrustee/signal/client"
"github.com/wiretrustee/wiretrustee/util"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
@@ -17,97 +17,45 @@ import (
var ( var (
upCmd = &cobra.Command{ upCmd = &cobra.Command{
Use: "up", Use: "up",
Short: "start wiretrustee", Short: "install, login and start wiretrustee client",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
err := util.InitLog(logLevel, logFile)
err := loginCmd.RunE(cmd, args)
if err != nil { if err != nil {
log.Errorf("failed initializing log %v", err) return err
}
if logFile == "console" {
return runClient()
}
s, err := newSVC(&program{}, newSVCConfig())
if err != nil {
cmd.PrintErrln(err)
return err return err
} }
config, err := internal.ReadConfig(managementURL, configPath) srvStatus, err := s.Status()
if err != nil { if err != nil {
log.Errorf("failed reading config %s %v", configPath, err) if err == service.ErrNotInstalled {
return err log.Infof("%s. Installing it now", err.Error())
e := installCmd.RunE(cmd, args)
if e != nil {
return e
}
} else {
log.Warnf("failed retrieving service status: %v", err)
}
} }
if srvStatus == service.StatusRunning {
//validate our peer's Wireguard PRIVATE key stopCmd.Run(cmd, args)
myPrivateKey, err := wgtypes.ParseKey(config.PrivateKey)
if err != nil {
log.Errorf("failed parsing Wireguard key %s: [%s]", config.PrivateKey, err.Error())
return err
} }
ctx, cancel := context.WithCancel(context.Background()) return startCmd.RunE(cmd, args)
defer cancel()
mgmTlsEnabled := false
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, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled)
if err != nil {
log.Warn(err)
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)
return err
}
engineConfig, err := createEngineConfig(myPrivateKey, config, loginResp.GetWiretrusteeConfig(), loginResp.GetPeerConfig())
if err != nil {
log.Error(err)
return err
}
// create start the Wiretrustee Engine that will connect to the Signal and Management streams and manage connections to remote peers.
engine := internal.NewEngine(signalClient, mgmClient, engineConfig, cancel)
err = engine.Start()
if err != nil {
log.Errorf("error while starting Wiretrustee Connection Engine: %s", err)
return err
}
SetupCloseHandler()
select {
case <-stopCh:
case <-ctx.Done():
}
log.Infof("receive signal to stop running")
err = mgmClient.Close()
if err != nil {
log.Errorf("failed closing Management Service client %v", err)
return err
}
err = signalClient.Close()
if err != nil {
log.Errorf("failed closing Signal Service client %v", err)
return err
}
err = engine.Stop()
if err != nil {
log.Errorf("failed stopping engine %v", err)
return err
}
return nil
}, },
} }
) )
func init() {
}
// createEngineConfig converts configuration received from Management Service to EngineConfig // createEngineConfig converts configuration received from Management Service to EngineConfig
func createEngineConfig(key wgtypes.Key, config *internal.Config, wtConfig *mgmProto.WiretrusteeConfig, peerConfig *mgmProto.PeerConfig) (*internal.EngineConfig, error) { func createEngineConfig(key wgtypes.Key, config *internal.Config, peerConfig *mgmProto.PeerConfig) (*internal.EngineConfig, error) {
iFaceBlackList := make(map[string]struct{}) iFaceBlackList := make(map[string]struct{})
for i := 0; i < len(config.IFaceBlackList); i += 2 { for i := 0; i < len(config.IFaceBlackList); i += 2 {
iFaceBlackList[config.IFaceBlackList[i]] = struct{}{} iFaceBlackList[config.IFaceBlackList[i]] = struct{}{}
@@ -163,7 +111,92 @@ func connectToManagement(ctx context.Context, managementAddr string, ourPrivateK
} }
} }
log.Infof("peer logged in to Management Service %s", managementAddr) log.Debugf("peer logged in to Management Service %s", managementAddr)
return client, loginResp, nil return client, loginResp, nil
} }
func runClient() error {
config, err := internal.ReadConfig(managementURL, configPath)
if err != nil {
log.Errorf("failed reading config %s %v", configPath, err)
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())
return err
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
mgmTlsEnabled := false
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, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled)
if err != nil {
log.Warn(err)
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)
return err
}
peerConfig := loginResp.GetPeerConfig()
engineConfig, err := createEngineConfig(myPrivateKey, config, peerConfig)
if err != nil {
log.Error(err)
return err
}
// create start the Wiretrustee Engine that will connect to the Signal and Management streams and manage connections to remote peers.
engine := internal.NewEngine(signalClient, mgmClient, engineConfig, cancel, ctx)
err = engine.Start()
if err != nil {
log.Errorf("error while starting Wiretrustee Connection Engine: %s", err)
return err
}
log.Print("Wiretrustee engine started, my IP is: ", peerConfig.Address)
select {
case <-stopCh:
case <-ctx.Done():
}
err = mgmClient.Close()
if err != nil {
log.Errorf("failed closing Management Service client %v", err)
return err
}
err = signalClient.Close()
if err != nil {
log.Errorf("failed closing Signal Service client %v", err)
return err
}
err = engine.Stop()
if err != nil {
log.Errorf("failed stopping engine %v", err)
return err
}
go func() {
cleanupCh <- struct{}{}
}()
log.Info("stopped Wiretrustee client")
return ctx.Err()
}

View File

@@ -1,13 +1,10 @@
package cmd package cmd
import ( import (
"errors"
"fmt"
"github.com/wiretrustee/wiretrustee/iface" "github.com/wiretrustee/wiretrustee/iface"
mgmt "github.com/wiretrustee/wiretrustee/management/server" mgmt "github.com/wiretrustee/wiretrustee/management/server"
"github.com/wiretrustee/wiretrustee/util" "github.com/wiretrustee/wiretrustee/util"
"net/url" "net/url"
"os"
"path/filepath" "path/filepath"
"testing" "testing"
"time" "time"
@@ -37,24 +34,6 @@ func TestUp_Start(t *testing.T) {
} }
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) { func TestUp(t *testing.T) {
defer iface.Close() defer iface.Close()
@@ -65,24 +44,17 @@ func TestUp(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
rootCmd.SetArgs([]string{ rootCmd.SetArgs([]string{
"login", "up",
"--config", "--config",
confPath, confPath,
"--setup-key", "--setup-key",
"A2C8E62B-38F5-4553-B31E-DD66C696CEBB", "A2C8E62B-38F5-4553-B31E-DD66C696CEBB",
"--management-url", "--management-url",
mgmtURL.String(), mgmtURL.String(),
}) "--log-file",
err = rootCmd.Execute() "console",
if err != nil {
t.Fatal(err)
}
rootCmd.SetArgs([]string{
"up",
"--config",
confPath,
}) })
go func() { go func() {
err = rootCmd.Execute() err = rootCmd.Execute()

14
client/cmd/version.go Normal file
View File

@@ -0,0 +1,14 @@
package cmd
import "github.com/spf13/cobra"
var (
Version string
versionCmd = &cobra.Command{
Use: "version",
Short: "prints wiretrustee version",
Run: func(cmd *cobra.Command, args []string) {
cmd.Println(Version)
},
}
)

View File

@@ -128,6 +128,8 @@ func (conn *Connection) Open(timeout time.Duration) error {
a, err := ice.NewAgent(&ice.AgentConfig{ a, err := ice.NewAgent(&ice.AgentConfig{
// MulticastDNSMode: ice.MulticastDNSModeQueryAndGather, // MulticastDNSMode: ice.MulticastDNSModeQueryAndGather,
NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4}, NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4},
PortMin: 57830,
PortMax: 57830,
Urls: conn.Config.StunTurnURLS, Urls: conn.Config.StunTurnURLS,
CandidateTypes: []ice.CandidateType{ice.CandidateTypeHost, ice.CandidateTypeServerReflexive, ice.CandidateTypeRelay}, CandidateTypes: []ice.CandidateType{ice.CandidateTypeHost, ice.CandidateTypeServerReflexive, ice.CandidateTypeRelay},
InterfaceFilter: func(s string) bool { InterfaceFilter: func(s string) bool {
@@ -160,13 +162,13 @@ func (conn *Connection) Open(timeout time.Duration) error {
} }
conn.Status = StatusConnecting conn.Status = StatusConnecting
log.Infof("trying to connect to peer %s", conn.Config.RemoteWgKey.String()) log.Debugf("trying to connect to peer %s", conn.Config.RemoteWgKey.String())
// wait until credentials have been sent from the remote peer (will arrive via a signal server) // wait until credentials have been sent from the remote peer (will arrive via a signal server)
select { select {
case remoteAuth := <-conn.remoteAuthChannel: case remoteAuth := <-conn.remoteAuthChannel:
log.Infof("got a connection confirmation from peer %s", conn.Config.RemoteWgKey.String()) log.Debugf("got a connection confirmation from peer %s", conn.Config.RemoteWgKey.String())
err = conn.agent.GatherCandidates() err = conn.agent.GatherCandidates()
if err != nil { if err != nil {
@@ -186,8 +188,11 @@ func (conn *Connection) Open(timeout time.Duration) error {
if err != nil { if err != nil {
return err return err
} }
useProxy := useProxy(pair)
// in case the remote peer is in the local network or one of the peers has public static IP -> no need for a Wireguard proxy, direct communication is possible. // in case the remote peer is in the local network or one of the peers has public static IP -> no need for a Wireguard proxy, direct communication is possible.
if !useProxy(pair) { if !useProxy {
log.Debugf("it is possible to establish a direct connection (without proxy) to peer %s - my addr: %s, remote addr: %s", conn.Config.RemoteWgKey.String(), pair.Local, pair.Remote) log.Debugf("it is possible to establish a direct connection (without proxy) to peer %s - my addr: %s, remote addr: %s", conn.Config.RemoteWgKey.String(), pair.Local, pair.Remote)
err = conn.wgProxy.StartLocal(fmt.Sprintf("%s:%d", pair.Remote.Address(), iface.WgPort)) err = conn.wgProxy.StartLocal(fmt.Sprintf("%s:%d", pair.Remote.Address(), iface.WgPort))
if err != nil { if err != nil {
@@ -195,19 +200,17 @@ func (conn *Connection) Open(timeout time.Duration) error {
} }
} else { } else {
log.Infof("establishing secure tunnel to peer %s via selected candidate pair %s", conn.Config.RemoteWgKey.String(), pair) log.Debugf("establishing secure tunnel to peer %s via selected candidate pair %s", conn.Config.RemoteWgKey.String(), pair)
err = conn.wgProxy.Start(remoteConn) err = conn.wgProxy.Start(remoteConn)
if err != nil { if err != nil {
return err return err
} }
} }
if pair.Remote.Type() == ice.CandidateTypeRelay || pair.Local.Type() == ice.CandidateTypeRelay { relayed := pair.Remote.Type() == ice.CandidateTypeRelay || pair.Local.Type() == ice.CandidateTypeRelay
log.Infof("using relay with peer %s", conn.Config.RemoteWgKey)
}
conn.Status = StatusConnected conn.Status = StatusConnected
log.Infof("opened connection to peer %s", conn.Config.RemoteWgKey.String()) log.Infof("opened connection to peer %s [localProxy=%v, relayed=%v]", conn.Config.RemoteWgKey.String(), useProxy, relayed)
case <-conn.closeCond.C: case <-conn.closeCond.C:
conn.Status = StatusDisconnected conn.Status = StatusDisconnected
return fmt.Errorf("connection to peer %s has been closed", conn.Config.RemoteWgKey.String()) return fmt.Errorf("connection to peer %s has been closed", conn.Config.RemoteWgKey.String())
@@ -271,7 +274,7 @@ func (conn *Connection) Close() error {
var err error var err error
conn.closeCond.Do(func() { conn.closeCond.Do(func() {
log.Warnf("closing connection to peer %s", conn.Config.RemoteWgKey.String()) log.Debugf("closing connection to peer %s", conn.Config.RemoteWgKey.String())
if a := conn.agent; a != nil { if a := conn.agent; a != nil {
e := a.Close() e := a.Close()

View File

@@ -57,6 +57,8 @@ type Engine struct {
TURNs []*ice.URL TURNs []*ice.URL
cancel context.CancelFunc cancel context.CancelFunc
ctx context.Context
} }
// Peer is an instance of the Connection Peer // Peer is an instance of the Connection Peer
@@ -66,7 +68,7 @@ type Peer struct {
} }
// NewEngine creates a new Connection Engine // NewEngine creates a new Connection Engine
func NewEngine(signalClient *signal.Client, mgmClient *mgm.Client, config *EngineConfig, cancel context.CancelFunc) *Engine { func NewEngine(signalClient *signal.Client, mgmClient *mgm.Client, config *EngineConfig, cancel context.CancelFunc, ctx context.Context) *Engine {
return &Engine{ return &Engine{
signal: signalClient, signal: signalClient,
mgmClient: mgmClient, mgmClient: mgmClient,
@@ -77,17 +79,25 @@ func NewEngine(signalClient *signal.Client, mgmClient *mgm.Client, config *Engin
STUNs: []*ice.URL{}, STUNs: []*ice.URL{},
TURNs: []*ice.URL{}, TURNs: []*ice.URL{},
cancel: cancel, cancel: cancel,
ctx: ctx,
} }
} }
func (e *Engine) Stop() error { func (e *Engine) Stop() error {
err := e.removeAllPeerConnections()
if err != nil {
return err
}
log.Debugf("removing Wiretrustee interface %s", e.config.WgIface) log.Debugf("removing Wiretrustee interface %s", e.config.WgIface)
err := iface.Close() err = iface.Close()
if err != nil { if err != nil {
log.Errorf("failed closing Wiretrustee interface %s %v", e.config.WgIface, err) log.Errorf("failed closing Wiretrustee interface %s %v", e.config.WgIface, err)
return err return err
} }
log.Infof("stopped Wiretrustee Engine")
return nil return nil
} }
@@ -127,7 +137,7 @@ func (e *Engine) Start() error {
// initializePeer peer agent attempt to open connection // initializePeer peer agent attempt to open connection
func (e *Engine) initializePeer(peer Peer) { func (e *Engine) initializePeer(peer Peer) {
var backOff = &backoff.ExponentialBackOff{ var backOff = backoff.WithContext(&backoff.ExponentialBackOff{
InitialInterval: backoff.DefaultInitialInterval, InitialInterval: backoff.DefaultInitialInterval,
RandomizationFactor: backoff.DefaultRandomizationFactor, RandomizationFactor: backoff.DefaultRandomizationFactor,
Multiplier: backoff.DefaultMultiplier, Multiplier: backoff.DefaultMultiplier,
@@ -135,19 +145,20 @@ func (e *Engine) initializePeer(peer Peer) {
MaxElapsedTime: time.Duration(0), //never stop MaxElapsedTime: time.Duration(0), //never stop
Stop: backoff.Stop, Stop: backoff.Stop,
Clock: backoff.SystemClock, Clock: backoff.SystemClock,
} }, e.ctx)
operation := func() error { operation := func() error {
_, err := e.openPeerConnection(e.wgPort, e.config.WgPrivateKey, peer) _, err := e.openPeerConnection(e.wgPort, e.config.WgPrivateKey, peer)
e.peerMux.Lock() e.peerMux.Lock()
defer e.peerMux.Unlock() defer e.peerMux.Unlock()
if _, ok := e.conns[peer.WgPubKey]; !ok { if _, ok := e.conns[peer.WgPubKey]; !ok {
log.Infof("removing connection attempt with Peer: %v, not retrying", peer.WgPubKey) log.Debugf("removed connection attempt to peer: %v, not retrying", peer.WgPubKey)
return nil return nil
} }
if err != nil { if err != nil {
log.Warnln(err) log.Warnln(err)
log.Warnln("retrying connection because of error: ", err.Error()) log.Debugf("retrying connection because of error: %s", err.Error())
return err return err
} }
return nil return nil
@@ -172,6 +183,19 @@ func (e *Engine) removePeerConnections(peers []string) error {
return nil return nil
} }
func (e *Engine) removeAllPeerConnections() error {
log.Debugf("removing all peer connections")
e.peerMux.Lock()
defer e.peerMux.Unlock()
for peer := range e.conns {
err := e.removePeerConnection(peer)
if err != nil {
return err
}
}
return nil
}
// removePeerConnection closes existing peer connection and removes peer // removePeerConnection closes existing peer connection and removes peer
func (e *Engine) removePeerConnection(peerKey string) error { func (e *Engine) removePeerConnection(peerKey string) error {
conn, exists := e.conns[peerKey] conn, exists := e.conns[peerKey]
@@ -179,6 +203,7 @@ func (e *Engine) removePeerConnection(peerKey string) error {
delete(e.conns, peerKey) delete(e.conns, peerKey)
return conn.Close() return conn.Close()
} }
log.Infof("removed connection to peer %s", peerKey)
return nil return nil
} }
@@ -310,7 +335,7 @@ func (e *Engine) receiveManagementEvents() {
e.cancel() e.cancel()
return return
} }
log.Infof("connected to Management Service updates stream") log.Debugf("stopped receiving updates from Management Service")
}() }()
log.Debugf("connecting to Management Service updates stream") log.Debugf("connecting to Management Service updates stream")
} }

View File

@@ -87,7 +87,7 @@ func (p *WgProxy) proxyToRemotePeer(remoteConn *ice.Conn) {
for { for {
select { select {
case <-p.close: case <-p.close:
log.Infof("stopped proxying from remote peer %s due to closed connection", p.remoteKey) log.Debugf("stopped proxying from remote peer %s due to closed connection", p.remoteKey)
return return
default: default:
n, err := p.wgConn.Read(buf) n, err := p.wgConn.Read(buf)
@@ -113,7 +113,7 @@ func (p *WgProxy) proxyToLocalWireguard(remoteConn *ice.Conn) {
for { for {
select { select {
case <-p.close: case <-p.close:
log.Infof("stopped proxying from remote peer %s due to closed connection", p.remoteKey) log.Debugf("stopped proxying from remote peer %s due to closed connection", p.remoteKey)
return return
default: default:
n, err := remoteConn.Read(buf) n, err := remoteConn.Read(buf)

View File

@@ -5,7 +5,11 @@ import (
"os" "os"
) )
var version = "development"
func main() { func main() {
cmd.Version = version
if err := cmd.Execute(); err != nil { if err := cmd.Execute(); err != nil {
os.Exit(1) os.Exit(1)
} }

View File

@@ -1,12 +1,86 @@
## Introduction ### Table of contents
Wiretrustee is a WireGuard®-based platform that connects your devices securely into a peer-to-peer private network. * [About Wiretrustee](#about-wiretrustee)
* [Why Wireguard with Wiretrustee?](#why-wireguard-with-wiretrustee)
* [Wiretrustee vs. Traditional VPN](#wiretrustee-vs-traditional-vpn)
* [High-level technology overview](#high-level-technology-overview)
* [Getting started](#getting-started)
It simplifies VPN creation and management for your organization without the hassle of opening ports, complex firewall rules, and so forth. ### About Wiretrustee
It literally takes less than 5 minutes to provision a secure peer-to-peer VPN with Wiretrustee. Wiretrustee is an open-source VPN platform built on top of [WireGuard®](https://www.wireguard.com/) making it easy to create secure private networks for your organization or home.
### High-level overview It requires zero configuration effort leaving behind the hassle of opening ports, complex firewall rules, vpn gateways, and so forth.
There is no centralized VPN server with Wiretrustee - your computers, devices, machines, and servers connect to each other directly over a fast encrypted tunnel.
It literally takes less than 5 minutes to provision a secure peer-to-peer VPN with Wiretrustee. Check our [Quickstart Guide Video](https://www.youtube.com/watch?v=cWTsGUJAUaU) to see the setup in action.
### Why Wireguard with Wiretrustee?
WireGuard is a modern and extremely fast VPN tunnel utilizing state-of-the-art [cryptography](https://www.wireguard.com/protocol/)
and Wiretrustee uses Wireguard to establish a secure tunnel between machines.
Built with simplicity in mind, Wireguard ensures that traffic between two machines is encrypted and flowing, however, it requires a few things to be done beforehand.
First, in order to connect, the machines have to be configured.
On each machine, you need to generate private and public keys and prepare a WireGuard configuration file.
The configuration also includes a private IP address that should be unique per machine.
Secondly, to accept the incoming traffic, the machines have to trust each other.
The generated public keys have to be pre-shared on the machines.
This works similarly to SSH with its authorised_keys file.
Lastly, the connectivity between the machines has to be ensured.
To make machines reach one another, you are required to set a WireGuard endpoint property which indicates the IP address and port of the remote machine to connect to.
On many occasions, machines are hidden behind firewalls and NAT devices,
meaning that you may need to configure a port forwarding or open holes in your firewall to ensure the machines are reachable.
The undertakings mentioned above might not be complicated if you have just a few machines, but the complexity grows as the number of machines increases.
Wiretrustee simplifies the setup by automatically generating private and public keys, assigning unique private IP addresses, and takes care of sharing public keys between the machines.
It is worth mentioning that the private key never leaves the machine.
So only the machine that owns the key can decrypt traffic addressed to it.
The same applies also to the relayed traffic mentioned below.
Furthermore, Wiretrustee ensures connectivity by leveraging advanced [NAT traversal techniques](https://en.wikipedia.org/wiki/NAT_traversal)
and removing the necessity of port forwarding, opening holes in the firewall, and having a public static IP address.
In cases when a direct peer-to-peer connection isn't possible, all traffic is relayed securely between peers.
Wiretrustee also monitors the connection health and restarts broken connections.
There are a few more things that we are working on to make secure private networks simple. A few examples are ACLs, MFA and activity monitoring.
Check out the WireGuard [Quick Start](https://www.wireguard.com/quickstart/) guide to learn more about configuring "plain" WireGuard without Wiretrustee.
### Wiretrustee vs. Traditional VPN
In the traditional VPN model, everything converges on a centralized, protected network where all the clients are connecting to a central VPN server.
An increasing amount of connections can easily overload the VPN server.
Even a short downtime of a server can cause expensive system disruptions, and a remote team's inability to work.
Centralized VPNs imply all the traffic going through the central server causing network delays and increased traffic usage.
Such systems require an experienced team to set up and maintain.
Configuring firewalls, setting up NATs, SSO integration, and managing access control lists can be a nightmare.
Traditional centralized VPNs are often compared to a [castle-and-moat](https://en.wikipedia.org/wiki/Moat) model
in which once accessed, user is trusted and can access critical infrastructure and resources without any restrictions.
Wiretrustee decentralizes networks using direct point-to-point connections, as opposed to traditional models.
Consequently, network performance is increased since traffic flows directly between the machines bypassing VPN servers or gateways.
To achieve this, Wiretrustee client applications employ signalling servers to find other machines and negotiate connections.
These are similar to the signaling servers used in [WebRTC](https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Signaling_and_video_calling#the_signaling_server)
Thanks to [NAT traversal techniques](https://en.wikipedia.org/wiki/NAT_traversal),
outlined in the [Why not just Wireguard?](#why-wireguard-with-wiretrustee) section above,
Wiretrustee installation doesn't require complex network and firewall configuration.
It just works, minimising the maintenance effort.
Finally, each machine or device in the Wiretrustee network verifies incoming connections accepting only the trusted ones.
This is ensured by Wireguard's [Crypto Routing concept](https://www.wireguard.com/#cryptokey-routing).
### High-level technology overview
In essence, Wiretrustee is an open source platform consisting of a collection of systems, responsible for handling peer-to-peer connections, tunneling and network management (IP, keys, ACLs, etc). In essence, Wiretrustee is an open source platform consisting of a collection of systems, responsible for handling peer-to-peer connections, tunneling and network management (IP, keys, ACLs, etc).
<p align="center"> <p align="center">
@@ -24,8 +98,7 @@ There are 2 ways of getting started with Wiretrustee:
- use Cloud Managed version - use Cloud Managed version
- self-hosting - self-hosting
We recommend starting with the cloud managed version hosted at [beta.wiretrustee.com](https://beta.wiretrustee.com) - the quickest way to get familiar with the system. We recommend starting with the cloud managed version hosted at [app.wiretrustee.com](https://app.wiretrustee.com) - the quickest way to get familiar with the system.
See [Quickstart Guide](../docs/quickstart.md) for instructions. See [Quickstart Guide](../docs/quickstart.md) for instructions.
If you don't want to use the managed version, check out our [Self-hosting Guide](../docs/self-hosting.md). If you don't want to use the managed version, check out our [Self-hosting Guide](../docs/self-hosting.md).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 42 KiB

BIN
docs/media/logo-full.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/media/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
docs/media/peerA.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 KiB

BIN
docs/media/peerB.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 KiB

View File

@@ -8,7 +8,7 @@ This guide describes how to create secure VPN and connect 2 machines peer-to-pee
One machine is a Raspberry Pi Compute Module 4 hosted at home (Peer A), and the other one is a regular Ubuntu server running in the Data Center (Peer B). One machine is a Raspberry Pi Compute Module 4 hosted at home (Peer A), and the other one is a regular Ubuntu server running in the Data Center (Peer B).
Both machines are running Linux (Raspbian and Ubuntu respectively), but you could also use Mac or Windows operating systems. Both machines are running Linux (Raspbian and Ubuntu respectively), but you could also use Mac or Windows operating systems.
1. Sign-up at [https://beta.wiretrustee.com/](https://beta.wiretrustee.com/peers) 1. Sign-up at [https://app.wiretrustee.com/](https://app.wiretrustee.com/peers)
You can use your email and password to sign-up or any available social login option (e.g., GitHub account) You can use your email and password to sign-up or any available social login option (e.g., GitHub account)

View File

@@ -10,13 +10,17 @@ We focused on connectivity instead.
If you would like to learn more about the architecture please refer to the [Wiretrustee Architecture section](architecture.md). If you would like to learn more about the architecture please refer to the [Wiretrustee Architecture section](architecture.md).
### Step-by-step video guide on YouTube:
[![IMAGE ALT TEXT](https://img.youtube.com/vi/Ofpgx5WhT0k/0.jpg)](https://youtu.be/Ofpgx5WhT0k "Wiretrustee Self-Hosting Guide")
### Requirements ### Requirements
- Virtual machine offered by any cloud provider (e.g., AWS, DigitalOcean, Hetzner, Google Cloud, Azure ...). - Virtual machine offered by any cloud provider (e.g., AWS, DigitalOcean, Hetzner, Google Cloud, Azure ...).
- Any Linux OS. - Any Linux OS.
- Docker Compose installed (see [Install Docker Compose](https://docs.docker.com/compose/install/)). - Docker Compose installed (see [Install Docker Compose](https://docs.docker.com/compose/install/)).
- Domain name pointing to the public IP address of your server. - Domain name pointing to the public IP address of your server.
- Open ports ```443, 33071, 33073, 3478``` (Dashboard, Management HTTP API, Management gRpc API, Coturn STUN/TURN respectively) on your server. - Open ports ```443, 33071, 33073, 10000, 3478``` (Dashboard, Management HTTP API, Management gRpc API, Signal gRpc, Coturn STUN/TURN respectively) on your server.
- Maybe a cup of coffee or tea :) - Maybe a cup of coffee or tea :)
### Step-by-step guide ### Step-by-step guide
@@ -68,7 +72,7 @@ For this tutorial we will be using domain ```test.wiretrustee.com``` which point
* set the property in the ```setup.env``` file. * set the property in the ```setup.env``` file.
6. Configure ```WIRETRUSTEE_LETSENCRYPT_EMAIL``` property. 6. Configure ```WIRETRUSTEE_LETSENCRYPT_EMAIL``` property.
This can be any email address. [Let's Encrypt](https://letsencrypt.org/) will create an account while creating a new domain. This can be any email address. [Let's Encrypt](https://letsencrypt.org/) will create an account while generating a new certificate.
7. Make sure all the properties set in the ```setup.env``` file and run: 7. Make sure all the properties set in the ```setup.env``` file and run:

2
go.mod
View File

@@ -8,7 +8,7 @@ require (
github.com/golang/protobuf v1.5.2 github.com/golang/protobuf v1.5.2
github.com/google/uuid v1.2.0 github.com/google/uuid v1.2.0
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/kardianos/service v1.2.0 github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7
github.com/onsi/ginkgo v1.16.4 github.com/onsi/ginkgo v1.16.4
github.com/onsi/gomega v1.13.0 github.com/onsi/gomega v1.13.0
github.com/pion/ice/v2 v2.1.7 github.com/pion/ice/v2 v2.1.7

4
go.sum
View File

@@ -146,8 +146,8 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kardianos/service v1.2.0 h1:bGuZ/epo3vrt8IPC7mnKQolqFeYJb7Cs8Rk4PSOBB/g= github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7 h1:oohm9Rk9JAxxmp2NLZa7Kebgz9h4+AJDcc64txg3dQ0=
github.com/kardianos/service v1.2.0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=

View File

@@ -31,8 +31,7 @@ services:
# command: ["--letsencrypt-domain", "$WIRETRUSTEE_DOMAIN", "--log-file", "console"] # command: ["--letsencrypt-domain", "$WIRETRUSTEE_DOMAIN", "--log-file", "console"]
# Management # Management
management: management:
# image: wiretrustee/management:latest image: wiretrustee/management:latest
image: wiretrustee/management:v0.1.0-beta.2-SNAPSHOT-39d450b-amd64
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
- dashboard - dashboard

View File

@@ -45,7 +45,7 @@ func NewClient(ctx context.Context, addr string, ourPrivateKey wgtypes.Key, tlsE
})) }))
if err != nil { if err != nil {
log.Errorf("failed creating connection to Management Srvice %v", err) log.Errorf("failed creating connection to Management Service %v", err)
return nil, err return nil, err
} }
@@ -65,8 +65,8 @@ func (c *Client) Close() error {
} }
//defaultBackoff is a basic backoff mechanism for general issues //defaultBackoff is a basic backoff mechanism for general issues
func defaultBackoff() backoff.BackOff { func defaultBackoff(ctx context.Context) backoff.BackOff {
return &backoff.ExponentialBackOff{ return backoff.WithContext(&backoff.ExponentialBackOff{
InitialInterval: 800 * time.Millisecond, InitialInterval: 800 * time.Millisecond,
RandomizationFactor: backoff.DefaultRandomizationFactor, RandomizationFactor: backoff.DefaultRandomizationFactor,
Multiplier: backoff.DefaultMultiplier, Multiplier: backoff.DefaultMultiplier,
@@ -74,14 +74,14 @@ func defaultBackoff() backoff.BackOff {
MaxElapsedTime: 24 * 3 * time.Hour, //stop after 3 days trying MaxElapsedTime: 24 * 3 * time.Hour, //stop after 3 days trying
Stop: backoff.Stop, Stop: backoff.Stop,
Clock: backoff.SystemClock, Clock: backoff.SystemClock,
} }, ctx)
} }
// Sync wraps the real client's Sync endpoint call and takes care of retries and encryption/decryption of messages // Sync wraps the real client's Sync endpoint call and takes care of retries and encryption/decryption of messages
// Blocking request. The result will be sent via msgHandler callback function // Blocking request. The result will be sent via msgHandler callback function
func (c *Client) Sync(msgHandler func(msg *proto.SyncResponse) error) error { func (c *Client) Sync(msgHandler func(msg *proto.SyncResponse) error) error {
var backOff = defaultBackoff() var backOff = defaultBackoff(c.ctx)
operation := func() error { operation := func() error {
@@ -114,7 +114,7 @@ func (c *Client) Sync(msgHandler func(msg *proto.SyncResponse) error) error {
err := backoff.Retry(operation, backOff) err := backoff.Retry(operation, backOff)
if err != nil { if err != nil {
log.Errorf("exiting Management Service connection retry loop due to unrecoverable error %s ", err) log.Warnf("exiting Management Service connection retry loop due to unrecoverable error: %s", err)
return err return err
} }
@@ -145,7 +145,7 @@ func (c *Client) receiveEvents(stream proto.ManagementService_SyncClient, server
return err return err
} }
if err != nil { if err != nil {
log.Errorf("disconnected from Management Service sync stream: %v", err) log.Warnf("disconnected from Management Service sync stream: %v", err)
return err return err
} }
@@ -159,7 +159,7 @@ func (c *Client) receiveEvents(stream proto.ManagementService_SyncClient, server
err = msgHandler(decryptedResp) err = msgHandler(decryptedResp)
if err != nil { if err != nil {
log.Errorf("failed handling an update message received from Management Service %v", err.Error()) log.Errorf("failed handling an update message received from Management Service: %v", err.Error())
return err return err
} }
} }

View File

@@ -12,30 +12,27 @@ fi
cleanInstall() { cleanInstall() {
printf "\033[32m Post Install of an clean install\033[0m\n" printf "\033[32m Post Install of an clean install\033[0m\n"
# Step 3 (clean install), enable the service in the proper way for this platform # Step 3 (clean install), enable the service in the proper way for this platform
if [ "${use_systemctl}" = "True" ]; then /usr/local/bin/wiretrustee service install
printf "\033[32m Reload the service unit from disk\033[0m\n" /usr/local/bin/wiretrustee service start
systemctl daemon-reload ||:
printf "\033[32m Unmask the service\033[0m\n"
systemctl unmask wiretrustee ||:
printf "\033[32m Set the preset flag for the service unit\033[0m\n"
systemctl preset wiretrustee ||:
printf "\033[32m Set the enabled flag for the service unit\033[0m\n"
systemctl enable wiretrustee ||:
systemctl restart wiretrustee ||:
fi
} }
upgrade() { upgrade() {
printf "\033[32m Post Install of an upgrade\033[0m\n" printf "\033[32m Post Install of an upgrade\033[0m\n"
if [ "${use_systemctl}" = "True" ]; then if [ "${use_systemctl}" = "True" ]; then
printf "\033[32m Reload the service unit from disk\033[0m\n" printf "\033[32m Stopping the service\033[0m\n"
systemctl daemon-reload ||: systemctl stop wiretrustee 2> /dev/null || true
printf "\033[32m Restarting the service\033[0m\n"
systemctl restart wiretrustee ||:
fi fi
if [ -e /lib/systemd/system/wiretrustee.service ]; then
rm -f /lib/systemd/system/wiretrustee.service
systemctl daemon-reload
fi
# will trow an error until everyone upgrade
/usr/local/bin/wiretrustee service uninstall 2> /dev/null || true
/usr/local/bin/wiretrustee service install
/usr/local/bin/wiretrustee service start
} }
# Step 2, check if this is a clean install or an upgrade # Check if this is a clean install or an upgrade
action="$1" action="$1"
if [ "$1" = "configure" ] && [ -z "$2" ]; then if [ "$1" = "configure" ] && [ -z "$2" ]; then
# Alpine linux does not pass args, and deb passes $1=configure # Alpine linux does not pass args, and deb passes $1=configure
@@ -50,12 +47,9 @@ case "$action" in
cleanInstall cleanInstall
;; ;;
"2" | "upgrade") "2" | "upgrade")
printf "\033[32m Post Install of an upgrade\033[0m\n"
upgrade upgrade
;; ;;
*) *)
# $1 == version being installed
printf "\033[32m install\033[0m"
cleanInstall cleanInstall
;; ;;
esac esac

View File

@@ -0,0 +1,43 @@
#!/bin/sh
# decide if we should use systemd or init/upstart
use_systemctl="True"
systemd_version=0
if ! command -V systemctl >/dev/null 2>&1; then
use_systemctl="False"
else
systemd_version=$(systemctl --version | head -1 | sed 's/systemd //g')
fi
remove() {
printf "\033[32m Pre uninstall\033[0m\n"
if [ "${use_systemctl}" = "True" ]; then
printf "\033[32m Stopping the service\033[0m\n"
systemctl stop wiretrustee || true
if [ -e /lib/systemd/system/wiretrustee.service ]; then
rm -f /lib/systemd/system/wiretrustee.service
systemctl daemon-reload || true
fi
fi
printf "\033[32m Uninstalling the service\033[0m\n"
/usr/local/bin/wiretrustee service uninstall || true
if [ "${use_systemctl}" = "True" ]; then
printf "\n\033[32m running daemon reload\033[0m\n"
systemctl daemon-reload || true
fi
}
action="$1"
case "$action" in
"0" | "remove")
remove
;;
*)
exit 0
;;
esac

View File

@@ -1,30 +0,0 @@
{
"PrivateKey": "",
"Peers": [
{
"WgPubKey": "",
"WgAllowedIps": ""
}
],
"StunTurnURLs": [
{
"Scheme": 1,
"Host": "",
"Port": 3468,
"Username": "",
"Password": "",
"Proto": 1
},
{
"Scheme": 3,
"Host": "",
"Port": 3468,
"Username": "",
"Password": "",
"Proto": 1
}
],
"SignalAddr": "",
"WgAddr": "",
"WgIface": ""
}

View File

@@ -1,10 +0,0 @@
[Unit]
Description=Wiretrustee Service
After=multi-user.target network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/wiretrustee up --config /etc/wiretrustee/config.json --log-level debug
[Install]
WantedBy=multi-user.target

View File

@@ -76,8 +76,8 @@ func NewClient(ctx context.Context, addr string, key wgtypes.Key, tlsEnabled boo
} }
//defaultBackoff is a basic backoff mechanism for general issues //defaultBackoff is a basic backoff mechanism for general issues
func defaultBackoff() backoff.BackOff { func defaultBackoff(ctx context.Context) backoff.BackOff {
return &backoff.ExponentialBackOff{ return backoff.WithContext(&backoff.ExponentialBackOff{
InitialInterval: 800 * time.Millisecond, InitialInterval: 800 * time.Millisecond,
RandomizationFactor: backoff.DefaultRandomizationFactor, RandomizationFactor: backoff.DefaultRandomizationFactor,
Multiplier: backoff.DefaultMultiplier, Multiplier: backoff.DefaultMultiplier,
@@ -85,7 +85,8 @@ func defaultBackoff() backoff.BackOff {
MaxElapsedTime: 24 * 3 * time.Hour, //stop after 3 days trying MaxElapsedTime: 24 * 3 * time.Hour, //stop after 3 days trying
Stop: backoff.Stop, Stop: backoff.Stop,
Clock: backoff.SystemClock, Clock: backoff.SystemClock,
} }, ctx)
} }
// Receive Connects to the Signal Exchange message stream and starts receiving messages. // Receive Connects to the Signal Exchange message stream and starts receiving messages.
@@ -96,12 +97,13 @@ func (c *Client) Receive(msgHandler func(msg *proto.Message) error) {
c.connWg.Add(1) c.connWg.Add(1)
go func() { go func() {
var backOff = defaultBackoff() var backOff = defaultBackoff(c.ctx)
operation := func() error { operation := func() error {
err := c.connect(c.key.PublicKey().String(), msgHandler) err := c.connect(c.key.PublicKey().String(), msgHandler)
if err != nil { if err != nil {
log.Warnf("disconnected from the Signal Exchange due to an error %s. Retrying ... ", err) log.Warnf("disconnected from the Signal Exchange due to an error: %v", err)
c.connWg.Add(1) c.connWg.Add(1)
return err return err
} }
@@ -112,7 +114,7 @@ func (c *Client) Receive(msgHandler func(msg *proto.Message) error) {
err := backoff.Retry(operation, backOff) err := backoff.Retry(operation, backOff)
if err != nil { if err != nil {
log.Errorf("error while communicating with the Signal Exchange %s ", err) log.Errorf("exiting Signal Service connection retry loop due to unrecoverable error: %s", err)
return return
} }
}() }()