mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-16 15:26:40 +00:00
Compare commits
117 Commits
v0.0.3
...
v0.0.8-hot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a362199f8 | ||
|
|
dd50f495ab | ||
|
|
bb2477491f | ||
|
|
f4d7faaf4e | ||
|
|
cffb08ad23 | ||
|
|
8d05789749 | ||
|
|
ca5970140f | ||
|
|
ac628b6efa | ||
|
|
80665049dc | ||
|
|
881f078759 | ||
|
|
1cf9b143e0 | ||
|
|
158547f3eb | ||
|
|
ab6452065d | ||
|
|
e553c5e97e | ||
|
|
3041ff4ef7 | ||
|
|
61a7f3013b | ||
|
|
dac865c61f | ||
|
|
a40669270a | ||
|
|
f2ca2fc7c1 | ||
|
|
729b16e599 | ||
|
|
561bd681d9 | ||
|
|
0e313eec24 | ||
|
|
4216cd2986 | ||
|
|
c18899d135 | ||
|
|
20248dadb7 | ||
|
|
1a06518f1b | ||
|
|
dd72a01ecf | ||
|
|
bbfbf797d5 | ||
|
|
52db303104 | ||
|
|
5122294adf | ||
|
|
a87f828844 | ||
|
|
8088c7a591 | ||
|
|
74355a2292 | ||
|
|
a66cdccda9 | ||
|
|
06c7af058b | ||
|
|
41b50a08d4 | ||
|
|
3c45da553a | ||
|
|
8dfccfc800 | ||
|
|
021092800b | ||
|
|
aa854c5899 | ||
|
|
e41fdedd5b | ||
|
|
923cabda9a | ||
|
|
db673ed34f | ||
|
|
6465e2556a | ||
|
|
89dba7951a | ||
|
|
9308a51800 | ||
|
|
94c0091a7b | ||
|
|
f247f9a2f8 | ||
|
|
c49bd23ac5 | ||
|
|
11174a50cd | ||
|
|
dfcf9f9087 | ||
|
|
5f8a489f90 | ||
|
|
9b9c7ada7d | ||
|
|
8b31088968 | ||
|
|
00f2ee34a0 | ||
|
|
51337fbf65 | ||
|
|
ca83e8c4a0 | ||
|
|
2784f6a098 | ||
|
|
6b5010f7d5 | ||
|
|
714c4c3c44 | ||
|
|
d5c4f6cb40 | ||
|
|
7df6cde968 | ||
|
|
744984861b | ||
|
|
83fe84d11a | ||
|
|
e059059e62 | ||
|
|
06b0c46a5d | ||
|
|
8acddfd510 | ||
|
|
caf2229d3b | ||
|
|
698ebe2287 | ||
|
|
54235f0a77 | ||
|
|
05168ae12f | ||
|
|
255ad7faa9 | ||
|
|
6e4c232ff2 | ||
|
|
59360519d6 | ||
|
|
3520b6471b | ||
|
|
74061597a3 | ||
|
|
33a98c7a2c | ||
|
|
9b327ea6ba | ||
|
|
45697a0000 | ||
|
|
884cd8dc55 | ||
|
|
f8eaf2f40e | ||
|
|
0609e1d75d | ||
|
|
8c9bc96c85 | ||
|
|
68112870dc | ||
|
|
ae69f4cf1b | ||
|
|
c8ad10d653 | ||
|
|
e622b2a529 | ||
|
|
44d5e7f205 | ||
|
|
790858c31b | ||
|
|
5342f10e7f | ||
|
|
f0048d16fb | ||
|
|
635cd2202d | ||
|
|
a773ec8150 | ||
|
|
84c6eb5e16 | ||
|
|
73720951d7 | ||
|
|
6d339295be | ||
|
|
f1cff0e13a | ||
|
|
e6358e7bb2 | ||
|
|
2337c3d84d | ||
|
|
9554247d70 | ||
|
|
a9d3ce227a | ||
|
|
33e9ecfcb9 | ||
|
|
7446ef857b | ||
|
|
8adf92a957 | ||
|
|
d47d14a7d3 | ||
|
|
47933bcbfa | ||
|
|
7080309842 | ||
|
|
74f7da61cc | ||
|
|
123687c2f1 | ||
|
|
bbf57b064c | ||
|
|
8c1cf88e07 | ||
|
|
d08b61b31d | ||
|
|
48659ff4ac | ||
|
|
6b8465886d | ||
|
|
06ca0853b6 | ||
|
|
54e4e74883 | ||
|
|
682049b49c |
50
.github/workflows/golang-test.yml
vendored
Normal file
50
.github/workflows/golang-test.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
name: Test
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.16.x]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Test
|
||||
run: GOBIN=$(which go) && sudo --preserve-env=GOROOT $GOBIN test ./...
|
||||
|
||||
test_build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows, linux, darwin ]
|
||||
go-version: [1.16.x]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Install modules
|
||||
run: go mod tidy
|
||||
|
||||
- name: run build
|
||||
run: GOOS=${{ matrix.os }} go build .
|
||||
14
.github/workflows/golangci-lint.yml
vendored
Normal file
14
.github/workflows/golangci-lint.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: golangci-lint
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
13
.github/workflows/release.yml
vendored
13
.github/workflows/release.yml
vendored
@@ -30,10 +30,21 @@ jobs:
|
||||
-
|
||||
name: Install modules
|
||||
run: go mod tidy
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Login to Docker hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
-
|
||||
name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
project_name: wiretrustee
|
||||
builds:
|
||||
- env: [CGO_ENABLED=0]
|
||||
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
goarch:
|
||||
- arm
|
||||
- amd64
|
||||
@@ -11,6 +13,12 @@ builds:
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
tags:
|
||||
- load_wintun_from_rsrc
|
||||
nfpms:
|
||||
- maintainer: Wiretrustee <wiretrustee@wiretrustee.com>
|
||||
description: Wiretrustee project.
|
||||
@@ -28,3 +36,41 @@ nfpms:
|
||||
|
||||
scripts:
|
||||
postinstall: "release_files/post_install.sh"
|
||||
dockers:
|
||||
- image_templates:
|
||||
- wiretrustee/wiretrustee:signal-{{ .Version }}-amd64
|
||||
goarch: amd64
|
||||
use_buildx: true
|
||||
dockerfile: Dockerfile
|
||||
build_flag_templates:
|
||||
- "--platform=linux/amd64"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||
- "--label=maintainer=wiretrustee@wiretrustee.com"
|
||||
- image_templates:
|
||||
- wiretrustee/wiretrustee:signal-{{ .Version }}-arm64v8
|
||||
goarch: arm64
|
||||
use_buildx: true
|
||||
dockerfile: Dockerfile
|
||||
build_flag_templates:
|
||||
- "--platform=linux/arm64"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||
- "--label=maintainer=wiretrustee@wiretrustee.com"
|
||||
|
||||
docker_manifests:
|
||||
- name_template: wiretrustee/wiretrustee:signal-{{ .Version }}
|
||||
image_templates:
|
||||
- wiretrustee/wiretrustee:signal-{{ .Version }}-arm64v8
|
||||
- wiretrustee/wiretrustee:signal-{{ .Version }}-amd64
|
||||
|
||||
- name_template: wiretrustee/wiretrustee:signal-latest
|
||||
image_templates:
|
||||
- wiretrustee/wiretrustee:signal-{{ .Version }}-arm64v8
|
||||
- wiretrustee/wiretrustee:signal-{{ .Version }}-amd64
|
||||
2
AUTHORS
Normal file
2
AUTHORS
Normal file
@@ -0,0 +1,2 @@
|
||||
Mikhail Bragin (https://github.com/braginini)
|
||||
Maycon Santos (https://github.com/mlsmaycon)
|
||||
5
Dockerfile
Normal file
5
Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
||||
FROM gcr.io/distroless/base:debug
|
||||
EXPOSE 10000
|
||||
ENTRYPOINT [ "/go/bin/wiretrustee","signal" ]
|
||||
CMD ["--log-level","DEBUG"]
|
||||
COPY wiretrustee /go/bin/wiretrustee
|
||||
13
LICENSE
Normal file
13
LICENSE
Normal file
@@ -0,0 +1,13 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021 Wiretrustee AUTHORS
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
192
README.md
Normal file
192
README.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# Wiretrustee
|
||||
|
||||
A WireGuard®-based mesh network that connects your devices into a single private network.
|
||||
|
||||
### 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.
|
||||
* No need to open ports and expose public IPs on the device.
|
||||
* Automatically reconnects in case of network failures or switches.
|
||||
* Automatic NAT traversal.
|
||||
* Relay server fallback in case of an unsuccessful peer-to-peer connection.
|
||||
* Private key never leaves your device.
|
||||
* Works on ARM devices (e.g. Raspberry Pi).
|
||||
|
||||
### A bit on Wiretrustee internals
|
||||
* Wiretrustee uses WebRTC ICE implemented in [pion/ice library](https://github.com/pion/ice) to discover connection candidates when establishing a peer-to-peer connection between devices.
|
||||
* A connection session negotiation between peers is achieved with the Wiretrustee Signalling server [signal](signal/)
|
||||
* Contents of the messages sent between peers through the signaling server are encrypted with Wireguard keys, making it impossible to inspect them.
|
||||
The routing of the messages on a Signalling server is based on public Wireguard keys.
|
||||
* Occasionally, the NAT-traversal is unsuccessful due to strict NATs (e.g. mobile carrier-grade NAT).
|
||||
For that matter, there is support for a relay server fallback (TURN) and a secure Wireguard tunnel is established via TURN server.
|
||||
[Coturn](https://github.com/coturn/coturn) is the one that has been successfully used for STUN and TURN in Wiretrustee setups.
|
||||
|
||||
### What Wiretrustee is not doing:
|
||||
* Wireguard key management. In consequence, you need to generate peer keys and specify them on Wiretrustee initialization step. This feature is on the roadmap.
|
||||
* Peer address management. You have to specify a unique peer local address (e.g. 10.30.30.1/24) when configuring Wiretrustee. This feature is on the roadmap.
|
||||
|
||||
### Product Roadmap
|
||||
- [Public Roadmap](https://github.com/wiretrustee/wiretrustee/projects/2)
|
||||
- [Public Roadmap Progress Tracking](https://github.com/wiretrustee/wiretrustee/projects/1)
|
||||
|
||||
### Client Installation
|
||||
#### Linux
|
||||
1. Checkout Wiretrustee [releases](https://github.com/wiretrustee/wiretrustee/releases)
|
||||
2. Download the latest release (**Switch VERSION to the latest**):
|
||||
|
||||
**Debian packages**
|
||||
```shell
|
||||
wget https://github.com/wiretrustee/wiretrustee/releases/download/v<VERSION>/wiretrustee_<VERSION>_linux_amd64.deb
|
||||
```
|
||||
3. Install the package
|
||||
```shell
|
||||
sudo dpkg -i wiretrustee_<VERSION>_linux_amd64.deb
|
||||
```
|
||||
**Fedora/Centos packages**
|
||||
```shell
|
||||
wget https://github.com/wiretrustee/wiretrustee/releases/download/v<VERSION>/wiretrustee_<VERSION>_linux_amd64.rpm
|
||||
```
|
||||
3. Install the package
|
||||
```shell
|
||||
sudo rpm -i wiretrustee_<VERSION>_linux_amd64.rpm
|
||||
```
|
||||
#### MACOS
|
||||
1. Checkout Wiretrustee [releases](https://github.com/wiretrustee/wiretrustee/releases/latest)
|
||||
2. Download the latest release (**Switch VERSION to the latest**):
|
||||
```shell
|
||||
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
|
||||
```shell
|
||||
tar xcf ./wiretrustee_<VERSION>_darwin_amd64.tar.gz
|
||||
sudo mv wiretrusee /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:
|
||||
````shell
|
||||
export PATH=$PATH:/usr/local/bin
|
||||
````
|
||||
|
||||
#### Windows
|
||||
1. Checkout Wiretrustee [releases](https://github.com/wiretrustee/wiretrustee/releases/latest)
|
||||
2. Download the latest Windows release ```wiretrustee_<VERSION>_windows_amd64.tar.gz``` (**Switch VERSION to the latest**):
|
||||
3. Decompress and move to a more fixed path in your system
|
||||
4. Open Powershell
|
||||
5. For Windows systems, we can use the service command to configure Wiretrustee as a service by running the following commands in Powershell:
|
||||
````shell
|
||||
cd C:\path\to\wiretrustee\bin
|
||||
.\wiretrustee.exe service --help
|
||||
.\wiretrustee.exe service install # This will prompt for administrator permissions in order to install a new service
|
||||
````
|
||||
> You may need to run Powershell as Administrator
|
||||
6. After installing you can follow the [Client Configuration](#Client-Configuration) steps.
|
||||
7. To uninstall the service simple run the command above with the uninstall flag:
|
||||
````shell
|
||||
.\wiretrustee.exe service uninstall
|
||||
````
|
||||
|
||||
### Client Configuration
|
||||
1. Initialize Wiretrustee:
|
||||
|
||||
For **Unix** systems:
|
||||
```shell
|
||||
sudo wiretrustee init \
|
||||
--stunURLs stun:stun.wiretrustee.com:3468,stun:stun.l.google.com:19302 \
|
||||
--turnURLs <TURN User>:<TURN password>@turn:stun.wiretrustee.com:3468 \
|
||||
--signalAddr signal.wiretrustee.com:10000 \
|
||||
--wgLocalAddr 10.30.30.1/24 \
|
||||
--log-level info
|
||||
```
|
||||
For **Windows** systems:
|
||||
```shell
|
||||
.\wiretrustee.exe init `
|
||||
--stunURLs stun:stun.wiretrustee.com:3468,stun:stun.l.google.com:19302 `
|
||||
--turnURLs <TURN User>:<TURN password>@turn:stun.wiretrustee.com:3468 `
|
||||
--signalAddr signal.wiretrustee.com:10000 `
|
||||
--wgLocalAddr 10.30.30.1/24 `
|
||||
--log-level info
|
||||
```
|
||||
|
||||
It is important to mention that the ```wgLocalAddr``` parameter has to be unique across your network.
|
||||
E.g. if you have Peer A with ```wgLocalAddr=10.30.30.1/24``` then another Peer B can have ```wgLocalAddr=10.30.30.2/24```
|
||||
|
||||
If for some reason, you already have a generated Wireguard key, you can specify it with the ```--wgKey``` parameter.
|
||||
If not specified, then a new one will be generated, and its corresponding public key will be output to the log.
|
||||
A new config will be generated and stored under ```/etc/wiretrustee/config.json```
|
||||
|
||||
2. Add a peer to connect to.
|
||||
|
||||
For **Unix** systems:
|
||||
```shell
|
||||
sudo wiretrustee add-peer --allowedIPs 10.30.30.2/32 --key '<REMOTE PEER WIREUARD PUBLIC KEY>'
|
||||
```
|
||||
For **Windows** systems:
|
||||
```shell
|
||||
.\wiretrustee.exe add-peer --allowedIPs 10.30.30.2/32 --key '<REMOTE PEER WIREUARD PUBLIC KEY>'
|
||||
```
|
||||
3. Restart Wiretrustee to reload changes
|
||||
For **MACOS** you will just start the service:
|
||||
````shell
|
||||
sudo wiretrustee up --log-level info
|
||||
# or
|
||||
sudo wiretrustee up --log-level info & # to run it in background
|
||||
````
|
||||
For **Linux** systems:
|
||||
```shell
|
||||
sudo systemctl restart wiretrustee.service
|
||||
sudo systemctl status wiretrustee.service
|
||||
```
|
||||
For **Windows** systems:
|
||||
```shell
|
||||
.\wiretrustee.exe service start
|
||||
```
|
||||
> You may need to run Powershell as Administrator
|
||||
### Running the Signal service
|
||||
After installing the application, you can run the signal using the command below:
|
||||
````shell
|
||||
/usr/local/bin/wiretrustee signal --log-level INFO
|
||||
````
|
||||
This will launch the Signal server on port 10000, in case you want to change the port, use the flag --port.
|
||||
#### Docker image
|
||||
We have packed the Signal server into docker image. You can pull the image from Docker Hub and execute it with the following commands:
|
||||
````shell
|
||||
docker pull wiretrustee/wiretrustee:signal-latest
|
||||
docker run -d --name wiretrustee-signal -p 10000:10000 wiretrustee/wiretrustee:signal-latest
|
||||
````
|
||||
The default log-level is set to INFO, if you need you can change it using by updating the docker cmd as followed:
|
||||
````shell
|
||||
docker run -d --name wiretrustee-signal -p 10000:10000 wiretrustee/wiretrustee:signal-latest --log-level DEBUG
|
||||
````
|
||||
|
||||
### Running Signal and Coturn
|
||||
Under infrastructure_files we have a docker-compose example to run both, Wiretrustee Signal server and an instance of [Coturn](https://github.com/coturn/coturn), it also provides a turnserver.conf file as a simple example of Coturn configuration.
|
||||
You can edit the turnserver.conf file and change its Realm setting (defaults to wiretrustee.com) to your own domain and user setting (defaults to username1:password1) to **proper credentials**.
|
||||
|
||||
The example is set to use the official images from Wiretrustee and Coturn, you can find our documentation to run the signal server in docker in [Running the Signal service](#Running the Signal service) and the Coturn official documentation [here](https://hub.docker.com/r/coturn/coturn).
|
||||
|
||||
> Run Coturn at your own risk, we are just providing an example, be sure to follow security best practices and to configure proper credentials as this service can be exploited and you may face large data transfer charges.
|
||||
|
||||
Also, if you have an SSL certificate you can modify the docker-compose.yml file to point to its files in your host machine, then switch the domainname to your own SSL domain. If you don't already have an SSL certificate, you can follow [Certbot's](https://certbot.eff.org/docs/intro.html) official documentation
|
||||
to generate one from [Let’s Encrypt](https://letsencrypt.org/), or, we found that the example provided by [BigBlueButton](https://docs.bigbluebutton.org/2.2/setup-turn-server.html#generating-tls-certificates) covers the basics to configure Coturn with Let's Encrypt certs.
|
||||
|
||||
Simple docker-composer execution:
|
||||
````shell
|
||||
cd infrastructure_files
|
||||
docker-compose up -d
|
||||
````
|
||||
You can check logs by running:
|
||||
````shell
|
||||
cd infrastructure_files
|
||||
docker-compose logs signal
|
||||
docker-compose logs coturn
|
||||
````
|
||||
If you need to stop the services, run the following:
|
||||
````shell
|
||||
cd infrastructure_files
|
||||
docker-compose down
|
||||
````
|
||||
|
||||
|
||||
### Legal
|
||||
[WireGuard](https://wireguard.com/) is a registered trademark of Jason A. Donenfeld.
|
||||
|
||||
@@ -40,6 +40,6 @@ var (
|
||||
func init() {
|
||||
addPeerCmd.PersistentFlags().StringVar(&key, "key", "", "Wireguard public key of the remote peer")
|
||||
addPeerCmd.PersistentFlags().StringVar(&allowedIPs, "allowedIPs", "", "Wireguard Allowed IPs for the remote peer, e.g 10.30.30.2/32")
|
||||
addPeerCmd.MarkPersistentFlagRequired("key")
|
||||
addPeerCmd.MarkPersistentFlagRequired("allowedIPs")
|
||||
addPeerCmd.MarkPersistentFlagRequired("key") //nolint
|
||||
addPeerCmd.MarkPersistentFlagRequired("allowedIPs") //nolint
|
||||
}
|
||||
|
||||
@@ -2,25 +2,37 @@ package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/pion/ice/v2"
|
||||
ice "github.com/pion/ice/v2"
|
||||
"github.com/wiretrustee/wiretrustee/connection"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Config Configuration type
|
||||
type Config struct {
|
||||
// Wireguard private key of local peer
|
||||
PrivateKey string
|
||||
Peers []connection.Peer
|
||||
StunTurnURLs []*ice.URL
|
||||
// host:port of the signal server
|
||||
SignalAddr string
|
||||
WgAddr string
|
||||
WgIface string
|
||||
SignalAddr string
|
||||
WgAddr string
|
||||
WgIface string
|
||||
IFaceBlackList []string
|
||||
}
|
||||
|
||||
//Write writes configPath to a file
|
||||
func (cfg *Config) Write(path string) error {
|
||||
|
||||
if path == defaultConfigPath {
|
||||
configDir := filepath.Dir(path)
|
||||
err := os.MkdirAll(configDir, 0750)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
bs, err := json.Marshal(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
23
cmd/init.go
23
cmd/init.go
@@ -1,7 +1,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/pion/ice/v2"
|
||||
ice "github.com/pion/ice/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
@@ -30,8 +30,17 @@ var (
|
||||
|
||||
if wgKey == "" {
|
||||
wgKey = generateKey()
|
||||
log.Warnf("there was no Wireguard private key specified, a new Wireguard key has been generated")
|
||||
}
|
||||
|
||||
parsedKey, err := wgtypes.ParseKey(wgKey)
|
||||
if err != nil {
|
||||
log.Errorf("invalid Wireguard private key %s", wgKey)
|
||||
os.Exit(ExitSetupFailed)
|
||||
}
|
||||
|
||||
log.Infof("my public Wireguard key is %s", parsedKey.PublicKey().String())
|
||||
|
||||
var stunTurnURLs []*ice.URL
|
||||
stuns := strings.Split(stunURLs, ",")
|
||||
for _, url := range stuns {
|
||||
@@ -80,11 +89,13 @@ var (
|
||||
WgIface: wgInterface,
|
||||
}
|
||||
|
||||
err := config.Write(configPath)
|
||||
err = config.Write(configPath)
|
||||
if err != nil {
|
||||
log.Errorf("failed writing config to %s: %s", config, err.Error())
|
||||
os.Exit(ExitSetupFailed)
|
||||
}
|
||||
|
||||
log.Infof("a new config has been generated and written to %s", configPath)
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -98,10 +109,10 @@ func init() {
|
||||
//todo user:password@protocol:host:port not the best way to pass TURN credentials, do it according to https://tools.ietf.org/html/rfc7065 E.g. use oauth
|
||||
initCmd.PersistentFlags().StringVar(&turnURLs, "turnURLs", "", "Comma separated TURN server URLs: user:password@protocol:host:port, e.g. user:password@turn:stun.wiretrustee.com:3468")
|
||||
//initCmd.MarkPersistentFlagRequired("configPath")
|
||||
initCmd.MarkPersistentFlagRequired("wgLocalAddr")
|
||||
initCmd.MarkPersistentFlagRequired("signalAddr")
|
||||
initCmd.MarkPersistentFlagRequired("stunURLs")
|
||||
initCmd.MarkPersistentFlagRequired("turnURLs")
|
||||
initCmd.MarkPersistentFlagRequired("wgLocalAddr") //nolint
|
||||
initCmd.MarkPersistentFlagRequired("signalAddr") //nolint
|
||||
initCmd.MarkPersistentFlagRequired("stunURLs") //nolint
|
||||
initCmd.MarkPersistentFlagRequired("turnURLs") //nolint
|
||||
}
|
||||
|
||||
// generateKey generates a new Wireguard private key
|
||||
|
||||
35
cmd/root.go
35
cmd/root.go
@@ -6,16 +6,18 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
const (
|
||||
// ExitSetupFailed defines exit code
|
||||
ExitSetupFailed = 1
|
||||
)
|
||||
|
||||
var (
|
||||
configPath string
|
||||
logLevel string
|
||||
configPath string
|
||||
defaultConfigPath string
|
||||
logLevel string
|
||||
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "wiretrustee",
|
||||
@@ -28,28 +30,39 @@ var (
|
||||
func Execute() error {
|
||||
return rootCmd.Execute()
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringVar(&configPath, "config", "/etc/wiretrustee/config.json", "Wiretrustee config file location to write new config to")
|
||||
defaultConfigPath = "/etc/wiretrustee/config.json"
|
||||
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.AddCommand(initCmd)
|
||||
rootCmd.AddCommand(addPeerCmd)
|
||||
rootCmd.AddCommand(upCmd)
|
||||
rootCmd.AddCommand(signalCmd)
|
||||
rootCmd.AddCommand(serviceCmd)
|
||||
serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd) // service control commands are subcommands of service
|
||||
serviceCmd.AddCommand(installCmd, uninstallCmd) // service installer commands are subcommands of service
|
||||
}
|
||||
|
||||
// SetupCloseHandler handles SIGTERM signal and exits with success
|
||||
func SetupCloseHandler() {
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
<-c
|
||||
fmt.Println("\r- Ctrl+C pressed in Terminal")
|
||||
os.Exit(0)
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
for range c {
|
||||
fmt.Println("\r- Ctrl+C pressed in Terminal")
|
||||
stopUP <- 0
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// InitLog parses and sets log-level input
|
||||
func InitLog(logLevel string) {
|
||||
level, err := log.ParseLevel(logLevel)
|
||||
if err != nil {
|
||||
log.Errorf("efailed parsing log-level %s: %s", logLevel, err)
|
||||
log.Errorf("Failed parsing log-level %s: %s", logLevel, err)
|
||||
os.Exit(ExitSetupFailed)
|
||||
}
|
||||
log.SetLevel(level)
|
||||
|
||||
48
cmd/service.go
Normal file
48
cmd/service.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/kardianos/service"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type program struct {
|
||||
cmd *cobra.Command
|
||||
args []string
|
||||
}
|
||||
|
||||
var logger service.Logger
|
||||
|
||||
func newSVCConfig() *service.Config {
|
||||
return &service.Config{
|
||||
Name: "wiretrustee",
|
||||
DisplayName: "Wiretrustee",
|
||||
Description: "A WireGuard-based mesh network that connects your devices into a single private network.",
|
||||
}
|
||||
}
|
||||
|
||||
func newSVC(prg *program, conf *service.Config) (service.Service, error) {
|
||||
s, err := service.New(prg, conf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
logger, err = s.Logger(nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
var (
|
||||
serviceCmd = &cobra.Command{
|
||||
Use: "service",
|
||||
Short: "manages wiretrustee service",
|
||||
//Run: func(cmd *cobra.Command, args []string) {
|
||||
//},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
}
|
||||
110
cmd/service_controller.go
Normal file
110
cmd/service_controller.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/kardianos/service"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func (p *program) Start(s service.Service) error {
|
||||
// Start should not block. Do the actual work async.
|
||||
logger.Info("Starting service") //nolint
|
||||
go upCmd.Run(p.cmd, p.args)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *program) Stop(s service.Service) error {
|
||||
stopUP <- 1
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
runCmd = &cobra.Command{
|
||||
Use: "run",
|
||||
Short: "runs wiretrustee as service",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
prg := &program{
|
||||
cmd: cmd,
|
||||
args: args,
|
||||
}
|
||||
|
||||
s, err := newSVC(prg, newSVCConfig())
|
||||
if err != nil {
|
||||
cmd.PrintErrln(err)
|
||||
return
|
||||
}
|
||||
err = s.Run()
|
||||
if err != nil {
|
||||
cmd.PrintErrln(err)
|
||||
return
|
||||
}
|
||||
cmd.Printf("Wiretrustee service is running")
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
startCmd = &cobra.Command{
|
||||
Use: "start",
|
||||
Short: "starts wiretrustee service",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
s, err := newSVC(&program{}, newSVCConfig())
|
||||
if err != nil {
|
||||
cmd.PrintErrln(err)
|
||||
return
|
||||
}
|
||||
err = s.Start()
|
||||
if err != nil {
|
||||
cmd.PrintErrln(err)
|
||||
return
|
||||
}
|
||||
cmd.Printf("Wiretrustee service has been started")
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
stopCmd = &cobra.Command{
|
||||
Use: "stop",
|
||||
Short: "stops wiretrustee service",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
s, err := newSVC(&program{}, newSVCConfig())
|
||||
if err != nil {
|
||||
cmd.PrintErrln(err)
|
||||
return
|
||||
}
|
||||
err = s.Stop()
|
||||
if err != nil {
|
||||
cmd.PrintErrln(err)
|
||||
return
|
||||
}
|
||||
cmd.Printf("Wiretrustee service has been stopped")
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
restartCmd = &cobra.Command{
|
||||
Use: "restart",
|
||||
Short: "restarts wiretrustee service",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
s, err := newSVC(&program{}, newSVCConfig())
|
||||
if err != nil {
|
||||
cmd.PrintErrln(err)
|
||||
return
|
||||
}
|
||||
err = s.Restart()
|
||||
if err != nil {
|
||||
cmd.PrintErrln(err)
|
||||
return
|
||||
}
|
||||
cmd.Printf("Wiretrustee service has been restarted")
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
}
|
||||
69
cmd/service_installer.go
Normal file
69
cmd/service_installer.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
installCmd = &cobra.Command{
|
||||
Use: "install",
|
||||
Short: "installs wiretrustee service",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
svcConfig := newSVCConfig()
|
||||
|
||||
svcConfig.Arguments = []string{
|
||||
"service",
|
||||
"run",
|
||||
"--config",
|
||||
configPath,
|
||||
"--log-level",
|
||||
logLevel,
|
||||
}
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
// Respected only by systemd systems
|
||||
svcConfig.Dependencies = []string{"After=network.target syslog.target"}
|
||||
}
|
||||
|
||||
s, err := newSVC(&program{}, svcConfig)
|
||||
if err != nil {
|
||||
cmd.PrintErrln(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = s.Install()
|
||||
if err != nil {
|
||||
cmd.PrintErrln(err)
|
||||
return
|
||||
}
|
||||
cmd.Printf("Wiretrustee service has been installed")
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
uninstallCmd = &cobra.Command{
|
||||
Use: "uninstall",
|
||||
Short: "uninstalls wiretrustee service from system",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
s, err := newSVC(&program{}, newSVCConfig())
|
||||
if err != nil {
|
||||
cmd.PrintErrln(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = s.Uninstall()
|
||||
if err != nil {
|
||||
cmd.PrintErrln(err)
|
||||
return
|
||||
}
|
||||
cmd.Printf("Wiretrustee has been uninstalled")
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
}
|
||||
131
cmd/service_test.go
Normal file
131
cmd/service_test.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/kardianos/service"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
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) {
|
||||
rootCmd.SetArgs([]string{
|
||||
"init",
|
||||
"--stunURLs",
|
||||
"stun:stun.wiretrustee.com:3468",
|
||||
"--signalAddr",
|
||||
"signal.wiretrustee.com:10000",
|
||||
"--turnURLs",
|
||||
"foo:bar@turn:stun.wiretrustee.com:3468",
|
||||
"--wgInterface",
|
||||
"utun99",
|
||||
"--wgLocalAddr",
|
||||
"10.100.100.1/24",
|
||||
"--config",
|
||||
"/tmp/config.json",
|
||||
})
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
sig "github.com/wiretrustee/wiretrustee/signal"
|
||||
sProto "github.com/wiretrustee/wiretrustee/signal/proto"
|
||||
sigProto "github.com/wiretrustee/wiretrustee/signal/proto"
|
||||
"google.golang.org/grpc"
|
||||
"net"
|
||||
)
|
||||
@@ -30,13 +30,14 @@ var (
|
||||
}
|
||||
var opts []grpc.ServerOption
|
||||
grpcServer := grpc.NewServer(opts...)
|
||||
sProto.RegisterSignalExchangeServer(grpcServer, sig.NewServer())
|
||||
sigProto.RegisterSignalExchangeServer(grpcServer, sig.NewServer())
|
||||
log.Printf("started server: localhost:%v", port)
|
||||
if err := grpcServer.Serve(lis); err != nil {
|
||||
log.Fatalf("failed to serve: %v", err)
|
||||
}
|
||||
|
||||
SetupCloseHandler()
|
||||
select {}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
23
cmd/up.go
23
cmd/up.go
@@ -10,10 +10,6 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func toByte32(key wgtypes.Key) *[32]byte {
|
||||
return (*[32]byte)(&key)
|
||||
}
|
||||
|
||||
var (
|
||||
upCmd = &cobra.Command{
|
||||
Use: "up",
|
||||
@@ -30,7 +26,7 @@ var (
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
signalClient, err := sig.NewClient(config.SignalAddr, myKey, ctx)
|
||||
signalClient, err := sig.NewClient(ctx, config.SignalAddr, myKey)
|
||||
if err != nil {
|
||||
log.Errorf("error while connecting to the Signal Exchange Service %s: %s", config.SignalAddr, err)
|
||||
os.Exit(ExitSetupFailed)
|
||||
@@ -38,16 +34,29 @@ var (
|
||||
//todo proper close handling
|
||||
defer func() { signalClient.Close() }()
|
||||
|
||||
engine := connection.NewEngine(signalClient, config.StunTurnURLs, config.WgIface, config.WgAddr)
|
||||
iFaceBlackList := make(map[string]struct{})
|
||||
for i := 0; i < len(config.IFaceBlackList); i += 2 {
|
||||
iFaceBlackList[config.IFaceBlackList[i]] = struct{}{}
|
||||
}
|
||||
engine := connection.NewEngine(signalClient, config.StunTurnURLs, config.WgIface, config.WgAddr, iFaceBlackList)
|
||||
|
||||
err = engine.Start(myKey, config.Peers)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("error while starting the engine: %s", err)
|
||||
os.Exit(ExitSetupFailed)
|
||||
}
|
||||
//signalClient.WaitConnected()
|
||||
|
||||
SetupCloseHandler()
|
||||
<-stopUP
|
||||
log.Println("Receive signal to stop running")
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// Execution control channel for stopUP signal
|
||||
var stopUP chan int
|
||||
|
||||
func init() {
|
||||
stopUP = make(chan int)
|
||||
}
|
||||
|
||||
@@ -3,22 +3,25 @@ package connection
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/pion/ice/v2"
|
||||
ice "github.com/pion/ice/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/wiretrustee/wiretrustee/iface"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultWgKeepAlive default Wireguard keep alive constant
|
||||
DefaultWgKeepAlive = 20 * time.Second
|
||||
)
|
||||
|
||||
// ConnConfig Connection configuration struct
|
||||
type ConnConfig struct {
|
||||
// Local Wireguard listening address e.g. 127.0.0.1:51820
|
||||
WgListenAddr string
|
||||
// A Local Wireguard Peer IP address in CIDR notation e.g. 10.30.30.1/24
|
||||
WgPeerIp string
|
||||
WgPeerIP string
|
||||
// Local Wireguard Interface name (e.g. wg0)
|
||||
WgIface string
|
||||
// Wireguard allowed IPs (e.g. 10.30.30.2/32)
|
||||
@@ -29,13 +32,17 @@ type ConnConfig struct {
|
||||
RemoteWgKey wgtypes.Key
|
||||
|
||||
StunTurnURLS []*ice.URL
|
||||
|
||||
iFaceBlackList map[string]struct{}
|
||||
}
|
||||
|
||||
// IceCredentials ICE protocol credentials struct
|
||||
type IceCredentials struct {
|
||||
uFrag string
|
||||
pwd string
|
||||
}
|
||||
|
||||
// Connection Holds information about a connection and handles signal protocol
|
||||
type Connection struct {
|
||||
Config ConnConfig
|
||||
// signalCandidate is a handler function to signal remote peer about local connection candidate
|
||||
@@ -61,6 +68,7 @@ type Connection struct {
|
||||
remoteAuthCond sync.Once
|
||||
}
|
||||
|
||||
// NewConnection Creates a new connection and sets handling functions for signal protocol
|
||||
func NewConnection(config ConnConfig,
|
||||
signalCandidate func(candidate ice.Candidate) error,
|
||||
signalOffer func(uFrag string, pwd string) error,
|
||||
@@ -86,8 +94,16 @@ func (conn *Connection) Open(timeout time.Duration) error {
|
||||
|
||||
// create an ice.Agent that will be responsible for negotiating and establishing actual peer-to-peer connection
|
||||
a, err := ice.NewAgent(&ice.AgentConfig{
|
||||
// MulticastDNSMode: ice.MulticastDNSModeQueryAndGather,
|
||||
NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4},
|
||||
Urls: conn.Config.StunTurnURLS,
|
||||
InterfaceFilter: func(s string) bool {
|
||||
if conn.Config.iFaceBlackList == nil {
|
||||
return true
|
||||
}
|
||||
_, ok := conn.Config.iFaceBlackList[s]
|
||||
return !ok
|
||||
},
|
||||
})
|
||||
conn.agent = a
|
||||
|
||||
@@ -130,10 +146,23 @@ func (conn *Connection) Open(timeout time.Duration) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = conn.wgProxy.Start(remoteConn)
|
||||
pair, err := conn.agent.GetSelectedCandidatePair()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// in case the remote peer is in the local network we don't need a Wireguard proxy, direct communication is possible.
|
||||
if pair.Local.Type() == ice.CandidateTypeHost && pair.Remote.Type() == ice.CandidateTypeHost {
|
||||
log.Debugf("remote peer %s is in the local network with an address %s", conn.Config.RemoteWgKey.String(), pair.Remote.Address())
|
||||
err = conn.wgProxy.StartLocal(fmt.Sprintf("%s:%d", pair.Remote.Address(), iface.WgPort))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = conn.wgProxy.Start(remoteConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("opened connection to peer %s", conn.Config.RemoteWgKey.String())
|
||||
case <-time.After(timeout):
|
||||
@@ -145,12 +174,11 @@ func (conn *Connection) Open(timeout time.Duration) error {
|
||||
}
|
||||
|
||||
// wait until connection has been closed
|
||||
select {
|
||||
case <-conn.closeCond.C:
|
||||
return fmt.Errorf("connection to peer %s has been closed", conn.Config.RemoteWgKey.String())
|
||||
}
|
||||
<-conn.closeCond.C
|
||||
return fmt.Errorf("connection to peer %s has been closed", conn.Config.RemoteWgKey.String())
|
||||
}
|
||||
|
||||
// Close Closes a peer connection
|
||||
func (conn *Connection) Close() error {
|
||||
var err error
|
||||
conn.closeCond.Do(func() {
|
||||
@@ -176,6 +204,7 @@ func (conn *Connection) Close() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// OnAnswer Handles the answer from the other peer
|
||||
func (conn *Connection) OnAnswer(remoteAuth IceCredentials) error {
|
||||
|
||||
conn.remoteAuthCond.Do(func() {
|
||||
@@ -185,23 +214,25 @@ func (conn *Connection) OnAnswer(remoteAuth IceCredentials) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnOffer Handles the offer from the other peer
|
||||
func (conn *Connection) OnOffer(remoteAuth IceCredentials) error {
|
||||
|
||||
conn.remoteAuthCond.Do(func() {
|
||||
log.Debugf("OnOffer from peer %s", conn.Config.RemoteWgKey.String())
|
||||
conn.remoteAuthChannel <- remoteAuth
|
||||
uFrag, pwd, err := conn.agent.GetLocalUserCredentials()
|
||||
if err != nil {
|
||||
if err != nil { //nolint
|
||||
}
|
||||
|
||||
err = conn.signalAnswer(uFrag, pwd)
|
||||
if err != nil {
|
||||
if err != nil { //nolint
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnRemoteCandidate Handles remote candidate provided by the peer.
|
||||
func (conn *Connection) OnRemoteCandidate(candidate ice.Candidate) error {
|
||||
|
||||
log.Debugf("onRemoteCandidate from peer %s -> %s", conn.Config.RemoteWgKey.String(), candidate.String())
|
||||
@@ -280,9 +311,8 @@ func (conn *Connection) listenOnConnectionStateChanges() error {
|
||||
log.Errorf("failed selecting active ICE candidate pair %s", err)
|
||||
return
|
||||
}
|
||||
log.Debugf("closed to peer %s via selected candidate pair %s", conn.Config.RemoteWgKey.String(), pair)
|
||||
log.Infof("will connect to peer %s via a selected connnection candidate pair %s", conn.Config.RemoteWgKey.String(), pair)
|
||||
} else if state == ice.ConnectionStateDisconnected || state == ice.ConnectionStateFailed {
|
||||
// todo do we really wanna have a connection restart within connection itself? Think of moving it outside
|
||||
err := conn.Close()
|
||||
if err != nil {
|
||||
log.Warnf("error while closing connection to peer %s -> %s", conn.Config.RemoteWgKey.String(), err.Error())
|
||||
|
||||
@@ -3,7 +3,7 @@ package connection
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/pion/ice/v2"
|
||||
ice "github.com/pion/ice/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/wiretrustee/wiretrustee/iface"
|
||||
"github.com/wiretrustee/wiretrustee/signal"
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Engine is an instance of the Connection Engine
|
||||
type Engine struct {
|
||||
// a list of STUN and TURN servers
|
||||
stunsTurns []*ice.URL
|
||||
@@ -22,27 +23,35 @@ type Engine struct {
|
||||
// Wireguard interface
|
||||
wgIface string
|
||||
// Wireguard local address
|
||||
wgIp string
|
||||
wgIP string
|
||||
// Network Interfaces to ignore
|
||||
iFaceBlackList map[string]struct{}
|
||||
}
|
||||
|
||||
// Peer is an instance of the Connection Peer
|
||||
type Peer struct {
|
||||
WgPubKey string
|
||||
WgAllowedIps string
|
||||
}
|
||||
|
||||
func NewEngine(signal *signal.Client, stunsTurns []*ice.URL, wgIface string, wgAddr string) *Engine {
|
||||
// NewEngine creates a new Connection Engine
|
||||
func NewEngine(signal *signal.Client, stunsTurns []*ice.URL, wgIface string, wgAddr string,
|
||||
iFaceBlackList map[string]struct{}) *Engine {
|
||||
return &Engine{
|
||||
stunsTurns: stunsTurns,
|
||||
signal: signal,
|
||||
wgIface: wgIface,
|
||||
wgIp: wgAddr,
|
||||
conns: map[string]*Connection{},
|
||||
stunsTurns: stunsTurns,
|
||||
signal: signal,
|
||||
wgIface: wgIface,
|
||||
wgIP: wgAddr,
|
||||
conns: map[string]*Connection{},
|
||||
iFaceBlackList: iFaceBlackList,
|
||||
}
|
||||
}
|
||||
|
||||
// Start creates a new tunnel interface and listens to signals from the Signal service.
|
||||
// It also creates an Go routine to handle each peer communication from the config file
|
||||
func (e *Engine) Start(myKey wgtypes.Key, peers []Peer) error {
|
||||
|
||||
err := iface.Create(e.wgIface, e.wgIp)
|
||||
err := iface.Create(e.wgIface, e.wgIP)
|
||||
if err != nil {
|
||||
log.Errorf("error while creating interface %s: [%s]", e.wgIface, err.Error())
|
||||
return err
|
||||
@@ -101,13 +110,14 @@ func (e *Engine) openPeerConnection(wgPort int, myKey wgtypes.Key, peer Peer) (*
|
||||
|
||||
remoteKey, _ := wgtypes.ParseKey(peer.WgPubKey)
|
||||
connConfig := &ConnConfig{
|
||||
WgListenAddr: fmt.Sprintf("127.0.0.1:%d", wgPort),
|
||||
WgPeerIp: e.wgIp,
|
||||
WgIface: e.wgIface,
|
||||
WgAllowedIPs: peer.WgAllowedIps,
|
||||
WgKey: myKey,
|
||||
RemoteWgKey: remoteKey,
|
||||
StunTurnURLS: e.stunsTurns,
|
||||
WgListenAddr: fmt.Sprintf("127.0.0.1:%d", wgPort),
|
||||
WgPeerIP: e.wgIP,
|
||||
WgIface: e.wgIface,
|
||||
WgAllowedIPs: peer.WgAllowedIps,
|
||||
WgKey: myKey,
|
||||
RemoteWgKey: remoteKey,
|
||||
StunTurnURLS: e.stunsTurns,
|
||||
iFaceBlackList: e.iFaceBlackList,
|
||||
}
|
||||
|
||||
signalOffer := func(uFrag string, pwd string) error {
|
||||
@@ -161,7 +171,9 @@ func signalAuth(uFrag string, pwd string, myKey wgtypes.Key, remoteKey wgtypes.K
|
||||
msg, err := signal.MarshalCredential(myKey, remoteKey, &signal.Credential{
|
||||
UFrag: uFrag,
|
||||
Pwd: pwd}, t)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.Send(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package connection
|
||||
|
||||
import (
|
||||
"github.com/pion/ice/v2"
|
||||
ice "github.com/pion/ice/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/wiretrustee/wiretrustee/iface"
|
||||
"net"
|
||||
)
|
||||
|
||||
// WgProxy an instance of an instance of the Connection Wireguard Proxy
|
||||
type WgProxy struct {
|
||||
iface string
|
||||
remoteKey string
|
||||
@@ -16,6 +17,7 @@ type WgProxy struct {
|
||||
wgConn net.Conn
|
||||
}
|
||||
|
||||
// NewWgProxy creates a new Connection Wireguard Proxy
|
||||
func NewWgProxy(iface string, remoteKey string, allowedIps string, wgAddr string) *WgProxy {
|
||||
return &WgProxy{
|
||||
iface: iface,
|
||||
@@ -26,6 +28,7 @@ func NewWgProxy(iface string, remoteKey string, allowedIps string, wgAddr string
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the proxy
|
||||
func (p *WgProxy) Close() error {
|
||||
|
||||
close(p.close)
|
||||
@@ -39,6 +42,16 @@ func (p *WgProxy) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *WgProxy) StartLocal(host string) error {
|
||||
err := iface.UpdatePeer(p.iface, p.remoteKey, p.allowedIps, DefaultWgKeepAlive, host)
|
||||
if err != nil {
|
||||
log.Errorf("error while configuring Wireguard peer [%s] %s", p.remoteKey, err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start starts a new proxy using the ICE connection
|
||||
func (p *WgProxy) Start(remoteConn *ice.Conn) error {
|
||||
|
||||
wgConn, err := net.Dial("udp", p.wgAddr)
|
||||
@@ -78,9 +91,10 @@ func (p *WgProxy) proxyToRemotePeer(remoteConn *ice.Conn) {
|
||||
continue
|
||||
}
|
||||
|
||||
n, err = remoteConn.Write(buf[:n])
|
||||
_, err = remoteConn.Write(buf[:n])
|
||||
if err != nil {
|
||||
//log.Warnln("failed writing to remote peer: ", err.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,11 +114,13 @@ func (p *WgProxy) proxyToLocalWireguard(remoteConn *ice.Conn) {
|
||||
n, err := remoteConn.Read(buf)
|
||||
if err != nil {
|
||||
//log.Errorf("failed reading from remote connection %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
n, err = p.wgConn.Write(buf[:n])
|
||||
_, err = p.wgConn.Write(buf[:n])
|
||||
if err != nil {
|
||||
//log.Errorf("failed writing to local Wireguard instance %s", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
go.mod
15
go.mod
@@ -4,15 +4,18 @@ go 1.16
|
||||
|
||||
require (
|
||||
github.com/cenkalti/backoff/v4 v4.1.0
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/google/nftables v0.0.0-20201230142148-715e31cb3c31
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/kardianos/service v1.2.0
|
||||
github.com/onsi/ginkgo v1.16.4
|
||||
github.com/onsi/gomega v1.13.0
|
||||
github.com/pion/ice/v2 v2.1.7
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
github.com/spf13/cobra v1.1.3
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
||||
golang.zx2c4.com/wireguard v0.0.20201118
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b
|
||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210604143328-f9b48a961cd2
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5
|
||||
golang.zx2c4.com/wireguard/windows v0.3.14
|
||||
google.golang.org/grpc v1.32.0
|
||||
)
|
||||
|
||||
139
go.sum
139
go.sum
@@ -45,12 +45,15 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
@@ -67,18 +70,21 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/nftables v0.0.0-20201230142148-715e31cb3c31 h1:kyEB9geFhgDyawmvavtNu9iGW9ri/iq54XTSNIEeHxI=
|
||||
github.com/google/nftables v0.0.0-20201230142148-715e31cb3c31/go.mod h1:cfspEyr/Ap+JDIITA+N9a0ernqG0qZ4W1aqMRgDZa1g=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
@@ -111,36 +117,55 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA=
|
||||
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4 h1:nwOc1YaOrYJ37sEBrtWZrdqzK22hiJs3GpDmP3sR2Yw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b h1:c3NTyLNozICy8B4mlMXemD3z/gXgQzVXZS/HqT+i3do=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
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/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.0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
||||
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/koneu/natend v0.0.0-20150829182554-ec0926ea948d h1:MFX8DxRnKMY/2M3H61iSsVbo/n3h0MWGmWNN1UViOU0=
|
||||
github.com/koneu/natend v0.0.0-20150829182554-ec0926ea948d/go.mod h1:QHb4k4cr1fQikUahfcRVPcEXiUgFsdIstGqlurL0XL4=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ=
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JAQI/ZLUkCZ7VJS3r74hwFIGXJsgZlY=
|
||||
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
|
||||
github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0=
|
||||
github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=
|
||||
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||
github.com/mdlayher/netlink v0.0.0-20191009155606-de872b0d824b/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||
github.com/mdlayher/netlink v1.1.0 h1:mpdLgm+brq10nI9zM1BpX1kpDbh3NLl3RSnVq6ZSkfg=
|
||||
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
|
||||
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
|
||||
github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8=
|
||||
github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
|
||||
github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
|
||||
github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys=
|
||||
github.com/mdlayher/netlink v1.4.0 h1:n3ARR+Fm0dDv37dj5wSWZXDKcy+U0zwcXS3zKMnSiT0=
|
||||
github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
||||
@@ -155,7 +180,19 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
|
||||
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pion/dtls/v2 v2.0.9 h1:7Ow+V++YSZQMYzggI0P9vLJz/hUFcffsfGMfT/Qy+u8=
|
||||
@@ -219,6 +256,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@@ -226,10 +264,10 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
@@ -241,12 +279,13 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o=
|
||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -265,8 +304,10 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -280,17 +321,23 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c h1:KHUzaHIpjWVlVVNh65G3hhuj3KB1HnjY6Cq5cTvRQT8=
|
||||
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -299,9 +346,11 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -315,24 +364,41 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201117222635-ba5294a509c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
|
||||
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7-0.20210503195748-5c7c50ebbd4f h1:yQJrRE0hDxDFmZLlRaw+3vusO4fwNHgHIjUOMO7bHYI=
|
||||
golang.org/x/text v0.3.7-0.20210503195748-5c7c50ebbd4f/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -353,15 +419,21 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wireguard v0.0.20200121/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4=
|
||||
golang.zx2c4.com/wireguard v0.0.20201118 h1:QL8y2C7uO8T6z1GY+UX/hSeWiYEBurQkXjOTRFtCvXU=
|
||||
golang.zx2c4.com/wireguard v0.0.20201118/go.mod h1:Dz+cq5bnrai9EpgYj4GDof/+qaGzbRWbeaAOs1bUYa0=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b h1:l4mBVCYinjzZuR5DtxHuBD6wyd4348TGiavJ5vLrhEc=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b/go.mod h1:UdS9frhv65KTfwxME1xE8+rHYoFpbm36gOud1GhBe9c=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210427022245-097af6e1351b/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210510202332-9844c74f67ec/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210604143328-f9b48a961cd2 h1:wfOOSvHgIzTZ9h5Vb6yUFZNn7uf3bT7PeYsHOO7tYDM=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210604143328-f9b48a961cd2/go.mod h1:laHzsbfMhGSobUmruXWAyMKKHSqvIcrqZJMyHD+/3O8=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5 h1:LpEwXnbN4q2EIPkqbG9KHBUrducJYDOOdL+eMcJAlFo=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5/go.mod h1:+1XihzyZUBJcSc5WO9SwNA7v26puQwOEDwanaxfNXPQ=
|
||||
golang.zx2c4.com/wireguard/windows v0.3.14 h1:5yIDYyrQyGkLqV+tzY4ilMNeIvQeMXAz0glZz9u179A=
|
||||
golang.zx2c4.com/wireguard/windows v0.3.14/go.mod h1:3P4IEAsb+BjlKZmpUXgy74c0iX9AVwwr3WcVJ8nPgME=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@@ -393,18 +465,27 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -2,9 +2,8 @@ package iface
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"golang.zx2c4.com/wireguard/wgctrl"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
@@ -14,54 +13,51 @@ import (
|
||||
|
||||
const (
|
||||
defaultMTU = 1280
|
||||
WgPort = 51820
|
||||
)
|
||||
|
||||
// Saves tun device object - is it required?
|
||||
var tunIface tun.Device
|
||||
|
||||
// Create Creates a new Wireguard interface, sets a given IP and brings it up.
|
||||
// Will reuse an existing one.
|
||||
func Create(iface string, address string) error {
|
||||
// CreateWithUserspace Creates a new Wireguard interface, using wireguard-go userspace implementation
|
||||
func CreateWithUserspace(iface string, address string) error {
|
||||
var err error
|
||||
|
||||
tunIface, err = tun.CreateTUN(iface, defaultMTU)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We need to create a wireguard-go device and listen to configuration requests
|
||||
tunDevice := device.NewDevice(tunIface, device.NewLogger(device.LogLevelSilent, "[wiretrustee] "))
|
||||
tunDevice.Up()
|
||||
tunSock, err := ipc.UAPIOpen(iface)
|
||||
tunDevice := device.NewDevice(tunIface, conn.NewDefaultBind(), device.NewLogger(device.LogLevelSilent, "[wiretrustee] "))
|
||||
err = tunDevice.Up()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uapi, err := ipc.UAPIListen(iface, tunSock)
|
||||
uapi, err := getUAPI(iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
conn, err := uapi.Accept()
|
||||
uapiConn, err := uapi.Accept()
|
||||
if err != nil {
|
||||
log.Debugln(err)
|
||||
return
|
||||
}
|
||||
go tunDevice.IpcHandle(conn)
|
||||
go tunDevice.IpcHandle(uapiConn)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Debugln("UAPI listener started")
|
||||
|
||||
err = assignAddr(iface, address)
|
||||
err = assignAddr(address, iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Extends the functionality of Configure(iface string, privateKey string) by generating a new Wireguard private key
|
||||
// ConfigureWithKeyGen Extends the functionality of Configure(iface string, privateKey string) by generating a new Wireguard private key
|
||||
func ConfigureWithKeyGen(iface string) (*wgtypes.Key, error) {
|
||||
key, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
@@ -70,7 +66,7 @@ func ConfigureWithKeyGen(iface string) (*wgtypes.Key, error) {
|
||||
return &key, Configure(iface, key.String())
|
||||
}
|
||||
|
||||
// Configures a Wireguard interface
|
||||
// Configure configures a Wireguard interface
|
||||
// The interface must exist before calling this method (e.g. call interface.Create() before)
|
||||
func Configure(iface string, privateKey string) error {
|
||||
|
||||
@@ -87,10 +83,12 @@ func Configure(iface string, privateKey string) error {
|
||||
return err
|
||||
}
|
||||
fwmark := 0
|
||||
p := WgPort
|
||||
cfg := wgtypes.Config{
|
||||
PrivateKey: &key,
|
||||
ReplacePeers: false,
|
||||
FirewallMark: &fwmark,
|
||||
ListenPort: &p,
|
||||
}
|
||||
err = wg.ConfigureDevice(iface, cfg)
|
||||
if err != nil {
|
||||
@@ -100,6 +98,7 @@ func Configure(iface string, privateKey string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetListenPort returns the listening port of the Wireguard endpoint
|
||||
func GetListenPort(iface string) (*int, error) {
|
||||
log.Debugf("getting Wireguard listen port of interface %s", iface)
|
||||
|
||||
@@ -114,12 +113,12 @@ func GetListenPort(iface string) (*int, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("got Wireguard device listen port %s, %d", iface, &d.ListenPort)
|
||||
log.Debugf("got Wireguard device listen port %s, %d", iface, d.ListenPort)
|
||||
|
||||
return &d.ListenPort, nil
|
||||
}
|
||||
|
||||
// Updates a Wireguard interface listen port
|
||||
// UpdateListenPort updates a Wireguard interface listen port
|
||||
func UpdateListenPort(iface string, newPort int) error {
|
||||
log.Debugf("updating Wireguard listen port of interface %s, new port %d", iface, newPort)
|
||||
|
||||
@@ -150,13 +149,7 @@ func UpdateListenPort(iface string, newPort int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ifname(n string) []byte {
|
||||
b := make([]byte, 16)
|
||||
copy(b, []byte(n+"\x00"))
|
||||
return b
|
||||
}
|
||||
|
||||
// Updates existing Wireguard Peer or creates a new one if doesn't exist
|
||||
// UpdatePeer updates existing Wireguard Peer or creates a new one if doesn't exist
|
||||
// Endpoint is optional
|
||||
func UpdatePeer(iface string, peerKey string, allowedIps string, keepAlive time.Duration, endpoint string) error {
|
||||
|
||||
@@ -175,13 +168,15 @@ func UpdatePeer(iface string, peerKey string, allowedIps string, keepAlive time.
|
||||
log.Debugf("got Wireguard device %s", iface)
|
||||
|
||||
//parse allowed ips
|
||||
ipNet, err := netlink.ParseIPNet(allowedIps)
|
||||
_, ipNet, err := net.ParseCIDR(allowedIps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
peers := make([]wgtypes.PeerConfig, 0)
|
||||
peer := wgtypes.PeerConfig{
|
||||
PublicKey: peerKeyParsed,
|
||||
@@ -207,7 +202,7 @@ func UpdatePeer(iface string, peerKey string, allowedIps string, keepAlive time.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Updates a Wireguard interface Peer with the new endpoint
|
||||
// UpdatePeerEndpoint updates a Wireguard interface Peer with the new endpoint
|
||||
// Used when NAT hole punching was successful and an update of the remote peer endpoint is required
|
||||
func UpdatePeerEndpoint(iface string, peerKey string, newEndpoint string) error {
|
||||
|
||||
@@ -233,6 +228,9 @@ func UpdatePeerEndpoint(iface string, peerKey string, newEndpoint string) error
|
||||
log.Debugf("parsed peer endpoint [%s]", peerAddr.String())
|
||||
|
||||
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
peers := make([]wgtypes.PeerConfig, 0)
|
||||
peer := wgtypes.PeerConfig{
|
||||
PublicKey: peerKeyParsed,
|
||||
@@ -253,15 +251,3 @@ func UpdatePeerEndpoint(iface string, peerKey string, newEndpoint string) error
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type wgLink struct {
|
||||
attrs *netlink.LinkAttrs
|
||||
}
|
||||
|
||||
func (w *wgLink) Attrs() *netlink.LinkAttrs {
|
||||
return w.attrs
|
||||
}
|
||||
|
||||
func (w *wgLink) Type() string {
|
||||
return "wireguard"
|
||||
}
|
||||
|
||||
@@ -7,20 +7,21 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
interfacePrefix = "utun"
|
||||
)
|
||||
// Create Creates a new Wireguard interface, sets a given IP and brings it up.
|
||||
func Create(iface string, address string) error {
|
||||
return CreateWithUserspace(iface, address)
|
||||
}
|
||||
|
||||
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided
|
||||
func assignAddr(iface string, address string) error {
|
||||
func assignAddr(address string, ifaceName string) error {
|
||||
ip := strings.Split(address, "/")
|
||||
cmd := exec.Command("ifconfig", iface, "inet", address, ip[0])
|
||||
cmd := exec.Command("ifconfig", ifaceName, "inet", address, ip[0])
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
log.Infoln("Command: %v failed with output %s and error: ", cmd.String(), out)
|
||||
log.Infof("Command: %v failed with output %s and error: ", cmd.String(), out)
|
||||
return err
|
||||
}
|
||||
_, resolvedNet, err := net.ParseCIDR(address)
|
||||
err = addRoute(iface, resolvedNet)
|
||||
err = addRoute(ifaceName, resolvedNet)
|
||||
if err != nil {
|
||||
log.Infoln("Adding route failed with error:", err)
|
||||
}
|
||||
|
||||
@@ -6,12 +6,21 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
interfacePrefix = "wg"
|
||||
)
|
||||
// Create Creates a new Wireguard interface, sets a given IP and brings it up.
|
||||
// Will reuse an existing one.
|
||||
func Create(iface string, address string) error {
|
||||
|
||||
// assignAddr Adds IP address to the tunnel interface
|
||||
func assignAddr(iface string, address string) error {
|
||||
if WireguardModExists() {
|
||||
log.Debug("using kernel Wireguard module")
|
||||
return CreateWithKernel(iface, address)
|
||||
} else {
|
||||
return CreateWithUserspace(iface, address)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateWithKernel Creates a new Wireguard interface using kernel Wireguard module.
|
||||
// Works for Linux and offers much better network performance
|
||||
func CreateWithKernel(iface string, address string) error {
|
||||
attrs := netlink.NewLinkAttrs()
|
||||
attrs.Name = iface
|
||||
|
||||
@@ -19,11 +28,67 @@ func assignAddr(iface string, address string) error {
|
||||
attrs: &attrs,
|
||||
}
|
||||
|
||||
log.Debugf("adding address %s to interface: %s", address, iface)
|
||||
addr, _ := netlink.ParseAddr(address)
|
||||
err := netlink.AddrAdd(&link, addr)
|
||||
log.Debugf("adding device: %s", iface)
|
||||
err := netlink.LinkAdd(&link)
|
||||
if os.IsExist(err) {
|
||||
log.Infof("interface %s already has the address: %s", iface, address)
|
||||
log.Infof("interface %s already exists. Will reuse.", iface)
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("adding address %s to interface: %s", address, iface)
|
||||
err = assignAddr(address, iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// todo do a discovery
|
||||
log.Debugf("setting MTU: %s", iface)
|
||||
err = netlink.LinkSetMTU(&link, defaultMTU)
|
||||
if err != nil {
|
||||
log.Errorf("error setting MTU on interface: %s", iface)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("bringing up interface: %s", iface)
|
||||
err = netlink.LinkSetUp(&link)
|
||||
if err != nil {
|
||||
log.Errorf("error bringing up interface: %s", iface)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// assignAddr Adds IP address to the tunnel interface
|
||||
func assignAddr(address, name string) error {
|
||||
var err error
|
||||
attrs := netlink.NewLinkAttrs()
|
||||
attrs.Name = name
|
||||
|
||||
link := wgLink{
|
||||
attrs: &attrs,
|
||||
}
|
||||
|
||||
//delete existing addresses
|
||||
list, err := netlink.AddrList(&link, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(list) > 0 {
|
||||
for _, a := range list {
|
||||
err = netlink.AddrDel(&link, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("adding address %s to interface: %s", address, attrs.Name)
|
||||
addr, _ := netlink.ParseAddr(address)
|
||||
err = netlink.AddrAdd(&link, addr)
|
||||
if os.IsExist(err) {
|
||||
log.Infof("interface %s already has the address: %s", attrs.Name, address)
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -31,3 +96,17 @@ func assignAddr(iface string, address string) error {
|
||||
err = netlink.LinkSetUp(&link)
|
||||
return err
|
||||
}
|
||||
|
||||
type wgLink struct {
|
||||
attrs *netlink.LinkAttrs
|
||||
}
|
||||
|
||||
// Attrs returns the Wireguard's default attributes
|
||||
func (w *wgLink) Attrs() *netlink.LinkAttrs {
|
||||
return w.attrs
|
||||
}
|
||||
|
||||
// Type returns the interface type
|
||||
func (w *wgLink) Type() string {
|
||||
return "wireguard"
|
||||
}
|
||||
|
||||
17
iface/iface_unix.go
Normal file
17
iface/iface_unix.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// +build linux darwin
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"net"
|
||||
)
|
||||
|
||||
// getUAPI returns a Listener
|
||||
func getUAPI(iface string) (net.Listener, error) {
|
||||
tunSock, err := ipc.UAPIOpen(iface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ipc.UAPIListen(iface, tunSock)
|
||||
}
|
||||
41
iface/iface_windows.go
Normal file
41
iface/iface_windows.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Create Creates a new Wireguard interface, sets a given IP and brings it up.
|
||||
func Create(iface string, address string) error {
|
||||
return CreateWithUserspace(iface, address)
|
||||
}
|
||||
|
||||
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided
|
||||
func assignAddr(address string, ifaceName string) error {
|
||||
|
||||
nativeTunDevice := tunIface.(*tun.NativeTun)
|
||||
luid := winipcfg.LUID(nativeTunDevice.LUID())
|
||||
|
||||
ip, ipnet, _ := net.ParseCIDR(address)
|
||||
|
||||
log.Debugf("adding address %s to interface: %s", address, ifaceName)
|
||||
err := luid.SetIPAddresses([]net.IPNet{{ip, ipnet.Mask}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("adding Routes to interface: %s", ifaceName)
|
||||
err = luid.SetRoutes([]*winipcfg.RouteData{{*ipnet, ipnet.IP, 0}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getUAPI returns a Listener
|
||||
func getUAPI(iface string) (net.Listener, error) {
|
||||
return ipc.UAPIListen(iface)
|
||||
}
|
||||
144
iface/mod.go
Normal file
144
iface/mod.go
Normal file
@@ -0,0 +1,144 @@
|
||||
// +build linux
|
||||
|
||||
package iface
|
||||
|
||||
// Holds logic to check existence of Wireguard kernel module
|
||||
// Copied from https://github.com/paultag/go-modprobe
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"fmt"
|
||||
"golang.org/x/sys/unix"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// get the root directory for the kernel modules. If this line panics,
|
||||
// it's because getModuleRoot has failed to get the uname of the running
|
||||
// kernel (likely a non-POSIX system, but maybe a broken kernel?)
|
||||
moduleRoot = getModuleRoot()
|
||||
)
|
||||
|
||||
// Get the module root (/lib/modules/$(uname -r)/)
|
||||
func getModuleRoot() string {
|
||||
uname := unix.Utsname{}
|
||||
if err := unix.Uname(&uname); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
i := 0
|
||||
for ; uname.Release[i] != 0; i++ {
|
||||
}
|
||||
|
||||
return filepath.Join(
|
||||
"/lib/modules",
|
||||
string(uname.Release[:i]),
|
||||
)
|
||||
}
|
||||
|
||||
// modName will, given a file descriptor to a Kernel Module (.ko file), parse the
|
||||
// binary to get the module name. For instance, given a handle to the file at
|
||||
// `kernel/drivers/usb/gadget/legacy/g_ether.ko`, return `g_ether`.
|
||||
func modName(file *os.File) (string, error) {
|
||||
f, err := elf.NewFile(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
syms, err := f.Symbols()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, sym := range syms {
|
||||
if strings.Compare(sym.Name, "__this_module") == 0 {
|
||||
section := f.Sections[sym.Section]
|
||||
data, err := section.Data()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(data) < 25 {
|
||||
return "", fmt.Errorf("modprobe: data is short, __this_module is '%s'", data)
|
||||
}
|
||||
|
||||
data = data[24:]
|
||||
i := 0
|
||||
for ; data[i] != 0x00; i++ {
|
||||
}
|
||||
return string(data[:i]), nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("No name found. Is this a .ko or just an ELF?")
|
||||
}
|
||||
|
||||
// Open every single kernel module under the root, and parse the ELF headers to
|
||||
// extract the module name.
|
||||
func elfMap(root string) (map[string]string, error) {
|
||||
ret := map[string]string{}
|
||||
|
||||
err := filepath.Walk(
|
||||
root,
|
||||
func(path string, info os.FileInfo, err error) error {
|
||||
|
||||
if err != nil {
|
||||
// skip broken files
|
||||
return nil
|
||||
}
|
||||
|
||||
if !info.Mode().IsRegular() {
|
||||
return nil
|
||||
}
|
||||
fd, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fd.Close()
|
||||
name, err := modName(fd)
|
||||
if err != nil {
|
||||
/* For now, let's just ignore that and avoid adding to it */
|
||||
return nil
|
||||
}
|
||||
|
||||
ret[name] = path
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Open every single kernel module under the kernel module directory
|
||||
// (/lib/modules/$(uname -r)/), and parse the ELF headers to extract the
|
||||
// module name.
|
||||
func generateMap() (map[string]string, error) {
|
||||
return elfMap(moduleRoot)
|
||||
}
|
||||
|
||||
// WireguardModExists returns true if Wireguard kernel module exists.
|
||||
func WireguardModExists() bool {
|
||||
_, err := resolveModName("wireguard")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// resolveModName will, given a module name (such as `wireguard`) return an absolute
|
||||
// path to the .ko that provides that module.
|
||||
func resolveModName(name string) (string, error) {
|
||||
paths, err := generateMap()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fsPath := paths[name]
|
||||
if !strings.HasPrefix(fsPath, moduleRoot) {
|
||||
return "", fmt.Errorf("module isn't in the module directory")
|
||||
}
|
||||
|
||||
return fsPath, nil
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"github.com/google/nftables"
|
||||
"github.com/google/nftables/expr"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netns"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// Configure routing and IP masquerading
|
||||
//todo more docs on what exactly happens here and why it is needed
|
||||
func ConfigureNAT(primaryIface string) error {
|
||||
log.Debugf("adding NAT / IP masquerading using nftables")
|
||||
ns, err := netns.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn := nftables.Conn{NetNS: int(ns)}
|
||||
|
||||
log.Debugf("flushing nftable rulesets")
|
||||
conn.FlushRuleset()
|
||||
|
||||
log.Debugf("setting up nftable rules for ip masquerading")
|
||||
|
||||
nat := conn.AddTable(&nftables.Table{
|
||||
Family: nftables.TableFamilyIPv4,
|
||||
Name: "nat",
|
||||
})
|
||||
|
||||
conn.AddChain(&nftables.Chain{
|
||||
Name: "prerouting",
|
||||
Table: nat,
|
||||
Type: nftables.ChainTypeNAT,
|
||||
Hooknum: nftables.ChainHookPrerouting,
|
||||
Priority: nftables.ChainPriorityFilter,
|
||||
})
|
||||
|
||||
post := conn.AddChain(&nftables.Chain{
|
||||
Name: "postrouting",
|
||||
Table: nat,
|
||||
Type: nftables.ChainTypeNAT,
|
||||
Hooknum: nftables.ChainHookPostrouting,
|
||||
Priority: nftables.ChainPriorityNATSource,
|
||||
})
|
||||
|
||||
conn.AddRule(&nftables.Rule{
|
||||
Table: nat,
|
||||
Chain: post,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Register: 1,
|
||||
Data: ifname(primaryIface),
|
||||
},
|
||||
&expr.Masq{},
|
||||
},
|
||||
})
|
||||
|
||||
if err := conn.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Enables IP forwarding system property.
|
||||
// Mostly used when you setup one peer as a VPN server.
|
||||
func EnableIPForward() error {
|
||||
f := "/proc/sys/net/ipv4/ip_forward"
|
||||
|
||||
content, err := ioutil.ReadFile(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if string(content) == "0\n" {
|
||||
log.Info("enabling IP Forward")
|
||||
return ioutil.WriteFile(f, []byte("1"), 0600)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
19
infrastructure_files/docker-compose.yml
Normal file
19
infrastructure_files/docker-compose.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
version: "3"
|
||||
services:
|
||||
# Signal
|
||||
signal:
|
||||
image: wiretrustee/wiretrustee:signal-latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 10000:10000
|
||||
|
||||
# Coturn
|
||||
coturn:
|
||||
image: coturn/coturn
|
||||
restart: unless-stopped
|
||||
domainname: stun.wiretrustee.com
|
||||
volumes:
|
||||
- ./turnserver.conf:/etc/turnserver.conf:ro
|
||||
# - ./privkey.pem:/etc/coturn/private/privkey.pem:ro
|
||||
# - ./cert.pem:/etc/coturn/certs/cert.pem:ro
|
||||
network_mode: host
|
||||
724
infrastructure_files/turnserver.conf
Normal file
724
infrastructure_files/turnserver.conf
Normal file
@@ -0,0 +1,724 @@
|
||||
# Coturn TURN SERVER configuration file
|
||||
#
|
||||
# Boolean values note: where a boolean value is supposed to be used,
|
||||
# you can use '0', 'off', 'no', 'false', or 'f' as 'false,
|
||||
# and you can use '1', 'on', 'yes', 'true', or 't' as 'true'
|
||||
# If the value is missing, then it means 'true' by default.
|
||||
#
|
||||
|
||||
# Listener interface device (optional, Linux only).
|
||||
# NOT RECOMMENDED.
|
||||
#
|
||||
#listening-device=eth0
|
||||
|
||||
# TURN listener port for UDP and TCP (Default: 3478).
|
||||
# Note: actually, TLS & DTLS sessions can connect to the
|
||||
# "plain" TCP & UDP port(s), too - if allowed by configuration.
|
||||
#
|
||||
listening-port=3478
|
||||
|
||||
# TURN listener port for TLS (Default: 5349).
|
||||
# Note: actually, "plain" TCP & UDP sessions can connect to the TLS & DTLS
|
||||
# port(s), too - if allowed by configuration. The TURN server
|
||||
# "automatically" recognizes the type of traffic. Actually, two listening
|
||||
# endpoints (the "plain" one and the "tls" one) are equivalent in terms of
|
||||
# functionality; but Coturn keeps both endpoints to satisfy the RFC 5766 specs.
|
||||
# For secure TCP connections, Coturn currently supports SSL version 3 and
|
||||
# TLS version 1.0, 1.1 and 1.2.
|
||||
# For secure UDP connections, Coturn supports DTLS version 1.
|
||||
#
|
||||
tls-listening-port=5349
|
||||
|
||||
# Alternative listening port for UDP and TCP listeners;
|
||||
# default (or zero) value means "listening port plus one".
|
||||
# This is needed for RFC 5780 support
|
||||
# (STUN extension specs, NAT behavior discovery). The TURN Server
|
||||
# supports RFC 5780 only if it is started with more than one
|
||||
# listening IP address of the same family (IPv4 or IPv6).
|
||||
# RFC 5780 is supported only by UDP protocol, other protocols
|
||||
# are listening to that endpoint only for "symmetry".
|
||||
#
|
||||
#alt-listening-port=0
|
||||
|
||||
# Alternative listening port for TLS and DTLS protocols.
|
||||
# Default (or zero) value means "TLS listening port plus one".
|
||||
#
|
||||
#alt-tls-listening-port=0
|
||||
|
||||
# Some network setups will require using a TCP reverse proxy in front
|
||||
# of the STUN server. If the proxy port option is set a single listener
|
||||
# is started on the given port that accepts connections using the
|
||||
# haproxy proxy protocol v2.
|
||||
# (https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)
|
||||
#
|
||||
#tcp-proxy-port=5555
|
||||
|
||||
# Listener IP address of relay server. Multiple listeners can be specified.
|
||||
# If no IP(s) specified in the config file or in the command line options,
|
||||
# then all IPv4 and IPv6 system IPs will be used for listening.
|
||||
#
|
||||
#listening-ip=172.17.19.101
|
||||
#listening-ip=10.207.21.238
|
||||
#listening-ip=2607:f0d0:1002:51::4
|
||||
|
||||
# Auxiliary STUN/TURN server listening endpoint.
|
||||
# Aux servers have almost full TURN and STUN functionality.
|
||||
# The (minor) limitations are:
|
||||
#
|
||||
# 1) Auxiliary servers do not have alternative ports and
|
||||
# they do not support STUN RFC 5780 functionality (CHANGE REQUEST).
|
||||
#
|
||||
# 2) Auxiliary servers also are never returning ALTERNATIVE-SERVER reply.
|
||||
#
|
||||
# Valid formats are 1.2.3.4:5555 for IPv4 and [1:2::3:4]:5555 for IPv6.
|
||||
#
|
||||
# There may be multiple aux-server options, each will be used for listening
|
||||
# to client requests.
|
||||
#
|
||||
#aux-server=172.17.19.110:33478
|
||||
#aux-server=[2607:f0d0:1002:51::4]:33478
|
||||
|
||||
# (recommended for older Linuxes only)
|
||||
# Automatically balance UDP traffic over auxiliary servers (if configured).
|
||||
# The load balancing is using the ALTERNATE-SERVER mechanism.
|
||||
# The TURN client must support 300 ALTERNATE-SERVER response for this
|
||||
# functionality.
|
||||
#
|
||||
#udp-self-balance
|
||||
|
||||
# Relay interface device for relay sockets (optional, Linux only).
|
||||
# NOT RECOMMENDED.
|
||||
#
|
||||
#relay-device=eth1
|
||||
|
||||
# Relay address (the local IP address that will be used to relay the
|
||||
# packets to the peer).
|
||||
# Multiple relay addresses may be used.
|
||||
# The same IP(s) can be used as both listening IP(s) and relay IP(s).
|
||||
#
|
||||
# If no relay IP(s) specified, then the turnserver will apply the default
|
||||
# policy: it will decide itself which relay addresses to be used, and it
|
||||
# will always be using the client socket IP address as the relay IP address
|
||||
# of the TURN session (if the requested relay address family is the same
|
||||
# as the family of the client socket).
|
||||
#
|
||||
#relay-ip=172.17.19.105
|
||||
#relay-ip=2607:f0d0:1002:51::5
|
||||
|
||||
# For Amazon EC2 users:
|
||||
#
|
||||
# TURN Server public/private address mapping, if the server is behind NAT.
|
||||
# In that situation, if a -X is used in form "-X <ip>" then that ip will be reported
|
||||
# as relay IP address of all allocations. This scenario works only in a simple case
|
||||
# when one single relay address is be used, and no RFC5780 functionality is required.
|
||||
# That single relay address must be mapped by NAT to the 'external' IP.
|
||||
# The "external-ip" value, if not empty, is returned in XOR-RELAYED-ADDRESS field.
|
||||
# For that 'external' IP, NAT must forward ports directly (relayed port 12345
|
||||
# must be always mapped to the same 'external' port 12345).
|
||||
#
|
||||
# In more complex case when more than one IP address is involved,
|
||||
# that option must be used several times, each entry must
|
||||
# have form "-X <public-ip/private-ip>", to map all involved addresses.
|
||||
# RFC5780 NAT discovery STUN functionality will work correctly,
|
||||
# if the addresses are mapped properly, even when the TURN server itself
|
||||
# is behind A NAT.
|
||||
#
|
||||
# By default, this value is empty, and no address mapping is used.
|
||||
#
|
||||
# external-ip=193.224.22.37
|
||||
#
|
||||
#OR:
|
||||
#
|
||||
#external-ip=60.70.80.91/172.17.19.101
|
||||
#external-ip=60.70.80.92/172.17.19.102
|
||||
|
||||
|
||||
# Number of the relay threads to handle the established connections
|
||||
# (in addition to authentication thread and the listener thread).
|
||||
# If explicitly set to 0 then application runs relay process in a
|
||||
# single thread, in the same thread with the listener process
|
||||
# (the authentication thread will still be a separate thread).
|
||||
#
|
||||
# If this parameter is not set, then the default OS-dependent
|
||||
# thread pattern algorithm will be employed. Usually the default
|
||||
# algorithm is optimal, so you have to change this option
|
||||
# if you want to make some fine tweaks.
|
||||
#
|
||||
# In the older systems (Linux kernel before 3.9),
|
||||
# the number of UDP threads is always one thread per network listening
|
||||
# endpoint - including the auxiliary endpoints - unless 0 (zero) or
|
||||
# 1 (one) value is set.
|
||||
#
|
||||
#relay-threads=0
|
||||
|
||||
# Lower and upper bounds of the UDP relay endpoints:
|
||||
# (default values are 49152 and 65535)
|
||||
#
|
||||
min-port=49152
|
||||
max-port=65535
|
||||
|
||||
# Uncomment to run TURN server in 'normal' 'moderate' verbose mode.
|
||||
# By default the verbose mode is off.
|
||||
verbose
|
||||
|
||||
# Uncomment to run TURN server in 'extra' verbose mode.
|
||||
# This mode is very annoying and produces lots of output.
|
||||
# Not recommended under normal circumstances.
|
||||
#
|
||||
#Verbose
|
||||
|
||||
# Uncomment to use fingerprints in the TURN messages.
|
||||
# By default the fingerprints are off.
|
||||
#
|
||||
fingerprint
|
||||
|
||||
# Uncomment to use long-term credential mechanism.
|
||||
# By default no credentials mechanism is used (any user allowed).
|
||||
#
|
||||
lt-cred-mech
|
||||
|
||||
# This option is the opposite of lt-cred-mech.
|
||||
# (TURN Server with no-auth option allows anonymous access).
|
||||
# If neither option is defined, and no users are defined,
|
||||
# then no-auth is default. If at least one user is defined,
|
||||
# in this file, in command line or in usersdb file, then
|
||||
# lt-cred-mech is default.
|
||||
#
|
||||
#no-auth
|
||||
|
||||
# TURN REST API flag.
|
||||
# (Time Limited Long Term Credential)
|
||||
# Flag that sets a special authorization option that is based upon authentication secret.
|
||||
#
|
||||
# This feature's purpose is to support "TURN Server REST API", see
|
||||
# "TURN REST API" link in the project's page
|
||||
# https://github.com/coturn/coturn/
|
||||
#
|
||||
# This option is used with timestamp:
|
||||
#
|
||||
# usercombo -> "timestamp:userid"
|
||||
# turn user -> usercombo
|
||||
# turn password -> base64(hmac(secret key, usercombo))
|
||||
#
|
||||
# This allows TURN credentials to be accounted for a specific user id.
|
||||
# If you don't have a suitable id, then the timestamp alone can be used.
|
||||
# This option is enabled by turning on secret-based authentication.
|
||||
# The actual value of the secret is defined either by the option static-auth-secret,
|
||||
# or can be found in the turn_secret table in the database (see below).
|
||||
#
|
||||
# Read more about it:
|
||||
# - https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00
|
||||
# - https://www.ietf.org/proceedings/87/slides/slides-87-behave-10.pdf
|
||||
#
|
||||
# Be aware that use-auth-secret overrides some parts of lt-cred-mech.
|
||||
# The use-auth-secret feature depends internally on lt-cred-mech, so if you set
|
||||
# this option then it automatically enables lt-cred-mech internally
|
||||
# as if you had enabled both.
|
||||
#
|
||||
# Note that you can use only one auth mechanism at the same time! This is because,
|
||||
# both mechanisms conduct username and password validation in different ways.
|
||||
#
|
||||
# Use either lt-cred-mech or use-auth-secret in the conf
|
||||
# to avoid any confusion.
|
||||
#
|
||||
#use-auth-secret
|
||||
|
||||
# 'Static' authentication secret value (a string) for TURN REST API only.
|
||||
# If not set, then the turn server
|
||||
# will try to use the 'dynamic' value in the turn_secret table
|
||||
# in the user database (if present). The database-stored value can be changed on-the-fly
|
||||
# by a separate program, so this is why that mode is considered 'dynamic'.
|
||||
#
|
||||
#static-auth-secret=north
|
||||
|
||||
# Server name used for
|
||||
# the oAuth authentication purposes.
|
||||
# The default value is the realm name.
|
||||
#
|
||||
# server-name=stun.wiretrustee.com
|
||||
|
||||
# Flag that allows oAuth authentication.
|
||||
#
|
||||
#oauth
|
||||
|
||||
# 'Static' user accounts for the long term credentials mechanism, only.
|
||||
# This option cannot be used with TURN REST API.
|
||||
# 'Static' user accounts are NOT dynamically checked by the turnserver process,
|
||||
# so they can NOT be changed while the turnserver is running.
|
||||
#
|
||||
#user=username1:key1
|
||||
#user=username2:key2
|
||||
# OR:
|
||||
user=username1:password1
|
||||
#user=username2:password2
|
||||
#
|
||||
# Keys must be generated by turnadmin utility. The key value depends
|
||||
# on user name, realm, and password:
|
||||
#
|
||||
# Example:
|
||||
# $ turnadmin -k -u ninefingers -r north.gov -p youhavetoberealistic
|
||||
# Output: 0xbc807ee29df3c9ffa736523fb2c4e8ee
|
||||
# ('0x' in the beginning of the key is what differentiates the key from
|
||||
# password. If it has 0x then it is a key, otherwise it is a password).
|
||||
#
|
||||
# The corresponding user account entry in the config file will be:
|
||||
#
|
||||
#user=ninefingers:0xbc807ee29df3c9ffa736523fb2c4e8ee
|
||||
# Or, equivalently, with open clear password (less secure):
|
||||
#user=ninefingers:youhavetoberealistic
|
||||
#
|
||||
|
||||
# SQLite database file name.
|
||||
#
|
||||
# The default file name is /var/db/turndb or /usr/local/var/db/turndb or
|
||||
# /var/lib/turn/turndb.
|
||||
#
|
||||
#userdb=/var/db/turndb
|
||||
|
||||
# PostgreSQL database connection string in the case that you are using PostgreSQL
|
||||
# as the user database.
|
||||
# This database can be used for the long-term credential mechanism
|
||||
# and it can store the secret value for secret-based timed authentication in TURN REST API.
|
||||
# See http://www.postgresql.org/docs/8.4/static/libpq-connect.html for 8.x PostgreSQL
|
||||
# versions connection string format, see
|
||||
# http://www.postgresql.org/docs/9.2/static/libpq-connect.html#LIBPQ-CONNSTRING
|
||||
# for 9.x and newer connection string formats.
|
||||
#
|
||||
#psql-userdb="host=<host> dbname=<database-name> user=<database-user> password=<database-user-password> connect_timeout=30"
|
||||
|
||||
# MySQL database connection string in the case that you are using MySQL
|
||||
# as the user database.
|
||||
# This database can be used for the long-term credential mechanism
|
||||
# and it can store the secret value for secret-based timed authentication in TURN REST API.
|
||||
#
|
||||
# Optional connection string parameters for the secure communications (SSL):
|
||||
# ca, capath, cert, key, cipher
|
||||
# (see http://dev.mysql.com/doc/refman/5.1/en/ssl-options.html for the
|
||||
# command options description).
|
||||
#
|
||||
# Use the string format below (space separated parameters, all optional):
|
||||
#
|
||||
# mysql-userdb="host=mysql dbname=coturn user=coturn password=CHANGE_ME port=3306 connect_timeout=10 read_timeout=10"
|
||||
|
||||
# If you want to use an encrypted password in the MySQL connection string,
|
||||
# then set the MySQL password encryption secret key file with this option.
|
||||
#
|
||||
# Warning: If this option is set, then the mysql password must be set in "mysql-userdb" in an encrypted format!
|
||||
# If you want to use a cleartext password then do not set this option!
|
||||
#
|
||||
# This is the file path for the aes encrypted secret key used for password encryption.
|
||||
#
|
||||
#secret-key-file=/path/
|
||||
|
||||
# MongoDB database connection string in the case that you are using MongoDB
|
||||
# as the user database.
|
||||
# This database can be used for long-term credential mechanism
|
||||
# and it can store the secret value for secret-based timed authentication in TURN REST API.
|
||||
# Use the string format described at http://hergert.me/docs/mongo-c-driver/mongoc_uri.html
|
||||
#
|
||||
#mongo-userdb="mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]"
|
||||
|
||||
# Redis database connection string in the case that you are using Redis
|
||||
# as the user database.
|
||||
# This database can be used for long-term credential mechanism
|
||||
# and it can store the secret value for secret-based timed authentication in TURN REST API.
|
||||
# Use the string format below (space separated parameters, all optional):
|
||||
#
|
||||
#redis-userdb="ip=<ip-address> dbname=<database-number> password=<database-user-password> port=<port> connect_timeout=<seconds>"
|
||||
|
||||
# Redis status and statistics database connection string, if used (default - empty, no Redis stats DB used).
|
||||
# This database keeps allocations status information, and it can be also used for publishing
|
||||
# and delivering traffic and allocation event notifications.
|
||||
# The connection string has the same parameters as redis-userdb connection string.
|
||||
# Use the string format below (space separated parameters, all optional):
|
||||
#
|
||||
#redis-statsdb="ip=<ip-address> dbname=<database-number> password=<database-user-password> port=<port> connect_timeout=<seconds>"
|
||||
|
||||
# The default realm to be used for the users when no explicit
|
||||
# origin/realm relationship is found in the database, or if the TURN
|
||||
# server is not using any database (just the commands-line settings
|
||||
# and the userdb file). Must be used with long-term credentials
|
||||
# mechanism or with TURN REST API.
|
||||
#
|
||||
# Note: If the default realm is not specified, then realm falls back to the host domain name.
|
||||
# If the domain name string is empty, or set to '(None)', then it is initialized as an empty string.
|
||||
#
|
||||
realm=wiretrustee.com
|
||||
# This flag sets the origin consistency
|
||||
# check. Across the session, all requests must have the same
|
||||
# main ORIGIN attribute value (if the ORIGIN was
|
||||
# initially used by the session).
|
||||
#
|
||||
#check-origin-consistency
|
||||
|
||||
# Per-user allocation quota.
|
||||
# default value is 0 (no quota, unlimited number of sessions per user).
|
||||
# This option can also be set through the database, for a particular realm.
|
||||
#
|
||||
#user-quota=0
|
||||
|
||||
# Total allocation quota.
|
||||
# default value is 0 (no quota).
|
||||
# This option can also be set through the database, for a particular realm.
|
||||
#
|
||||
#total-quota=0
|
||||
|
||||
# Max bytes-per-second bandwidth a TURN session is allowed to handle
|
||||
# (input and output network streams are treated separately). Anything above
|
||||
# that limit will be dropped or temporarily suppressed (within
|
||||
# the available buffer limits).
|
||||
# This option can also be set through the database, for a particular realm.
|
||||
#
|
||||
#max-bps=0
|
||||
|
||||
#
|
||||
# Maximum server capacity.
|
||||
# Total bytes-per-second bandwidth the TURN server is allowed to allocate
|
||||
# for the sessions, combined (input and output network streams are treated separately).
|
||||
#
|
||||
# bps-capacity=0
|
||||
|
||||
# Uncomment if no UDP client listener is desired.
|
||||
# By default UDP client listener is always started.
|
||||
#
|
||||
#no-udp
|
||||
|
||||
# Uncomment if no TCP client listener is desired.
|
||||
# By default TCP client listener is always started.
|
||||
#
|
||||
#no-tcp
|
||||
|
||||
# Uncomment if no TLS client listener is desired.
|
||||
# By default TLS client listener is always started.
|
||||
#
|
||||
#no-tls
|
||||
|
||||
# Uncomment if no DTLS client listener is desired.
|
||||
# By default DTLS client listener is always started.
|
||||
#
|
||||
#no-dtls
|
||||
|
||||
# Uncomment if no UDP relay endpoints are allowed.
|
||||
# By default UDP relay endpoints are enabled (like in RFC 5766).
|
||||
#
|
||||
#no-udp-relay
|
||||
|
||||
# Uncomment if no TCP relay endpoints are allowed.
|
||||
# By default TCP relay endpoints are enabled (like in RFC 6062).
|
||||
#
|
||||
#no-tcp-relay
|
||||
|
||||
# Uncomment if extra security is desired,
|
||||
# with nonce value having a limited lifetime.
|
||||
# The nonce value is unique for a session.
|
||||
# Set this option to limit the nonce lifetime.
|
||||
# Set it to 0 for unlimited lifetime.
|
||||
# It defaults to 600 secs (10 min) if no value is provided. After that delay,
|
||||
# the client will get 438 error and will have to re-authenticate itself.
|
||||
#
|
||||
#stale-nonce=600
|
||||
|
||||
# Uncomment if you want to set the maximum allocation
|
||||
# time before it has to be refreshed.
|
||||
# Default is 3600s.
|
||||
#
|
||||
#max-allocate-lifetime=3600
|
||||
|
||||
|
||||
# Uncomment to set the lifetime for the channel.
|
||||
# Default value is 600 secs (10 minutes).
|
||||
# This value MUST not be changed for production purposes.
|
||||
#
|
||||
#channel-lifetime=600
|
||||
|
||||
# Uncomment to set the permission lifetime.
|
||||
# Default to 300 secs (5 minutes).
|
||||
# In production this value MUST not be changed,
|
||||
# however it can be useful for test purposes.
|
||||
#
|
||||
#permission-lifetime=300
|
||||
|
||||
# Certificate file.
|
||||
# Use an absolute path or path relative to the
|
||||
# configuration file.
|
||||
# Use PEM file format.
|
||||
#
|
||||
cert=/etc/coturn/certs/cert.pem
|
||||
|
||||
# Private key file.
|
||||
# Use an absolute path or path relative to the
|
||||
# configuration file.
|
||||
# Use PEM file format.
|
||||
#
|
||||
pkey=/etc/coturn/private/privkey.pem
|
||||
|
||||
# Private key file password, if it is in encoded format.
|
||||
# This option has no default value.
|
||||
#
|
||||
#pkey-pwd=...
|
||||
|
||||
# Allowed OpenSSL cipher list for TLS/DTLS connections.
|
||||
# Default value is "DEFAULT".
|
||||
#
|
||||
#cipher-list="DEFAULT"
|
||||
|
||||
# CA file in OpenSSL format.
|
||||
# Forces TURN server to verify the client SSL certificates.
|
||||
# By default this is not set: there is no default value and the client
|
||||
# certificate is not checked.
|
||||
#
|
||||
# Example:
|
||||
#CA-file=/etc/ssh/id_rsa.cert
|
||||
|
||||
# Curve name for EC ciphers, if supported by OpenSSL
|
||||
# library (TLS and DTLS). The default value is prime256v1,
|
||||
# if pre-OpenSSL 1.0.2 is used. With OpenSSL 1.0.2+,
|
||||
# an optimal curve will be automatically calculated, if not defined
|
||||
# by this option.
|
||||
#
|
||||
#ec-curve-name=prime256v1
|
||||
|
||||
# Use 566 bits predefined DH TLS key. Default size of the key is 2066.
|
||||
#
|
||||
#dh566
|
||||
|
||||
# Use 1066 bits predefined DH TLS key. Default size of the key is 2066.
|
||||
#
|
||||
#dh1066
|
||||
|
||||
# Use custom DH TLS key, stored in PEM format in the file.
|
||||
# Flags --dh566 and --dh2066 are ignored when the DH key is taken from a file.
|
||||
#
|
||||
#dh-file=<DH-PEM-file-name>
|
||||
|
||||
# Flag to prevent stdout log messages.
|
||||
# By default, all log messages go to both stdout and to
|
||||
# the configured log file. With this option everything will
|
||||
# go to the configured log only (unless the log file itself is stdout).
|
||||
#
|
||||
#no-stdout-log
|
||||
|
||||
# Option to set the log file name.
|
||||
# By default, the turnserver tries to open a log file in
|
||||
# /var/log, /var/tmp, /tmp and the current directory
|
||||
# (Whichever file open operation succeeds first will be used).
|
||||
# With this option you can set the definite log file name.
|
||||
# The special names are "stdout" and "-" - they will force everything
|
||||
# to the stdout. Also, the "syslog" name will force everything to
|
||||
# the system log (syslog).
|
||||
# In the runtime, the logfile can be reset with the SIGHUP signal
|
||||
# to the turnserver process.
|
||||
#
|
||||
log-file=stdout
|
||||
|
||||
# Option to redirect all log output into system log (syslog).
|
||||
#
|
||||
# syslog
|
||||
|
||||
# This flag means that no log file rollover will be used, and the log file
|
||||
# name will be constructed as-is, without PID and date appendage.
|
||||
# This option can be used, for example, together with the logrotate tool.
|
||||
#
|
||||
#simple-log
|
||||
|
||||
# Option to set the "redirection" mode. The value of this option
|
||||
# will be the address of the alternate server for UDP & TCP service in the form of
|
||||
# <ip>[:<port>]. The server will send this value in the attribute
|
||||
# ALTERNATE-SERVER, with error 300, on ALLOCATE request, to the client.
|
||||
# Client will receive only values with the same address family
|
||||
# as the client network endpoint address family.
|
||||
# See RFC 5389 and RFC 5766 for the description of ALTERNATE-SERVER functionality.
|
||||
# The client must use the obtained value for subsequent TURN communications.
|
||||
# If more than one --alternate-server option is provided, then the functionality
|
||||
# can be more accurately described as "load-balancing" than a mere "redirection".
|
||||
# If the port number is omitted, then the default port
|
||||
# number 3478 for the UDP/TCP protocols will be used.
|
||||
# Colon (:) characters in IPv6 addresses may conflict with the syntax of
|
||||
# the option. To alleviate this conflict, literal IPv6 addresses are enclosed
|
||||
# in square brackets in such resource identifiers, for example:
|
||||
# [2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478 .
|
||||
# Multiple alternate servers can be set. They will be used in the
|
||||
# round-robin manner. All servers in the pool are considered of equal weight and
|
||||
# the load will be distributed equally. For example, if you have 4 alternate servers,
|
||||
# then each server will receive 25% of ALLOCATE requests. A alternate TURN server
|
||||
# address can be used more than one time with the alternate-server option, so this
|
||||
# can emulate "weighting" of the servers.
|
||||
#
|
||||
# Examples:
|
||||
#alternate-server=1.2.3.4:5678
|
||||
#alternate-server=11.22.33.44:56789
|
||||
#alternate-server=5.6.7.8
|
||||
#alternate-server=[2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478
|
||||
|
||||
# Option to set alternative server for TLS & DTLS services in form of
|
||||
# <ip>:<port>. If the port number is omitted, then the default port
|
||||
# number 5349 for the TLS/DTLS protocols will be used. See the previous
|
||||
# option for the functionality description.
|
||||
#
|
||||
# Examples:
|
||||
#tls-alternate-server=1.2.3.4:5678
|
||||
#tls-alternate-server=11.22.33.44:56789
|
||||
#tls-alternate-server=[2001:db8:85a3:8d3:1319:8a2e:370:7348]:3478
|
||||
|
||||
# Option to suppress TURN functionality, only STUN requests will be processed.
|
||||
# Run as STUN server only, all TURN requests will be ignored.
|
||||
# By default, this option is NOT set.
|
||||
#
|
||||
#stun-only
|
||||
|
||||
# Option to hide software version. Enhance security when used in production.
|
||||
# Revealing the specific software version of the agent through the
|
||||
# SOFTWARE attribute might allow them to become more vulnerable to
|
||||
# attacks against software that is known to contain security holes.
|
||||
# Implementers SHOULD make usage of the SOFTWARE attribute a
|
||||
# configurable option (https://tools.ietf.org/html/rfc5389#section-16.1.2)
|
||||
#
|
||||
no-software-attribute
|
||||
|
||||
# Option to suppress STUN functionality, only TURN requests will be processed.
|
||||
# Run as TURN server only, all STUN requests will be ignored.
|
||||
# By default, this option is NOT set.
|
||||
#
|
||||
#no-stun
|
||||
|
||||
# This is the timestamp/username separator symbol (character) in TURN REST API.
|
||||
# The default value is ':'.
|
||||
# rest-api-separator=:
|
||||
|
||||
# Flag that can be used to allow peers on the loopback addresses (127.x.x.x and ::1).
|
||||
# This is an extra security measure.
|
||||
#
|
||||
# (To avoid any security issue that allowing loopback access may raise,
|
||||
# the no-loopback-peers option is replaced by allow-loopback-peers.)
|
||||
#
|
||||
# Allow it only for testing in a development environment!
|
||||
# In production it adds a possible security vulnerability, so for security reasons
|
||||
# it is not allowed using it together with empty cli-password.
|
||||
#
|
||||
#allow-loopback-peers
|
||||
|
||||
# Flag that can be used to disallow peers on well-known broadcast addresses (224.0.0.0 and above, and FFXX:*).
|
||||
# This is an extra security measure.
|
||||
#
|
||||
#no-multicast-peers
|
||||
|
||||
# Option to set the max time, in seconds, allowed for full allocation establishment.
|
||||
# Default is 60 seconds.
|
||||
#
|
||||
#max-allocate-timeout=60
|
||||
|
||||
# Option to allow or ban specific ip addresses or ranges of ip addresses.
|
||||
# If an ip address is specified as both allowed and denied, then the ip address is
|
||||
# considered to be allowed. This is useful when you wish to ban a range of ip
|
||||
# addresses, except for a few specific ips within that range.
|
||||
#
|
||||
# This can be used when you do not want users of the turn server to be able to access
|
||||
# machines reachable by the turn server, but would otherwise be unreachable from the
|
||||
# internet (e.g. when the turn server is sitting behind a NAT)
|
||||
#
|
||||
# Examples:
|
||||
# denied-peer-ip=83.166.64.0-83.166.95.255
|
||||
# allowed-peer-ip=83.166.68.45
|
||||
|
||||
# File name to store the pid of the process.
|
||||
# Default is /var/run/turnserver.pid (if superuser account is used) or
|
||||
# /var/tmp/turnserver.pid .
|
||||
#
|
||||
pidfile="/var/tmp/turnserver.pid"
|
||||
|
||||
# Require authentication of the STUN Binding request.
|
||||
# By default, the clients are allowed anonymous access to the STUN Binding functionality.
|
||||
#
|
||||
#secure-stun
|
||||
|
||||
# Mobility with ICE (MICE) specs support.
|
||||
#
|
||||
#mobility
|
||||
|
||||
# Allocate Address Family according
|
||||
# If enabled then TURN server allocates address family according the TURN
|
||||
# Client <=> Server communication address family.
|
||||
# (By default Coturn works according RFC 6156.)
|
||||
# !!Warning: Enabling this option breaks RFC6156 section-4.2 (violates use default IPv4)!!
|
||||
#
|
||||
#keep-address-family
|
||||
|
||||
|
||||
# User name to run the process. After the initialization, the turnserver process
|
||||
# will attempt to change the current user ID to that user.
|
||||
#
|
||||
#proc-user=<user-name>
|
||||
|
||||
# Group name to run the process. After the initialization, the turnserver process
|
||||
# will attempt to change the current group ID to that group.
|
||||
#
|
||||
#proc-group=<group-name>
|
||||
|
||||
# Turn OFF the CLI support.
|
||||
# By default it is always ON.
|
||||
# See also options cli-ip and cli-port.
|
||||
#
|
||||
no-cli
|
||||
|
||||
#Local system IP address to be used for CLI server endpoint. Default value
|
||||
# is 127.0.0.1.
|
||||
#
|
||||
# cli-ip=127.0.0.1
|
||||
|
||||
# CLI server port. Default is 5766.
|
||||
#
|
||||
# cli-port=5766
|
||||
|
||||
# CLI access password. Default is empty (no password).
|
||||
# For the security reasons, it is recommended that you use the encrypted
|
||||
# form of the password (see the -P command in the turnadmin utility).
|
||||
#
|
||||
# Secure form for password 'qwerty':
|
||||
#
|
||||
#cli-password=$5$79a316b350311570$81df9cfb9af7f5e5a76eada31e7097b663a0670f99a3c07ded3f1c8e59c5658a
|
||||
#
|
||||
# Or unsecure form for the same password:
|
||||
#
|
||||
# cli-password=CHANGE_ME
|
||||
|
||||
# Enable Web-admin support on https. By default it is Disabled.
|
||||
# If it is enabled it also enables a http a simple static banner page
|
||||
# with a small reminder that the admin page is available only on https.
|
||||
#
|
||||
#web-admin
|
||||
|
||||
# Local system IP address to be used for Web-admin server endpoint. Default value is 127.0.0.1.
|
||||
#
|
||||
#web-admin-ip=127.0.0.1
|
||||
|
||||
# Web-admin server port. Default is 8080.
|
||||
#
|
||||
#web-admin-port=8080
|
||||
|
||||
# Web-admin server listen on STUN/TURN worker threads
|
||||
# By default it is disabled for security resons! (Not recommended in any production environment!)
|
||||
#
|
||||
#web-admin-listen-on-workers
|
||||
|
||||
# Server relay. NON-STANDARD AND DANGEROUS OPTION.
|
||||
# Only for those applications when you want to run
|
||||
# server applications on the relay endpoints.
|
||||
# This option eliminates the IP permissions check on
|
||||
# the packets incoming to the relay endpoints.
|
||||
#
|
||||
#server-relay
|
||||
|
||||
# Maximum number of output sessions in ps CLI command.
|
||||
# This value can be changed on-the-fly in CLI. The default value is 256.
|
||||
#
|
||||
#cli-max-output-sessions
|
||||
|
||||
# Set network engine type for the process (for internal purposes).
|
||||
#
|
||||
#ne=[1|2|3]
|
||||
|
||||
# Do not allow an TLS/DTLS version of protocol
|
||||
#
|
||||
#no-tlsv1
|
||||
#no-tlsv1_1
|
||||
#no-tlsv1_2
|
||||
17
manifest.xml
Normal file
17
manifest.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
version="0.0.0.1"
|
||||
processorArchitecture="*"
|
||||
name="wiretrustee.exe"
|
||||
type="win32"
|
||||
/>
|
||||
<description>Wiretrustee application</description>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
</assembly>
|
||||
@@ -5,6 +5,6 @@ Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/wiretrustee up --config /etc/wiretrustee/wiretrustee.json --log-level debug
|
||||
ExecStart=/usr/local/bin/wiretrustee up --config /etc/wiretrustee/config.json --log-level debug
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
9
resources.rc
Normal file
9
resources.rc
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <windows.h>
|
||||
|
||||
#pragma code_page(65001) // UTF-8
|
||||
|
||||
#define STRINGIZE(x) #x
|
||||
#define EXPAND(x) STRINGIZE(x)
|
||||
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST manifest.xml
|
||||
wintun.dll RCDATA wintun.dll
|
||||
|
||||
BIN
resources_windows_amd64.syso
Normal file
BIN
resources_windows_amd64.syso
Normal file
Binary file not shown.
@@ -1,18 +1,23 @@
|
||||
# Wiretrustee Signal Server
|
||||
|
||||
This is a Wiretrustee signal-exchange server and client library to exchange connection information between Wiretrustee Trusted Device and Wiretrustee Hub
|
||||
|
||||
The project uses gRPC library and defines service in protobuf file located in:
|
||||
```proto/signal_exchange.proto```
|
||||
|
||||
|
||||
To build the project you have to do the following things.
|
||||
|
||||
Install protobuf version 3 (by default v3 is installed on ubuntu 20.04. On previous versions it is proto 2):
|
||||
```
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
sudo apt install protoc-gen-go
|
||||
sudo apt install golang-goprotobuf-dev
|
||||
```
|
||||
|
||||
Generate gRPC code:
|
||||
```
|
||||
protoc -I proto/ proto/signalexchange.proto --go_out=plugins=grpc:proto
|
||||
```
|
||||
|
||||
```
|
||||
Generate gRPC code:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
protoc -I proto/ proto/signalexchange.proto --go_out=plugins=grpc:proto
|
||||
```
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
pb "github.com/golang/protobuf/proto"
|
||||
pb "github.com/golang/protobuf/proto" //nolint
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/wiretrustee/wiretrustee/signal/proto"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
@@ -21,24 +21,24 @@ import (
|
||||
|
||||
// A set of tools to exchange connection details (Wireguard endpoints) with the remote peer.
|
||||
|
||||
// Wraps the Signal Exchange Service gRpc client
|
||||
// Client Wraps the Signal Exchange Service gRpc client
|
||||
type Client struct {
|
||||
key wgtypes.Key
|
||||
encryptionKey string
|
||||
realClient proto.SignalExchangeClient
|
||||
signalConn *grpc.ClientConn
|
||||
ctx context.Context
|
||||
stream proto.SignalExchange_ConnectStreamClient
|
||||
key wgtypes.Key
|
||||
realClient proto.SignalExchangeClient
|
||||
signalConn *grpc.ClientConn
|
||||
ctx context.Context
|
||||
stream proto.SignalExchange_ConnectStreamClient
|
||||
//waiting group to notify once stream is connected
|
||||
connWg sync.WaitGroup //todo use a channel instead??
|
||||
connWg *sync.WaitGroup //todo use a channel instead??
|
||||
}
|
||||
|
||||
// Closes underlying connections to the Signal Exchange
|
||||
// Close Closes underlying connections to the Signal Exchange
|
||||
func (c *Client) Close() error {
|
||||
return c.signalConn.Close()
|
||||
}
|
||||
|
||||
func NewClient(addr string, key wgtypes.Key, ctx context.Context) (*Client, error) {
|
||||
// NewClient creates a new Signal client
|
||||
func NewClient(ctx context.Context, addr string, key wgtypes.Key) (*Client, error) {
|
||||
|
||||
conn, err := grpc.DialContext(
|
||||
ctx,
|
||||
@@ -55,15 +55,17 @@ func NewClient(addr string, key wgtypes.Key, ctx context.Context) (*Client, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
return &Client{
|
||||
realClient: proto.NewSignalExchangeClient(conn),
|
||||
ctx: ctx,
|
||||
signalConn: conn,
|
||||
key: key,
|
||||
connWg: &wg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Connects to the Signal Exchange message stream and starts receiving messages.
|
||||
// Receive Connects to the Signal Exchange message stream and starts receiving messages.
|
||||
// The messages will be handled by msgHandler function provided.
|
||||
// This function runs a goroutine underneath and reconnects to the Signal Exchange if errors occur (e.g. Exchange restart)
|
||||
// The key is the identifier of our Peer (could be Wireguard public key)
|
||||
@@ -107,8 +109,6 @@ func (c *Client) connect(key string, msgHandler func(msg *proto.Message) error)
|
||||
// add key fingerprint to the request header to be identified on the server side
|
||||
md := metadata.New(map[string]string{proto.HeaderId: key})
|
||||
ctx := metadata.NewOutgoingContext(c.ctx, md)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
stream, err := c.realClient.ConnectStream(ctx)
|
||||
|
||||
@@ -116,6 +116,15 @@ func (c *Client) connect(key string, msgHandler func(msg *proto.Message) error)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// blocks
|
||||
header, err := c.stream.Header()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registered := header.Get(proto.HeaderRegistered)
|
||||
if len(registered) == 0 {
|
||||
return fmt.Errorf("didn't receive a registration header from the Signal server whille connecting to the streams")
|
||||
}
|
||||
//connection established we are good to use the stream
|
||||
c.connWg.Done()
|
||||
|
||||
@@ -124,12 +133,12 @@ func (c *Client) connect(key string, msgHandler func(msg *proto.Message) error)
|
||||
return c.receive(stream, msgHandler)
|
||||
}
|
||||
|
||||
// Waits until the client is connected to the message stream
|
||||
// WaitConnected waits until the client is connected to the message stream
|
||||
func (c *Client) WaitConnected() {
|
||||
c.connWg.Wait()
|
||||
}
|
||||
|
||||
// Sends a message to the remote Peer through the Signal Exchange using established stream connection to the Signal Server
|
||||
// SendToStream sends a message to the remote Peer through the Signal Exchange using established stream connection to the Signal Server
|
||||
// The Client.Receive method must be called before sending messages to establish initial connection to the Signal Exchange
|
||||
// Client.connWg can be used to wait
|
||||
func (c *Client) SendToStream(msg *proto.EncryptedMessage) error {
|
||||
@@ -154,6 +163,9 @@ func (c *Client) decryptMessage(msg *proto.EncryptedMessage) (*proto.Message, er
|
||||
return nil, err
|
||||
}
|
||||
decryptedBody, err := Decrypt(msg.GetBody(), remoteKey, c.key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body := &proto.Body{}
|
||||
err = pb.Unmarshal(decryptedBody, body)
|
||||
if err != nil {
|
||||
@@ -190,7 +202,7 @@ func (c *Client) encryptMessage(msg *proto.Message) (*proto.EncryptedMessage, er
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Sends a message to the remote Peer through the Signal Exchange.
|
||||
// Send sends a message to the remote Peer through the Signal Exchange.
|
||||
func (c *Client) Send(msg *proto.Message) error {
|
||||
|
||||
encryptedMessage, err := c.encryptMessage(msg)
|
||||
@@ -206,7 +218,7 @@ func (c *Client) Send(msg *proto.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Receives messages from other peers coming through the Signal Exchange
|
||||
// receive receives messages from other peers coming through the Signal Exchange
|
||||
func (c *Client) receive(stream proto.SignalExchange_ConnectStreamClient,
|
||||
msgHandler func(msg *proto.Message) error) error {
|
||||
|
||||
@@ -240,6 +252,7 @@ func (c *Client) receive(stream proto.SignalExchange_ConnectStreamClient,
|
||||
}
|
||||
}
|
||||
|
||||
// UnMarshalCredential parses the credentials from the message and returns a Credential instance
|
||||
func UnMarshalCredential(msg *proto.Message) (*Credential, error) {
|
||||
|
||||
credential := strings.Split(msg.GetBody().GetPayload(), ":")
|
||||
@@ -252,6 +265,7 @@ func UnMarshalCredential(msg *proto.Message) (*Credential, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MarshalCredential marsharl a Credential instance and returns a Message object
|
||||
func MarshalCredential(myKey wgtypes.Key, remoteKey wgtypes.Key, credential *Credential, t proto.Body_Type) (*proto.Message, error) {
|
||||
return &proto.Message{
|
||||
Key: myKey.PublicKey().String(),
|
||||
@@ -263,6 +277,7 @@ func MarshalCredential(myKey wgtypes.Key, remoteKey wgtypes.Key, credential *Cre
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Credential is an instance of a Client's Credential
|
||||
type Credential struct {
|
||||
UFrag string
|
||||
Pwd string
|
||||
|
||||
@@ -13,25 +13,25 @@ import (
|
||||
// These tools use Golang crypto package (Curve25519, XSalsa20 and Poly1305 to encrypt and authenticate)
|
||||
// Wireguard keys are used for encryption
|
||||
|
||||
// Encrypts a message using local Wireguard private key and remote peer's public key.
|
||||
func Encrypt(msg []byte, peersPublicKey wgtypes.Key, privateKey wgtypes.Key) ([]byte, error) {
|
||||
// Encrypt encrypts a message using local Wireguard private key and remote peer's public key.
|
||||
func Encrypt(msg []byte, peerPublicKey wgtypes.Key, privateKey wgtypes.Key) ([]byte, error) {
|
||||
nonce, err := genNonce()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return box.Seal(nonce[:], msg, nonce, toByte32(peersPublicKey), toByte32(privateKey)), nil
|
||||
return box.Seal(nonce[:], msg, nonce, toByte32(peerPublicKey), toByte32(privateKey)), nil
|
||||
}
|
||||
|
||||
// Decrypts a message that has been encrypted by the remote peer using Wireguard private key and remote peer's public key.
|
||||
func Decrypt(encryptedMsg []byte, peersPublicKey wgtypes.Key, privateKey wgtypes.Key) ([]byte, error) {
|
||||
// Decrypt decrypts a message that has been encrypted by the remote peer using Wireguard private key and remote peer's public key.
|
||||
func Decrypt(encryptedMsg []byte, peerPublicKey wgtypes.Key, privateKey wgtypes.Key) ([]byte, error) {
|
||||
nonce, err := genNonce()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(nonce[:], encryptedMsg[:24])
|
||||
opened, ok := box.Open(nil, encryptedMsg[24:], nonce, toByte32(peersPublicKey), toByte32(privateKey))
|
||||
opened, ok := box.Open(nil, encryptedMsg[24:], nonce, toByte32(peerPublicKey), toByte32(privateKey))
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to decrypt message from peer %s", peersPublicKey.String())
|
||||
return nil, fmt.Errorf("failed to decrypt message from peer %s", peerPublicKey.String())
|
||||
}
|
||||
|
||||
return opened, nil
|
||||
|
||||
40
signal/encryption_test.go
Normal file
40
signal/encryption_test.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package signal
|
||||
|
||||
import (
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEncryptDecrypt(t *testing.T) {
|
||||
strMsg := "message to encrypt"
|
||||
bytesMsg := []byte(strMsg)
|
||||
|
||||
peerAKey, err := wgtypes.GenerateKey()
|
||||
if err != nil {
|
||||
t.Error()
|
||||
return
|
||||
}
|
||||
|
||||
peerBKey, err := wgtypes.GenerateKey()
|
||||
if err != nil {
|
||||
t.Error()
|
||||
return
|
||||
}
|
||||
|
||||
encryptedMessage, err := Encrypt(bytesMsg, peerBKey.PublicKey(), peerAKey)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
decryptedMessage, err := Decrypt(encryptedMessage, peerAKey.PublicKey(), peerBKey)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if string(decryptedMessage) != strMsg {
|
||||
t.Error()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,10 +6,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// HexTable Table of Hexadecimal chars
|
||||
HexTable = "0123456789abcdef"
|
||||
)
|
||||
|
||||
// Generates a SHA256 Fingerprint of the string
|
||||
// FingerPrint generates a SHA256 Fingerprint of the string
|
||||
func FingerPrint(key string) string {
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(key))
|
||||
|
||||
@@ -3,9 +3,10 @@ package peer
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/wiretrustee/wiretrustee/signal/proto"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Representation of a connected Peer
|
||||
// Peer representation of a connected Peer
|
||||
type Peer struct {
|
||||
// a unique id of the Peer (e.g. sha256 fingerprint of the Wireguard public key)
|
||||
Id string
|
||||
@@ -14,6 +15,7 @@ type Peer struct {
|
||||
Stream proto.SignalExchange_ConnectStreamServer
|
||||
}
|
||||
|
||||
// NewPeer creates a new instance of a connected Peer
|
||||
func NewPeer(id string, stream proto.SignalExchange_ConnectStreamServer) *Peer {
|
||||
return &Peer{
|
||||
Id: id,
|
||||
@@ -21,34 +23,49 @@ func NewPeer(id string, stream proto.SignalExchange_ConnectStreamServer) *Peer {
|
||||
}
|
||||
}
|
||||
|
||||
// registry that holds all currently connected Peers
|
||||
// Registry registry that holds all currently connected Peers
|
||||
type Registry struct {
|
||||
// Peer.key -> Peer
|
||||
Peers map[string]*Peer
|
||||
Peers sync.Map
|
||||
}
|
||||
|
||||
// NewRegistry creates a new connected Peer registry
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
Peers: make(map[string]*Peer),
|
||||
}
|
||||
return &Registry{}
|
||||
}
|
||||
|
||||
// Registers peer in the registry
|
||||
func (reg *Registry) Register(peer *Peer) {
|
||||
if _, exists := reg.Peers[peer.Id]; exists {
|
||||
log.Warnf("peer [%s] has been already registered", peer.Id)
|
||||
} else {
|
||||
log.Printf("registering new peer [%s]", peer.Id)
|
||||
// Get gets a peer from the registry
|
||||
func (registry *Registry) Get(peerId string) (*Peer, bool) {
|
||||
if load, ok := registry.Peers.Load(peerId); ok {
|
||||
return load.(*Peer), ok
|
||||
}
|
||||
//replace Peer even if exists
|
||||
//todo should we really replace?
|
||||
reg.Peers[peer.Id] = peer
|
||||
return nil, false
|
||||
|
||||
}
|
||||
|
||||
// Deregister Peer from the Registry (usually once it disconnects)
|
||||
func (reg *Registry) DeregisterHub(peer *Peer) {
|
||||
if _, ok := reg.Peers[peer.Id]; ok {
|
||||
delete(reg.Peers, peer.Id)
|
||||
func (registry *Registry) IsPeerRegistered(peerId string) bool {
|
||||
if _, ok := registry.Peers.Load(peerId); ok {
|
||||
return ok
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Register registers peer in the registry
|
||||
func (registry *Registry) Register(peer *Peer) {
|
||||
// can be that peer already exists but it is fine (e.g. reconnect)
|
||||
// todo investigate what happens to the old peer (especially Peer.Stream) when we override it
|
||||
registry.Peers.Store(peer.Id, peer)
|
||||
log.Printf("registered peer [%s]", peer.Id)
|
||||
|
||||
}
|
||||
|
||||
// Deregister deregister Peer from the Registry (usually once it disconnects)
|
||||
func (registry *Registry) Deregister(peer *Peer) {
|
||||
_, loaded := registry.Peers.LoadAndDelete(peer.Id)
|
||||
if loaded {
|
||||
log.Printf("deregistered peer [%s]", peer.Id)
|
||||
} else {
|
||||
log.Warnf("attempted to remove non-existent peer [%s]", peer.Id)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
54
signal/peer/peer_test.go
Normal file
54
signal/peer/peer_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package peer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRegistry_GetNonExistentPeer(t *testing.T) {
|
||||
r := NewRegistry()
|
||||
|
||||
peer, ok := r.Get("non_existent_peer")
|
||||
|
||||
if peer != nil {
|
||||
t.Errorf("expected non_existent_peer not found in the registry")
|
||||
}
|
||||
|
||||
if ok {
|
||||
t.Errorf("expected non_existent_peer not found in the registry")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistry_Register(t *testing.T) {
|
||||
r := NewRegistry()
|
||||
peer1 := NewPeer("test_peer_1", nil)
|
||||
peer2 := NewPeer("test_peer_2", nil)
|
||||
r.Register(peer1)
|
||||
r.Register(peer2)
|
||||
|
||||
if _, ok := r.Get("test_peer_1"); !ok {
|
||||
t.Errorf("expected test_peer_1 not found in the registry")
|
||||
}
|
||||
|
||||
if _, ok := r.Get("test_peer_2"); !ok {
|
||||
t.Errorf("expected test_peer_2 not found in the registry")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistry_Deregister(t *testing.T) {
|
||||
r := NewRegistry()
|
||||
peer1 := NewPeer("test_peer_1", nil)
|
||||
peer2 := NewPeer("test_peer_2", nil)
|
||||
r.Register(peer1)
|
||||
r.Register(peer2)
|
||||
|
||||
r.Deregister(peer1)
|
||||
|
||||
if _, ok := r.Get("test_peer_1"); ok {
|
||||
t.Errorf("expected test_peer_1 to absent in the registry after deregistering")
|
||||
}
|
||||
|
||||
if _, ok := r.Get("test_peer_2"); !ok {
|
||||
t.Errorf("expected test_peer_2 not found in the registry")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,3 +2,4 @@ package proto
|
||||
|
||||
// protocol constants, field names that can be used by both client and server
|
||||
const HeaderId = "x-wiretrustee-peer-id"
|
||||
const HeaderRegistered = "x-wiretrustee-peer-registered"
|
||||
|
||||
@@ -6,7 +6,7 @@ package proto
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
proto "github.com/golang/protobuf/proto" //nolint
|
||||
_ "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
@@ -57,11 +57,11 @@ func (Body_Type) EnumDescriptor() ([]byte, []int) {
|
||||
// Used for sending through signal.
|
||||
// The body of this message is the Body message encrypted with the Wireguard private key and the remote Peer key
|
||||
type EncryptedMessage struct {
|
||||
// a sha256 fingerprint of the Wireguard public key
|
||||
// Wireguard public key
|
||||
Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
|
||||
// a sha256 fingerprint of the Wireguard public key of the remote peer to connect to
|
||||
// Wireguard public key of the remote peer to connect to
|
||||
RemoteKey string `protobuf:"bytes,3,opt,name=remoteKey,proto3" json:"remoteKey,omitempty"`
|
||||
// encrypted message body
|
||||
// encrypted message Body
|
||||
Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
@@ -116,9 +116,9 @@ func (m *EncryptedMessage) GetBody() []byte {
|
||||
|
||||
// A decrypted representation of the EncryptedMessage. Used locally before/after encryption
|
||||
type Message struct {
|
||||
// a sha256 fingerprint of the Wireguard public key
|
||||
// Wireguard public key
|
||||
Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
|
||||
// a sha256 fingerprint of the Wireguard public key of the remote peer to connect to
|
||||
// Wireguard public key of the remote peer to connect to
|
||||
RemoteKey string `protobuf:"bytes,3,opt,name=remoteKey,proto3" json:"remoteKey,omitempty"`
|
||||
Body *Body `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
|
||||
@@ -2,7 +2,6 @@ package signal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/wiretrustee/wiretrustee/signal/peer"
|
||||
@@ -13,27 +12,26 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
port = flag.Int("port", 10000, "The server port")
|
||||
)
|
||||
|
||||
type SignalExchangeServer struct {
|
||||
// Server an instance of a Signal server
|
||||
type Server struct {
|
||||
registry *peer.Registry
|
||||
}
|
||||
|
||||
func NewServer() *SignalExchangeServer {
|
||||
return &SignalExchangeServer{
|
||||
// NewServer creates a new Signal server
|
||||
func NewServer() *Server {
|
||||
return &Server{
|
||||
registry: peer.NewRegistry(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SignalExchangeServer) Send(ctx context.Context, msg *proto.EncryptedMessage) (*proto.EncryptedMessage, error) {
|
||||
// Send forwards a message to the signal peer
|
||||
func (s *Server) Send(ctx context.Context, msg *proto.EncryptedMessage) (*proto.EncryptedMessage, error) {
|
||||
|
||||
if _, found := s.registry.Peers[msg.Key]; !found {
|
||||
if !s.registry.IsPeerRegistered(msg.Key) {
|
||||
return nil, fmt.Errorf("unknown peer %s", msg.Key)
|
||||
}
|
||||
|
||||
if dstPeer, found := s.registry.Peers[msg.RemoteKey]; found {
|
||||
if dstPeer, found := s.registry.Get(msg.RemoteKey); found {
|
||||
//forward the message to the target peer
|
||||
err := dstPeer.Stream.Send(msg)
|
||||
if err != nil {
|
||||
@@ -47,14 +45,21 @@ func (s *SignalExchangeServer) Send(ctx context.Context, msg *proto.EncryptedMes
|
||||
return &proto.EncryptedMessage{}, nil
|
||||
}
|
||||
|
||||
func (s *SignalExchangeServer) ConnectStream(stream proto.SignalExchange_ConnectStreamServer) error {
|
||||
// ConnectStream connects to the exchange stream
|
||||
func (s *Server) ConnectStream(stream proto.SignalExchange_ConnectStreamServer) error {
|
||||
p, err := s.connectPeer(stream)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("peer [%s] has successfully connected", p.Id)
|
||||
//needed to confirm that the peer has been registered so that the client can proceed
|
||||
header := metadata.Pairs(proto.HeaderRegistered, "1")
|
||||
err = stream.SendHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("peer [%s] has successfully connected", p.Id)
|
||||
for {
|
||||
msg, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
@@ -64,7 +69,7 @@ func (s *SignalExchangeServer) ConnectStream(stream proto.SignalExchange_Connect
|
||||
}
|
||||
log.Debugf("received a new message from peer [%s] to peer [%s]", p.Id, msg.RemoteKey)
|
||||
// lookup the target peer where the message is going to
|
||||
if dstPeer, found := s.registry.Peers[msg.RemoteKey]; found {
|
||||
if dstPeer, found := s.registry.Get(msg.RemoteKey); found {
|
||||
//forward the message to the target peer
|
||||
err := dstPeer.Stream.Send(msg)
|
||||
if err != nil {
|
||||
@@ -84,7 +89,7 @@ func (s *SignalExchangeServer) ConnectStream(stream proto.SignalExchange_Connect
|
||||
// Handles initial Peer connection.
|
||||
// Each connection must provide an ID header.
|
||||
// At this moment the connecting Peer will be registered in the peer.Registry
|
||||
func (s SignalExchangeServer) connectPeer(stream proto.SignalExchange_ConnectStreamServer) (*peer.Peer, error) {
|
||||
func (s Server) connectPeer(stream proto.SignalExchange_ConnectStreamServer) (*peer.Peer, error) {
|
||||
if meta, hasMeta := metadata.FromIncomingContext(stream.Context()); hasMeta {
|
||||
if id, found := meta[proto.HeaderId]; found {
|
||||
p := peer.NewPeer(id[0], stream)
|
||||
|
||||
13
signal/signal_suite_test.go
Normal file
13
signal/signal_suite_test.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package signal_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSignal(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Signal Suite")
|
||||
}
|
||||
199
signal/signal_test.go
Normal file
199
signal/signal_test.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package signal_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/wiretrustee/wiretrustee/signal"
|
||||
sigProto "github.com/wiretrustee/wiretrustee/signal/proto"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ = Describe("Client", func() {
|
||||
|
||||
var (
|
||||
addr string
|
||||
listener net.Listener
|
||||
server *grpc.Server
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
server, listener = startSignal()
|
||||
addr = listener.Addr().String()
|
||||
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
server.Stop()
|
||||
listener.Close()
|
||||
})
|
||||
|
||||
Describe("Exchanging messages", func() {
|
||||
Context("between connected peers", func() {
|
||||
It("should be successful", func() {
|
||||
|
||||
var msgReceived sync.WaitGroup
|
||||
msgReceived.Add(2)
|
||||
|
||||
var receivedOnA string
|
||||
var receivedOnB string
|
||||
|
||||
// connect PeerA to Signal
|
||||
keyA, _ := wgtypes.GenerateKey()
|
||||
clientA := createSignalClient(addr, keyA)
|
||||
clientA.Receive(func(msg *sigProto.Message) error {
|
||||
receivedOnA = msg.GetBody().GetPayload()
|
||||
msgReceived.Done()
|
||||
return nil
|
||||
})
|
||||
clientA.WaitConnected()
|
||||
|
||||
// connect PeerB to Signal
|
||||
keyB, _ := wgtypes.GenerateKey()
|
||||
clientB := createSignalClient(addr, keyB)
|
||||
clientB.Receive(func(msg *sigProto.Message) error {
|
||||
receivedOnB = msg.GetBody().GetPayload()
|
||||
err := clientB.Send(&sigProto.Message{
|
||||
Key: keyB.PublicKey().String(),
|
||||
RemoteKey: keyA.PublicKey().String(),
|
||||
Body: &sigProto.Body{Payload: "pong"},
|
||||
})
|
||||
if err != nil {
|
||||
Fail("failed sending a message to PeerA")
|
||||
}
|
||||
msgReceived.Done()
|
||||
return nil
|
||||
})
|
||||
clientB.WaitConnected()
|
||||
|
||||
// PeerA initiates ping-pong
|
||||
err := clientA.Send(&sigProto.Message{
|
||||
Key: keyA.PublicKey().String(),
|
||||
RemoteKey: keyB.PublicKey().String(),
|
||||
Body: &sigProto.Body{Payload: "ping"},
|
||||
})
|
||||
if err != nil {
|
||||
Fail("failed sending a message to PeerB")
|
||||
}
|
||||
|
||||
if waitTimeout(&msgReceived, 3*time.Second) {
|
||||
Fail("test timed out on waiting for peers to exchange messages")
|
||||
}
|
||||
|
||||
Expect(receivedOnA).To(BeEquivalentTo("pong"))
|
||||
Expect(receivedOnB).To(BeEquivalentTo("ping"))
|
||||
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Connecting to the Signal stream channel", func() {
|
||||
Context("with a signal client", func() {
|
||||
It("should be successful", func() {
|
||||
|
||||
key, _ := wgtypes.GenerateKey()
|
||||
client := createSignalClient(addr, key)
|
||||
client.Receive(func(msg *sigProto.Message) error {
|
||||
return nil
|
||||
})
|
||||
client.WaitConnected()
|
||||
|
||||
Expect(client).NotTo(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
Context("with a raw client and no ID header", func() {
|
||||
It("should fail", func() {
|
||||
|
||||
client := createRawSignalClient(addr)
|
||||
stream, err := client.ConnectStream(context.Background())
|
||||
if err != nil {
|
||||
Fail("error connecting to stream")
|
||||
}
|
||||
|
||||
_, err = stream.Recv()
|
||||
|
||||
Expect(stream).NotTo(BeNil())
|
||||
Expect(err).NotTo(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
Context("with a raw client and with an ID header", func() {
|
||||
It("should be successful", func() {
|
||||
|
||||
md := metadata.New(map[string]string{sigProto.HeaderId: "peer"})
|
||||
ctx := metadata.NewOutgoingContext(context.Background(), md)
|
||||
|
||||
client := createRawSignalClient(addr)
|
||||
stream, err := client.ConnectStream(ctx)
|
||||
|
||||
Expect(stream).NotTo(BeNil())
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
func createSignalClient(addr string, key wgtypes.Key) *signal.Client {
|
||||
client, err := signal.NewClient(context.Background(), addr, key)
|
||||
if err != nil {
|
||||
Fail("failed creating signal client")
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
func createRawSignalClient(addr string) sigProto.SignalExchangeClient {
|
||||
ctx := context.Background()
|
||||
conn, err := grpc.DialContext(ctx, addr, grpc.WithInsecure(),
|
||||
grpc.WithBlock(),
|
||||
grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
||||
Time: 3 * time.Second,
|
||||
Timeout: 2 * time.Second,
|
||||
}))
|
||||
if err != nil {
|
||||
Fail("failed creating raw signal client")
|
||||
}
|
||||
|
||||
return sigProto.NewSignalExchangeClient(conn)
|
||||
}
|
||||
|
||||
func startSignal() (*grpc.Server, net.Listener) {
|
||||
lis, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s := grpc.NewServer()
|
||||
sigProto.RegisterSignalExchangeServer(s, signal.NewServer())
|
||||
go func() {
|
||||
if err := s.Serve(lis); err != nil {
|
||||
log.Fatalf("failed to serve: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return s, lis
|
||||
}
|
||||
|
||||
// waitTimeout waits for the waitgroup for the specified max timeout.
|
||||
// Returns true if waiting timed out.
|
||||
func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
defer close(c)
|
||||
wg.Wait()
|
||||
}()
|
||||
select {
|
||||
case <-c:
|
||||
return false // completed normally
|
||||
case <-time.After(timeout):
|
||||
return true // timed out
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Retries a given toExec function calling onError on failed attempts
|
||||
// Retry retries a given toExec function calling onError on failed attempts
|
||||
// onError shouldn be a lightweight function and shouldn't be blocking
|
||||
func Retry(attempts int, sleep time.Duration, toExec func() error, onError func(e error)) error {
|
||||
if err := toExec(); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user