Compare commits
66 Commits
v0.1.0-rc-
...
v0.2.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
842b143a48 | ||
|
|
1323a74db0 | ||
|
|
74485d3b13 | ||
|
|
bef3b3392b | ||
|
|
fcea3c99d4 | ||
|
|
96799a25b5 | ||
|
|
07291cdb93 | ||
|
|
21139938c1 | ||
|
|
5cf2d0a6a9 | ||
|
|
8551afe04e | ||
|
|
1685817171 | ||
|
|
e17f662683 | ||
|
|
a764fb870c | ||
|
|
cabff941ac | ||
|
|
b5f35dfb5e | ||
|
|
1d426b7f81 | ||
|
|
e4f9406d44 | ||
|
|
7c79ff62ee | ||
|
|
32c369257b | ||
|
|
08dd719aa1 | ||
|
|
84c714dd93 | ||
|
|
996c8d7c62 | ||
|
|
25e68ce493 | ||
|
|
4881dcbd51 | ||
|
|
d505f70972 | ||
|
|
6a80684378 | ||
|
|
2624a7c4e6 | ||
|
|
9a412e7bf1 | ||
|
|
b5d1690129 | ||
|
|
d4bec15ca3 | ||
|
|
3212aca7c7 | ||
|
|
b97a2251d3 | ||
|
|
528a26ea3e | ||
|
|
13288374f1 | ||
|
|
ec759bc461 | ||
|
|
a859f6c511 | ||
|
|
081162864d | ||
|
|
090f3ae5d0 | ||
|
|
fb1116e77b | ||
|
|
879750af7c | ||
|
|
13b4be31df | ||
|
|
15f7d856db | ||
|
|
72197d1970 | ||
|
|
a56aba8b06 | ||
|
|
4acbdc47e5 | ||
|
|
ee3c292699 | ||
|
|
6c233fcc3f | ||
|
|
a4db0b4e94 | ||
|
|
81c5aa1341 | ||
|
|
8c5f6186f1 | ||
|
|
88e9d2c20d | ||
|
|
9d76cf1ea7 | ||
|
|
737d4b5f2c | ||
|
|
4485124b67 | ||
|
|
b17424d630 | ||
|
|
86f3b1e5c8 | ||
|
|
a31cbb1f5b | ||
|
|
4f4edf8442 | ||
|
|
a78e518327 | ||
|
|
2c1d7c0fd4 | ||
|
|
593c66fea6 | ||
|
|
5f8211773d | ||
|
|
64ca05c8e7 | ||
|
|
307d41c08a | ||
|
|
5c7260298f | ||
|
|
7f7858b0a6 |
4
.github/workflows/golangci-lint.yml
vendored
@@ -11,4 +11,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
15
.github/workflows/release.yml
vendored
@@ -49,4 +49,17 @@ jobs:
|
|||||||
version: latest
|
version: latest
|
||||||
args: release --rm-dist
|
args: release --rm-dist
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
|
||||||
|
UPLOAD_DEBIAN_SECRET: ${{ secrets.PKG_UPLOAD_SECRET }}
|
||||||
|
UPLOAD_YUM_SECRET: ${{ secrets.PKG_UPLOAD_SECRET }}
|
||||||
|
|
||||||
|
-
|
||||||
|
name: Trigger Windows binaries sign pipeline
|
||||||
|
uses: benc-uk/workflow-dispatch@v1
|
||||||
|
with:
|
||||||
|
workflow: Sign windows bin and installer
|
||||||
|
repo: wiretrustee/windows-sign-pipeline
|
||||||
|
ref: v0.0.1
|
||||||
|
token: ${{ secrets.SIGN_GITHUB_TOKEN }}
|
||||||
|
inputs: '{ "tag": "${{ github.ref }}" }'
|
||||||
4
.gitignore
vendored
@@ -3,4 +3,6 @@
|
|||||||
dist/
|
dist/
|
||||||
.env
|
.env
|
||||||
conf.json
|
conf.json
|
||||||
http-cmds.sh
|
http-cmds.sh
|
||||||
|
infrastructure_files/management.json
|
||||||
|
infrastructure_files/docker-compose.yml
|
||||||
@@ -13,13 +13,18 @@ builds:
|
|||||||
- arm
|
- arm
|
||||||
- amd64
|
- amd64
|
||||||
- arm64
|
- arm64
|
||||||
|
- mips
|
||||||
|
gomips:
|
||||||
|
- hardfloat
|
||||||
|
- softfloat
|
||||||
ignore:
|
ignore:
|
||||||
- goos: darwin
|
|
||||||
goarch: arm64
|
|
||||||
- goos: windows
|
- goos: windows
|
||||||
goarch: arm64
|
goarch: arm64
|
||||||
- goos: windows
|
- goos: windows
|
||||||
goarch: arm
|
goarch: arm
|
||||||
|
ldflags:
|
||||||
|
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
||||||
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
tags:
|
tags:
|
||||||
- load_wintun_from_rsrc
|
- load_wintun_from_rsrc
|
||||||
|
|
||||||
@@ -32,6 +37,9 @@ builds:
|
|||||||
goarch:
|
goarch:
|
||||||
- amd64
|
- amd64
|
||||||
- arm64
|
- arm64
|
||||||
|
ldflags:
|
||||||
|
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
||||||
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
|
|
||||||
- id: wiretrustee-signal
|
- id: wiretrustee-signal
|
||||||
dir: signal
|
dir: signal
|
||||||
@@ -42,28 +50,40 @@ builds:
|
|||||||
goarch:
|
goarch:
|
||||||
- amd64
|
- amd64
|
||||||
- arm64
|
- arm64
|
||||||
|
ldflags:
|
||||||
|
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
||||||
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
archives:
|
archives:
|
||||||
- builds:
|
- builds:
|
||||||
- wiretrustee
|
- wiretrustee
|
||||||
nfpms:
|
nfpms:
|
||||||
- maintainer: Wiretrustee <wiretrustee@wiretrustee.com>
|
- maintainer: Wiretrustee <dev@wiretrustee.com>
|
||||||
description: Wiretrustee project.
|
description: Wiretrustee client.
|
||||||
homepage: https://wiretrustee.com/
|
homepage: https://wiretrustee.com/
|
||||||
|
id: deb
|
||||||
builds:
|
builds:
|
||||||
- wiretrustee
|
- wiretrustee
|
||||||
formats:
|
formats:
|
||||||
- deb
|
- deb
|
||||||
- rpm
|
|
||||||
contents:
|
|
||||||
- src: release_files/wiretrustee.service
|
|
||||||
dst: /lib/systemd/system/wiretrustee.service
|
|
||||||
|
|
||||||
- src: release_files/wiretrustee.json
|
|
||||||
dst: /etc/wiretrustee/wiretrustee.json
|
|
||||||
type: "config|noreplace"
|
|
||||||
|
|
||||||
scripts:
|
scripts:
|
||||||
postinstall: "release_files/post_install.sh"
|
postinstall: "release_files/post_install.sh"
|
||||||
|
preremove: "release_files/pre_remove.sh"
|
||||||
|
replacements:
|
||||||
|
arm6: armf
|
||||||
|
|
||||||
|
- maintainer: Wiretrustee <dev@wiretrustee.com>
|
||||||
|
description: Wiretrustee client.
|
||||||
|
homepage: https://wiretrustee.com/
|
||||||
|
id: rpm
|
||||||
|
builds:
|
||||||
|
- wiretrustee
|
||||||
|
formats:
|
||||||
|
- rpm
|
||||||
|
|
||||||
|
scripts:
|
||||||
|
postinstall: "release_files/post_install.sh"
|
||||||
|
preremove: "release_files/pre_remove.sh"
|
||||||
dockers:
|
dockers:
|
||||||
- image_templates:
|
- image_templates:
|
||||||
- wiretrustee/signal:{{ .Version }}-amd64
|
- wiretrustee/signal:{{ .Version }}-amd64
|
||||||
@@ -103,7 +123,7 @@ dockers:
|
|||||||
use: buildx
|
use: buildx
|
||||||
dockerfile: management/Dockerfile
|
dockerfile: management/Dockerfile
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--platform=linux/arm64"
|
- "--platform=linux/amd64"
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
||||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
@@ -133,7 +153,7 @@ dockers:
|
|||||||
use: buildx
|
use: buildx
|
||||||
dockerfile: management/Dockerfile.debug
|
dockerfile: management/Dockerfile.debug
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--platform=linux/arm64"
|
- "--platform=linux/amd64"
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
||||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
@@ -180,4 +200,36 @@ docker_manifests:
|
|||||||
- name_template: wiretrustee/management:debug-latest
|
- name_template: wiretrustee/management:debug-latest
|
||||||
image_templates:
|
image_templates:
|
||||||
- wiretrustee/management:{{ .Version }}-debug-arm64v8
|
- wiretrustee/management:{{ .Version }}-debug-arm64v8
|
||||||
- wiretrustee/management:{{ .Version }}-debug-amd64
|
- wiretrustee/management:{{ .Version }}-debug-amd64
|
||||||
|
|
||||||
|
brews:
|
||||||
|
-
|
||||||
|
tap:
|
||||||
|
owner: wiretrustee
|
||||||
|
name: homebrew-client
|
||||||
|
token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
|
||||||
|
commit_author:
|
||||||
|
name: Wiretrustee
|
||||||
|
email: wiretrustee@wiretrustee.com
|
||||||
|
description: Wiretrustee project.
|
||||||
|
download_strategy: CurlDownloadStrategy
|
||||||
|
homepage: https://wiretrustee.com/
|
||||||
|
license: "BSD3"
|
||||||
|
test: |
|
||||||
|
system "#{bin}/{{ .ProjectName }} -h"
|
||||||
|
|
||||||
|
uploads:
|
||||||
|
- name: debian
|
||||||
|
ids:
|
||||||
|
- deb
|
||||||
|
mode: archive
|
||||||
|
target: https://pkgs.wiretrustee.com/debian/pool/{{ .ArtifactName }};deb.distribution=stable;deb.component=main;deb.architecture={{ .Arch }}{{ if .Arm }}{{ .Arm }}{{ end }}
|
||||||
|
username: dev@wiretrustee.com
|
||||||
|
method: PUT
|
||||||
|
- name: yum
|
||||||
|
ids:
|
||||||
|
- rpm
|
||||||
|
mode: archive
|
||||||
|
target: https://pkgs.wiretrustee.com/yum/{{ .Arch }}{{ if .Arm }}{{ .Arm }}{{ end }}
|
||||||
|
username: dev@wiretrustee.com
|
||||||
|
method: PUT
|
||||||
222
README.md
@@ -1,9 +1,50 @@
|
|||||||
# Wiretrustee
|
<div align="center">
|
||||||
|
|
||||||
A WireGuard®-based mesh network that connects your devices into a single private network.
|
<p align="center">
|
||||||
|
<img width="250" src="docs/media/logo-full.png"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<img src="https://img.shields.io/badge/license-BSD--3-blue" />
|
||||||
|
<img src="https://img.shields.io/docker/pulls/wiretrustee/management" />
|
||||||
|
<img src="https://badgen.net/badge/Open%20Source%3F/Yes%21/blue?icon=github" />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<strong>
|
||||||
|
Start using Wiretrustee at <a href="https://app.wiretrustee.com/">app.wiretrustee.com</a>
|
||||||
|
<br/>
|
||||||
|
See <a href="docs/README.md">Documentation</a>
|
||||||
|
<br/>
|
||||||
|
Join our <a href="https://join.slack.com/t/wiretrustee/shared_invite/zt-vrahf41g-ik1v7fV8du6t0RwxSrJ96A">Slack channel</a>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
**Wiretrustee is an open-source VPN platform built on top of WireGuard® making it easy to create secure private networks for your organization or home.**
|
||||||
|
|
||||||
|
It requires zero configuration effort leaving behind the hassle of opening ports, complex firewall rules, vpn gateways, and so forth.
|
||||||
|
|
||||||
|
There is no centralized VPN server with Wiretrustee - your computers, devices, machines, and servers connect to each other directly over a fast encrypted tunnel.
|
||||||
|
|
||||||
|
### Secure peer-to-peer VPN in minutes
|
||||||
|
<p float="left" align="middle">
|
||||||
|
<img src="docs/media/peerA.gif" width="400"/>
|
||||||
|
<img src="docs/media/peerB.gif" width="400"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
**Note**: The `main` branch may be in an *unstable or even broken state* during development. For stable versions, see [releases](https://github.com/wiretrustee/wiretrustee/releases).
|
**Note**: The `main` branch may be in an *unstable or even broken state* during development. For stable versions, see [releases](https://github.com/wiretrustee/wiretrustee/releases).
|
||||||
|
|
||||||
|
Hosted demo version:
|
||||||
|
[https://app.wiretrustee.com/](https://app.wiretrustee.com/peers).
|
||||||
|
|
||||||
|
[UI Dashboard Repo](https://github.com/wiretrustee/wiretrustee-dashboard)
|
||||||
|
|
||||||
|
|
||||||
### Why using Wiretrustee?
|
### Why using Wiretrustee?
|
||||||
|
|
||||||
* Connect multiple devices to each other via a secure peer-to-peer Wireguard VPN tunnel. At home, the office, or anywhere else.
|
* Connect multiple devices to each other via a secure peer-to-peer Wireguard VPN tunnel. At home, the office, or anywhere else.
|
||||||
@@ -19,8 +60,6 @@ A WireGuard®-based mesh network that connects your devices into a single privat
|
|||||||
* Works on ARM devices (e.g. Raspberry Pi).
|
* Works on ARM devices (e.g. Raspberry Pi).
|
||||||
* Open-source (including Management Service)
|
* Open-source (including Management Service)
|
||||||
|
|
||||||
### Secure peer-to-peer VPN in minutes
|
|
||||||

|
|
||||||
|
|
||||||
### A bit on Wiretrustee internals
|
### A bit on Wiretrustee internals
|
||||||
* Wiretrustee features a Management Service that offers peer IP management and network updates distribution (e.g. when new peer joins the network).
|
* Wiretrustee features a Management Service that offers peer IP management and network updates distribution (e.g. when new peer joins the network).
|
||||||
@@ -38,116 +77,119 @@ A WireGuard®-based mesh network that connects your devices into a single privat
|
|||||||
|
|
||||||
### Client Installation
|
### Client Installation
|
||||||
#### Linux
|
#### Linux
|
||||||
1. Checkout Wiretrustee [releases](https://github.com/wiretrustee/wiretrustee/releases)
|
|
||||||
2. Download the latest release (**Switch VERSION to the latest**):
|
|
||||||
|
|
||||||
**Debian packages**
|
**APT/Debian**
|
||||||
```shell
|
1. Add the repository:
|
||||||
wget https://github.com/wiretrustee/wiretrustee/releases/download/v<VERSION>/wiretrustee_<VERSION>_linux_amd64.deb
|
```shell
|
||||||
```
|
sudo apt-get update
|
||||||
3. Install the package
|
sudo apt-get install ca-certificates curl gnupg -y
|
||||||
```shell
|
curl -L https://pkgs.wiretrustee.com/debian/public.key | sudo apt-key add -
|
||||||
sudo dpkg -i wiretrustee_<VERSION>_linux_amd64.deb
|
echo 'deb https://pkgs.wiretrustee.com/debian stable main' | sudo tee /etc/apt/sources.list.d/wiretrustee.list
|
||||||
```
|
```
|
||||||
**Fedora/Centos packages**
|
2. Install the package
|
||||||
```shell
|
```shell
|
||||||
wget https://github.com/wiretrustee/wiretrustee/releases/download/v<VERSION>/wiretrustee_<VERSION>_linux_amd64.rpm
|
sudo apt-get update
|
||||||
```
|
sudo apt-get install wiretrustee
|
||||||
3. Install the package
|
```
|
||||||
```shell
|
**RPM/Red hat**
|
||||||
sudo rpm -i wiretrustee_<VERSION>_linux_amd64.rpm
|
1. Add the repository:
|
||||||
```
|
```shell
|
||||||
|
cat <<EOF | sudo tee /etc/yum.repos.d/wiretrustee.repo
|
||||||
|
[Wiretrustee]
|
||||||
|
name=Wiretrustee
|
||||||
|
baseurl=https://pkgs.wiretrustee.com/yum/
|
||||||
|
enabled=1
|
||||||
|
gpgcheck=0
|
||||||
|
gpgkey=https://pkgs.wiretrustee.com/yum/repodata/repomd.xml.key
|
||||||
|
repo_gpgcheck=1
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
2. Install the package
|
||||||
|
```shell
|
||||||
|
sudo yum install wiretrustee
|
||||||
|
```
|
||||||
#### MACOS
|
#### MACOS
|
||||||
|
**Brew install**
|
||||||
|
1. Download and install Brew at https://brew.sh/
|
||||||
|
2. Install the client
|
||||||
|
```shell
|
||||||
|
brew install wiretrustee/client/wiretrustee
|
||||||
|
```
|
||||||
|
**Installation from binary**
|
||||||
1. Checkout Wiretrustee [releases](https://github.com/wiretrustee/wiretrustee/releases/latest)
|
1. Checkout Wiretrustee [releases](https://github.com/wiretrustee/wiretrustee/releases/latest)
|
||||||
2. Download the latest release (**Switch VERSION to the latest**):
|
2. Download the latest release (**Switch VERSION to the latest**):
|
||||||
```shell
|
```shell
|
||||||
curl -o ./wiretrustee_<VERSION>_darwin_amd64.tar.gz https://github.com/wiretrustee/wiretrustee/releases/download/v<VERSION>/wiretrustee_<VERSION>_darwin_amd64.tar.gz
|
curl -o ./wiretrustee_<VERSION>_darwin_amd64.tar.gz https://github.com/wiretrustee/wiretrustee/releases/download/v<VERSION>/wiretrustee_<VERSION>_darwin_amd64.tar.gz
|
||||||
```
|
```
|
||||||
3. Decompress
|
3. Decompress
|
||||||
```shell
|
```shell
|
||||||
tar xcf ./wiretrustee_<VERSION>_darwin_amd64.tar.gz
|
tar xcf ./wiretrustee_<VERSION>_darwin_amd64.tar.gz
|
||||||
sudo mv wiretrusee /usr/local/bin/wiretrustee
|
sudo mv wiretrusee /usr/local/bin/wiretrustee
|
||||||
chmod +x /usr/local/bin/wiretrustee
|
chmod +x /usr/local/bin/wiretrustee
|
||||||
```
|
```
|
||||||
After that you may need to add /usr/local/bin in your MAC's PATH environment variable:
|
After that you may need to add /usr/local/bin in your MAC's PATH environment variable:
|
||||||
````shell
|
````shell
|
||||||
export PATH=$PATH:/usr/local/bin
|
export PATH=$PATH:/usr/local/bin
|
||||||
````
|
````
|
||||||
|
|
||||||
#### Windows
|
#### Windows
|
||||||
1. Checkout Wiretrustee [releases](https://github.com/wiretrustee/wiretrustee/releases/latest)
|
1. Checkout Wiretrustee [releases](https://github.com/wiretrustee/wiretrustee/releases/latest)
|
||||||
2. Download the latest Windows release ```wiretrustee_<VERSION>_windows_amd64.tar.gz``` (**Switch VERSION to the latest**):
|
2. Download the latest Windows release installer ```wiretrustee_installer_<VERSION>_windows_amd64.exe``` (**Switch VERSION to the latest**):
|
||||||
3. Decompress and move to a more fixed path in your system
|
3. Proceed with installation steps
|
||||||
4. Open Powershell
|
4. This will install the client in the C:\\Program Files\\Wiretrustee and add the client service
|
||||||
5. For Windows systems, we can use the service command to configure Wiretrustee as a service by running the following commands in Powershell:
|
5. After installing, you can follow the [Client Configuration](#Client-Configuration) steps.
|
||||||
````shell
|
> To uninstall the client and service, you can use Add/Remove programs
|
||||||
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
|
### Client Configuration
|
||||||
1. Login to the Management Service. You need to have a `setup key` in hand (see ).
|
1. Login to the Management Service. You need to have a `setup key` in hand (see ).
|
||||||
|
|
||||||
For **Unix** systems:
|
For **Unix** systems:
|
||||||
```shell
|
```shell
|
||||||
sudo wiretrustee login --setup-key <SETUP KEY>
|
sudo wiretrustee up --setup-key <SETUP KEY>
|
||||||
```
|
```
|
||||||
For **Windows** systems:
|
For **Windows** systems, start powershell as administrator and:
|
||||||
```shell
|
```shell
|
||||||
.\wiretrustee.exe login --setup-key <SETUP KEY>
|
wiretrustee up --setup-key <SETUP KEY>
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, if you are hosting your own Management Service provide `--management-url` property pointing to your Management Service:
|
Alternatively, if you are hosting your own Management Service provide `--management-url` property pointing to your Management Service:
|
||||||
```shell
|
```shell
|
||||||
sudo wiretrustee login --setup-key <SETUP KEY> --management-url https://localhost:33073
|
sudo wiretrustee up --setup-key <SETUP KEY> --management-url https://localhost:33073
|
||||||
```
|
```
|
||||||
|
|
||||||
You could also omit `--setup-key` property. In this case the tool will prompt it the key.
|
> You could also omit `--setup-key` property. In this case the tool will prompt it the key.
|
||||||
|
|
||||||
2. Start Wiretrustee:
|
|
||||||
|
|
||||||
For **MACOS** you will just start the service:
|
2. Check your IP:
|
||||||
````shell
|
For **MACOS** you will just start the service:
|
||||||
sudo wiretrustee up
|
````shell
|
||||||
# or
|
sudo ipconfig getifaddr utun100
|
||||||
sudo wiretrustee up & # to run it in background
|
````
|
||||||
````
|
|
||||||
For **Linux** systems:
|
For **Linux** systems:
|
||||||
```shell
|
```shell
|
||||||
sudo systemctl restart wiretrustee.service
|
ip addr show wt0
|
||||||
sudo systemctl status wiretrustee.service
|
```
|
||||||
```
|
|
||||||
For **Windows** systems:
|
For **Windows** systems:
|
||||||
```shell
|
```shell
|
||||||
.\wiretrustee.exe service start
|
netsh interface ip show config name="wt0"
|
||||||
```
|
```
|
||||||
> You may need to run Powershell as Administrator
|
|
||||||
|
|
||||||
3. Check your IP:
|
3. Repeat on other machines.
|
||||||
For **MACOS** you will just start the service:
|
|
||||||
````shell
|
|
||||||
sudo ipconfig getifaddr utun100
|
|
||||||
````
|
|
||||||
For **Linux** systems:
|
|
||||||
```shell
|
|
||||||
ip addr show wt0
|
|
||||||
```
|
|
||||||
For **Windows** systems:
|
|
||||||
```shell
|
|
||||||
netsh interface ip show config name="wt0"
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Repeat on other machines.
|
### Running Dashboard, Management, Signal and Coturn
|
||||||
|
Wiretrustee uses [Auth0](https://auth0.com) for user authentication and authorization, therefore you will need to create a free account
|
||||||
|
and configure Auth0 variables in the compose file (dashboard) and in the management config file.
|
||||||
|
We chose Auth0 to "outsource" the user management part of our platform because we believe that implementing a proper user auth is not a trivial task and requires significant amount of time to make it right. We focused on connectivity instead.
|
||||||
|
It is worth mentioning that dependency to Auth0 is the only one that cannot be self-hosted.
|
||||||
|
|
||||||
### Running Management, Signal and Coturn
|
Configuring Wiretrustee Auth0 integration:
|
||||||
Under infrastructure_files we have a docker-compose example to run both, Wiretrustee Management and Signal services, plus an instance of [Coturn](https://github.com/coturn/coturn), it also provides a turnserver.conf file as a simple example of Coturn configuration.
|
- check [How to run](https://github.com/wiretrustee/wiretrustee-dashboard#how-to-run) to obtain Auth0 environment variables for UI Dashboard
|
||||||
|
- set these variables in the [environment section of the docker-compose file](https://github.com/wiretrustee/wiretrustee/blob/main/infrastructure_files/docker-compose.yml)
|
||||||
|
- check [Auth0 Golang API Guide](https://auth0.com/docs/quickstart/backend/golang) to obtain ```AuthIssuer```, ```AuthAudience```, and ```AuthKeysLocation```
|
||||||
|
- set these properties in the [management config files](https://github.com/wiretrustee/wiretrustee/blob/main/infrastructure_files/management.json#L33)
|
||||||
|
|
||||||
|
|
||||||
|
Under infrastructure_files we have a docker-compose example to run Dashboard, Wiretrustee Management and Signal services, plus 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**.
|
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), the management in [Management](./management/README.md), and the Coturn official documentation [here](https://hub.docker.com/r/coturn/coturn).
|
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), the management in [Management](./management/README.md), and the Coturn official documentation [here](https://hub.docker.com/r/coturn/coturn).
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/wiretrustee/wiretrustee/client/internal"
|
"github.com/wiretrustee/wiretrustee/client/internal"
|
||||||
mgm "github.com/wiretrustee/wiretrustee/management/client"
|
mgm "github.com/wiretrustee/wiretrustee/management/client"
|
||||||
mgmProto "github.com/wiretrustee/wiretrustee/management/proto"
|
mgmProto "github.com/wiretrustee/wiretrustee/management/proto"
|
||||||
|
"github.com/wiretrustee/wiretrustee/util"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
@@ -23,7 +24,11 @@ var (
|
|||||||
Use: "login",
|
Use: "login",
|
||||||
Short: "login to the Wiretrustee Management Service (first run)",
|
Short: "login to the Wiretrustee Management Service (first run)",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
InitLog(logLevel)
|
err := util.InitLog(logLevel, logFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed initializing log %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
config, err := internal.GetConfig(managementURL, configPath)
|
config, err := internal.GetConfig(managementURL, configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -147,6 +152,5 @@ func promptPeerSetupKey() (string, error) {
|
|||||||
return "", s.Err()
|
return "", s.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
//func init() {
|
||||||
loginCmd.PersistentFlags().StringVar(&setupKey, "setup-key", "", "Setup key obtained from the Management Service Dashboard (used to register peer)")
|
//}
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
"github.com/wiretrustee/wiretrustee/client/internal"
|
"github.com/wiretrustee/wiretrustee/client/internal"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"syscall"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -21,16 +21,18 @@ var (
|
|||||||
configPath string
|
configPath string
|
||||||
defaultConfigPath string
|
defaultConfigPath string
|
||||||
logLevel string
|
logLevel string
|
||||||
|
defaultLogFile string
|
||||||
|
logFile string
|
||||||
managementURL string
|
managementURL string
|
||||||
|
rootCmd = &cobra.Command{
|
||||||
rootCmd = &cobra.Command{
|
|
||||||
Use: "wiretrustee",
|
Use: "wiretrustee",
|
||||||
Short: "",
|
Short: "",
|
||||||
Long: "",
|
Long: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execution control channel for stopCh signal
|
// Execution control channel for stopCh signal
|
||||||
stopCh chan int
|
stopCh chan int
|
||||||
|
cleanupCh chan struct{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Execute executes the root command.
|
// Execute executes the root command.
|
||||||
@@ -40,18 +42,24 @@ func Execute() error {
|
|||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
stopCh = make(chan int)
|
stopCh = make(chan int)
|
||||||
|
cleanupCh = make(chan struct{})
|
||||||
|
|
||||||
defaultConfigPath = "/etc/wiretrustee/config.json"
|
defaultConfigPath = "/etc/wiretrustee/config.json"
|
||||||
|
defaultLogFile = "/var/log/wiretrustee/client.log"
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
defaultConfigPath = os.Getenv("PROGRAMDATA") + "\\Wiretrustee\\" + "config.json"
|
defaultConfigPath = os.Getenv("PROGRAMDATA") + "\\Wiretrustee\\" + "config.json"
|
||||||
|
defaultLogFile = os.Getenv("PROGRAMDATA") + "\\Wiretrustee\\" + "client.log"
|
||||||
}
|
}
|
||||||
|
|
||||||
rootCmd.PersistentFlags().StringVar(&managementURL, "management-url", "", fmt.Sprintf("Management Service URL [http|https]://[host]:[port] (default \"%s\")", internal.ManagementURLDefault().String()))
|
rootCmd.PersistentFlags().StringVar(&managementURL, "management-url", "", fmt.Sprintf("Management Service URL [http|https]://[host]:[port] (default \"%s\")", internal.ManagementURLDefault().String()))
|
||||||
rootCmd.PersistentFlags().StringVar(&configPath, "config", defaultConfigPath, "Wiretrustee config file location")
|
rootCmd.PersistentFlags().StringVar(&configPath, "config", defaultConfigPath, "Wiretrustee config file location")
|
||||||
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "sets Wiretrustee log level")
|
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "sets Wiretrustee log level")
|
||||||
|
rootCmd.PersistentFlags().StringVar(&logFile, "log-file", defaultLogFile, "sets Wiretrustee log path. If console is specified the the log will be output to stdout")
|
||||||
|
rootCmd.PersistentFlags().StringVar(&setupKey, "setup-key", "", "Setup key obtained from the Management Service Dashboard (used to register peer)")
|
||||||
rootCmd.AddCommand(serviceCmd)
|
rootCmd.AddCommand(serviceCmd)
|
||||||
rootCmd.AddCommand(upCmd)
|
rootCmd.AddCommand(upCmd)
|
||||||
rootCmd.AddCommand(loginCmd)
|
rootCmd.AddCommand(loginCmd)
|
||||||
|
rootCmd.AddCommand(versionCmd)
|
||||||
serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd) // service control commands are subcommands of service
|
serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd) // service control commands are subcommands of service
|
||||||
serviceCmd.AddCommand(installCmd, uninstallCmd) // service installer commands are subcommands of service
|
serviceCmd.AddCommand(installCmd, uninstallCmd) // service installer commands are subcommands of service
|
||||||
}
|
}
|
||||||
@@ -59,21 +67,11 @@ func init() {
|
|||||||
// SetupCloseHandler handles SIGTERM signal and exits with success
|
// SetupCloseHandler handles SIGTERM signal and exits with success
|
||||||
func SetupCloseHandler() {
|
func SetupCloseHandler() {
|
||||||
c := make(chan os.Signal, 1)
|
c := make(chan os.Signal, 1)
|
||||||
signal.Notify(c, os.Interrupt)
|
signal.Notify(c, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||||
go func() {
|
go func() {
|
||||||
for range c {
|
for range c {
|
||||||
fmt.Println("\r- Ctrl+C pressed in Terminal")
|
log.Info("shutdown signal received")
|
||||||
stopCh <- 0
|
stopCh <- 0
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitLog parses and sets log-level input
|
|
||||||
func InitLog(logLevel string) {
|
|
||||||
level, err := log.ParseLevel(logLevel)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed parsing log-level %s: %s", logLevel, err)
|
|
||||||
os.Exit(ExitSetupFailed)
|
|
||||||
}
|
|
||||||
log.SetLevel(level)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,26 +1,58 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/cenkalti/backoff/v4"
|
||||||
"github.com/kardianos/service"
|
"github.com/kardianos/service"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/wiretrustee/wiretrustee/util"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *program) Start(s service.Service) error {
|
func (p *program) Start(s service.Service) error {
|
||||||
|
|
||||||
|
var backOff = &backoff.ExponentialBackOff{
|
||||||
|
InitialInterval: time.Second,
|
||||||
|
RandomizationFactor: backoff.DefaultRandomizationFactor,
|
||||||
|
Multiplier: backoff.DefaultMultiplier,
|
||||||
|
MaxInterval: 30 * time.Second,
|
||||||
|
MaxElapsedTime: 24 * 3 * time.Hour, //stop after 3 days trying
|
||||||
|
Stop: backoff.Stop,
|
||||||
|
Clock: backoff.SystemClock,
|
||||||
|
}
|
||||||
|
|
||||||
// Start should not block. Do the actual work async.
|
// Start should not block. Do the actual work async.
|
||||||
log.Info("starting service") //nolint
|
log.Info("starting service") //nolint
|
||||||
go func() {
|
go func() {
|
||||||
err := upCmd.RunE(p.cmd, p.args)
|
operation := func() error {
|
||||||
if err != nil {
|
err := runClient()
|
||||||
return
|
if err != nil {
|
||||||
|
log.Warnf("retrying Wiretrustee client app due to error: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := backoff.Retry(operation, backOff)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("exiting client retry loop due to unrecoverable error: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *program) Stop(s service.Service) error {
|
func (p *program) Stop(s service.Service) error {
|
||||||
stopCh <- 1
|
go func() {
|
||||||
|
stopCh <- 1
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-cleanupCh:
|
||||||
|
case <-time.After(time.Second * 10):
|
||||||
|
log.Warnf("failed waiting for service cleanup, terminating")
|
||||||
|
}
|
||||||
|
log.Info("stopped Wiretrustee service") //nolint
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,6 +62,14 @@ var (
|
|||||||
Short: "runs wiretrustee as service",
|
Short: "runs wiretrustee as service",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
|
err := util.InitLog(logLevel, logFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed initializing log %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
SetupCloseHandler()
|
||||||
|
|
||||||
prg := &program{
|
prg := &program{
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
args: args,
|
args: args,
|
||||||
@@ -54,19 +94,24 @@ var (
|
|||||||
startCmd = &cobra.Command{
|
startCmd = &cobra.Command{
|
||||||
Use: "start",
|
Use: "start",
|
||||||
Short: "starts wiretrustee service",
|
Short: "starts wiretrustee service",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := util.InitLog(logLevel, logFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed initializing log %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
s, err := newSVC(&program{}, newSVCConfig())
|
s, err := newSVC(&program{}, newSVCConfig())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.PrintErrln(err)
|
cmd.PrintErrln(err)
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
err = s.Start()
|
err = s.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.PrintErrln(err)
|
cmd.PrintErrln(err)
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
cmd.Printf("Wiretrustee service has been started")
|
cmd.Println("Wiretrustee service has been started")
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -76,7 +121,10 @@ var (
|
|||||||
Use: "stop",
|
Use: "stop",
|
||||||
Short: "stops wiretrustee service",
|
Short: "stops wiretrustee service",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
err := util.InitLog(logLevel, logFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed initializing log %v", err)
|
||||||
|
}
|
||||||
s, err := newSVC(&program{}, newSVCConfig())
|
s, err := newSVC(&program{}, newSVCConfig())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.PrintErrln(err)
|
cmd.PrintErrln(err)
|
||||||
@@ -87,7 +135,7 @@ var (
|
|||||||
cmd.PrintErrln(err)
|
cmd.PrintErrln(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cmd.Printf("Wiretrustee service has been stopped")
|
cmd.Println("Wiretrustee service has been stopped")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -97,7 +145,10 @@ var (
|
|||||||
Use: "restart",
|
Use: "restart",
|
||||||
Short: "restarts wiretrustee service",
|
Short: "restarts wiretrustee service",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
err := util.InitLog(logLevel, logFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed initializing log %v", err)
|
||||||
|
}
|
||||||
s, err := newSVC(&program{}, newSVCConfig())
|
s, err := newSVC(&program{}, newSVCConfig())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.PrintErrln(err)
|
cmd.PrintErrln(err)
|
||||||
@@ -108,7 +159,7 @@ var (
|
|||||||
cmd.PrintErrln(err)
|
cmd.PrintErrln(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cmd.Printf("Wiretrustee service has been restarted")
|
cmd.Println("Wiretrustee service has been restarted")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ var (
|
|||||||
installCmd = &cobra.Command{
|
installCmd = &cobra.Command{
|
||||||
Use: "install",
|
Use: "install",
|
||||||
Short: "installs wiretrustee service",
|
Short: "installs wiretrustee service",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
svcConfig := newSVCConfig()
|
svcConfig := newSVCConfig()
|
||||||
|
|
||||||
@@ -30,15 +30,16 @@ var (
|
|||||||
s, err := newSVC(&program{}, svcConfig)
|
s, err := newSVC(&program{}, svcConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.PrintErrln(err)
|
cmd.PrintErrln(err)
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.Install()
|
err = s.Install()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.PrintErrln(err)
|
cmd.PrintErrln(err)
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
cmd.Printf("Wiretrustee service has been installed")
|
cmd.Println("Wiretrustee service has been installed")
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -60,7 +61,7 @@ var (
|
|||||||
cmd.PrintErrln(err)
|
cmd.PrintErrln(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cmd.Printf("Wiretrustee has been uninstalled")
|
cmd.Println("Wiretrustee has been uninstalled")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -37,8 +37,10 @@ func startManagement(config *mgmt.Config, t *testing.T) (*grpc.Server, net.Liste
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
accountManager := mgmt.NewManager(store)
|
peersUpdateManager := mgmt.NewPeersUpdateManager()
|
||||||
mgmtServer, err := mgmt.NewServer(config, accountManager)
|
accountManager := mgmt.NewManager(store, peersUpdateManager)
|
||||||
|
turnManager := mgmt.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig)
|
||||||
|
mgmtServer, err := mgmt.NewServer(config, accountManager, peersUpdateManager, turnManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
219
client/cmd/up.go
@@ -2,11 +2,10 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/pion/ice/v2"
|
"github.com/kardianos/service"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/wiretrustee/wiretrustee/client/internal"
|
"github.com/wiretrustee/wiretrustee/client/internal"
|
||||||
"github.com/wiretrustee/wiretrustee/iface"
|
|
||||||
mgm "github.com/wiretrustee/wiretrustee/management/client"
|
mgm "github.com/wiretrustee/wiretrustee/management/client"
|
||||||
mgmProto "github.com/wiretrustee/wiretrustee/management/proto"
|
mgmProto "github.com/wiretrustee/wiretrustee/management/proto"
|
||||||
signal "github.com/wiretrustee/wiretrustee/signal/client"
|
signal "github.com/wiretrustee/wiretrustee/signal/client"
|
||||||
@@ -18,110 +17,51 @@ import (
|
|||||||
var (
|
var (
|
||||||
upCmd = &cobra.Command{
|
upCmd = &cobra.Command{
|
||||||
Use: "up",
|
Use: "up",
|
||||||
Short: "start wiretrustee",
|
Short: "install, login and start wiretrustee client",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
InitLog(logLevel)
|
|
||||||
|
|
||||||
config, err := internal.ReadConfig(managementURL, configPath)
|
err := loginCmd.RunE(cmd, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed reading config %s %v", configPath, err)
|
return err
|
||||||
//os.Exit(ExitSetupFailed)
|
}
|
||||||
|
if logFile == "console" {
|
||||||
|
return runClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := newSVC(&program{}, newSVCConfig())
|
||||||
|
if err != nil {
|
||||||
|
cmd.PrintErrln(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
//validate our peer's Wireguard PRIVATE key
|
srvStatus, err := s.Status()
|
||||||
myPrivateKey, err := wgtypes.ParseKey(config.PrivateKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed parsing Wireguard key %s: [%s]", config.PrivateKey, err.Error())
|
if err == service.ErrNotInstalled {
|
||||||
//os.Exit(ExitSetupFailed)
|
log.Infof("%s. Installing it now", err.Error())
|
||||||
return err
|
e := installCmd.RunE(cmd, args)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Warnf("failed retrieving service status: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if srvStatus == service.StatusRunning {
|
||||||
ctx := context.Background()
|
stopCmd.Run(cmd, args)
|
||||||
|
|
||||||
mgmTlsEnabled := false
|
|
||||||
if config.ManagementURL.Scheme == "https" {
|
|
||||||
mgmTlsEnabled = true
|
|
||||||
}
|
}
|
||||||
|
return startCmd.RunE(cmd, args)
|
||||||
// connect (just a connection, no stream yet) and login to Management Service to get an initial global Wiretrustee config
|
|
||||||
mgmClient, loginResp, err := connectToManagement(ctx, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn(err)
|
|
||||||
//os.Exit(ExitSetupFailed)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// with the global Wiretrustee config in hand connect (just a connection, no stream yet) Signal
|
|
||||||
signalClient, err := connectToSignal(ctx, loginResp.GetWiretrusteeConfig(), myPrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
//os.Exit(ExitSetupFailed)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
engineConfig, err := createEngineConfig(myPrivateKey, config, loginResp.GetWiretrusteeConfig(), loginResp.GetPeerConfig())
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
//os.Exit(ExitSetupFailed)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// create start the Wiretrustee Engine that will connect to the Signal and Management streams and manage connections to remote peers.
|
|
||||||
engine := internal.NewEngine(signalClient, mgmClient, engineConfig)
|
|
||||||
err = engine.Start()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("error while starting Wiretrustee Connection Engine: %s", err)
|
|
||||||
//os.Exit(ExitSetupFailed)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
SetupCloseHandler()
|
|
||||||
<-stopCh
|
|
||||||
log.Infof("receive signal to stop running")
|
|
||||||
err = mgmClient.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed closing Management Service client %v", err)
|
|
||||||
//os.Exit(ExitSetupFailed)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = signalClient.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed closing Signal Service client %v", err)
|
|
||||||
//os.Exit(ExitSetupFailed)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("removing Wiretrustee interface %s", config.WgIface)
|
|
||||||
err = iface.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed closing Wiretrustee interface %s %v", config.WgIface, err)
|
|
||||||
//os.Exit(ExitSetupFailed)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// createEngineConfig converts configuration received from Management Service to EngineConfig
|
// createEngineConfig converts configuration received from Management Service to EngineConfig
|
||||||
func createEngineConfig(key wgtypes.Key, config *internal.Config, wtConfig *mgmProto.WiretrusteeConfig, peerConfig *mgmProto.PeerConfig) (*internal.EngineConfig, error) {
|
func createEngineConfig(key wgtypes.Key, config *internal.Config, peerConfig *mgmProto.PeerConfig) (*internal.EngineConfig, error) {
|
||||||
iFaceBlackList := make(map[string]struct{})
|
iFaceBlackList := make(map[string]struct{})
|
||||||
for i := 0; i < len(config.IFaceBlackList); i += 2 {
|
for i := 0; i < len(config.IFaceBlackList); i += 2 {
|
||||||
iFaceBlackList[config.IFaceBlackList[i]] = struct{}{}
|
iFaceBlackList[config.IFaceBlackList[i]] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
stunTurns, err := toStunTurnURLs(wtConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.FailedPrecondition, "failed parsing STUN and TURN URLs received from Management Service : %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &internal.EngineConfig{
|
return &internal.EngineConfig{
|
||||||
StunsTurns: stunTurns,
|
|
||||||
WgIface: config.WgIface,
|
WgIface: config.WgIface,
|
||||||
WgAddr: peerConfig.Address,
|
WgAddr: peerConfig.Address,
|
||||||
IFaceBlackList: iFaceBlackList,
|
IFaceBlackList: iFaceBlackList,
|
||||||
@@ -129,30 +69,6 @@ func createEngineConfig(key wgtypes.Key, config *internal.Config, wtConfig *mgmP
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// toStunTurnURLs converts Wiretrustee STUN and TURN configs to ice.URL array
|
|
||||||
func toStunTurnURLs(wtConfig *mgmProto.WiretrusteeConfig) ([]*ice.URL, error) {
|
|
||||||
|
|
||||||
var stunsTurns []*ice.URL
|
|
||||||
for _, stun := range wtConfig.Stuns {
|
|
||||||
url, err := ice.ParseURL(stun.Uri)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
stunsTurns = append(stunsTurns, url)
|
|
||||||
}
|
|
||||||
for _, turn := range wtConfig.Turns {
|
|
||||||
url, err := ice.ParseURL(turn.HostConfig.Uri)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
url.Username = turn.User
|
|
||||||
url.Password = turn.Password
|
|
||||||
stunsTurns = append(stunsTurns, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
return stunsTurns, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// connectToSignal creates Signal Service client and established a connection
|
// connectToSignal creates Signal Service client and established a connection
|
||||||
func connectToSignal(ctx context.Context, wtConfig *mgmProto.WiretrusteeConfig, ourPrivateKey wgtypes.Key) (*signal.Client, error) {
|
func connectToSignal(ctx context.Context, wtConfig *mgmProto.WiretrusteeConfig, ourPrivateKey wgtypes.Key) (*signal.Client, error) {
|
||||||
var sigTLSEnabled bool
|
var sigTLSEnabled bool
|
||||||
@@ -195,7 +111,88 @@ func connectToManagement(ctx context.Context, managementAddr string, ourPrivateK
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("peer logged in to Management Service %s", managementAddr)
|
log.Debugf("peer logged in to Management Service %s", managementAddr)
|
||||||
|
|
||||||
return client, loginResp, nil
|
return client, loginResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runClient() error {
|
||||||
|
config, err := internal.ReadConfig(managementURL, configPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed reading config %s %v", configPath, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//validate our peer's Wireguard PRIVATE key
|
||||||
|
myPrivateKey, err := wgtypes.ParseKey(config.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed parsing Wireguard key %s: [%s]", config.PrivateKey, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
mgmTlsEnabled := false
|
||||||
|
if config.ManagementURL.Scheme == "https" {
|
||||||
|
mgmTlsEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect (just a connection, no stream yet) and login to Management Service to get an initial global Wiretrustee config
|
||||||
|
mgmClient, loginResp, err := connectToManagement(ctx, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// with the global Wiretrustee config in hand connect (just a connection, no stream yet) Signal
|
||||||
|
signalClient, err := connectToSignal(ctx, loginResp.GetWiretrusteeConfig(), myPrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
peerConfig := loginResp.GetPeerConfig()
|
||||||
|
|
||||||
|
engineConfig, err := createEngineConfig(myPrivateKey, config, peerConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create start the Wiretrustee Engine that will connect to the Signal and Management streams and manage connections to remote peers.
|
||||||
|
engine := internal.NewEngine(signalClient, mgmClient, engineConfig, cancel, ctx)
|
||||||
|
err = engine.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error while starting Wiretrustee Connection Engine: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Print("Wiretrustee engine started, my IP is: ", peerConfig.Address)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-stopCh:
|
||||||
|
case <-ctx.Done():
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mgmClient.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed closing Management Service client %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = signalClient.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed closing Signal Service client %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = engine.Stop()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed stopping engine %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("stopped Wiretrustee client")
|
||||||
|
cleanupCh <- struct{}{}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"github.com/wiretrustee/wiretrustee/iface"
|
"github.com/wiretrustee/wiretrustee/iface"
|
||||||
mgmt "github.com/wiretrustee/wiretrustee/management/server"
|
mgmt "github.com/wiretrustee/wiretrustee/management/server"
|
||||||
"github.com/wiretrustee/wiretrustee/util"
|
"github.com/wiretrustee/wiretrustee/util"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -37,24 +34,6 @@ func TestUp_Start(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUp_ShouldFail_On_NoConfig(t *testing.T) {
|
|
||||||
|
|
||||||
tempDir := t.TempDir()
|
|
||||||
confPath := tempDir + "/config.json"
|
|
||||||
mgmtURL := fmt.Sprintf("http://%s", mgmAddr)
|
|
||||||
rootCmd.SetArgs([]string{
|
|
||||||
"up",
|
|
||||||
"--config",
|
|
||||||
confPath,
|
|
||||||
"--management-url",
|
|
||||||
mgmtURL,
|
|
||||||
})
|
|
||||||
err := rootCmd.Execute()
|
|
||||||
if err == nil || !errors.Is(err, os.ErrNotExist) {
|
|
||||||
t.Errorf("expecting login command to fail on absence of config")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUp(t *testing.T) {
|
func TestUp(t *testing.T) {
|
||||||
|
|
||||||
defer iface.Close()
|
defer iface.Close()
|
||||||
@@ -65,24 +44,17 @@ func TestUp(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rootCmd.SetArgs([]string{
|
rootCmd.SetArgs([]string{
|
||||||
"login",
|
"up",
|
||||||
"--config",
|
"--config",
|
||||||
confPath,
|
confPath,
|
||||||
"--setup-key",
|
"--setup-key",
|
||||||
"A2C8E62B-38F5-4553-B31E-DD66C696CEBB",
|
"A2C8E62B-38F5-4553-B31E-DD66C696CEBB",
|
||||||
"--management-url",
|
"--management-url",
|
||||||
mgmtURL.String(),
|
mgmtURL.String(),
|
||||||
})
|
"--log-file",
|
||||||
err = rootCmd.Execute()
|
"console",
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rootCmd.SetArgs([]string{
|
|
||||||
"up",
|
|
||||||
"--config",
|
|
||||||
confPath,
|
|
||||||
})
|
})
|
||||||
go func() {
|
go func() {
|
||||||
err = rootCmd.Execute()
|
err = rootCmd.Execute()
|
||||||
|
|||||||
14
client/cmd/version.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
|
var (
|
||||||
|
Version string
|
||||||
|
versionCmd = &cobra.Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: "prints wiretrustee version",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
cmd.Println(Version)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
118
client/installer.nsis
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
!define APP_NAME "Wiretrustee"
|
||||||
|
!define COMP_NAME "Wiretrustee"
|
||||||
|
!define WEB_SITE "wiretrustee.com"
|
||||||
|
!define VERSION $%APPVER%
|
||||||
|
!define COPYRIGHT "Wiretrustee Authors, 2021"
|
||||||
|
!define DESCRIPTION "A WireGuard®-based mesh network that connects your devices into a single private network"
|
||||||
|
!define INSTALLER_NAME "wiretrustee-installer.exe"
|
||||||
|
!define MAIN_APP_EXE "Wiretrustee"
|
||||||
|
!define ICON "ui\\wiretrustee.ico"
|
||||||
|
!define BANNER "ui\\banner.bmp"
|
||||||
|
!define LICENSE_DATA "..\\LICENSE"
|
||||||
|
|
||||||
|
!define INSTALL_DIR "$PROGRAMFILES64\${APP_NAME}"
|
||||||
|
!define INSTALL_TYPE "SetShellVarContext all"
|
||||||
|
!define REG_ROOT "HKLM"
|
||||||
|
!define REG_APP_PATH "Software\Microsoft\Windows\CurrentVersion\App Paths\${MAIN_APP_EXE}"
|
||||||
|
!define UNINSTALL_PATH "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}"
|
||||||
|
Unicode True
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
VIProductVersion "${VERSION}"
|
||||||
|
VIAddVersionKey "ProductName" "${APP_NAME}"
|
||||||
|
VIAddVersionKey "CompanyName" "${COMP_NAME}"
|
||||||
|
VIAddVersionKey "LegalCopyright" "${COPYRIGHT}"
|
||||||
|
VIAddVersionKey "FileDescription" "${DESCRIPTION}"
|
||||||
|
VIAddVersionKey "FileVersion" "${VERSION}"
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
SetCompressor /SOLID Lzma
|
||||||
|
Name "${APP_NAME}"
|
||||||
|
Caption "${APP_NAME}"
|
||||||
|
OutFile "..\\${INSTALLER_NAME}"
|
||||||
|
BrandingText "${APP_NAME}"
|
||||||
|
InstallDirRegKey "${REG_ROOT}" "${REG_APP_PATH}" ""
|
||||||
|
InstallDir "${INSTALL_DIR}"
|
||||||
|
LicenseData "${LICENSE_DATA}"
|
||||||
|
ShowInstDetails Show
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
!define MUI_ICON "${ICON}"
|
||||||
|
!define MUI_UNICON "${ICON}"
|
||||||
|
!define MUI_WELCOMEFINISHPAGE_BITMAP "${BANNER}"
|
||||||
|
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "${BANNER}"
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
!include "MUI2.nsh"
|
||||||
|
|
||||||
|
!define MUI_ABORTWARNING
|
||||||
|
!define MUI_UNABORTWARNING
|
||||||
|
|
||||||
|
!insertmacro MUI_PAGE_WELCOME
|
||||||
|
|
||||||
|
!insertmacro MUI_PAGE_LICENSE "${LICENSE_DATA}"
|
||||||
|
|
||||||
|
!insertmacro MUI_PAGE_DIRECTORY
|
||||||
|
|
||||||
|
!insertmacro MUI_PAGE_INSTFILES
|
||||||
|
|
||||||
|
!insertmacro MUI_PAGE_FINISH
|
||||||
|
|
||||||
|
!insertmacro MUI_UNPAGE_CONFIRM
|
||||||
|
|
||||||
|
!insertmacro MUI_UNPAGE_INSTFILES
|
||||||
|
|
||||||
|
!insertmacro MUI_UNPAGE_FINISH
|
||||||
|
|
||||||
|
!insertmacro MUI_LANGUAGE "English"
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
Section -MainProgram
|
||||||
|
${INSTALL_TYPE}
|
||||||
|
SetOverwrite ifnewer
|
||||||
|
SetOutPath "$INSTDIR"
|
||||||
|
File /r "..\\dist\\wiretrustee_windows_amd64\\"
|
||||||
|
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
Section -Icons_Reg
|
||||||
|
SetOutPath "$INSTDIR"
|
||||||
|
WriteUninstaller "$INSTDIR\wiretrustee_uninstall.exe"
|
||||||
|
|
||||||
|
WriteRegStr ${REG_ROOT} "${REG_APP_PATH}" "" "$INSTDIR\${MAIN_APP_EXE}"
|
||||||
|
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayName" "${APP_NAME}"
|
||||||
|
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "UninstallString" "$INSTDIR\wiretrustee_uninstall.exe"
|
||||||
|
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayIcon" "$INSTDIR\${MAIN_APP_EXE}"
|
||||||
|
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayVersion" "${VERSION}"
|
||||||
|
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "Publisher" "${COMP_NAME}"
|
||||||
|
|
||||||
|
EnVar::SetHKLM
|
||||||
|
EnVar::AddValueEx "path" "$INSTDIR"
|
||||||
|
|
||||||
|
Exec '"$INSTDIR\${MAIN_APP_EXE}" service install'
|
||||||
|
# sleep a bit for visibility
|
||||||
|
Sleep 1000
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
Section Uninstall
|
||||||
|
${INSTALL_TYPE}
|
||||||
|
|
||||||
|
Exec '"$INSTDIR\${MAIN_APP_EXE}" service uninstall'
|
||||||
|
# wait the service uninstall take unblock the executable
|
||||||
|
Sleep 3000
|
||||||
|
RmDir /r "$INSTDIR"
|
||||||
|
|
||||||
|
DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}"
|
||||||
|
DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}"
|
||||||
|
EnVar::SetHKLM
|
||||||
|
EnVar::DeleteValue "path" "$INSTDIR"
|
||||||
|
SectionEnd
|
||||||
@@ -127,8 +127,9 @@ 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
|
// create an ice.Agent that will be responsible for negotiating and establishing actual peer-to-peer connection
|
||||||
a, err := ice.NewAgent(&ice.AgentConfig{
|
a, err := ice.NewAgent(&ice.AgentConfig{
|
||||||
// MulticastDNSMode: ice.MulticastDNSModeQueryAndGather,
|
// MulticastDNSMode: ice.MulticastDNSModeQueryAndGather,
|
||||||
NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4},
|
NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4},
|
||||||
Urls: conn.Config.StunTurnURLS,
|
Urls: conn.Config.StunTurnURLS,
|
||||||
|
CandidateTypes: []ice.CandidateType{ice.CandidateTypeHost, ice.CandidateTypeServerReflexive, ice.CandidateTypeRelay},
|
||||||
InterfaceFilter: func(s string) bool {
|
InterfaceFilter: func(s string) bool {
|
||||||
if conn.Config.iFaceBlackList == nil {
|
if conn.Config.iFaceBlackList == nil {
|
||||||
return true
|
return true
|
||||||
@@ -165,7 +166,7 @@ func (conn *Connection) Open(timeout time.Duration) error {
|
|||||||
select {
|
select {
|
||||||
case remoteAuth := <-conn.remoteAuthChannel:
|
case remoteAuth := <-conn.remoteAuthChannel:
|
||||||
|
|
||||||
log.Infof("got a connection confirmation from peer %s", conn.Config.RemoteWgKey.String())
|
log.Debugf("got a connection confirmation from peer %s", conn.Config.RemoteWgKey.String())
|
||||||
|
|
||||||
err = conn.agent.GatherCandidates()
|
err = conn.agent.GatherCandidates()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -173,40 +174,46 @@ func (conn *Connection) Open(timeout time.Duration) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isControlling := conn.Config.WgKey.PublicKey().String() > conn.Config.RemoteWgKey.String()
|
isControlling := conn.Config.WgKey.PublicKey().String() > conn.Config.RemoteWgKey.String()
|
||||||
remoteConn, err := conn.openConnectionToRemote(isControlling, remoteAuth)
|
var remoteConn *ice.Conn
|
||||||
|
remoteConn, err = conn.openConnectionToRemote(isControlling, remoteAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed establishing connection with the remote peer %s %s", conn.Config.RemoteWgKey.String(), err)
|
log.Errorf("failed establishing connection with the remote peer %s %s", conn.Config.RemoteWgKey.String(), err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
pair, err := conn.agent.GetSelectedCandidatePair()
|
var pair *ice.CandidatePair
|
||||||
|
pair, err = conn.agent.GetSelectedCandidatePair()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
remoteIP := net.ParseIP(pair.Remote.Address())
|
|
||||||
myIp := net.ParseIP(pair.Remote.Address())
|
useProxy := useProxy(pair)
|
||||||
|
|
||||||
// in case the remote peer is in the local network or one of the peers has public static IP -> no need for a Wireguard proxy, direct communication is possible.
|
// in case the remote peer is in the local network or one of the peers has public static IP -> no need for a Wireguard proxy, direct communication is possible.
|
||||||
if (pair.Local.Type() == ice.CandidateTypeHost && pair.Remote.Type() == ice.CandidateTypeHost) && (isPublicIP(remoteIP) || isPublicIP(myIp)) {
|
if !useProxy {
|
||||||
log.Debugf("it is possible to establish a direct connection (without proxy) to peer %s - my addr: %s, remote addr: %s", conn.Config.RemoteWgKey.String(), pair.Local.Address(), pair.Remote.Address())
|
log.Debugf("it is possible to establish a direct connection (without proxy) to peer %s - my addr: %s, remote addr: %s", conn.Config.RemoteWgKey.String(), pair.Local, pair.Remote)
|
||||||
err = conn.wgProxy.StartLocal(fmt.Sprintf("%s:%d", pair.Remote.Address(), iface.WgPort))
|
err = conn.wgProxy.StartLocal(fmt.Sprintf("%s:%d", pair.Remote.Address(), iface.WgPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log.Infof("establishing secure tunnel to peer %s via selected candidate pair %s", conn.Config.RemoteWgKey.String(), pair)
|
log.Debugf("establishing secure tunnel to peer %s via selected candidate pair %s", conn.Config.RemoteWgKey.String(), pair)
|
||||||
err = conn.wgProxy.Start(remoteConn)
|
err = conn.wgProxy.Start(remoteConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
relayed := pair.Remote.Type() == ice.CandidateTypeRelay || pair.Local.Type() == ice.CandidateTypeRelay
|
||||||
|
|
||||||
conn.Status = StatusConnected
|
conn.Status = StatusConnected
|
||||||
log.Infof("opened connection to peer %s", conn.Config.RemoteWgKey.String())
|
log.Infof("opened connection to peer %s [localProxy=%v, relayed=%v]", conn.Config.RemoteWgKey.String(), useProxy, relayed)
|
||||||
case <-conn.closeCond.C:
|
case <-conn.closeCond.C:
|
||||||
conn.Status = StatusDisconnected
|
conn.Status = StatusDisconnected
|
||||||
return fmt.Errorf("connection to peer %s has been closed", conn.Config.RemoteWgKey.String())
|
return fmt.Errorf("connection to peer %s has been closed", conn.Config.RemoteWgKey.String())
|
||||||
case <-time.After(timeout):
|
case <-time.After(timeout):
|
||||||
err := conn.Close()
|
err = conn.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("error while closing connection to peer %s -> %s", conn.Config.RemoteWgKey.String(), err.Error())
|
log.Warnf("error while closing connection to peer %s -> %s", conn.Config.RemoteWgKey.String(), err.Error())
|
||||||
}
|
}
|
||||||
@@ -233,12 +240,39 @@ func isPublicIP(ip net.IP) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//useProxy determines whether a direct connection (without a go proxy) is possible
|
||||||
|
//There are 3 cases: one of the peers has a public IP or both peers are in the same private network
|
||||||
|
//Please note, that this check happens when peers were already able to ping each other with ICE layer.
|
||||||
|
func useProxy(pair *ice.CandidatePair) bool {
|
||||||
|
remoteIP := net.ParseIP(pair.Remote.Address())
|
||||||
|
myIp := net.ParseIP(pair.Local.Address())
|
||||||
|
remoteIsPublic := isPublicIP(remoteIP)
|
||||||
|
myIsPublic := isPublicIP(myIp)
|
||||||
|
|
||||||
|
//one of the hosts has a public IP
|
||||||
|
if remoteIsPublic && pair.Remote.Type() == ice.CandidateTypeHost {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if myIsPublic && pair.Local.Type() == ice.CandidateTypeHost {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if pair.Local.Type() == ice.CandidateTypeHost && pair.Remote.Type() == ice.CandidateTypeHost {
|
||||||
|
if !remoteIsPublic && !myIsPublic {
|
||||||
|
//both hosts are in the same private network
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Close Closes a peer connection
|
// Close Closes a peer connection
|
||||||
func (conn *Connection) Close() error {
|
func (conn *Connection) Close() error {
|
||||||
var err error
|
var err error
|
||||||
conn.closeCond.Do(func() {
|
conn.closeCond.Do(func() {
|
||||||
|
|
||||||
log.Warnf("closing connection to peer %s", conn.Config.RemoteWgKey.String())
|
log.Debugf("closing connection to peer %s", conn.Config.RemoteWgKey.String())
|
||||||
|
|
||||||
if a := conn.agent; a != nil {
|
if a := conn.agent; a != nil {
|
||||||
e := a.Close()
|
e := a.Close()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/cenkalti/backoff/v4"
|
"github.com/cenkalti/backoff/v4"
|
||||||
ice "github.com/pion/ice/v2"
|
ice "github.com/pion/ice/v2"
|
||||||
@@ -18,13 +19,11 @@ import (
|
|||||||
|
|
||||||
// PeerConnectionTimeout is a timeout of an initial connection attempt to a remote peer.
|
// PeerConnectionTimeout is a timeout of an initial connection attempt to a remote peer.
|
||||||
// E.g. this peer will wait PeerConnectionTimeout for the remote peer to respond, if not successful then it will retry the connection attempt.
|
// E.g. this peer will wait PeerConnectionTimeout for the remote peer to respond, if not successful then it will retry the connection attempt.
|
||||||
const PeerConnectionTimeout = 60 * time.Second
|
const PeerConnectionTimeout = 40 * time.Second
|
||||||
|
|
||||||
// EngineConfig is a config for the Engine
|
// EngineConfig is a config for the Engine
|
||||||
type EngineConfig struct {
|
type EngineConfig struct {
|
||||||
// StunsTurns is a list of STUN and TURN servers used by ICE
|
WgIface string
|
||||||
StunsTurns []*ice.URL
|
|
||||||
WgIface string
|
|
||||||
// WgAddr is a Wireguard local address (Wiretrustee Network IP)
|
// WgAddr is a Wireguard local address (Wiretrustee Network IP)
|
||||||
WgAddr string
|
WgAddr string
|
||||||
// WgPrivateKey is a Wireguard private key of our peer (it MUST never leave the machine)
|
// WgPrivateKey is a Wireguard private key of our peer (it MUST never leave the machine)
|
||||||
@@ -51,6 +50,15 @@ type Engine struct {
|
|||||||
|
|
||||||
// wgPort is a Wireguard local listen port
|
// wgPort is a Wireguard local listen port
|
||||||
wgPort int
|
wgPort int
|
||||||
|
|
||||||
|
// STUNs is a list of STUN servers used by ICE
|
||||||
|
STUNs []*ice.URL
|
||||||
|
// TURNs is a list of STUN servers used by ICE
|
||||||
|
TURNs []*ice.URL
|
||||||
|
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// Peer is an instance of the Connection Peer
|
// Peer is an instance of the Connection Peer
|
||||||
@@ -60,7 +68,7 @@ type Peer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewEngine creates a new Connection Engine
|
// NewEngine creates a new Connection Engine
|
||||||
func NewEngine(signalClient *signal.Client, mgmClient *mgm.Client, config *EngineConfig) *Engine {
|
func NewEngine(signalClient *signal.Client, mgmClient *mgm.Client, config *EngineConfig, cancel context.CancelFunc, ctx context.Context) *Engine {
|
||||||
return &Engine{
|
return &Engine{
|
||||||
signal: signalClient,
|
signal: signalClient,
|
||||||
mgmClient: mgmClient,
|
mgmClient: mgmClient,
|
||||||
@@ -68,9 +76,31 @@ func NewEngine(signalClient *signal.Client, mgmClient *mgm.Client, config *Engin
|
|||||||
peerMux: &sync.Mutex{},
|
peerMux: &sync.Mutex{},
|
||||||
syncMsgMux: &sync.Mutex{},
|
syncMsgMux: &sync.Mutex{},
|
||||||
config: config,
|
config: config,
|
||||||
|
STUNs: []*ice.URL{},
|
||||||
|
TURNs: []*ice.URL{},
|
||||||
|
cancel: cancel,
|
||||||
|
ctx: ctx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Engine) Stop() error {
|
||||||
|
err := e.removeAllPeerConnections()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("removing Wiretrustee interface %s", e.config.WgIface)
|
||||||
|
err = iface.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed closing Wiretrustee interface %s %v", e.config.WgIface, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("stopped Wiretrustee Engine")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Start creates a new Wireguard tunnel interface and listens to events from Signal and Management services
|
// Start creates a new Wireguard tunnel interface and listens to events from Signal and Management services
|
||||||
// Connections to remote peers are not established here.
|
// Connections to remote peers are not established here.
|
||||||
// However, they will be established once an event with a list of peers to connect to will be received from Management Service
|
// However, they will be established once an event with a list of peers to connect to will be received from Management Service
|
||||||
@@ -107,7 +137,7 @@ func (e *Engine) Start() error {
|
|||||||
|
|
||||||
// initializePeer peer agent attempt to open connection
|
// initializePeer peer agent attempt to open connection
|
||||||
func (e *Engine) initializePeer(peer Peer) {
|
func (e *Engine) initializePeer(peer Peer) {
|
||||||
var backOff = &backoff.ExponentialBackOff{
|
var backOff = backoff.WithContext(&backoff.ExponentialBackOff{
|
||||||
InitialInterval: backoff.DefaultInitialInterval,
|
InitialInterval: backoff.DefaultInitialInterval,
|
||||||
RandomizationFactor: backoff.DefaultRandomizationFactor,
|
RandomizationFactor: backoff.DefaultRandomizationFactor,
|
||||||
Multiplier: backoff.DefaultMultiplier,
|
Multiplier: backoff.DefaultMultiplier,
|
||||||
@@ -115,13 +145,14 @@ func (e *Engine) initializePeer(peer Peer) {
|
|||||||
MaxElapsedTime: time.Duration(0), //never stop
|
MaxElapsedTime: time.Duration(0), //never stop
|
||||||
Stop: backoff.Stop,
|
Stop: backoff.Stop,
|
||||||
Clock: backoff.SystemClock,
|
Clock: backoff.SystemClock,
|
||||||
}
|
}, e.ctx)
|
||||||
|
|
||||||
operation := func() error {
|
operation := func() error {
|
||||||
_, err := e.openPeerConnection(e.wgPort, e.config.WgPrivateKey, peer)
|
_, err := e.openPeerConnection(e.wgPort, e.config.WgPrivateKey, peer)
|
||||||
e.peerMux.Lock()
|
e.peerMux.Lock()
|
||||||
defer e.peerMux.Unlock()
|
defer e.peerMux.Unlock()
|
||||||
if _, ok := e.conns[peer.WgPubKey]; !ok {
|
if _, ok := e.conns[peer.WgPubKey]; !ok {
|
||||||
log.Infof("removing connection attempt with Peer: %v, not retrying", peer.WgPubKey)
|
log.Debugf("removed connection attempt to peer: %v, not retrying", peer.WgPubKey)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +183,19 @@ func (e *Engine) removePeerConnections(peers []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Engine) removeAllPeerConnections() error {
|
||||||
|
log.Debugf("removing all peer connections")
|
||||||
|
e.peerMux.Lock()
|
||||||
|
defer e.peerMux.Unlock()
|
||||||
|
for peer := range e.conns {
|
||||||
|
err := e.removePeerConnection(peer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// removePeerConnection closes existing peer connection and removes peer
|
// removePeerConnection closes existing peer connection and removes peer
|
||||||
func (e *Engine) removePeerConnection(peerKey string) error {
|
func (e *Engine) removePeerConnection(peerKey string) error {
|
||||||
conn, exists := e.conns[peerKey]
|
conn, exists := e.conns[peerKey]
|
||||||
@@ -159,6 +203,7 @@ func (e *Engine) removePeerConnection(peerKey string) error {
|
|||||||
delete(e.conns, peerKey)
|
delete(e.conns, peerKey)
|
||||||
return conn.Close()
|
return conn.Close()
|
||||||
}
|
}
|
||||||
|
log.Infof("removed connection to peer %s", peerKey)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +232,7 @@ func (e *Engine) openPeerConnection(wgPort int, myKey wgtypes.Key, peer Peer) (*
|
|||||||
WgAllowedIPs: peer.WgAllowedIps,
|
WgAllowedIPs: peer.WgAllowedIps,
|
||||||
WgKey: myKey,
|
WgKey: myKey,
|
||||||
RemoteWgKey: remoteKey,
|
RemoteWgKey: remoteKey,
|
||||||
StunTurnURLS: e.config.StunsTurns,
|
StunTurnURLS: append(e.STUNs, e.TURNs...),
|
||||||
iFaceBlackList: e.config.IFaceBlackList,
|
iFaceBlackList: e.config.IFaceBlackList,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,54 +302,114 @@ func signalAuth(uFrag string, pwd string, myKey wgtypes.Key, remoteKey wgtypes.K
|
|||||||
// receiveManagementEvents connects to the Management Service event stream to receive updates from the management service
|
// receiveManagementEvents connects to the Management Service event stream to receive updates from the management service
|
||||||
// E.g. when a new peer has been registered and we are allowed to connect to it.
|
// E.g. when a new peer has been registered and we are allowed to connect to it.
|
||||||
func (e *Engine) receiveManagementEvents() {
|
func (e *Engine) receiveManagementEvents() {
|
||||||
|
go func() {
|
||||||
|
err := e.mgmClient.Sync(func(update *mgmProto.SyncResponse) error {
|
||||||
|
e.syncMsgMux.Lock()
|
||||||
|
defer e.syncMsgMux.Unlock()
|
||||||
|
|
||||||
|
if update.GetWiretrusteeConfig() != nil {
|
||||||
|
err := e.updateTURNs(update.GetWiretrusteeConfig().GetTurns())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = e.updateSTUNs(update.GetWiretrusteeConfig().GetStuns())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo update signal
|
||||||
|
}
|
||||||
|
|
||||||
|
if update.GetRemotePeers() != nil || update.GetRemotePeersIsEmpty() {
|
||||||
|
// empty arrays are serialized by protobuf to null, but for our case empty array is a valid state.
|
||||||
|
err := e.updatePeers(update.GetRemotePeers())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
e.cancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debugf("stopped receiving updates from Management Service")
|
||||||
|
}()
|
||||||
log.Debugf("connecting to Management Service updates stream")
|
log.Debugf("connecting to Management Service updates stream")
|
||||||
|
}
|
||||||
|
|
||||||
e.mgmClient.Sync(func(update *mgmProto.SyncResponse) error {
|
func (e *Engine) updateSTUNs(stuns []*mgmProto.HostConfig) error {
|
||||||
// todo handle changes of global settings (in update.GetWiretrusteeConfig())
|
if len(stuns) == 0 {
|
||||||
// todo handle changes of peer settings (in update.GetPeerConfig())
|
return nil
|
||||||
|
}
|
||||||
|
var newSTUNs []*ice.URL
|
||||||
|
log.Debugf("got STUNs update from Management Service, updating")
|
||||||
|
for _, stun := range stuns {
|
||||||
|
url, err := ice.ParseURL(stun.Uri)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newSTUNs = append(newSTUNs, url)
|
||||||
|
}
|
||||||
|
e.STUNs = newSTUNs
|
||||||
|
|
||||||
e.syncMsgMux.Lock()
|
return nil
|
||||||
defer e.syncMsgMux.Unlock()
|
}
|
||||||
|
|
||||||
remotePeers := update.GetRemotePeers()
|
func (e *Engine) updateTURNs(turns []*mgmProto.ProtectedHostConfig) error {
|
||||||
if len(remotePeers) != 0 {
|
if len(turns) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var newTURNs []*ice.URL
|
||||||
|
log.Debugf("got TURNs update from Management Service, updating")
|
||||||
|
for _, turn := range turns {
|
||||||
|
url, err := ice.ParseURL(turn.HostConfig.Uri)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
url.Username = turn.User
|
||||||
|
url.Password = turn.Password
|
||||||
|
newTURNs = append(newTURNs, url)
|
||||||
|
}
|
||||||
|
e.TURNs = newTURNs
|
||||||
|
|
||||||
remotePeerMap := make(map[string]struct{})
|
return nil
|
||||||
for _, peer := range remotePeers {
|
}
|
||||||
remotePeerMap[peer.GetWgPubKey()] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
//remove peers that are no longer available for us
|
func (e *Engine) updatePeers(remotePeers []*mgmProto.RemotePeerConfig) error {
|
||||||
toRemove := []string{}
|
log.Debugf("got peers update from Management Service, updating")
|
||||||
for p := range e.conns {
|
remotePeerMap := make(map[string]struct{})
|
||||||
if _, ok := remotePeerMap[p]; !ok {
|
for _, peer := range remotePeers {
|
||||||
toRemove = append(toRemove, p)
|
remotePeerMap[peer.GetWgPubKey()] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
err := e.removePeerConnections(toRemove)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// add new peers
|
//remove peers that are no longer available for us
|
||||||
for _, peer := range remotePeers {
|
toRemove := []string{}
|
||||||
peerKey := peer.GetWgPubKey()
|
for p := range e.conns {
|
||||||
peerIPs := peer.GetAllowedIps()
|
if _, ok := remotePeerMap[p]; !ok {
|
||||||
if _, ok := e.conns[peerKey]; !ok {
|
toRemove = append(toRemove, p)
|
||||||
go e.initializePeer(Peer{
|
}
|
||||||
WgPubKey: peerKey,
|
}
|
||||||
WgAllowedIps: strings.Join(peerIPs, ","),
|
err := e.removePeerConnections(toRemove)
|
||||||
})
|
if err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
}
|
// add new peers
|
||||||
|
for _, peer := range remotePeers {
|
||||||
|
peerKey := peer.GetWgPubKey()
|
||||||
|
peerIPs := peer.GetAllowedIps()
|
||||||
|
if _, ok := e.conns[peerKey]; !ok {
|
||||||
|
go e.initializePeer(Peer{
|
||||||
|
WgPubKey: peerKey,
|
||||||
|
WgAllowedIps: strings.Join(peerIPs, ","),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
}
|
||||||
})
|
return nil
|
||||||
|
|
||||||
log.Infof("connected to Management Service updates stream")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// receiveSignalEvents connects to the Signal Service event stream to negotiate connection with remote peers
|
// receiveSignalEvents connects to the Signal Service event stream to negotiate connection with remote peers
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ func (p *WgProxy) proxyToRemotePeer(remoteConn *ice.Conn) {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-p.close:
|
case <-p.close:
|
||||||
log.Infof("stopped proxying from remote peer %s due to closed connection", p.remoteKey)
|
log.Debugf("stopped proxying from remote peer %s due to closed connection", p.remoteKey)
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
n, err := p.wgConn.Read(buf)
|
n, err := p.wgConn.Read(buf)
|
||||||
@@ -113,7 +113,7 @@ func (p *WgProxy) proxyToLocalWireguard(remoteConn *ice.Conn) {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-p.close:
|
case <-p.close:
|
||||||
log.Infof("stopped proxying from remote peer %s due to closed connection", p.remoteKey)
|
log.Debugf("stopped proxying from remote peer %s due to closed connection", p.remoteKey)
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
n, err := remoteConn.Read(buf)
|
n, err := remoteConn.Read(buf)
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var version = "development"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
|
cmd.Version = version
|
||||||
if err := cmd.Execute(); err != nil {
|
if err := cmd.Execute(); err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|||||||
14
client/system/info.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
//Info is an object that contains machine information
|
||||||
|
// Most of the code is taken from https://github.com/matishsiao/goInfo
|
||||||
|
type Info struct {
|
||||||
|
GoOS string
|
||||||
|
Kernel string
|
||||||
|
Core string
|
||||||
|
Platform string
|
||||||
|
OS string
|
||||||
|
OSVersion string
|
||||||
|
Hostname string
|
||||||
|
CPUs int
|
||||||
|
}
|
||||||
39
client/system/info_darwin.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetInfo() *Info {
|
||||||
|
out := _getInfo()
|
||||||
|
for strings.Contains(out, "broken pipe") {
|
||||||
|
out = _getInfo()
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
osStr := strings.Replace(out, "\n", "", -1)
|
||||||
|
osStr = strings.Replace(osStr, "\r\n", "", -1)
|
||||||
|
osInfo := strings.Split(osStr, " ")
|
||||||
|
gio := &Info{Kernel: osInfo[0], OSVersion: osInfo[1], Core: osInfo[1], Platform: osInfo[2], OS: osInfo[0], GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
||||||
|
gio.Hostname, _ = os.Hostname()
|
||||||
|
return gio
|
||||||
|
}
|
||||||
|
|
||||||
|
func _getInfo() string {
|
||||||
|
cmd := exec.Command("uname", "-srm")
|
||||||
|
cmd.Stdin = strings.NewReader("some input")
|
||||||
|
var out bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("getInfo:", err)
|
||||||
|
}
|
||||||
|
return out.String()
|
||||||
|
}
|
||||||
39
client/system/info_freebsd.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetInfo() *Info {
|
||||||
|
out := _getInfo()
|
||||||
|
for strings.Contains(out, "broken pipe") {
|
||||||
|
out = _getInfo()
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
osStr := strings.Replace(out, "\n", "", -1)
|
||||||
|
osStr = strings.Replace(osStr, "\r\n", "", -1)
|
||||||
|
osInfo := strings.Split(osStr, " ")
|
||||||
|
gio := &Info{Kernel: osInfo[0], Core: osInfo[1], Platform: runtime.GOARCH, OS: osInfo[2], GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
||||||
|
gio.Hostname, _ = os.Hostname()
|
||||||
|
return gio
|
||||||
|
}
|
||||||
|
|
||||||
|
func _getInfo() string {
|
||||||
|
cmd := exec.Command("uname", "-sri")
|
||||||
|
cmd.Stdin = strings.NewReader("some input")
|
||||||
|
var out bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("getInfo:", err)
|
||||||
|
}
|
||||||
|
return out.String()
|
||||||
|
}
|
||||||
76
client/system/info_linux.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetInfo() *Info {
|
||||||
|
info := _getInfo()
|
||||||
|
for strings.Contains(info, "broken pipe") {
|
||||||
|
info = _getInfo()
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseInfo := _getReleaseInfo()
|
||||||
|
for strings.Contains(info, "broken pipe") {
|
||||||
|
releaseInfo = _getReleaseInfo()
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
osRelease := strings.Split(releaseInfo, "\n")
|
||||||
|
var osName string
|
||||||
|
var osVer string
|
||||||
|
for _, s := range osRelease {
|
||||||
|
if strings.HasPrefix(s, "NAME=") {
|
||||||
|
osName = strings.Split(s, "=")[1]
|
||||||
|
osName = strings.ReplaceAll(osName, "\"", "")
|
||||||
|
} else if strings.HasPrefix(s, "VERSION_ID=") {
|
||||||
|
osVer = strings.Split(s, "=")[1]
|
||||||
|
osVer = strings.ReplaceAll(osVer, "\"", "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
osStr := strings.Replace(info, "\n", "", -1)
|
||||||
|
osStr = strings.Replace(osStr, "\r\n", "", -1)
|
||||||
|
osInfo := strings.Split(osStr, " ")
|
||||||
|
if osName == "" {
|
||||||
|
osName = osInfo[3]
|
||||||
|
}
|
||||||
|
gio := &Info{Kernel: osInfo[0], Core: osInfo[1], Platform: osInfo[2], OS: osName, OSVersion: osVer, GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
||||||
|
gio.Hostname, _ = os.Hostname()
|
||||||
|
return gio
|
||||||
|
}
|
||||||
|
|
||||||
|
func _getInfo() string {
|
||||||
|
cmd := exec.Command("uname", "-srio")
|
||||||
|
cmd.Stdin = strings.NewReader("some")
|
||||||
|
var out bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("getInfo:", err)
|
||||||
|
}
|
||||||
|
return out.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func _getReleaseInfo() string {
|
||||||
|
cmd := exec.Command("cat", "/etc/os-release")
|
||||||
|
cmd.Stdin = strings.NewReader("some")
|
||||||
|
var out bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("getReleaseInfo:", err)
|
||||||
|
}
|
||||||
|
return out.String()
|
||||||
|
}
|
||||||
35
client/system/info_windows.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetInfo() *Info {
|
||||||
|
cmd := exec.Command("cmd", "ver")
|
||||||
|
cmd.Stdin = strings.NewReader("some")
|
||||||
|
var out bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
osStr := strings.Replace(out.String(), "\n", "", -1)
|
||||||
|
osStr = strings.Replace(osStr, "\r\n", "", -1)
|
||||||
|
tmp1 := strings.Index(osStr, "[Version")
|
||||||
|
tmp2 := strings.Index(osStr, "]")
|
||||||
|
var ver string
|
||||||
|
if tmp1 == -1 || tmp2 == -1 {
|
||||||
|
ver = "unknown"
|
||||||
|
} else {
|
||||||
|
ver = osStr[tmp1+9 : tmp2]
|
||||||
|
}
|
||||||
|
gio := &Info{Kernel: "windows", OSVersion: ver, Core: ver, Platform: "unknown", OS: "windows", GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
||||||
|
gio.Hostname, _ = os.Hostname()
|
||||||
|
return gio
|
||||||
|
}
|
||||||
32
client/testdata/management.json
vendored
@@ -7,14 +7,19 @@
|
|||||||
"Password": null
|
"Password": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Turns": [
|
"TURNConfig": {
|
||||||
{
|
"Turns": [
|
||||||
"Proto": "udp",
|
{
|
||||||
"URI": "turn:stun.wiretrustee.com:3468",
|
"Proto": "udp",
|
||||||
"Username": "some_user",
|
"URI": "turn:stun.wiretrustee.com:3468",
|
||||||
"Password": "c29tZV9wYXNzd29yZA=="
|
"Username": "some_user",
|
||||||
}
|
"Password": "c29tZV9wYXNzd29yZA=="
|
||||||
],
|
}
|
||||||
|
],
|
||||||
|
"CredentialsTTL": "1h",
|
||||||
|
"Secret": "c29tZV9wYXNzd29yZA==",
|
||||||
|
"TimeBasedCredentials": true
|
||||||
|
},
|
||||||
"Signal": {
|
"Signal": {
|
||||||
"Proto": "http",
|
"Proto": "http",
|
||||||
"URI": "signal.wiretrustee.com:10000",
|
"URI": "signal.wiretrustee.com:10000",
|
||||||
@@ -23,11 +28,10 @@
|
|||||||
},
|
},
|
||||||
"DataDir": "",
|
"DataDir": "",
|
||||||
"HttpConfig": {
|
"HttpConfig": {
|
||||||
"LetsEncryptDomain": "",
|
"LetsEncryptDomain": "<PASTE YOUR LET'S ENCRYPT DOMAIN HERE>",
|
||||||
"Address": "0.0.0.0:3000",
|
"Address": "0.0.0.0:33071",
|
||||||
"AuthDomain": "<PASTE YOUR AUTH0 DOMAIN HERE>",
|
"AuthIssuer": "<PASTE YOUR AUTH0 ISSUER HERE>,",
|
||||||
"AuthClientId": "<PASTE YOUR AUTH0 CLIENT ID HERE>",
|
"AuthAudience": "<PASTE YOUR AUTH0 AUDIENCE HERE>",
|
||||||
"AuthClientSecret": "<PASTE YOUR AUTH0 SECRET>",
|
"AuthKeysLocation": "<PASTE YOUR AUTH0 PUBLIC JWT KEYS LOCATION HERE>"
|
||||||
"AuthCallback": "http://localhost:3000/callback"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BIN
client/ui/banner.bmp
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
client/ui/wiretrustee.ico
Normal file
|
After Width: | Height: | Size: 99 KiB |
105
docs/README.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
### Table of contents
|
||||||
|
|
||||||
|
* [About Wiretrustee](#about-wiretrustee)
|
||||||
|
* [Why not just Wireguard?](#why-not-just-wireguard)
|
||||||
|
* [Wiretrustee vs. Traditional VPN](#wiretrustee-vs-traditional-vpn)
|
||||||
|
* [High-level technology overview](#high-level-technology-overview)
|
||||||
|
* [Getting started](#getting-started)
|
||||||
|
|
||||||
|
### About Wiretrustee
|
||||||
|
|
||||||
|
Wiretrustee is an open-source VPN platform built on top of [WireGuard®](https://www.wireguard.com/) making it easy to create secure private networks for your organization or home.
|
||||||
|
|
||||||
|
It requires zero configuration effort leaving behind the hassle of opening ports, complex firewall rules, vpn gateways, and so forth.
|
||||||
|
|
||||||
|
There is no centralized VPN server with Wiretrustee - your computers, devices, machines, and servers connect to each other directly over a fast encrypted tunnel.
|
||||||
|
|
||||||
|
It literally takes less than 5 minutes to provision a secure peer-to-peer VPN with Wiretrustee. Check our [Quickstart Guide Video](https://www.youtube.com/watch?v=cWTsGUJAUaU) to see the setup in action.
|
||||||
|
|
||||||
|
### Why not just Wireguard?
|
||||||
|
|
||||||
|
WireGuard is a modern and extremely fast VPN tunnel utilizing state-of-the-art [cryptography](https://www.wireguard.com/protocol/)
|
||||||
|
and Wiretrustee uses Wireguard to establish a secure tunnel between machines.
|
||||||
|
|
||||||
|
Built with simplicity in mind, Wireguard ensures that traffic between two machines is encrypted and flowing, however, it requires a few things to be done beforehand.
|
||||||
|
|
||||||
|
First, in order to connect, the machines have to be configured.
|
||||||
|
On each machine, you need to generate private and public keys and prepare a WireGuard configuration file.
|
||||||
|
The configuration also includes a private IP address that should be unique per machine.
|
||||||
|
|
||||||
|
Secondly, to accept the incoming traffic, the machines have to trust each other.
|
||||||
|
The generated public keys have to be pre-shared on the machines.
|
||||||
|
This works similarly to SSH with its authorised_keys file.
|
||||||
|
|
||||||
|
Lastly, the connectivity between the machines has to be ensured.
|
||||||
|
To make machines reach one another, you are required to set a WireGuard endpoint property which indicates the IP address and port of the remote machine to connect to.
|
||||||
|
On many occasions, machines are hidden behind firewalls and NAT devices,
|
||||||
|
meaning that you may need to configure a port forwarding or open holes in your firewall to ensure the machines are reachable.
|
||||||
|
|
||||||
|
The undertakings mentioned above might not be complicated if you have just a few machines, but the complexity grows as the number of machines increases.
|
||||||
|
|
||||||
|
Wiretrustee simplifies the setup by automatically generating private and public keys, assigning unique private IP addresses, and takes care of sharing public keys between the machines.
|
||||||
|
It is worth mentioning that the private key never leaves the machine.
|
||||||
|
So only the machine that owns the key can decrypt traffic addressed to it.
|
||||||
|
The same applies also to the relayed traffic mentioned below.
|
||||||
|
|
||||||
|
Furthermore, Wiretrustee ensures connectivity by leveraging advanced [NAT traversal techniques](https://en.wikipedia.org/wiki/NAT_traversal)
|
||||||
|
and removing the necessity of port forwarding, opening holes in the firewall, and having a public static IP address.
|
||||||
|
In cases when a direct peer-to-peer connection isn't possible all traffic is relayed securely between peers.
|
||||||
|
Wiretrustee also monitors the connection health and restarts broken connections.
|
||||||
|
|
||||||
|
There are a few more things that we are working on to make secure private networks simple. A few examples are ACLs, MFA and activity monitoring.
|
||||||
|
|
||||||
|
Check out the WireGuard [Quick Start](https://www.wireguard.com/quickstart/) guide to learn more about configuring "plain" WireGuard without Wiretrustee.
|
||||||
|
|
||||||
|
### Wiretrustee vs. Traditional VPN
|
||||||
|
|
||||||
|
In the traditional VPN model, everything converges on a centralized, protected network where all the clients are connecting to a central VPN server.
|
||||||
|
|
||||||
|
An increasing amount of connections can easily overload the VPN server.
|
||||||
|
Even a short downtime of a server can cause expensive system disruptions, and a remote team's inability to work.
|
||||||
|
|
||||||
|
Centralized VPNs imply all the traffic going through the central server causing network delays and increased traffic usage.
|
||||||
|
|
||||||
|
Such systems require an experienced team to set up and maintain.
|
||||||
|
Configuring firewalls, setting up NATs, SSO integration, and managing access control lists can be a nightmare.
|
||||||
|
|
||||||
|
Traditional centralized VPNs are often compared to a [castle-and-moat](https://en.wikipedia.org/wiki/Moat) model
|
||||||
|
in which once accessed, user is trusted and can access critical infrastructure and resources without any restrictions.
|
||||||
|
|
||||||
|
Wiretrustee decentralizes networks using direct point-to-point connections, as opposed to traditional models.
|
||||||
|
Consequently, network performance is increased since traffic flows directly between the machines bypassing VPN servers or gateways.
|
||||||
|
To achieve this, Wiretrustee client applications employ signalling servers to find other machines and negotiate connections.
|
||||||
|
These are similar to the signaling servers used in [WebRTC](https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Signaling_and_video_calling#the_signaling_server)
|
||||||
|
|
||||||
|
Thanks to [NAT traversal techniques](https://en.wikipedia.org/wiki/NAT_traversal),
|
||||||
|
outlined in the [Why not just Wireguard?](#why-not-just-wireguard) section above,
|
||||||
|
Wiretrustee installation doesn't require complex network and firewall configuration.
|
||||||
|
It just works, minimising the maintenance effort.
|
||||||
|
|
||||||
|
Finally, each machine or device in the Wiretrustee network verifies incoming connections accepting only the trusted ones.
|
||||||
|
This is ensured by Wireguard's [Crypto Routing concept](https://www.wireguard.com/#cryptokey-routing).
|
||||||
|
|
||||||
|
### High-level technology overview
|
||||||
|
In essence, Wiretrustee is an open source platform consisting of a collection of systems, responsible for handling peer-to-peer connections, tunneling and network management (IP, keys, ACLs, etc).
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="media/high-level-dia.png" alt="high-level-dia" width="781"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
Wiretrustee uses open-source technologies like [WireGuard®](https://www.wireguard.com/), [Pion ICE (WebRTC)](https://github.com/pion/ice), [Coturn](https://github.com/coturn/coturn),
|
||||||
|
and [software](https://github.com/wiretrustee/wiretrustee) developed by Wiretrustee authors to make it all work together.
|
||||||
|
|
||||||
|
To learn more about Wiretrustee architecture, please refer to the [architecture section](../docs/architecture.md).
|
||||||
|
|
||||||
|
### Getting Started
|
||||||
|
|
||||||
|
There are 2 ways of getting started with Wiretrustee:
|
||||||
|
- use Cloud Managed version
|
||||||
|
- self-hosting
|
||||||
|
|
||||||
|
We recommend starting with the cloud managed version hosted at [app.wiretrustee.com](https://app.wiretrustee.com) - the quickest way to get familiar with the system.
|
||||||
|
See [Quickstart Guide](../docs/quickstart.md) for instructions.
|
||||||
|
|
||||||
|
If you don't want to use the managed version, check out our [Self-hosting Guide](../docs/self-hosting.md).
|
||||||
|
|
||||||
2
docs/architecture.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
### Architecture
|
||||||
|
TODO
|
||||||
BIN
docs/media/add-peer.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
docs/media/auth.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
docs/media/empty-peers.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/media/high-level-dia.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
docs/media/logo-full.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
docs/media/logo.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
docs/media/peerA.gif
Normal file
|
After Width: | Height: | Size: 409 KiB |
BIN
docs/media/peerB.gif
Normal file
|
After Width: | Height: | Size: 526 KiB |
|
Before Width: | Height: | Size: 5.9 MiB After Width: | Height: | Size: 5.9 MiB |
BIN
docs/media/peers.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
41
docs/quickstart.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
## Quickstart guide (Cloud Managed version)
|
||||||
|
Step-by-step video guide on YouTube:
|
||||||
|
|
||||||
|
[](https://youtu.be/cWTsGUJAUaU "Wiretrustee - secure private network in less than 5 minutes")
|
||||||
|
|
||||||
|
This guide describes how to create secure VPN and connect 2 machines peer-to-peer.
|
||||||
|
|
||||||
|
One machine is a Raspberry Pi Compute Module 4 hosted at home (Peer A), and the other one is a regular Ubuntu server running in the Data Center (Peer B).
|
||||||
|
Both machines are running Linux (Raspbian and Ubuntu respectively), but you could also use Mac or Windows operating systems.
|
||||||
|
|
||||||
|
1. Sign-up at [https://app.wiretrustee.com/](https://app.wiretrustee.com/peers)
|
||||||
|
|
||||||
|
You can use your email and password to sign-up or any available social login option (e.g., GitHub account)
|
||||||
|
|
||||||
|
<img src="media/auth.png" alt="auth" width="350"/>
|
||||||
|
|
||||||
|
2. After a successful login you will be redirected to the ```Peers``` screen which is empty because you don't have any peers yet.
|
||||||
|
|
||||||
|
Click ```Add peer``` to add a new machine.
|
||||||
|
|
||||||
|
<img src="media/empty-peers.png" alt="empty-peers" width="700"/>
|
||||||
|
|
||||||
|
3. Choose a setup key which will be used to associate your new machine with your account (in our case it is ```Default key```).
|
||||||
|
|
||||||
|
Choose your machine operating system (in our case it is ```Linux```) and proceed with the installation steps on the machine.
|
||||||
|
|
||||||
|
<img src="media/add-peer.png" alt="add-peer" width="700"/>
|
||||||
|
|
||||||
|
4. Repeat #3 for the 2nd machine.
|
||||||
|
5. Return to ```Peers``` and you should notice 2 new machines with status ```Connected```
|
||||||
|
|
||||||
|
<img src="media/peers.png" alt="peers" width="700"/>
|
||||||
|
|
||||||
|
6. To test the connection you could try pinging devices:
|
||||||
|
|
||||||
|
On Peer A:
|
||||||
|
```ping 100.64.0.2```
|
||||||
|
|
||||||
|
On Peer B:
|
||||||
|
```ping 100.64.0.1```
|
||||||
|
7. Done! You now have a secure peer-to-peer VPN configured.
|
||||||
96
docs/self-hosting.md
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
### Self-hosting
|
||||||
|
Wiretrustee is an open-source platform that can be self-hosted on your servers.
|
||||||
|
|
||||||
|
It relies on components developed by Wiretrustee Authors [Management Service](https://github.com/wiretrustee/wiretrustee/tree/main/management), [Management UI Dashboard](https://github.com/wiretrustee/wiretrustee-dashboard), [Signal Service](https://github.com/wiretrustee/wiretrustee/tree/main/signal),
|
||||||
|
a 3rd party open-source STUN/TURN service [Coturn](https://github.com/coturn/coturn) and a 3rd party service [Auth0](https://auth0.com/).
|
||||||
|
|
||||||
|
All the components can be self-hosted except for the Auth0 service.
|
||||||
|
We chose Auth0 to "outsource" the user management part of the platform because we believe that implementing a proper user auth requires significant amount of time to make it right.
|
||||||
|
We focused on connectivity instead.
|
||||||
|
|
||||||
|
If you would like to learn more about the architecture please refer to the [Wiretrustee Architecture section](architecture.md).
|
||||||
|
|
||||||
|
### Step-by-step video guide on YouTube:
|
||||||
|
|
||||||
|
[](https://youtu.be/Ofpgx5WhT0k "Wiretrustee Self-Hosting Guide")
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- Virtual machine offered by any cloud provider (e.g., AWS, DigitalOcean, Hetzner, Google Cloud, Azure ...).
|
||||||
|
- Any Linux OS.
|
||||||
|
- Docker Compose installed (see [Install Docker Compose](https://docs.docker.com/compose/install/)).
|
||||||
|
- Domain name pointing to the public IP address of your server.
|
||||||
|
- Open ports ```443, 33071, 33073, 10000, 3478``` (Dashboard, Management HTTP API, Management gRpc API, Signal gRpc, Coturn STUN/TURN respectively) on your server.
|
||||||
|
- Maybe a cup of coffee or tea :)
|
||||||
|
|
||||||
|
### Step-by-step guide
|
||||||
|
|
||||||
|
For this tutorial we will be using domain ```test.wiretrustee.com``` which points to our Ubuntu 20.04 machine hosted at Hetzner.
|
||||||
|
|
||||||
|
1. Create Auth0 account at [auth0.com](https://auth0.com/).
|
||||||
|
2. Login to your server, clone Wiretrustee repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/wiretrustee/wiretrustee.git wiretrustee/
|
||||||
|
```
|
||||||
|
|
||||||
|
and switch to the ```wiretrustee/infrastructure_files/``` folder that contains docker compose file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd wiretrustee/infrastructure_files/
|
||||||
|
```
|
||||||
|
3. Prepare configuration files.
|
||||||
|
|
||||||
|
To simplify the setup we have prepared a script to substitute required properties in the [docker-compose.yml.tmpl](../infrastructure_files/docker-compose.yml.tmpl) and [management.json.tmpl](../infrastructure_files/management.json.tmpl) files.
|
||||||
|
|
||||||
|
The [setup.env](../infrastructure_files/setup.env) file contains the following properties that have to be filled:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# e.g. app.mydomain.com
|
||||||
|
WIRETRUSTEE_DOMAIN=""
|
||||||
|
# e.g. dev-24vkclam.us.auth0.com
|
||||||
|
WIRETRUSTEE_AUTH0_DOMAIN=""
|
||||||
|
# e.g. 61u3JMXRO0oOevc7gCkZLCwePQvT4lL0
|
||||||
|
WIRETRUSTEE_AUTH0_CLIENT_ID=""
|
||||||
|
# e.g. https://app.mydomain.com/
|
||||||
|
WIRETRUSTEE_AUTH0_AUDIENCE=""
|
||||||
|
# e.g. hello@mydomain.com
|
||||||
|
WIRETRUSTEE_LETSENCRYPT_EMAIL=""
|
||||||
|
```
|
||||||
|
|
||||||
|
Please follow the steps to get the values.
|
||||||
|
|
||||||
|
4. Configure ```WIRETRUSTEE_AUTH0_DOMAIN``` ```WIRETRUSTEE_AUTH0_CLIENT_ID``` ```WIRETRUSTEE_AUTH0_AUDIENCE``` properties.
|
||||||
|
|
||||||
|
* To obtain these, please use [Auth0 React SDK Guide](https://auth0.com/docs/quickstart/spa/react/01-login#configure-auth0) up until "Install the Auth0 React SDK".
|
||||||
|
|
||||||
|
:grey_exclamation: Use ```https://YOUR DOMAIN``` as ````Allowed Callback URLs````, ```Allowed Logout URLs```, ```Allowed Web Origins``` and ```Allowed Origins (CORS)```
|
||||||
|
* set the variables in the ```setup.env```
|
||||||
|
5. Configure ```WIRETRUSTEE_AUTH0_AUDIENCE``` property.
|
||||||
|
|
||||||
|
* Check [Auth0 Golang API Guide](https://auth0.com/docs/quickstart/backend/golang) to obtain AuthAudience.
|
||||||
|
* set the property in the ```setup.env``` file.
|
||||||
|
6. Configure ```WIRETRUSTEE_LETSENCRYPT_EMAIL``` property.
|
||||||
|
|
||||||
|
This can be any email address. [Let's Encrypt](https://letsencrypt.org/) will create an account while generating a new certificate.
|
||||||
|
|
||||||
|
7. Make sure all the properties set in the ```setup.env``` file and run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./configure.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This will export all the properties as environment variables and generate ```docker-compose.yml``` and ```management.json``` files substituting required variables.
|
||||||
|
|
||||||
|
8. Run docker compose:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
9. Optionally check the logs by running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose logs signal
|
||||||
|
docker-compose logs management
|
||||||
|
docker-compose logs coturn
|
||||||
|
docker-compose logs dashboard
|
||||||
4
go.mod
@@ -8,8 +8,7 @@ require (
|
|||||||
github.com/golang/protobuf v1.5.2
|
github.com/golang/protobuf v1.5.2
|
||||||
github.com/google/uuid v1.2.0
|
github.com/google/uuid v1.2.0
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/kardianos/service v1.2.0
|
github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7
|
||||||
github.com/matishsiao/goInfo v0.0.0-20200404012835-b5f882ee2288
|
|
||||||
github.com/onsi/ginkgo v1.16.4
|
github.com/onsi/ginkgo v1.16.4
|
||||||
github.com/onsi/gomega v1.13.0
|
github.com/onsi/gomega v1.13.0
|
||||||
github.com/pion/ice/v2 v2.1.7
|
github.com/pion/ice/v2 v2.1.7
|
||||||
@@ -24,4 +23,5 @@ require (
|
|||||||
golang.zx2c4.com/wireguard/windows v0.4.5
|
golang.zx2c4.com/wireguard/windows v0.4.5
|
||||||
google.golang.org/grpc v1.32.0
|
google.golang.org/grpc v1.32.0
|
||||||
google.golang.org/protobuf v1.26.0
|
google.golang.org/protobuf v1.26.0
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||||
)
|
)
|
||||||
|
|||||||
9
go.sum
@@ -11,6 +11,7 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl
|
|||||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
@@ -145,8 +146,8 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
|
|||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/kardianos/service v1.2.0 h1:bGuZ/epo3vrt8IPC7mnKQolqFeYJb7Cs8Rk4PSOBB/g=
|
github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7 h1:oohm9Rk9JAxxmp2NLZa7Kebgz9h4+AJDcc64txg3dQ0=
|
||||||
github.com/kardianos/service v1.2.0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
@@ -160,8 +161,6 @@ github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdA
|
|||||||
github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ=
|
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/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/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/matishsiao/goInfo v0.0.0-20200404012835-b5f882ee2288 h1:cdM7et8/VlNnSBpq3KbyQWsYLCY0WsB7tvV8Fr0DUNE=
|
|
||||||
github.com/matishsiao/goInfo v0.0.0-20200404012835-b5f882ee2288/go.mod h1:yLZrFIhv+Z20hxHvcZpEyKVQp9HMsOJkXAxx7yDqtvg=
|
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
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/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
@@ -501,6 +500,8 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
|
|||||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||||
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
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 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
|||||||
@@ -13,10 +13,13 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
defaultMTU = 1280
|
defaultMTU = 1280
|
||||||
WgPort = 51820
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var tunIface tun.Device
|
var (
|
||||||
|
tunIface tun.Device
|
||||||
|
// todo check after move the WgPort constant to the client
|
||||||
|
WgPort = 51820
|
||||||
|
)
|
||||||
|
|
||||||
// CreateWithUserspace Creates a new Wireguard interface, using wireguard-go userspace implementation
|
// CreateWithUserspace Creates a new Wireguard interface, using wireguard-go userspace implementation
|
||||||
func CreateWithUserspace(iface string, address string) error {
|
func CreateWithUserspace(iface string, address string) error {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package iface
|
|||||||
import (
|
import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -40,5 +41,23 @@ func addRoute(iface string, ipNet *net.IPNet) error {
|
|||||||
|
|
||||||
// Closes the tunnel interface
|
// Closes the tunnel interface
|
||||||
func Close() error {
|
func Close() error {
|
||||||
return CloseWithUserspace()
|
name, err := tunIface.Name()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sockPath := "/var/run/wireguard/" + name + ".sock"
|
||||||
|
|
||||||
|
err = CloseWithUserspace()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(sockPath); err == nil {
|
||||||
|
err = os.Remove(sockPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func CreateWithKernel(iface string, address string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if interface exists
|
// check if interface exists
|
||||||
l, err := netlink.LinkByName(WgInterfaceDefault)
|
l, err := netlink.LinkByName(iface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case netlink.LinkNotFoundError:
|
case netlink.LinkNotFoundError:
|
||||||
@@ -148,6 +148,7 @@ func Close() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, wgDev := range devList {
|
for _, wgDev := range devList {
|
||||||
|
// todo check after move the WgPort constant to the client
|
||||||
if wgDev.ListenPort == WgPort {
|
if wgDev.ListenPort == WgPort {
|
||||||
iface = wgDev.Name
|
iface = wgDev.Name
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -12,33 +12,61 @@ import (
|
|||||||
|
|
||||||
// keep darwin compability
|
// keep darwin compability
|
||||||
const (
|
const (
|
||||||
ifaceName = "utun999"
|
|
||||||
key = "0PMI6OkB5JmB+Jj/iWWHekuQRx+bipZirWCWKFXexHc="
|
key = "0PMI6OkB5JmB+Jj/iWWHekuQRx+bipZirWCWKFXexHc="
|
||||||
peerPubKey = "Ok0mC0qlJyXEPKh2UFIpsI2jG0L7LRpC3sLAusSJ5CQ="
|
peerPubKey = "Ok0mC0qlJyXEPKh2UFIpsI2jG0L7LRpC3sLAusSJ5CQ="
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
func Test_CreateInterface(t *testing.T) {
|
func Test_CreateInterface(t *testing.T) {
|
||||||
level, _ := log.ParseLevel("Debug")
|
ifaceName := "utun999"
|
||||||
log.SetLevel(level)
|
|
||||||
wgIP := "10.99.99.1/24"
|
wgIP := "10.99.99.1/24"
|
||||||
err := Create(ifaceName, wgIP)
|
err := Create(ifaceName, wgIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
err = Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
wg, err := wgctrl.New()
|
wg, err := wgctrl.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer wg.Close()
|
defer func() {
|
||||||
|
err = wg.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
_, err = wg.Device(ifaceName)
|
d, err := wg.Device(ifaceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
// todo move the WgPort constant to the client
|
||||||
|
WgPort = d.ListenPort
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_ConfigureInterface(t *testing.T) {
|
func Test_ConfigureInterface(t *testing.T) {
|
||||||
err := Configure(ifaceName, key)
|
ifaceName := "utun1000"
|
||||||
|
wgIP := "10.99.99.10/24"
|
||||||
|
err := Create(ifaceName, wgIP)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err = Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = Configure(ifaceName, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -47,7 +75,12 @@ func Test_ConfigureInterface(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer wg.Close()
|
defer func() {
|
||||||
|
err = wg.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
wgDevice, err := wg.Device(ifaceName)
|
wgDevice, err := wg.Device(ifaceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -59,14 +92,30 @@ func Test_ConfigureInterface(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_UpdatePeer(t *testing.T) {
|
func Test_UpdatePeer(t *testing.T) {
|
||||||
keepAlive := 15 * time.Second
|
ifaceName := "utun1001"
|
||||||
allowedIP := "10.99.99.2/32"
|
wgIP := "10.99.99.20/24"
|
||||||
endpoint := "127.0.0.1:9900"
|
err := Create(ifaceName, wgIP)
|
||||||
err := UpdatePeer(ifaceName, peerPubKey, allowedIP, keepAlive, endpoint)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
peer, err := getPeer()
|
defer func() {
|
||||||
|
err = Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
err = Configure(ifaceName, key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
keepAlive := 15 * time.Second
|
||||||
|
allowedIP := "10.99.99.2/32"
|
||||||
|
endpoint := "127.0.0.1:9900"
|
||||||
|
err = UpdatePeer(ifaceName, peerPubKey, allowedIP, keepAlive, endpoint)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
peer, err := getPeer(ifaceName, t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -95,13 +144,37 @@ func Test_UpdatePeer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_UpdatePeerEndpoint(t *testing.T) {
|
func Test_UpdatePeerEndpoint(t *testing.T) {
|
||||||
newEndpoint := "127.0.0.1:9999"
|
ifaceName := "utun1002"
|
||||||
err := UpdatePeerEndpoint(ifaceName, peerPubKey, newEndpoint)
|
wgIP := "10.99.99.30/24"
|
||||||
|
err := Create(ifaceName, wgIP)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err = Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
err = Configure(ifaceName, key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
keepAlive := 15 * time.Second
|
||||||
|
allowedIP := "10.99.99.2/32"
|
||||||
|
endpoint := "127.0.0.1:9900"
|
||||||
|
err = UpdatePeer(ifaceName, peerPubKey, allowedIP, keepAlive, endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
peer, err := getPeer()
|
newEndpoint := "127.0.0.1:9999"
|
||||||
|
err = UpdatePeerEndpoint(ifaceName, peerPubKey, newEndpoint)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
peer, err := getPeer(ifaceName, t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -112,28 +185,79 @@ func Test_UpdatePeerEndpoint(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_RemovePeer(t *testing.T) {
|
func Test_RemovePeer(t *testing.T) {
|
||||||
err := RemovePeer(ifaceName, peerPubKey)
|
ifaceName := "utun1003"
|
||||||
|
wgIP := "10.99.99.40/24"
|
||||||
|
err := Create(ifaceName, wgIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = getPeer()
|
defer func() {
|
||||||
|
err = Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
err = Configure(ifaceName, key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
keepAlive := 15 * time.Second
|
||||||
|
allowedIP := "10.99.99.2/32"
|
||||||
|
endpoint := "127.0.0.1:9900"
|
||||||
|
err = UpdatePeer(ifaceName, peerPubKey, allowedIP, keepAlive, endpoint)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = RemovePeer(ifaceName, peerPubKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = getPeer(ifaceName, t)
|
||||||
if err.Error() != "peer not found" {
|
if err.Error() != "peer not found" {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func Test_Close(t *testing.T) {
|
func Test_Close(t *testing.T) {
|
||||||
err := Close()
|
ifaceName := "utun1004"
|
||||||
|
wgIP := "10.99.99.50/24"
|
||||||
|
err := Create(ifaceName, wgIP)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
wg, err := wgctrl.New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err = wg.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
d, err := wg.Device(ifaceName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// todo move the WgPort constant to the client
|
||||||
|
WgPort = d.ListenPort
|
||||||
|
err = Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func getPeer() (wgtypes.Peer, error) {
|
func getPeer(ifaceName string, t *testing.T) (wgtypes.Peer, error) {
|
||||||
emptyPeer := wgtypes.Peer{}
|
emptyPeer := wgtypes.Peer{}
|
||||||
wg, err := wgctrl.New()
|
wg, err := wgctrl.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return emptyPeer, err
|
return emptyPeer, err
|
||||||
}
|
}
|
||||||
defer wg.Close()
|
defer func() {
|
||||||
|
err = wg.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
wgDevice, err := wg.Device(ifaceName)
|
wgDevice, err := wg.Device(ifaceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
{
|
|
||||||
"Stuns": [
|
|
||||||
{
|
|
||||||
"Proto": "udp",
|
|
||||||
"URI": "stun:stun.wiretrustee.com:3468",
|
|
||||||
"Username": "",
|
|
||||||
"Password": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Turns": [
|
|
||||||
{
|
|
||||||
"Proto": "udp",
|
|
||||||
"URI": "turn:stun.wiretrustee.com:3468",
|
|
||||||
"Username": "some_user",
|
|
||||||
"Password": "c29tZV9wYXNzd29yZA=="
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Signal": {
|
|
||||||
"Proto": "http",
|
|
||||||
"URI": "signal.wiretrustee.com:10000",
|
|
||||||
"Username": "",
|
|
||||||
"Password": null
|
|
||||||
},
|
|
||||||
"Datadir": "",
|
|
||||||
"HttpConfig": {
|
|
||||||
"LetsEncryptDomain": "",
|
|
||||||
"Address": "0.0.0.0:3000",
|
|
||||||
"AuthIssuer": "<PASTE YOUR AUTH0 ISSUER HERE>",
|
|
||||||
"AuthAudience": "<PASTE YOUR AUTH0 AUDIENCE HERE>",
|
|
||||||
"AuthKeysLocation": "<PASTE YOUR JWT KEY SET location>",
|
|
||||||
"UIFilesLocation": "/var/lib/wiretrustee/ui/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
7
infrastructure_files/configure.sh
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
unset $(grep -v '^#' ./setup.env | sed -E 's/(.*)=.*/\1/' | xargs)
|
||||||
|
export $(grep -v '^#' ./setup.env | xargs)
|
||||||
|
|
||||||
|
envsubst < docker-compose.yml.tmpl > docker-compose.yml
|
||||||
|
envsubst < management.json.tmpl > management.json
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
version: "3"
|
|
||||||
services:
|
|
||||||
# Signal
|
|
||||||
signal:
|
|
||||||
image: wiretrustee/signal:latest
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
- wiretrustee-mgmt:/var/lib/wiretrustee
|
|
||||||
ports:
|
|
||||||
- 10000:10000
|
|
||||||
# # port and command for Let's Encrypt validation
|
|
||||||
# - 443:443
|
|
||||||
# command: ["--letsencrypt-domain", "<YOUR-DOMAIN>"]
|
|
||||||
# Management
|
|
||||||
management:
|
|
||||||
image: wiretrustee/management:latest
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
- wiretrustee-mgmt:/var/lib/wiretrustee
|
|
||||||
- ./config.json:/etc/wiretrustee/config.json
|
|
||||||
ports:
|
|
||||||
- 33073:33073
|
|
||||||
# # port and command for Let's Encrypt validation
|
|
||||||
# - 443:443
|
|
||||||
# command: ["--letsencrypt-domain", "<YOUR-DOMAIN>"]
|
|
||||||
# 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
|
|
||||||
volumes:
|
|
||||||
wiretrustee-mgmt:
|
|
||||||
wiretrustee-signal:
|
|
||||||
61
infrastructure_files/docker-compose.yml.tmpl
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
#UI dashboard
|
||||||
|
dashboard:
|
||||||
|
image: wiretrustee/dashboard:main
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
- 443:443
|
||||||
|
environment:
|
||||||
|
- AUTH0_DOMAIN=$WIRETRUSTEE_AUTH0_DOMAIN
|
||||||
|
- AUTH0_CLIENT_ID=$WIRETRUSTEE_AUTH0_CLIENT_ID
|
||||||
|
- AUTH0_AUDIENCE=$WIRETRUSTEE_AUTH0_AUDIENCE
|
||||||
|
- WIRETRUSTEE_MGMT_API_ENDPOINT=https://$WIRETRUSTEE_DOMAIN:33071
|
||||||
|
- NGINX_SSL_PORT=443
|
||||||
|
- LETSENCRYPT_DOMAIN=$WIRETRUSTEE_DOMAIN
|
||||||
|
- LETSENCRYPT_EMAIL=$WIRETRUSTEE_LETSENCRYPT_EMAIL
|
||||||
|
volumes:
|
||||||
|
- /var/lib/wiretrustee/dashboard/letsencrypt:/etc/letsencrypt/
|
||||||
|
# Signal
|
||||||
|
signal:
|
||||||
|
image: wiretrustee/signal:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- wiretrustee-signal:/var/lib/wiretrustee
|
||||||
|
# - /var/log/wiretrustee/signal.log:/var/log/wiretrustee/signal.log
|
||||||
|
ports:
|
||||||
|
- 10000:10000
|
||||||
|
# # port and command for Let's Encrypt validation
|
||||||
|
# - 443:443
|
||||||
|
# command: ["--letsencrypt-domain", "$WIRETRUSTEE_DOMAIN", "--log-file", "console"]
|
||||||
|
# Management
|
||||||
|
management:
|
||||||
|
image: wiretrustee/management:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- dashboard
|
||||||
|
volumes:
|
||||||
|
- wiretrustee-mgmt:/var/lib/wiretrustee
|
||||||
|
- /var/lib/wiretrustee/dashboard/letsencrypt:/etc/letsencrypt:ro
|
||||||
|
- ./management.json:/etc/wiretrustee/management.json
|
||||||
|
# - /var/log/wiretrustee/management.log:/var/log/wiretrustee/management.log
|
||||||
|
ports:
|
||||||
|
- 33073:33073 #gRPC port
|
||||||
|
- 33071:33071 #HTTP port
|
||||||
|
# # port and command for Let's Encrypt validation
|
||||||
|
# - 443:443
|
||||||
|
# command: ["--letsencrypt-domain", "$WIRETRUSTEE_DOMAIN", "--log-file", "console"]
|
||||||
|
# Coturn
|
||||||
|
coturn:
|
||||||
|
image: coturn/coturn
|
||||||
|
restart: unless-stopped
|
||||||
|
domainname: <YOUR DOMAIN>
|
||||||
|
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
|
||||||
|
volumes:
|
||||||
|
wiretrustee-mgmt:
|
||||||
|
wiretrustee-signal:
|
||||||
39
infrastructure_files/management.json.tmpl
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"Stuns": [
|
||||||
|
{
|
||||||
|
"Proto": "udp",
|
||||||
|
"URI": "stun:$WIRETRUSTEE_DOMAIN:3478",
|
||||||
|
"Username": "",
|
||||||
|
"Password": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TURNConfig": {
|
||||||
|
"Turns": [
|
||||||
|
{
|
||||||
|
"Proto": "udp",
|
||||||
|
"URI": "turn:$WIRETRUSTEE_DOMAIN:3478",
|
||||||
|
"Username": "",
|
||||||
|
"Password": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"CredentialsTTL": "12h",
|
||||||
|
"Secret": "secret",
|
||||||
|
"TimeBasedCredentials": false
|
||||||
|
},
|
||||||
|
"Signal": {
|
||||||
|
"Proto": "http",
|
||||||
|
"URI": "$WIRETRUSTEE_DOMAIN:10000",
|
||||||
|
"Username": "",
|
||||||
|
"Password": null
|
||||||
|
},
|
||||||
|
"Datadir": "",
|
||||||
|
"HttpConfig": {
|
||||||
|
"LetsEncryptDomain": "",
|
||||||
|
"CertFile":"/etc/letsencrypt/live/$WIRETRUSTEE_DOMAIN/fullchain.pem",
|
||||||
|
"CertKey":"/etc/letsencrypt/live/$WIRETRUSTEE_DOMAIN/privkey.pem",
|
||||||
|
"Address": "0.0.0.0:33071",
|
||||||
|
"AuthIssuer": "https://$WIRETRUSTEE_AUTH0_DOMAIN/",
|
||||||
|
"AuthAudience": "$WIRETRUSTEE_AUTH0_AUDIENCE",
|
||||||
|
"AuthKeysLocation": "https://$WIRETRUSTEE_AUTH0_DOMAIN/.well-known/jwks.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
10
infrastructure_files/setup.env
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# e.g. app.mydomain.com
|
||||||
|
WIRETRUSTEE_DOMAIN=""
|
||||||
|
# e.g. dev-24vkclam.us.auth0.com
|
||||||
|
WIRETRUSTEE_AUTH0_DOMAIN=""
|
||||||
|
# e.g. 61u3JMXRO0oOevc7gCkZLCwePQvT4lL0
|
||||||
|
WIRETRUSTEE_AUTH0_CLIENT_ID=""
|
||||||
|
# e.g. https://app.mydomain.com/
|
||||||
|
WIRETRUSTEE_AUTH0_AUDIENCE=""
|
||||||
|
# e.g. hello@mydomain.com
|
||||||
|
WIRETRUSTEE_LETSENCRYPT_EMAIL=""
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
FROM gcr.io/distroless/base
|
FROM gcr.io/distroless/base
|
||||||
ENTRYPOINT [ "/go/bin/wiretrustee-mgmt","management"]
|
ENTRYPOINT [ "/go/bin/wiretrustee-mgmt","management"]
|
||||||
|
CMD ["--log-file", "console"]
|
||||||
COPY wiretrustee-mgmt /go/bin/wiretrustee-mgmt
|
COPY wiretrustee-mgmt /go/bin/wiretrustee-mgmt
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
FROM gcr.io/distroless/base:debug
|
FROM gcr.io/distroless/base:debug
|
||||||
ENTRYPOINT [ "/go/bin/wiretrustee-mgmt","management","--log-level","debug"]
|
ENTRYPOINT [ "/go/bin/wiretrustee-mgmt","management","--log-level","debug"]
|
||||||
|
CMD ["--log-file", "console"]
|
||||||
COPY wiretrustee-mgmt /go/bin/wiretrustee-mgmt
|
COPY wiretrustee-mgmt /go/bin/wiretrustee-mgmt
|
||||||
@@ -14,10 +14,12 @@ Flags:
|
|||||||
-h, --help help for management
|
-h, --help help for management
|
||||||
--letsencrypt-domain string a domain to issue Let's Encrypt certificate for. Enables TLS using Let's Encrypt. Will fetch and renew certificate, and run the server with TLS
|
--letsencrypt-domain string a domain to issue Let's Encrypt certificate for. Enables TLS using Let's Encrypt. Will fetch and renew certificate, and run the server with TLS
|
||||||
--port int server port to listen on (default 33073)
|
--port int server port to listen on (default 33073)
|
||||||
|
--cert-file string Location of your SSL certificate. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect
|
||||||
|
--cert-key string Location of your SSL certificate private key. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect
|
||||||
Global Flags:
|
Global Flags:
|
||||||
--config string Wiretrustee config file location to write new config to (default "/etc/wiretrustee/config.json")
|
--config string Wiretrustee config file location to write new config to (default "/etc/wiretrustee/config.json")
|
||||||
--log-level string (default "info")
|
--log-level string (default "info")
|
||||||
|
--log-file string sets Wiretrustee log path. If console is specified the the log will be output to stdout (default "/var/log/wiretrustee/management.log")
|
||||||
```
|
```
|
||||||
## Run Management service (Docker)
|
## Run Management service (Docker)
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"github.com/cenkalti/backoff/v4"
|
"github.com/cenkalti/backoff/v4"
|
||||||
"github.com/matishsiao/goInfo"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/wiretrustee/wiretrustee/client/system"
|
||||||
"github.com/wiretrustee/wiretrustee/encryption"
|
"github.com/wiretrustee/wiretrustee/encryption"
|
||||||
"github.com/wiretrustee/wiretrustee/management/proto"
|
"github.com/wiretrustee/wiretrustee/management/proto"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
@@ -45,7 +45,7 @@ func NewClient(ctx context.Context, addr string, ourPrivateKey wgtypes.Key, tlsE
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed creating connection to Management Srvice %v", err)
|
log.Errorf("failed creating connection to Management Service %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,54 +64,61 @@ func (c *Client) Close() error {
|
|||||||
return c.conn.Close()
|
return c.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//defaultBackoff is a basic backoff mechanism for general issues
|
||||||
|
func defaultBackoff(ctx context.Context) backoff.BackOff {
|
||||||
|
return backoff.WithContext(&backoff.ExponentialBackOff{
|
||||||
|
InitialInterval: 800 * time.Millisecond,
|
||||||
|
RandomizationFactor: backoff.DefaultRandomizationFactor,
|
||||||
|
Multiplier: backoff.DefaultMultiplier,
|
||||||
|
MaxInterval: 30 * time.Second,
|
||||||
|
MaxElapsedTime: 24 * 3 * time.Hour, //stop after 3 days trying
|
||||||
|
Stop: backoff.Stop,
|
||||||
|
Clock: backoff.SystemClock,
|
||||||
|
}, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// Sync wraps the real client's Sync endpoint call and takes care of retries and encryption/decryption of messages
|
// Sync wraps the real client's Sync endpoint call and takes care of retries and encryption/decryption of messages
|
||||||
// Non blocking request (executed in go routine). The result will be sent via msgHandler callback function
|
// Blocking request. The result will be sent via msgHandler callback function
|
||||||
func (c *Client) Sync(msgHandler func(msg *proto.SyncResponse) error) {
|
func (c *Client) Sync(msgHandler func(msg *proto.SyncResponse) error) error {
|
||||||
|
|
||||||
go func() {
|
var backOff = defaultBackoff(c.ctx)
|
||||||
|
|
||||||
var backOff = &backoff.ExponentialBackOff{
|
operation := func() error {
|
||||||
InitialInterval: 800 * time.Millisecond,
|
|
||||||
RandomizationFactor: backoff.DefaultRandomizationFactor,
|
|
||||||
Multiplier: backoff.DefaultMultiplier,
|
|
||||||
MaxInterval: 3 * time.Second,
|
|
||||||
MaxElapsedTime: time.Duration(0), //never stop retrying
|
|
||||||
Stop: backoff.Stop,
|
|
||||||
Clock: backoff.SystemClock,
|
|
||||||
}
|
|
||||||
|
|
||||||
operation := func() error {
|
// todo we already have it since we did the Login, maybe cache it locally?
|
||||||
|
serverPubKey, err := c.GetServerPublicKey()
|
||||||
// todo we already have it since we did the Login, maybe cache it locally?
|
|
||||||
serverPubKey, err := c.GetServerPublicKey()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed getting Management Service public key: %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
stream, err := c.connectToStream(*serverPubKey)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed to open Management Service stream: %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("connected to the Management Service Stream")
|
|
||||||
|
|
||||||
// blocking until error
|
|
||||||
err = c.receiveEvents(stream, *serverPubKey, msgHandler)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
backOff.Reset()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := backoff.Retry(operation, backOff)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed communicating with Management Service %s ", err)
|
log.Errorf("failed getting Management Service public key: %s", err)
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
stream, err := c.connectToStream(*serverPubKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to open Management Service stream: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("connected to the Management Service Stream")
|
||||||
|
|
||||||
|
// blocking until error
|
||||||
|
err = c.receiveEvents(stream, *serverPubKey, msgHandler)
|
||||||
|
if err != nil {
|
||||||
|
/*if errStatus, ok := status.FromError(err); ok && errStatus.Code() == codes.PermissionDenied {
|
||||||
|
//todo handle differently??
|
||||||
|
}*/
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
backOff.Reset()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := backoff.Retry(operation, backOff)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("exiting Management Service connection retry loop due to unrecoverable error: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) connectToStream(serverPubKey wgtypes.Key) (proto.ManagementService_SyncClient, error) {
|
func (c *Client) connectToStream(serverPubKey wgtypes.Key) (proto.ManagementService_SyncClient, error) {
|
||||||
@@ -138,7 +145,7 @@ func (c *Client) receiveEvents(stream proto.ManagementService_SyncClient, server
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("disconnected from Management Service syn stream: %v", err)
|
log.Warnf("disconnected from Management Service sync stream: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +159,7 @@ func (c *Client) receiveEvents(stream proto.ManagementService_SyncClient, server
|
|||||||
|
|
||||||
err = msgHandler(decryptedResp)
|
err = msgHandler(decryptedResp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed handling an update message received from Management Service %v", err.Error())
|
log.Errorf("failed handling an update message received from Management Service: %v", err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,12 +213,12 @@ func (c *Client) login(serverKey wgtypes.Key, req *proto.LoginRequest) (*proto.L
|
|||||||
// Takes care of encrypting and decrypting messages.
|
// Takes care of encrypting and decrypting messages.
|
||||||
// This method will also collect system info and send it with the request (e.g. hostname, os, etc)
|
// This method will also collect system info and send it with the request (e.g. hostname, os, etc)
|
||||||
func (c *Client) Register(serverKey wgtypes.Key, setupKey string) (*proto.LoginResponse, error) {
|
func (c *Client) Register(serverKey wgtypes.Key, setupKey string) (*proto.LoginResponse, error) {
|
||||||
gi := goInfo.GetInfo()
|
gi := system.GetInfo()
|
||||||
meta := &proto.PeerSystemMeta{
|
meta := &proto.PeerSystemMeta{
|
||||||
Hostname: gi.Hostname,
|
Hostname: gi.Hostname,
|
||||||
GoOS: gi.GoOS,
|
GoOS: gi.GoOS,
|
||||||
OS: gi.OS,
|
OS: gi.OS,
|
||||||
Core: gi.Core,
|
Core: gi.OSVersion,
|
||||||
Platform: gi.Platform,
|
Platform: gi.Platform,
|
||||||
Kernel: gi.Kernel,
|
Kernel: gi.Kernel,
|
||||||
WiretrusteeVersion: "",
|
WiretrusteeVersion: "",
|
||||||
|
|||||||
@@ -60,8 +60,10 @@ func startManagement(config *mgmt.Config, t *testing.T) (*grpc.Server, net.Liste
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
accountManager := mgmt.NewManager(store)
|
peersUpdateManager := mgmt.NewPeersUpdateManager()
|
||||||
mgmtServer, err := mgmt.NewServer(config, accountManager)
|
accountManager := mgmt.NewManager(store, peersUpdateManager)
|
||||||
|
turnManager := mgmt.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig)
|
||||||
|
mgmtServer, err := mgmt.NewServer(config, accountManager, peersUpdateManager, turnManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -144,10 +146,15 @@ func TestClient_Sync(t *testing.T) {
|
|||||||
|
|
||||||
ch := make(chan *mgmtProto.SyncResponse, 1)
|
ch := make(chan *mgmtProto.SyncResponse, 1)
|
||||||
|
|
||||||
tested.Sync(func(msg *mgmtProto.SyncResponse) error {
|
go func() {
|
||||||
ch <- msg
|
err = tested.Sync(func(msg *mgmtProto.SyncResponse) error {
|
||||||
return nil
|
ch <- msg
|
||||||
})
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case resp := <-ch:
|
case resp := <-ch:
|
||||||
@@ -160,6 +167,9 @@ func TestClient_Sync(t *testing.T) {
|
|||||||
if len(resp.GetRemotePeers()) != 1 {
|
if len(resp.GetRemotePeers()) != 1 {
|
||||||
t.Errorf("expecting RemotePeers size %d got %d", 1, len(resp.GetRemotePeers()))
|
t.Errorf("expecting RemotePeers size %d got %d", 1, len(resp.GetRemotePeers()))
|
||||||
}
|
}
|
||||||
|
if resp.GetRemotePeersIsEmpty() == true {
|
||||||
|
t.Error("expecting RemotePeers property to be false, got true")
|
||||||
|
}
|
||||||
if resp.GetRemotePeers()[0].GetWgPubKey() != remoteKey.PublicKey().String() {
|
if resp.GetRemotePeers()[0].GetWgPubKey() != remoteKey.PublicKey().String() {
|
||||||
t.Errorf("expecting RemotePeer public key %s got %s", remoteKey.PublicKey().String(), resp.GetRemotePeers()[0].GetWgPubKey())
|
t.Errorf("expecting RemotePeer public key %s got %s", remoteKey.PublicKey().String(), resp.GetRemotePeers()[0].GetWgPubKey())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/wiretrustee/wiretrustee/management/server"
|
"github.com/wiretrustee/wiretrustee/management/server"
|
||||||
@@ -25,6 +26,8 @@ var (
|
|||||||
mgmtDataDir string
|
mgmtDataDir string
|
||||||
mgmtConfig string
|
mgmtConfig string
|
||||||
mgmtLetsencryptDomain string
|
mgmtLetsencryptDomain string
|
||||||
|
certFile string
|
||||||
|
certKey string
|
||||||
|
|
||||||
kaep = keepalive.EnforcementPolicy{
|
kaep = keepalive.EnforcementPolicy{
|
||||||
MinTime: 15 * time.Second,
|
MinTime: 15 * time.Second,
|
||||||
@@ -43,6 +46,10 @@ var (
|
|||||||
Short: "start Wiretrustee Management Server",
|
Short: "start Wiretrustee Management Server",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
err := util.InitLog(logLevel, logFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed initializing log %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
config, err := loadConfig()
|
config, err := loadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -60,25 +67,37 @@ var (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed creating a store: %s: %v", config.Datadir, err)
|
log.Fatalf("failed creating a store: %s: %v", config.Datadir, err)
|
||||||
}
|
}
|
||||||
accountManager := server.NewManager(store)
|
peersUpdateManager := server.NewPeersUpdateManager()
|
||||||
|
accountManager := server.NewManager(store, peersUpdateManager)
|
||||||
|
|
||||||
var opts []grpc.ServerOption
|
var opts []grpc.ServerOption
|
||||||
|
|
||||||
var httpServer *http.Server
|
var httpServer *http.Server
|
||||||
if config.HttpConfig.LetsEncryptDomain != "" {
|
if config.HttpConfig.LetsEncryptDomain != "" {
|
||||||
|
//automatically generate a new certificate with Let's Encrypt
|
||||||
certManager := encryption.CreateCertManager(config.Datadir, config.HttpConfig.LetsEncryptDomain)
|
certManager := encryption.CreateCertManager(config.Datadir, config.HttpConfig.LetsEncryptDomain)
|
||||||
transportCredentials := credentials.NewTLS(certManager.TLSConfig())
|
transportCredentials := credentials.NewTLS(certManager.TLSConfig())
|
||||||
opts = append(opts, grpc.Creds(transportCredentials))
|
opts = append(opts, grpc.Creds(transportCredentials))
|
||||||
|
|
||||||
httpServer = http.NewHttpsServer(config.HttpConfig, certManager, accountManager)
|
httpServer = http.NewHttpsServer(config.HttpConfig, certManager, accountManager)
|
||||||
|
} else if config.HttpConfig.CertFile != "" && config.HttpConfig.CertKey != "" {
|
||||||
|
//use provided certificate
|
||||||
|
tlsConfig, err := loadTLSConfig(config.HttpConfig.CertFile, config.HttpConfig.CertKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("cannot load TLS credentials: ", err)
|
||||||
|
}
|
||||||
|
transportCredentials := credentials.NewTLS(tlsConfig)
|
||||||
|
opts = append(opts, grpc.Creds(transportCredentials))
|
||||||
|
httpServer = http.NewHttpsServerWithTLSConfig(config.HttpConfig, tlsConfig, accountManager)
|
||||||
} else {
|
} else {
|
||||||
|
//start server without SSL
|
||||||
httpServer = http.NewHttpServer(config.HttpConfig, accountManager)
|
httpServer = http.NewHttpServer(config.HttpConfig, accountManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
opts = append(opts, grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))
|
opts = append(opts, grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))
|
||||||
grpcServer := grpc.NewServer(opts...)
|
grpcServer := grpc.NewServer(opts...)
|
||||||
|
turnManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig)
|
||||||
server, err := server.NewServer(config, accountManager)
|
server, err := server.NewServer(config, accountManager, peersUpdateManager, turnManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed creating new server: %v", err)
|
log.Fatalf("failed creating new server: %v", err)
|
||||||
}
|
}
|
||||||
@@ -131,14 +150,37 @@ func loadConfig() (*server.Config, error) {
|
|||||||
config.Datadir = mgmtDataDir
|
config.Datadir = mgmtDataDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if certKey != "" && certFile != "" {
|
||||||
|
config.HttpConfig.CertFile = certFile
|
||||||
|
config.HttpConfig.CertKey = certKey
|
||||||
|
}
|
||||||
|
|
||||||
return config, err
|
return config, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadTLSConfig(certFile string, certKey string) (*tls.Config, error) {
|
||||||
|
// Load server's certificate and private key
|
||||||
|
serverCert, err := tls.LoadX509KeyPair(certFile, certKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the credentials and return it
|
||||||
|
config := &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{serverCert},
|
||||||
|
ClientAuth: tls.NoClientCert,
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
mgmtCmd.Flags().IntVar(&mgmtPort, "port", 33073, "server port to listen on")
|
mgmtCmd.Flags().IntVar(&mgmtPort, "port", 33073, "server port to listen on")
|
||||||
mgmtCmd.Flags().StringVar(&mgmtDataDir, "datadir", "/var/lib/wiretrustee/", "server data directory location")
|
mgmtCmd.Flags().StringVar(&mgmtDataDir, "datadir", "/var/lib/wiretrustee/", "server data directory location")
|
||||||
mgmtCmd.Flags().StringVar(&mgmtConfig, "config", "/etc/wiretrustee/management.json", "Wiretrustee config file location. Config params specified via command line (e.g. datadir) have a precedence over configuration from this file")
|
mgmtCmd.Flags().StringVar(&mgmtConfig, "config", "/etc/wiretrustee/management.json", "Wiretrustee config file location. Config params specified via command line (e.g. datadir) have a precedence over configuration from this file")
|
||||||
mgmtCmd.Flags().StringVar(&mgmtLetsencryptDomain, "letsencrypt-domain", "", "a domain to issue Let's Encrypt certificate for. Enables TLS using Let's Encrypt. Will fetch and renew certificate, and run the server with TLS")
|
mgmtCmd.Flags().StringVar(&mgmtLetsencryptDomain, "letsencrypt-domain", "", "a domain to issue Let's Encrypt certificate for. Enables TLS using Let's Encrypt. Will fetch and renew certificate, and run the server with TLS")
|
||||||
|
mgmtCmd.Flags().StringVar(&certFile, "cert-file", "", "Location of your SSL certificate. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect")
|
||||||
|
mgmtCmd.Flags().StringVar(&certKey, "cert-key", "", "Location of your SSL certificate private key. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect")
|
||||||
|
|
||||||
rootCmd.MarkFlagRequired("config") //nolint
|
rootCmd.MarkFlagRequired("config") //nolint
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -19,6 +17,8 @@ var (
|
|||||||
configPath string
|
configPath string
|
||||||
defaultConfigPath string
|
defaultConfigPath string
|
||||||
logLevel string
|
logLevel string
|
||||||
|
defaultLogFile string
|
||||||
|
logFile string
|
||||||
|
|
||||||
rootCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
Use: "wiretrustee-mgmt",
|
Use: "wiretrustee-mgmt",
|
||||||
@@ -38,12 +38,15 @@ func init() {
|
|||||||
|
|
||||||
stopCh = make(chan int)
|
stopCh = make(chan int)
|
||||||
|
|
||||||
defaultConfigPath = "/etc/wiretrustee/config.json"
|
defaultConfigPath = "/etc/wiretrustee/management.json"
|
||||||
|
defaultLogFile = "/var/log/wiretrustee/management.log"
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
defaultConfigPath = os.Getenv("PROGRAMDATA") + "\\Wiretrustee\\" + "config.json"
|
defaultConfigPath = os.Getenv("PROGRAMDATA") + "\\Wiretrustee\\" + "management.json"
|
||||||
|
defaultLogFile = os.Getenv("PROGRAMDATA") + "\\Wiretrustee\\" + "management.log"
|
||||||
}
|
}
|
||||||
rootCmd.PersistentFlags().StringVar(&configPath, "config", defaultConfigPath, "Wiretrustee config file location to write new config to")
|
rootCmd.PersistentFlags().StringVar(&configPath, "config", defaultConfigPath, "Wiretrustee config file location to write new config to")
|
||||||
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "")
|
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "")
|
||||||
|
rootCmd.PersistentFlags().StringVar(&logFile, "log-file", defaultLogFile, "sets Wiretrustee log path. If console is specified the the log will be output to stdout")
|
||||||
rootCmd.AddCommand(mgmtCmd)
|
rootCmd.AddCommand(mgmtCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,13 +61,3 @@ func SetupCloseHandler() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitLog parses and sets log-level input
|
|
||||||
func InitLog(logLevel string) {
|
|
||||||
level, err := log.ParseLevel(logLevel)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed parsing log-level %s: %s", logLevel, err)
|
|
||||||
os.Exit(ExitSetupFailed)
|
|
||||||
}
|
|
||||||
log.SetLevel(level)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -181,6 +181,8 @@ type SyncResponse struct {
|
|||||||
WiretrusteeConfig *WiretrusteeConfig `protobuf:"bytes,1,opt,name=wiretrusteeConfig,proto3" json:"wiretrusteeConfig,omitempty"`
|
WiretrusteeConfig *WiretrusteeConfig `protobuf:"bytes,1,opt,name=wiretrusteeConfig,proto3" json:"wiretrusteeConfig,omitempty"`
|
||||||
PeerConfig *PeerConfig `protobuf:"bytes,2,opt,name=peerConfig,proto3" json:"peerConfig,omitempty"`
|
PeerConfig *PeerConfig `protobuf:"bytes,2,opt,name=peerConfig,proto3" json:"peerConfig,omitempty"`
|
||||||
RemotePeers []*RemotePeerConfig `protobuf:"bytes,3,rep,name=remotePeers,proto3" json:"remotePeers,omitempty"`
|
RemotePeers []*RemotePeerConfig `protobuf:"bytes,3,rep,name=remotePeers,proto3" json:"remotePeers,omitempty"`
|
||||||
|
// Indicates whether remotePeers array is empty or not to bypass protobuf null and empty array equality.
|
||||||
|
RemotePeersIsEmpty bool `protobuf:"varint,4,opt,name=remotePeersIsEmpty,proto3" json:"remotePeersIsEmpty,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SyncResponse) Reset() {
|
func (x *SyncResponse) Reset() {
|
||||||
@@ -236,6 +238,13 @@ func (x *SyncResponse) GetRemotePeers() []*RemotePeerConfig {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *SyncResponse) GetRemotePeersIsEmpty() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.RemotePeersIsEmpty
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type LoginRequest struct {
|
type LoginRequest struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -860,7 +869,7 @@ var file_management_proto_rawDesc = []byte{
|
|||||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12,
|
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12,
|
||||||
0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62,
|
0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62,
|
||||||
0x6f, 0x64, 0x79, 0x22, 0x0d, 0x0a, 0x0b, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65,
|
0x6f, 0x64, 0x79, 0x22, 0x0d, 0x0a, 0x0b, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x73, 0x74, 0x22, 0xd3, 0x01, 0x0a, 0x0c, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
0x73, 0x74, 0x22, 0x83, 0x02, 0x0a, 0x0c, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||||
0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74,
|
0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74,
|
||||||
0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d,
|
0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d,
|
||||||
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x57, 0x69, 0x72, 0x65,
|
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x57, 0x69, 0x72, 0x65,
|
||||||
@@ -873,7 +882,10 @@ var file_management_proto_rawDesc = []byte{
|
|||||||
0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
|
0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
|
||||||
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74,
|
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74,
|
||||||
0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x72, 0x65, 0x6d,
|
0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x72, 0x65, 0x6d,
|
||||||
0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x22, 0x5a, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x69,
|
0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f,
|
||||||
|
0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x04,
|
||||||
|
0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72,
|
||||||
|
0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x5a, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x69,
|
||||||
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x75,
|
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x75,
|
||||||
0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x74, 0x75,
|
0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x74, 0x75,
|
||||||
0x70, 0x4b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01,
|
0x70, 0x4b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01,
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ message SyncResponse {
|
|||||||
PeerConfig peerConfig = 2;
|
PeerConfig peerConfig = 2;
|
||||||
|
|
||||||
repeated RemotePeerConfig remotePeers = 3;
|
repeated RemotePeerConfig remotePeers = 3;
|
||||||
|
// Indicates whether remotePeers array is empty or not to bypass protobuf null and empty array equality.
|
||||||
|
bool remotePeersIsEmpty = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LoginRequest {
|
message LoginRequest {
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import (
|
|||||||
type AccountManager struct {
|
type AccountManager struct {
|
||||||
Store Store
|
Store Store
|
||||||
// mutex to synchronise account operations (e.g. generating Peer IP address inside the Network)
|
// mutex to synchronise account operations (e.g. generating Peer IP address inside the Network)
|
||||||
mux sync.Mutex
|
mux sync.Mutex
|
||||||
|
peersUpdateManager *PeersUpdateManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account represents a unique account of the system
|
// Account represents a unique account of the system
|
||||||
@@ -25,19 +26,20 @@ type Account struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewManager creates a new AccountManager with a provided Store
|
// NewManager creates a new AccountManager with a provided Store
|
||||||
func NewManager(store Store) *AccountManager {
|
func NewManager(store Store, peersUpdateManager *PeersUpdateManager) *AccountManager {
|
||||||
return &AccountManager{
|
return &AccountManager{
|
||||||
Store: store,
|
Store: store,
|
||||||
mux: sync.Mutex{},
|
mux: sync.Mutex{},
|
||||||
|
peersUpdateManager: peersUpdateManager,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//AddSetupKey generates a new setup key with a given name and type, and adds it to the specified account
|
//AddSetupKey generates a new setup key with a given name and type, and adds it to the specified account
|
||||||
func (manager *AccountManager) AddSetupKey(accountId string, keyName string, keyType SetupKeyType, expiresIn time.Duration) (*SetupKey, error) {
|
func (am *AccountManager) AddSetupKey(accountId string, keyName string, keyType SetupKeyType, expiresIn time.Duration) (*SetupKey, error) {
|
||||||
manager.mux.Lock()
|
am.mux.Lock()
|
||||||
defer manager.mux.Unlock()
|
defer am.mux.Unlock()
|
||||||
|
|
||||||
account, err := manager.Store.GetAccount(accountId)
|
account, err := am.Store.GetAccount(accountId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.NotFound, "account not found")
|
return nil, status.Errorf(codes.NotFound, "account not found")
|
||||||
}
|
}
|
||||||
@@ -45,7 +47,7 @@ func (manager *AccountManager) AddSetupKey(accountId string, keyName string, key
|
|||||||
setupKey := GenerateSetupKey(keyName, keyType, expiresIn)
|
setupKey := GenerateSetupKey(keyName, keyType, expiresIn)
|
||||||
account.SetupKeys[setupKey.Key] = setupKey
|
account.SetupKeys[setupKey.Key] = setupKey
|
||||||
|
|
||||||
err = manager.Store.SaveAccount(account)
|
err = am.Store.SaveAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "failed adding account key")
|
return nil, status.Errorf(codes.Internal, "failed adding account key")
|
||||||
}
|
}
|
||||||
@@ -54,11 +56,11 @@ func (manager *AccountManager) AddSetupKey(accountId string, keyName string, key
|
|||||||
}
|
}
|
||||||
|
|
||||||
//RevokeSetupKey marks SetupKey as revoked - becomes not valid anymore
|
//RevokeSetupKey marks SetupKey as revoked - becomes not valid anymore
|
||||||
func (manager *AccountManager) RevokeSetupKey(accountId string, keyId string) (*SetupKey, error) {
|
func (am *AccountManager) RevokeSetupKey(accountId string, keyId string) (*SetupKey, error) {
|
||||||
manager.mux.Lock()
|
am.mux.Lock()
|
||||||
defer manager.mux.Unlock()
|
defer am.mux.Unlock()
|
||||||
|
|
||||||
account, err := manager.Store.GetAccount(accountId)
|
account, err := am.Store.GetAccount(accountId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.NotFound, "account not found")
|
return nil, status.Errorf(codes.NotFound, "account not found")
|
||||||
}
|
}
|
||||||
@@ -71,7 +73,7 @@ func (manager *AccountManager) RevokeSetupKey(accountId string, keyId string) (*
|
|||||||
keyCopy := setupKey.Copy()
|
keyCopy := setupKey.Copy()
|
||||||
keyCopy.Revoked = true
|
keyCopy.Revoked = true
|
||||||
account.SetupKeys[keyCopy.Key] = keyCopy
|
account.SetupKeys[keyCopy.Key] = keyCopy
|
||||||
err = manager.Store.SaveAccount(account)
|
err = am.Store.SaveAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "failed adding account key")
|
return nil, status.Errorf(codes.Internal, "failed adding account key")
|
||||||
}
|
}
|
||||||
@@ -80,11 +82,11 @@ func (manager *AccountManager) RevokeSetupKey(accountId string, keyId string) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
//RenameSetupKey renames existing setup key of the specified account.
|
//RenameSetupKey renames existing setup key of the specified account.
|
||||||
func (manager *AccountManager) RenameSetupKey(accountId string, keyId string, newName string) (*SetupKey, error) {
|
func (am *AccountManager) RenameSetupKey(accountId string, keyId string, newName string) (*SetupKey, error) {
|
||||||
manager.mux.Lock()
|
am.mux.Lock()
|
||||||
defer manager.mux.Unlock()
|
defer am.mux.Unlock()
|
||||||
|
|
||||||
account, err := manager.Store.GetAccount(accountId)
|
account, err := am.Store.GetAccount(accountId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.NotFound, "account not found")
|
return nil, status.Errorf(codes.NotFound, "account not found")
|
||||||
}
|
}
|
||||||
@@ -97,7 +99,7 @@ func (manager *AccountManager) RenameSetupKey(accountId string, keyId string, ne
|
|||||||
keyCopy := setupKey.Copy()
|
keyCopy := setupKey.Copy()
|
||||||
keyCopy.Name = newName
|
keyCopy.Name = newName
|
||||||
account.SetupKeys[keyCopy.Key] = keyCopy
|
account.SetupKeys[keyCopy.Key] = keyCopy
|
||||||
err = manager.Store.SaveAccount(account)
|
err = am.Store.SaveAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "failed adding account key")
|
return nil, status.Errorf(codes.Internal, "failed adding account key")
|
||||||
}
|
}
|
||||||
@@ -106,11 +108,11 @@ func (manager *AccountManager) RenameSetupKey(accountId string, keyId string, ne
|
|||||||
}
|
}
|
||||||
|
|
||||||
//GetAccount returns an existing account or error (NotFound) if doesn't exist
|
//GetAccount returns an existing account or error (NotFound) if doesn't exist
|
||||||
func (manager *AccountManager) GetAccount(accountId string) (*Account, error) {
|
func (am *AccountManager) GetAccount(accountId string) (*Account, error) {
|
||||||
manager.mux.Lock()
|
am.mux.Lock()
|
||||||
defer manager.mux.Unlock()
|
defer am.mux.Unlock()
|
||||||
|
|
||||||
account, err := manager.Store.GetAccount(accountId)
|
account, err := am.Store.GetAccount(accountId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.NotFound, "account not found")
|
return nil, status.Errorf(codes.NotFound, "account not found")
|
||||||
}
|
}
|
||||||
@@ -119,21 +121,21 @@ func (manager *AccountManager) GetAccount(accountId string) (*Account, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetOrCreateAccount returns an existing account or creates a new one if doesn't exist
|
// GetOrCreateAccount returns an existing account or creates a new one if doesn't exist
|
||||||
func (manager *AccountManager) GetOrCreateAccount(accountId string) (*Account, error) {
|
func (am *AccountManager) GetOrCreateAccount(accountId string) (*Account, error) {
|
||||||
manager.mux.Lock()
|
am.mux.Lock()
|
||||||
defer manager.mux.Unlock()
|
defer am.mux.Unlock()
|
||||||
|
|
||||||
_, err := manager.Store.GetAccount(accountId)
|
_, err := am.Store.GetAccount(accountId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
|
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
|
||||||
return manager.createAccount(accountId)
|
return am.createAccount(accountId)
|
||||||
} else {
|
} else {
|
||||||
// other error
|
// other error
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
account, err := manager.Store.GetAccount(accountId)
|
account, err := am.Store.GetAccount(accountId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "failed retrieving account")
|
return nil, status.Errorf(codes.Internal, "failed retrieving account")
|
||||||
}
|
}
|
||||||
@@ -142,12 +144,12 @@ func (manager *AccountManager) GetOrCreateAccount(accountId string) (*Account, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
//AccountExists checks whether account exists (returns true) or not (returns false)
|
//AccountExists checks whether account exists (returns true) or not (returns false)
|
||||||
func (manager *AccountManager) AccountExists(accountId string) (*bool, error) {
|
func (am *AccountManager) AccountExists(accountId string) (*bool, error) {
|
||||||
manager.mux.Lock()
|
am.mux.Lock()
|
||||||
defer manager.mux.Unlock()
|
defer am.mux.Unlock()
|
||||||
|
|
||||||
var res bool
|
var res bool
|
||||||
_, err := manager.Store.GetAccount(accountId)
|
_, err := am.Store.GetAccount(accountId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
|
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
|
||||||
res = false
|
res = false
|
||||||
@@ -162,19 +164,19 @@ func (manager *AccountManager) AccountExists(accountId string) (*bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddAccount generates a new Account with a provided accountId and saves to the Store
|
// AddAccount generates a new Account with a provided accountId and saves to the Store
|
||||||
func (manager *AccountManager) AddAccount(accountId string) (*Account, error) {
|
func (am *AccountManager) AddAccount(accountId string) (*Account, error) {
|
||||||
|
|
||||||
manager.mux.Lock()
|
am.mux.Lock()
|
||||||
defer manager.mux.Unlock()
|
defer am.mux.Unlock()
|
||||||
|
|
||||||
return manager.createAccount(accountId)
|
return am.createAccount(accountId)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (manager *AccountManager) createAccount(accountId string) (*Account, error) {
|
func (am *AccountManager) createAccount(accountId string) (*Account, error) {
|
||||||
account, _ := newAccountWithId(accountId)
|
account, _ := newAccountWithId(accountId)
|
||||||
|
|
||||||
err := manager.Store.SaveAccount(account)
|
err := am.Store.SaveAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "failed creating account")
|
return nil, status.Errorf(codes.Internal, "failed creating account")
|
||||||
}
|
}
|
||||||
@@ -188,17 +190,19 @@ func newAccountWithId(accountId string) (*Account, *SetupKey) {
|
|||||||
log.Debugf("creating new account")
|
log.Debugf("creating new account")
|
||||||
|
|
||||||
setupKeys := make(map[string]*SetupKey)
|
setupKeys := make(map[string]*SetupKey)
|
||||||
setupKey := GenerateDefaultSetupKey()
|
defaultKey := GenerateDefaultSetupKey()
|
||||||
setupKeys[setupKey.Key] = setupKey
|
oneOffKey := GenerateSetupKey("One-off key", SetupKeyOneOff, DefaultSetupKeyDuration)
|
||||||
|
setupKeys[defaultKey.Key] = defaultKey
|
||||||
|
setupKeys[oneOffKey.Key] = oneOffKey
|
||||||
network := &Network{
|
network := &Network{
|
||||||
Id: uuid.New().String(),
|
Id: uuid.New().String(),
|
||||||
Net: net.IPNet{IP: net.ParseIP("100.64.0.0"), Mask: net.IPMask{255, 192, 0, 0}},
|
Net: net.IPNet{IP: net.ParseIP("100.64.0.0"), Mask: net.IPMask{255, 192, 0, 0}},
|
||||||
Dns: ""}
|
Dns: ""}
|
||||||
peers := make(map[string]*Peer)
|
peers := make(map[string]*Peer)
|
||||||
|
|
||||||
log.Debugf("created new account %s with setup key %s", accountId, setupKey.Key)
|
log.Debugf("created new account %s with setup key %s", accountId, defaultKey.Key)
|
||||||
|
|
||||||
return &Account{Id: accountId, SetupKeys: setupKeys, Network: network, Peers: peers}, setupKey
|
return &Account{Id: accountId, SetupKeys: setupKeys, Network: network, Peers: peers}, defaultKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// newAccount creates a new Account with a default SetupKey (doesn't store in a Store)
|
// newAccount creates a new Account with a default SetupKey (doesn't store in a Store)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func TestAccountManager_AddAccount(t *testing.T) {
|
|||||||
|
|
||||||
expectedId := "test_account"
|
expectedId := "test_account"
|
||||||
expectedPeersSize := 0
|
expectedPeersSize := 0
|
||||||
expectedSetupKeysSize := 1
|
expectedSetupKeysSize := 2
|
||||||
expectedNetwork := net.IPNet{
|
expectedNetwork := net.IPNet{
|
||||||
IP: net.IP{100, 64, 0, 0},
|
IP: net.IP{100, 64, 0, 0},
|
||||||
Mask: net.IPMask{255, 192, 0, 0},
|
Mask: net.IPMask{255, 192, 0, 0},
|
||||||
@@ -201,7 +201,7 @@ func createManager(t *testing.T) (*AccountManager, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewManager(store), nil
|
return NewManager(store, NewPeersUpdateManager()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createStore(t *testing.T) (Store, error) {
|
func createStore(t *testing.T) (Store, error) {
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/wiretrustee/wiretrustee/util"
|
||||||
|
)
|
||||||
|
|
||||||
type Protocol string
|
type Protocol string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -12,19 +16,31 @@ const (
|
|||||||
|
|
||||||
// Config of the Management service
|
// Config of the Management service
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Stuns []*Host
|
Stuns []*Host
|
||||||
Turns []*Host
|
TURNConfig *TURNConfig
|
||||||
Signal *Host
|
Signal *Host
|
||||||
|
|
||||||
Datadir string
|
Datadir string
|
||||||
|
|
||||||
HttpConfig *HttpServerConfig
|
HttpConfig *HttpServerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TURNConfig is a config of the TURNCredentialsManager
|
||||||
|
type TURNConfig struct {
|
||||||
|
TimeBasedCredentials bool
|
||||||
|
CredentialsTTL util.Duration
|
||||||
|
Secret string
|
||||||
|
Turns []*Host
|
||||||
|
}
|
||||||
|
|
||||||
// HttpServerConfig is a config of the HTTP Management service server
|
// HttpServerConfig is a config of the HTTP Management service server
|
||||||
type HttpServerConfig struct {
|
type HttpServerConfig struct {
|
||||||
LetsEncryptDomain string
|
LetsEncryptDomain string
|
||||||
Address string
|
//CertFile is the location of the certificate
|
||||||
|
CertFile string
|
||||||
|
//CertKey is the location of the certificate private key
|
||||||
|
CertKey string
|
||||||
|
Address string
|
||||||
// AuthAudience identifies the recipients that the JWT is intended for (aud in JWT)
|
// AuthAudience identifies the recipients that the JWT is intended for (aud in JWT)
|
||||||
AuthAudience string
|
AuthAudience string
|
||||||
// AuthIssuer identifies principal that issued the JWT.
|
// AuthIssuer identifies principal that issued the JWT.
|
||||||
@@ -39,5 +55,5 @@ type Host struct {
|
|||||||
// URI e.g. turns://stun.wiretrustee.com:4430 or signal.wiretrustee.com:10000
|
// URI e.g. turns://stun.wiretrustee.com:4430 or signal.wiretrustee.com:10000
|
||||||
URI string
|
URI string
|
||||||
Username string
|
Username string
|
||||||
Password []byte
|
Password string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,6 +190,22 @@ func (s *FileStore) GetAccountBySetupKey(setupKey string) (*Account, error) {
|
|||||||
|
|
||||||
return account, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
func (s *FileStore) GetAccountPeers(accountId string) ([]*Peer, error) {
|
||||||
|
s.mux.Lock()
|
||||||
|
defer s.mux.Unlock()
|
||||||
|
|
||||||
|
account, err := s.GetAccount(accountId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var peers []*Peer
|
||||||
|
for _, peer := range account.Peers {
|
||||||
|
peers = append(peers, peer)
|
||||||
|
}
|
||||||
|
|
||||||
|
return peers, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *FileStore) GetAccount(accountId string) (*Account, error) {
|
func (s *FileStore) GetAccount(accountId string) (*Account, error) {
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package server
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/ptypes/timestamp"
|
"github.com/golang/protobuf/ptypes/timestamp"
|
||||||
@@ -20,31 +19,28 @@ type Server struct {
|
|||||||
accountManager *AccountManager
|
accountManager *AccountManager
|
||||||
wgKey wgtypes.Key
|
wgKey wgtypes.Key
|
||||||
proto.UnimplementedManagementServiceServer
|
proto.UnimplementedManagementServiceServer
|
||||||
peerChannels map[string]chan *UpdateChannelMessage
|
peersUpdateManager *PeersUpdateManager
|
||||||
channelsMux *sync.Mutex
|
config *Config
|
||||||
config *Config
|
turnCredentialsManager TURNCredentialsManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllowedIPsFormat generates Wireguard AllowedIPs format (e.g. 100.30.30.1/32)
|
// AllowedIPsFormat generates Wireguard AllowedIPs format (e.g. 100.30.30.1/32)
|
||||||
const AllowedIPsFormat = "%s/32"
|
const AllowedIPsFormat = "%s/32"
|
||||||
|
|
||||||
type UpdateChannelMessage struct {
|
|
||||||
Update *proto.SyncResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServer creates a new Management server
|
// NewServer creates a new Management server
|
||||||
func NewServer(config *Config, accountManager *AccountManager) (*Server, error) {
|
func NewServer(config *Config, accountManager *AccountManager, peersUpdateManager *PeersUpdateManager, turnCredentialsManager TURNCredentialsManager) (*Server, error) {
|
||||||
key, err := wgtypes.GeneratePrivateKey()
|
key, err := wgtypes.GeneratePrivateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Server{
|
return &Server{
|
||||||
wgKey: key,
|
wgKey: key,
|
||||||
// peerKey -> event channel
|
// peerKey -> event channel
|
||||||
peerChannels: make(map[string]chan *UpdateChannelMessage),
|
peersUpdateManager: peersUpdateManager,
|
||||||
channelsMux: &sync.Mutex{},
|
accountManager: accountManager,
|
||||||
accountManager: accountManager,
|
config: config,
|
||||||
config: config,
|
turnCredentialsManager: turnCredentialsManager,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,8 +86,15 @@ func (s *Server) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_S
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
updates := s.openUpdatesChannel(peerKey.String())
|
updates := s.peersUpdateManager.CreateChannel(peerKey.String())
|
||||||
|
err = s.accountManager.MarkPeerConnected(peerKey.String(), true)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed marking peer as connected %s %v", peerKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.config.TURNConfig.TimeBasedCredentials {
|
||||||
|
s.turnCredentialsManager.SetupRefresh(peerKey.String())
|
||||||
|
}
|
||||||
// keep a connection to the peer and send updates when available
|
// keep a connection to the peer and send updates when available
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -115,19 +118,24 @@ func (s *Server) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_S
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return status.Errorf(codes.Internal, "failed sending update message")
|
return status.Errorf(codes.Internal, "failed sending update message")
|
||||||
}
|
}
|
||||||
|
log.Debugf("sent an update to peer %s", peerKey.String())
|
||||||
// condition when client <-> server connection has been terminated
|
// condition when client <-> server connection has been terminated
|
||||||
case <-srv.Context().Done():
|
case <-srv.Context().Done():
|
||||||
// happens when connection drops, e.g. client disconnects
|
// happens when connection drops, e.g. client disconnects
|
||||||
log.Debugf("stream of peer %s has been closed", peerKey.String())
|
log.Debugf("stream of peer %s has been closed", peerKey.String())
|
||||||
s.closeUpdatesChannel(peerKey.String())
|
s.peersUpdateManager.CloseChannel(peerKey.String())
|
||||||
|
s.turnCredentialsManager.CancelRefresh(peerKey.String())
|
||||||
|
err = s.accountManager.MarkPeerConnected(peerKey.String(), false)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed marking peer as disconnected %s %v", peerKey, err)
|
||||||
|
}
|
||||||
|
// todo stop turn goroutine
|
||||||
return srv.Context().Err()
|
return srv.Context().Err()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) registerPeer(peerKey wgtypes.Key, req *proto.LoginRequest) (*Peer, error) {
|
func (s *Server) registerPeer(peerKey wgtypes.Key, req *proto.LoginRequest) (*Peer, error) {
|
||||||
s.channelsMux.Lock()
|
|
||||||
defer s.channelsMux.Unlock()
|
|
||||||
|
|
||||||
meta := req.GetMeta()
|
meta := req.GetMeta()
|
||||||
if meta == nil {
|
if meta == nil {
|
||||||
@@ -157,16 +165,18 @@ func (s *Server) registerPeer(peerKey wgtypes.Key, req *proto.LoginRequest) (*Pe
|
|||||||
|
|
||||||
// notify other peers of our registration
|
// notify other peers of our registration
|
||||||
for _, remotePeer := range peers {
|
for _, remotePeer := range peers {
|
||||||
if channel, ok := s.peerChannels[remotePeer.Key]; ok {
|
// exclude notified peer and add ourselves
|
||||||
// exclude notified peer and add ourselves
|
peersToSend := []*Peer{peer}
|
||||||
peersToSend := []*Peer{peer}
|
for _, p := range peers {
|
||||||
for _, p := range peers {
|
if remotePeer.Key != p.Key {
|
||||||
if remotePeer.Key != p.Key {
|
peersToSend = append(peersToSend, p)
|
||||||
peersToSend = append(peersToSend, p)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
update := toSyncResponse(s.config, peer, peersToSend)
|
}
|
||||||
channel <- &UpdateChannelMessage{Update: update}
|
update := toSyncResponse(s.config, peer, peersToSend, nil)
|
||||||
|
err = s.peersUpdateManager.SendUpdate(remotePeer.Key, &UpdateMessage{Update: update})
|
||||||
|
if err != nil {
|
||||||
|
// todo rethink if we should keep this return
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +225,7 @@ func (s *Server) Login(ctx context.Context, req *proto.EncryptedMessage) (*proto
|
|||||||
|
|
||||||
// if peer has reached this point then it has logged in
|
// if peer has reached this point then it has logged in
|
||||||
loginResp := &proto.LoginResponse{
|
loginResp := &proto.LoginResponse{
|
||||||
WiretrusteeConfig: toWiretrusteeConfig(s.config),
|
WiretrusteeConfig: toWiretrusteeConfig(s.config, nil),
|
||||||
PeerConfig: toPeerConfig(peer),
|
PeerConfig: toPeerConfig(peer),
|
||||||
}
|
}
|
||||||
encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, loginResp)
|
encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, loginResp)
|
||||||
@@ -229,7 +239,7 @@ func (s *Server) Login(ctx context.Context, req *proto.EncryptedMessage) (*proto
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toResponseProto(configProto Protocol) proto.HostConfig_Protocol {
|
func ToResponseProto(configProto Protocol) proto.HostConfig_Protocol {
|
||||||
switch configProto {
|
switch configProto {
|
||||||
case UDP:
|
case UDP:
|
||||||
return proto.HostConfig_UDP
|
return proto.HostConfig_UDP
|
||||||
@@ -247,24 +257,33 @@ func toResponseProto(configProto Protocol) proto.HostConfig_Protocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toWiretrusteeConfig(config *Config) *proto.WiretrusteeConfig {
|
func toWiretrusteeConfig(config *Config, turnCredentials *TURNCredentials) *proto.WiretrusteeConfig {
|
||||||
|
|
||||||
var stuns []*proto.HostConfig
|
var stuns []*proto.HostConfig
|
||||||
for _, stun := range config.Stuns {
|
for _, stun := range config.Stuns {
|
||||||
stuns = append(stuns, &proto.HostConfig{
|
stuns = append(stuns, &proto.HostConfig{
|
||||||
Uri: stun.URI,
|
Uri: stun.URI,
|
||||||
Protocol: toResponseProto(stun.Proto),
|
Protocol: ToResponseProto(stun.Proto),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
var turns []*proto.ProtectedHostConfig
|
var turns []*proto.ProtectedHostConfig
|
||||||
for _, turn := range config.Turns {
|
for _, turn := range config.TURNConfig.Turns {
|
||||||
|
var username string
|
||||||
|
var password string
|
||||||
|
if turnCredentials != nil {
|
||||||
|
username = turnCredentials.Username
|
||||||
|
password = turnCredentials.Password
|
||||||
|
} else {
|
||||||
|
username = turn.Username
|
||||||
|
password = turn.Password
|
||||||
|
}
|
||||||
turns = append(turns, &proto.ProtectedHostConfig{
|
turns = append(turns, &proto.ProtectedHostConfig{
|
||||||
HostConfig: &proto.HostConfig{
|
HostConfig: &proto.HostConfig{
|
||||||
Uri: turn.URI,
|
Uri: turn.URI,
|
||||||
Protocol: toResponseProto(turn.Proto),
|
Protocol: ToResponseProto(turn.Proto),
|
||||||
},
|
},
|
||||||
User: turn.Username,
|
User: username,
|
||||||
Password: string(turn.Password),
|
Password: password,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +292,7 @@ func toWiretrusteeConfig(config *Config) *proto.WiretrusteeConfig {
|
|||||||
Turns: turns,
|
Turns: turns,
|
||||||
Signal: &proto.HostConfig{
|
Signal: &proto.HostConfig{
|
||||||
Uri: config.Signal.URI,
|
Uri: config.Signal.URI,
|
||||||
Protocol: toResponseProto(config.Signal.Proto),
|
Protocol: ToResponseProto(config.Signal.Proto),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -284,13 +303,9 @@ func toPeerConfig(peer *Peer) *proto.PeerConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toSyncResponse(config *Config, peer *Peer, peers []*Peer) *proto.SyncResponse {
|
func toRemotePeerConfig(peers []*Peer) []*proto.RemotePeerConfig {
|
||||||
|
|
||||||
wtConfig := toWiretrusteeConfig(config)
|
remotePeers := []*proto.RemotePeerConfig{}
|
||||||
|
|
||||||
pConfig := toPeerConfig(peer)
|
|
||||||
|
|
||||||
remotePeers := make([]*proto.RemotePeerConfig, 0, len(peers))
|
|
||||||
for _, rPeer := range peers {
|
for _, rPeer := range peers {
|
||||||
remotePeers = append(remotePeers, &proto.RemotePeerConfig{
|
remotePeers = append(remotePeers, &proto.RemotePeerConfig{
|
||||||
WgPubKey: rPeer.Key,
|
WgPubKey: rPeer.Key,
|
||||||
@@ -298,10 +313,23 @@ func toSyncResponse(config *Config, peer *Peer, peers []*Peer) *proto.SyncRespon
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return remotePeers
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSyncResponse(config *Config, peer *Peer, peers []*Peer, turnCredentials *TURNCredentials) *proto.SyncResponse {
|
||||||
|
|
||||||
|
wtConfig := toWiretrusteeConfig(config, turnCredentials)
|
||||||
|
|
||||||
|
pConfig := toPeerConfig(peer)
|
||||||
|
|
||||||
|
remotePeers := toRemotePeerConfig(peers)
|
||||||
|
|
||||||
return &proto.SyncResponse{
|
return &proto.SyncResponse{
|
||||||
WiretrusteeConfig: wtConfig,
|
WiretrusteeConfig: wtConfig,
|
||||||
PeerConfig: pConfig,
|
PeerConfig: pConfig,
|
||||||
RemotePeers: remotePeers,
|
RemotePeers: remotePeers,
|
||||||
|
RemotePeersIsEmpty: len(remotePeers) == 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,44 +338,6 @@ func (s *Server) IsHealthy(ctx context.Context, req *proto.Empty) (*proto.Empty,
|
|||||||
return &proto.Empty{}, nil
|
return &proto.Empty{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// openUpdatesChannel creates a go channel for a given peer used to deliver updates relevant to the peer.
|
|
||||||
func (s *Server) openUpdatesChannel(peerKey string) chan *UpdateChannelMessage {
|
|
||||||
s.channelsMux.Lock()
|
|
||||||
defer s.channelsMux.Unlock()
|
|
||||||
if channel, ok := s.peerChannels[peerKey]; ok {
|
|
||||||
delete(s.peerChannels, peerKey)
|
|
||||||
close(channel)
|
|
||||||
}
|
|
||||||
//mbragin: todo shouldn't it be more? or configurable?
|
|
||||||
channel := make(chan *UpdateChannelMessage, 100)
|
|
||||||
s.peerChannels[peerKey] = channel
|
|
||||||
|
|
||||||
err := s.accountManager.MarkPeerConnected(peerKey, true)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("failed marking peer as connected %s %v", peerKey, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("opened updates channel for a peer %s", peerKey)
|
|
||||||
return channel
|
|
||||||
}
|
|
||||||
|
|
||||||
// closeUpdatesChannel closes updates channel of a given peer
|
|
||||||
func (s *Server) closeUpdatesChannel(peerKey string) {
|
|
||||||
s.channelsMux.Lock()
|
|
||||||
defer s.channelsMux.Unlock()
|
|
||||||
if channel, ok := s.peerChannels[peerKey]; ok {
|
|
||||||
delete(s.peerChannels, peerKey)
|
|
||||||
close(channel)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := s.accountManager.MarkPeerConnected(peerKey, false)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("failed marking peer as disconnected %s %v", peerKey, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("closed updates channel of a peer %s", peerKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendInitialSync sends initial proto.SyncResponse to the peer requesting synchronization
|
// sendInitialSync sends initial proto.SyncResponse to the peer requesting synchronization
|
||||||
func (s *Server) sendInitialSync(peerKey wgtypes.Key, peer *Peer, srv proto.ManagementService_SyncServer) error {
|
func (s *Server) sendInitialSync(peerKey wgtypes.Key, peer *Peer, srv proto.ManagementService_SyncServer) error {
|
||||||
|
|
||||||
@@ -356,7 +346,16 @@ func (s *Server) sendInitialSync(peerKey wgtypes.Key, peer *Peer, srv proto.Mana
|
|||||||
log.Warnf("error getting a list of peers for a peer %s", peer.Key)
|
log.Warnf("error getting a list of peers for a peer %s", peer.Key)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
plainResp := toSyncResponse(s.config, peer, peers)
|
|
||||||
|
// make secret time based TURN credentials optional
|
||||||
|
var turnCredentials *TURNCredentials
|
||||||
|
if s.config.TURNConfig.TimeBasedCredentials {
|
||||||
|
creds := s.turnCredentialsManager.GenerateCredentials()
|
||||||
|
turnCredentials = &creds
|
||||||
|
} else {
|
||||||
|
turnCredentials = nil
|
||||||
|
}
|
||||||
|
plainResp := toSyncResponse(s.config, peer, peers, turnCredentials)
|
||||||
|
|
||||||
encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, plainResp)
|
encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, plainResp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -122,6 +122,6 @@ func toPeerResponse(peer *server.Peer) *PeerResponse {
|
|||||||
IP: peer.IP.String(),
|
IP: peer.IP.String(),
|
||||||
Connected: peer.Status.Connected,
|
Connected: peer.Status.Connected,
|
||||||
LastSeen: peer.Status.LastSeen,
|
LastSeen: peer.Status.LastSeen,
|
||||||
OS: fmt.Sprintf("%s %s", peer.Meta.GoOS, peer.Meta.Core),
|
OS: fmt.Sprintf("%s %s", peer.Meta.OS, peer.Meta.Core),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package http
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/rs/cors"
|
"github.com/rs/cors"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -17,10 +18,11 @@ type Server struct {
|
|||||||
server *http.Server
|
server *http.Server
|
||||||
config *s.HttpServerConfig
|
config *s.HttpServerConfig
|
||||||
certManager *autocert.Manager
|
certManager *autocert.Manager
|
||||||
|
tlsConfig *tls.Config
|
||||||
accountManager *s.AccountManager
|
accountManager *s.AccountManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHttpsServer creates a new HTTPs server (with HTTPS support)
|
// NewHttpsServer creates a new HTTPs server (with HTTPS support) and a certManager that is responsible for generating and renewing Let's Encrypt certificate
|
||||||
// The listening address will be :443 no matter what was specified in s.HttpServerConfig.Address
|
// The listening address will be :443 no matter what was specified in s.HttpServerConfig.Address
|
||||||
func NewHttpsServer(config *s.HttpServerConfig, certManager *autocert.Manager, accountManager *s.AccountManager) *Server {
|
func NewHttpsServer(config *s.HttpServerConfig, certManager *autocert.Manager, accountManager *s.AccountManager) *Server {
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
@@ -32,6 +34,18 @@ func NewHttpsServer(config *s.HttpServerConfig, certManager *autocert.Manager, a
|
|||||||
return &Server{server: server, config: config, certManager: certManager, accountManager: accountManager}
|
return &Server{server: server, config: config, certManager: certManager, accountManager: accountManager}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewHttpsServerWithTLSConfig creates a new HTTPs server with a provided tls.Config.
|
||||||
|
// Usually used when you already have a certificate
|
||||||
|
func NewHttpsServerWithTLSConfig(config *s.HttpServerConfig, tlsConfig *tls.Config, accountManager *s.AccountManager) *Server {
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: config.Address,
|
||||||
|
WriteTimeout: time.Second * 15,
|
||||||
|
ReadTimeout: time.Second * 15,
|
||||||
|
IdleTimeout: time.Second * 60,
|
||||||
|
}
|
||||||
|
return &Server{server: server, config: config, tlsConfig: tlsConfig, accountManager: accountManager}
|
||||||
|
}
|
||||||
|
|
||||||
// NewHttpServer creates a new HTTP server (without HTTPS)
|
// NewHttpServer creates a new HTTP server (without HTTPS)
|
||||||
func NewHttpServer(config *s.HttpServerConfig, accountManager *s.AccountManager) *Server {
|
func NewHttpServer(config *s.HttpServerConfig, accountManager *s.AccountManager) *Server {
|
||||||
return NewHttpsServer(config, nil, accountManager)
|
return NewHttpsServer(config, nil, accountManager)
|
||||||
@@ -71,13 +85,26 @@ func (s *Server) Start() error {
|
|||||||
if s.certManager != nil {
|
if s.certManager != nil {
|
||||||
// if HTTPS is enabled we reuse the listener from the cert manager
|
// if HTTPS is enabled we reuse the listener from the cert manager
|
||||||
listener := s.certManager.Listener()
|
listener := s.certManager.Listener()
|
||||||
log.Infof("http server listening on %s", listener.Addr())
|
log.Infof("HTTPs server listening on %s with Let's Encrypt autocert configured", listener.Addr())
|
||||||
if err = http.Serve(listener, s.certManager.HTTPHandler(r)); err != nil {
|
if err = http.Serve(listener, s.certManager.HTTPHandler(r)); err != nil {
|
||||||
log.Errorf("failed to serve https server: %v", err)
|
log.Errorf("failed to serve https server: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
} else if s.tlsConfig != nil {
|
||||||
|
listener, err := tls.Listen("tcp", s.config.Address, s.tlsConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to serve https server: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Infof("HTTPs server listening on %s", listener.Addr())
|
||||||
|
|
||||||
|
if err = http.Serve(listener, r); err != nil {
|
||||||
|
log.Errorf("failed to serve https server: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log.Infof("http server listening on %s", s.server.Addr)
|
log.Infof("HTTP server listening on %s", s.server.Addr)
|
||||||
if err = s.server.ListenAndServe(); err != nil {
|
if err = s.server.ListenAndServe(); err != nil {
|
||||||
log.Errorf("failed to serve http server: %v", err)
|
log.Errorf("failed to serve http server: %v", err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -119,19 +119,18 @@ var _ = Describe("Management service", func() {
|
|||||||
Uri: "stun:stun.wiretrustee.com:3468",
|
Uri: "stun:stun.wiretrustee.com:3468",
|
||||||
Protocol: mgmtProto.HostConfig_UDP,
|
Protocol: mgmtProto.HostConfig_UDP,
|
||||||
}
|
}
|
||||||
expectedTurnsConfig := &mgmtProto.ProtectedHostConfig{
|
expectedTRUNHost := &mgmtProto.HostConfig{
|
||||||
HostConfig: &mgmtProto.HostConfig{
|
Uri: "turn:stun.wiretrustee.com:3468",
|
||||||
Uri: "turn:stun.wiretrustee.com:3468",
|
Protocol: mgmtProto.HostConfig_UDP,
|
||||||
Protocol: mgmtProto.HostConfig_UDP,
|
|
||||||
},
|
|
||||||
User: "some_user",
|
|
||||||
Password: "some_password",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(resp.WiretrusteeConfig.Signal).To(BeEquivalentTo(expectedSignalConfig))
|
Expect(resp.WiretrusteeConfig.Signal).To(BeEquivalentTo(expectedSignalConfig))
|
||||||
Expect(resp.WiretrusteeConfig.Stuns).To(ConsistOf(expectedStunsConfig))
|
Expect(resp.WiretrusteeConfig.Stuns).To(ConsistOf(expectedStunsConfig))
|
||||||
Expect(resp.WiretrusteeConfig.Turns).To(ConsistOf(expectedTurnsConfig))
|
// TURN validation is special because credentials are dynamically generated
|
||||||
|
Expect(resp.WiretrusteeConfig.Turns).To(HaveLen(1))
|
||||||
|
actualTURN := resp.WiretrusteeConfig.Turns[0]
|
||||||
|
Expect(len(actualTURN.User) > 0).To(BeTrue())
|
||||||
|
Expect(actualTURN.HostConfig).To(BeEquivalentTo(expectedTRUNHost))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -368,7 +367,10 @@ var _ = Describe("Management service", func() {
|
|||||||
resp := &mgmtProto.SyncResponse{}
|
resp := &mgmtProto.SyncResponse{}
|
||||||
err = pb.Unmarshal(decryptedBytes, resp)
|
err = pb.Unmarshal(decryptedBytes, resp)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
wg.Done()
|
if len(resp.GetRemotePeers()) > 0 {
|
||||||
|
//only consider peer updates
|
||||||
|
wg.Done()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
@@ -388,7 +390,6 @@ var _ = Describe("Management service", func() {
|
|||||||
err := syncClient.CloseSend()
|
err := syncClient.CloseSend()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -486,12 +487,15 @@ func startServer(config *server.Config) (*grpc.Server, net.Listener) {
|
|||||||
lis, err := net.Listen("tcp", ":0")
|
lis, err := net.Listen("tcp", ":0")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
s := grpc.NewServer()
|
s := grpc.NewServer()
|
||||||
|
|
||||||
store, err := server.NewStore(config.Datadir)
|
store, err := server.NewStore(config.Datadir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed creating a store: %s: %v", config.Datadir, err)
|
log.Fatalf("failed creating a store: %s: %v", config.Datadir, err)
|
||||||
}
|
}
|
||||||
accountManager := server.NewManager(store)
|
peersUpdateManager := server.NewPeersUpdateManager()
|
||||||
mgmtServer, err := server.NewServer(config, accountManager)
|
accountManager := server.NewManager(store, peersUpdateManager)
|
||||||
|
turnManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig)
|
||||||
|
mgmtServer, err := server.NewServer(config, accountManager, peersUpdateManager, turnManager)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
mgmtProto.RegisterManagementServiceServer(s, mgmtServer)
|
mgmtProto.RegisterManagementServiceServer(s, mgmtServer)
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/wiretrustee/wiretrustee/management/proto"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
"net"
|
"net"
|
||||||
@@ -55,11 +56,11 @@ func (p *Peer) Copy() *Peer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//GetPeer returns a peer from a Store
|
//GetPeer returns a peer from a Store
|
||||||
func (manager *AccountManager) GetPeer(peerKey string) (*Peer, error) {
|
func (am *AccountManager) GetPeer(peerKey string) (*Peer, error) {
|
||||||
manager.mux.Lock()
|
am.mux.Lock()
|
||||||
defer manager.mux.Unlock()
|
defer am.mux.Unlock()
|
||||||
|
|
||||||
peer, err := manager.Store.GetPeer(peerKey)
|
peer, err := am.Store.GetPeer(peerKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -68,16 +69,16 @@ func (manager *AccountManager) GetPeer(peerKey string) (*Peer, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//MarkPeerConnected marks peer as connected (true) or disconnected (false)
|
//MarkPeerConnected marks peer as connected (true) or disconnected (false)
|
||||||
func (manager *AccountManager) MarkPeerConnected(peerKey string, connected bool) error {
|
func (am *AccountManager) MarkPeerConnected(peerKey string, connected bool) error {
|
||||||
manager.mux.Lock()
|
am.mux.Lock()
|
||||||
defer manager.mux.Unlock()
|
defer am.mux.Unlock()
|
||||||
|
|
||||||
peer, err := manager.Store.GetPeer(peerKey)
|
peer, err := am.Store.GetPeer(peerKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
account, err := manager.Store.GetPeerAccount(peerKey)
|
account, err := am.Store.GetPeerAccount(peerKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -85,7 +86,7 @@ func (manager *AccountManager) MarkPeerConnected(peerKey string, connected bool)
|
|||||||
peerCopy := peer.Copy()
|
peerCopy := peer.Copy()
|
||||||
peerCopy.Status.LastSeen = time.Now()
|
peerCopy.Status.LastSeen = time.Now()
|
||||||
peerCopy.Status.Connected = connected
|
peerCopy.Status.Connected = connected
|
||||||
err = manager.Store.SavePeer(account.Id, peerCopy)
|
err = am.Store.SavePeer(account.Id, peerCopy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -93,18 +94,18 @@ func (manager *AccountManager) MarkPeerConnected(peerKey string, connected bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//RenamePeer changes peer's name
|
//RenamePeer changes peer's name
|
||||||
func (manager *AccountManager) RenamePeer(accountId string, peerKey string, newName string) (*Peer, error) {
|
func (am *AccountManager) RenamePeer(accountId string, peerKey string, newName string) (*Peer, error) {
|
||||||
manager.mux.Lock()
|
am.mux.Lock()
|
||||||
defer manager.mux.Unlock()
|
defer am.mux.Unlock()
|
||||||
|
|
||||||
peer, err := manager.Store.GetPeer(peerKey)
|
peer, err := am.Store.GetPeer(peerKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
peerCopy := peer.Copy()
|
peerCopy := peer.Copy()
|
||||||
peerCopy.Name = newName
|
peerCopy.Name = newName
|
||||||
err = manager.Store.SavePeer(accountId, peerCopy)
|
err = am.Store.SavePeer(accountId, peerCopy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -113,18 +114,60 @@ func (manager *AccountManager) RenamePeer(accountId string, peerKey string, newN
|
|||||||
}
|
}
|
||||||
|
|
||||||
//DeletePeer removes peer from the account by it's IP
|
//DeletePeer removes peer from the account by it's IP
|
||||||
func (manager *AccountManager) DeletePeer(accountId string, peerKey string) (*Peer, error) {
|
func (am *AccountManager) DeletePeer(accountId string, peerKey string) (*Peer, error) {
|
||||||
manager.mux.Lock()
|
am.mux.Lock()
|
||||||
defer manager.mux.Unlock()
|
defer am.mux.Unlock()
|
||||||
return manager.Store.DeletePeer(accountId, peerKey)
|
|
||||||
|
peer, err := am.Store.DeletePeer(accountId, peerKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = am.peersUpdateManager.SendUpdate(peerKey,
|
||||||
|
&UpdateMessage{
|
||||||
|
Update: &proto.SyncResponse{
|
||||||
|
RemotePeers: []*proto.RemotePeerConfig{},
|
||||||
|
RemotePeersIsEmpty: true,
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//notify other peers of the change
|
||||||
|
peers, err := am.Store.GetAccountPeers(accountId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range peers {
|
||||||
|
peersToSend := []*Peer{}
|
||||||
|
for _, remote := range peers {
|
||||||
|
if p.Key != remote.Key {
|
||||||
|
peersToSend = append(peersToSend, remote)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update := toRemotePeerConfig(peersToSend)
|
||||||
|
err = am.peersUpdateManager.SendUpdate(p.Key,
|
||||||
|
&UpdateMessage{
|
||||||
|
Update: &proto.SyncResponse{
|
||||||
|
RemotePeers: update,
|
||||||
|
RemotePeersIsEmpty: len(update) == 0,
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
am.peersUpdateManager.CloseChannel(peerKey)
|
||||||
|
return peer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//GetPeerByIP returns peer by it's IP
|
//GetPeerByIP returns peer by it's IP
|
||||||
func (manager *AccountManager) GetPeerByIP(accountId string, peerIP string) (*Peer, error) {
|
func (am *AccountManager) GetPeerByIP(accountId string, peerIP string) (*Peer, error) {
|
||||||
manager.mux.Lock()
|
am.mux.Lock()
|
||||||
defer manager.mux.Unlock()
|
defer am.mux.Unlock()
|
||||||
|
|
||||||
account, err := manager.Store.GetAccount(accountId)
|
account, err := am.Store.GetAccount(accountId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.NotFound, "account not found")
|
return nil, status.Errorf(codes.NotFound, "account not found")
|
||||||
}
|
}
|
||||||
@@ -140,11 +183,11 @@ func (manager *AccountManager) GetPeerByIP(accountId string, peerIP string) (*Pe
|
|||||||
|
|
||||||
// GetPeersForAPeer returns a list of peers available for a given peer (key)
|
// GetPeersForAPeer returns a list of peers available for a given peer (key)
|
||||||
// Effectively all the peers of the original peer's account except for the peer itself
|
// Effectively all the peers of the original peer's account except for the peer itself
|
||||||
func (manager *AccountManager) GetPeersForAPeer(peerKey string) ([]*Peer, error) {
|
func (am *AccountManager) GetPeersForAPeer(peerKey string) ([]*Peer, error) {
|
||||||
manager.mux.Lock()
|
am.mux.Lock()
|
||||||
defer manager.mux.Unlock()
|
defer am.mux.Unlock()
|
||||||
|
|
||||||
account, err := manager.Store.GetPeerAccount(peerKey)
|
account, err := am.Store.GetPeerAccount(peerKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "Invalid peer key %s", peerKey)
|
return nil, status.Errorf(codes.Internal, "Invalid peer key %s", peerKey)
|
||||||
}
|
}
|
||||||
@@ -165,9 +208,9 @@ func (manager *AccountManager) GetPeersForAPeer(peerKey string) ([]*Peer, error)
|
|||||||
// Each new Peer will be assigned a new next net.IP from the Account.Network and Account.Network.LastIP will be updated (IP's are not reused).
|
// Each new Peer will be assigned a new next net.IP from the Account.Network and Account.Network.LastIP will be updated (IP's are not reused).
|
||||||
// If the specified setupKey is empty then a new Account will be created //todo remove this part
|
// If the specified setupKey is empty then a new Account will be created //todo remove this part
|
||||||
// The peer property is just a placeholder for the Peer properties to pass further
|
// The peer property is just a placeholder for the Peer properties to pass further
|
||||||
func (manager *AccountManager) AddPeer(setupKey string, peer Peer) (*Peer, error) {
|
func (am *AccountManager) AddPeer(setupKey string, peer Peer) (*Peer, error) {
|
||||||
manager.mux.Lock()
|
am.mux.Lock()
|
||||||
defer manager.mux.Unlock()
|
defer am.mux.Unlock()
|
||||||
|
|
||||||
upperKey := strings.ToUpper(setupKey)
|
upperKey := strings.ToUpper(setupKey)
|
||||||
|
|
||||||
@@ -178,7 +221,7 @@ func (manager *AccountManager) AddPeer(setupKey string, peer Peer) (*Peer, error
|
|||||||
// Empty setup key, create a new account for it.
|
// Empty setup key, create a new account for it.
|
||||||
account, sk = newAccount()
|
account, sk = newAccount()
|
||||||
} else {
|
} else {
|
||||||
account, err = manager.Store.GetAccountBySetupKey(upperKey)
|
account, err = am.Store.GetAccountBySetupKey(upperKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.NotFound, "unknown setupKey %s", upperKey)
|
return nil, status.Errorf(codes.NotFound, "unknown setupKey %s", upperKey)
|
||||||
}
|
}
|
||||||
@@ -213,7 +256,7 @@ func (manager *AccountManager) AddPeer(setupKey string, peer Peer) (*Peer, error
|
|||||||
|
|
||||||
account.Peers[newPeer.Key] = newPeer
|
account.Peers[newPeer.Key] = newPeer
|
||||||
account.SetupKeys[sk.Key] = sk.IncrementUsage()
|
account.SetupKeys[sk.Key] = sk.IncrementUsage()
|
||||||
err = manager.Store.SaveAccount(account)
|
err = am.Store.SaveAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "failed adding peer")
|
return nil, status.Errorf(codes.Internal, "failed adding peer")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ type Store interface {
|
|||||||
DeletePeer(accountId string, peerKey string) (*Peer, error)
|
DeletePeer(accountId string, peerKey string) (*Peer, error)
|
||||||
SavePeer(accountId string, peer *Peer) error
|
SavePeer(accountId string, peer *Peer) error
|
||||||
GetAccount(accountId string) (*Account, error)
|
GetAccount(accountId string) (*Account, error)
|
||||||
|
GetAccountPeers(accountId string) ([]*Peer, error)
|
||||||
GetPeerAccount(peerKey string) (*Account, error)
|
GetPeerAccount(peerKey string) (*Account, error)
|
||||||
GetAccountBySetupKey(setupKey string) (*Account, error)
|
GetAccountBySetupKey(setupKey string) (*Account, error)
|
||||||
SaveAccount(account *Account) error
|
SaveAccount(account *Account) error
|
||||||
|
|||||||
30
management/server/testdata/management.json
vendored
@@ -7,14 +7,19 @@
|
|||||||
"Password": null
|
"Password": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Turns": [
|
"TURNConfig": {
|
||||||
{
|
"Turns": [
|
||||||
"Proto": "udp",
|
{
|
||||||
"URI": "turn:stun.wiretrustee.com:3468",
|
"Proto": "udp",
|
||||||
"Username": "some_user",
|
"URI": "turn:stun.wiretrustee.com:3468",
|
||||||
"Password": "c29tZV9wYXNzd29yZA=="
|
"Username": "some_user",
|
||||||
}
|
"Password": "some_password"
|
||||||
],
|
}
|
||||||
|
],
|
||||||
|
"CredentialsTTL": "1h",
|
||||||
|
"Secret": "c29tZV9wYXNzd29yZA==",
|
||||||
|
"TimeBasedCredentials": true
|
||||||
|
},
|
||||||
"Signal": {
|
"Signal": {
|
||||||
"Proto": "http",
|
"Proto": "http",
|
||||||
"URI": "signal.wiretrustee.com:10000",
|
"URI": "signal.wiretrustee.com:10000",
|
||||||
@@ -23,11 +28,10 @@
|
|||||||
},
|
},
|
||||||
"DataDir": "",
|
"DataDir": "",
|
||||||
"HttpConfig": {
|
"HttpConfig": {
|
||||||
"LetsEncryptDomain": "",
|
"LetsEncryptDomain": "<PASTE YOUR LET'S ENCRYPT DOMAIN HERE>",
|
||||||
"Address": "0.0.0.0:3000",
|
"Address": "0.0.0.0:3000",
|
||||||
"AuthDomain": "<PASTE YOUR AUTH0 DOMAIN HERE>",
|
"AuthIssuer": "<PASTE YOUR AUTH0 ISSUER HERE>,",
|
||||||
"AuthClientId": "<PASTE YOUR AUTH0 CLIENT ID HERE>",
|
"AuthAudience": "<PASTE YOUR AUTH0 AUDIENCE HERE>",
|
||||||
"AuthClientSecret": "<PASTE YOUR AUTH0 SECRET>",
|
"AuthKeysLocation": "<PASTE YOUR AUTH0 PUBLIC JWT KEYS LOCATION HERE>"
|
||||||
"AuthCallback": "http://localhost:3000/callback"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
123
management/server/turncredentials.go
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/wiretrustee/wiretrustee/management/proto"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//TURNCredentialsManager used to manage TURN credentials
|
||||||
|
type TURNCredentialsManager interface {
|
||||||
|
GenerateCredentials() TURNCredentials
|
||||||
|
SetupRefresh(peerKey string)
|
||||||
|
CancelRefresh(peerKey string)
|
||||||
|
}
|
||||||
|
|
||||||
|
//TimeBasedAuthSecretsManager generates credentials with TTL and using pre-shared secret known to TURN server
|
||||||
|
type TimeBasedAuthSecretsManager struct {
|
||||||
|
mux sync.Mutex
|
||||||
|
config *TURNConfig
|
||||||
|
updateManager *PeersUpdateManager
|
||||||
|
cancelMap map[string]chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TURNCredentials struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTimeBasedAuthSecretsManager(updateManager *PeersUpdateManager, config *TURNConfig) *TimeBasedAuthSecretsManager {
|
||||||
|
return &TimeBasedAuthSecretsManager{
|
||||||
|
mux: sync.Mutex{},
|
||||||
|
config: config,
|
||||||
|
updateManager: updateManager,
|
||||||
|
cancelMap: make(map[string]chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//GenerateCredentials generates new time-based secret credentials - basically username is a unix timestamp and password is a HMAC hash of a timestamp with a preshared TURN secret
|
||||||
|
func (m *TimeBasedAuthSecretsManager) GenerateCredentials() TURNCredentials {
|
||||||
|
mac := hmac.New(sha1.New, []byte(m.config.Secret))
|
||||||
|
|
||||||
|
timeAuth := time.Now().Add(m.config.CredentialsTTL.Duration).Unix()
|
||||||
|
|
||||||
|
username := fmt.Sprint(timeAuth)
|
||||||
|
|
||||||
|
_, err := mac.Write([]byte(username))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("Generating turn password failed with error: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytePassword := mac.Sum(nil)
|
||||||
|
password := base64.StdEncoding.EncodeToString(bytePassword)
|
||||||
|
|
||||||
|
return TURNCredentials{
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TimeBasedAuthSecretsManager) cancel(peerKey string) {
|
||||||
|
if channel, ok := m.cancelMap[peerKey]; ok {
|
||||||
|
close(channel)
|
||||||
|
delete(m.cancelMap, peerKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//CancelRefresh cancels scheduled peer credentials refresh
|
||||||
|
func (m *TimeBasedAuthSecretsManager) CancelRefresh(peerKey string) {
|
||||||
|
m.mux.Lock()
|
||||||
|
defer m.mux.Unlock()
|
||||||
|
m.cancel(peerKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
//SetupRefresh starts peer credentials refresh. Since credentials are expiring (TTL) it is necessary to always generate them and send to the peer.
|
||||||
|
//A goroutine is created and put into TimeBasedAuthSecretsManager.cancelMap. This routine should be cancelled if peer is gone.
|
||||||
|
func (m *TimeBasedAuthSecretsManager) SetupRefresh(peerKey string) {
|
||||||
|
m.mux.Lock()
|
||||||
|
defer m.mux.Unlock()
|
||||||
|
m.cancel(peerKey)
|
||||||
|
cancel := make(chan struct{}, 1)
|
||||||
|
m.cancelMap[peerKey] = cancel
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-cancel:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
//we don't want to regenerate credentials right on expiration, so we do it slightly before (at 3/4 of TTL)
|
||||||
|
time.Sleep(m.config.CredentialsTTL.Duration / 4 * 3)
|
||||||
|
|
||||||
|
c := m.GenerateCredentials()
|
||||||
|
var turns []*proto.ProtectedHostConfig
|
||||||
|
for _, host := range m.config.Turns {
|
||||||
|
turns = append(turns, &proto.ProtectedHostConfig{
|
||||||
|
HostConfig: &proto.HostConfig{
|
||||||
|
Uri: host.URI,
|
||||||
|
Protocol: ToResponseProto(host.Proto),
|
||||||
|
},
|
||||||
|
User: c.Username,
|
||||||
|
Password: c.Password,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
update := &proto.SyncResponse{
|
||||||
|
WiretrusteeConfig: &proto.WiretrusteeConfig{
|
||||||
|
Turns: turns,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := m.updateManager.SendUpdate(peerKey, &UpdateMessage{Update: update})
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error while sending TURN update to peer %s %v", peerKey, err)
|
||||||
|
// todo maybe continue trying?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
133
management/server/turncredentials_test.go
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/base64"
|
||||||
|
"github.com/wiretrustee/wiretrustee/util"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var TurnTestHost = &Host{
|
||||||
|
Proto: UDP,
|
||||||
|
URI: "turn:turn.wiretrustee.com:77777",
|
||||||
|
Username: "username",
|
||||||
|
Password: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeBasedAuthSecretsManager_GenerateCredentials(t *testing.T) {
|
||||||
|
ttl := util.Duration{Duration: time.Hour}
|
||||||
|
secret := "some_secret"
|
||||||
|
peersManager := NewPeersUpdateManager()
|
||||||
|
|
||||||
|
tested := NewTimeBasedAuthSecretsManager(peersManager, &TURNConfig{
|
||||||
|
CredentialsTTL: ttl,
|
||||||
|
Secret: secret,
|
||||||
|
Turns: []*Host{TurnTestHost},
|
||||||
|
})
|
||||||
|
|
||||||
|
credentials := tested.GenerateCredentials()
|
||||||
|
|
||||||
|
if credentials.Username == "" {
|
||||||
|
t.Errorf("expected generated TURN username not to be empty, got empty")
|
||||||
|
}
|
||||||
|
if credentials.Password == "" {
|
||||||
|
t.Errorf("expected generated TURN password not to be empty, got empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
validateMAC(credentials.Username, credentials.Password, []byte(secret), t)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeBasedAuthSecretsManager_SetupRefresh(t *testing.T) {
|
||||||
|
ttl := util.Duration{Duration: 2 * time.Second}
|
||||||
|
secret := "some_secret"
|
||||||
|
peersManager := NewPeersUpdateManager()
|
||||||
|
peer := "some_peer"
|
||||||
|
updateChannel := peersManager.CreateChannel(peer)
|
||||||
|
|
||||||
|
tested := NewTimeBasedAuthSecretsManager(peersManager, &TURNConfig{
|
||||||
|
CredentialsTTL: ttl,
|
||||||
|
Secret: secret,
|
||||||
|
Turns: []*Host{TurnTestHost},
|
||||||
|
})
|
||||||
|
|
||||||
|
tested.SetupRefresh(peer)
|
||||||
|
|
||||||
|
if _, ok := tested.cancelMap[peer]; !ok {
|
||||||
|
t.Errorf("expecting peer to be present in a cancel map, got not present")
|
||||||
|
}
|
||||||
|
|
||||||
|
var updates []*UpdateMessage
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for timeout := time.After(5 * time.Second); ; {
|
||||||
|
|
||||||
|
select {
|
||||||
|
case update := <-updateChannel:
|
||||||
|
updates = append(updates, update)
|
||||||
|
case <-timeout:
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(updates) >= 2 {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(updates) < 2 {
|
||||||
|
t.Errorf("expecting 2 peer credentials updates, got %v", len(updates))
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdate := updates[0].Update.GetWiretrusteeConfig().Turns[0]
|
||||||
|
secondUpdate := updates[1].Update.GetWiretrusteeConfig().Turns[0]
|
||||||
|
|
||||||
|
if firstUpdate.Password == secondUpdate.Password {
|
||||||
|
t.Errorf("expecting first credential update password %v to be diffeerent from second, got equal", firstUpdate.Password)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeBasedAuthSecretsManager_CancelRefresh(t *testing.T) {
|
||||||
|
ttl := util.Duration{Duration: time.Hour}
|
||||||
|
secret := "some_secret"
|
||||||
|
peersManager := NewPeersUpdateManager()
|
||||||
|
peer := "some_peer"
|
||||||
|
|
||||||
|
tested := NewTimeBasedAuthSecretsManager(peersManager, &TURNConfig{
|
||||||
|
CredentialsTTL: ttl,
|
||||||
|
Secret: secret,
|
||||||
|
Turns: []*Host{TurnTestHost},
|
||||||
|
})
|
||||||
|
|
||||||
|
tested.SetupRefresh(peer)
|
||||||
|
if _, ok := tested.cancelMap[peer]; !ok {
|
||||||
|
t.Errorf("expecting peer to be present in a cancel map, got not present")
|
||||||
|
}
|
||||||
|
|
||||||
|
tested.CancelRefresh(peer)
|
||||||
|
if _, ok := tested.cancelMap[peer]; ok {
|
||||||
|
t.Errorf("expecting peer to be not present in a cancel map, got present")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateMAC(username string, actualMAC string, key []byte, t *testing.T) {
|
||||||
|
mac := hmac.New(sha1.New, key)
|
||||||
|
|
||||||
|
_, err := mac.Write([]byte(username))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedMAC := mac.Sum(nil)
|
||||||
|
decodedMAC, err := base64.StdEncoding.DecodeString(actualMAC)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
equal := hmac.Equal(decodedMAC, expectedMAC)
|
||||||
|
|
||||||
|
if !equal {
|
||||||
|
t.Errorf("expected password MAC to be %s. got %s", expectedMAC, decodedMAC)
|
||||||
|
}
|
||||||
|
}
|
||||||
64
management/server/updatechannel.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/wiretrustee/wiretrustee/management/proto"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpdateMessage struct {
|
||||||
|
Update *proto.SyncResponse
|
||||||
|
}
|
||||||
|
type PeersUpdateManager struct {
|
||||||
|
peerChannels map[string]chan *UpdateMessage
|
||||||
|
channelsMux *sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeersUpdateManager returns a new instance of PeersUpdateManager
|
||||||
|
func NewPeersUpdateManager() *PeersUpdateManager {
|
||||||
|
return &PeersUpdateManager{
|
||||||
|
peerChannels: make(map[string]chan *UpdateMessage),
|
||||||
|
channelsMux: &sync.Mutex{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendUpdate sends update message to the peer's channel
|
||||||
|
func (p *PeersUpdateManager) SendUpdate(peer string, update *UpdateMessage) error {
|
||||||
|
p.channelsMux.Lock()
|
||||||
|
defer p.channelsMux.Unlock()
|
||||||
|
if channel, ok := p.peerChannels[peer]; ok {
|
||||||
|
channel <- update
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
log.Debugf("peer %s has no channel", peer)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateChannel creates a go channel for a given peer used to deliver updates relevant to the peer.
|
||||||
|
func (p *PeersUpdateManager) CreateChannel(peerKey string) chan *UpdateMessage {
|
||||||
|
p.channelsMux.Lock()
|
||||||
|
defer p.channelsMux.Unlock()
|
||||||
|
|
||||||
|
if channel, ok := p.peerChannels[peerKey]; ok {
|
||||||
|
delete(p.peerChannels, peerKey)
|
||||||
|
close(channel)
|
||||||
|
}
|
||||||
|
//mbragin: todo shouldn't it be more? or configurable?
|
||||||
|
channel := make(chan *UpdateMessage, 100)
|
||||||
|
p.peerChannels[peerKey] = channel
|
||||||
|
|
||||||
|
log.Debugf("opened updates channel for a peer %s", peerKey)
|
||||||
|
return channel
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseChannel closes updates channel of a given peer
|
||||||
|
func (p *PeersUpdateManager) CloseChannel(peerKey string) {
|
||||||
|
p.channelsMux.Lock()
|
||||||
|
defer p.channelsMux.Unlock()
|
||||||
|
if channel, ok := p.peerChannels[peerKey]; ok {
|
||||||
|
delete(p.peerChannels, peerKey)
|
||||||
|
close(channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("closed updates channel of a peer %s", peerKey)
|
||||||
|
}
|
||||||
49
management/server/updatechannel_test.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/wiretrustee/wiretrustee/management/proto"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var peersUpdater *PeersUpdateManager
|
||||||
|
|
||||||
|
func TestCreateChannel(t *testing.T) {
|
||||||
|
peer := "test-create"
|
||||||
|
peersUpdater = NewPeersUpdateManager()
|
||||||
|
defer peersUpdater.CloseChannel(peer)
|
||||||
|
|
||||||
|
_ = peersUpdater.CreateChannel(peer)
|
||||||
|
if _, ok := peersUpdater.peerChannels[peer]; !ok {
|
||||||
|
t.Error("Error creating the channel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSendUpdate(t *testing.T) {
|
||||||
|
peer := "test-sendupdate"
|
||||||
|
update := &UpdateMessage{Update: &proto.SyncResponse{}}
|
||||||
|
_ = peersUpdater.CreateChannel(peer)
|
||||||
|
if _, ok := peersUpdater.peerChannels[peer]; !ok {
|
||||||
|
t.Error("Error creating the channel")
|
||||||
|
}
|
||||||
|
err := peersUpdater.SendUpdate(peer, update)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Error sending update: ", err)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-peersUpdater.peerChannels[peer]:
|
||||||
|
default:
|
||||||
|
t.Error("Update wasn't send")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloseChannel(t *testing.T) {
|
||||||
|
peer := "test-close"
|
||||||
|
_ = peersUpdater.CreateChannel(peer)
|
||||||
|
if _, ok := peersUpdater.peerChannels[peer]; !ok {
|
||||||
|
t.Error("Error creating the channel")
|
||||||
|
}
|
||||||
|
peersUpdater.CloseChannel(peer)
|
||||||
|
if _, ok := peersUpdater.peerChannels[peer]; ok {
|
||||||
|
t.Error("Error closing the channel")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,30 +12,27 @@ fi
|
|||||||
cleanInstall() {
|
cleanInstall() {
|
||||||
printf "\033[32m Post Install of an clean install\033[0m\n"
|
printf "\033[32m Post Install of an clean install\033[0m\n"
|
||||||
# Step 3 (clean install), enable the service in the proper way for this platform
|
# Step 3 (clean install), enable the service in the proper way for this platform
|
||||||
if [ "${use_systemctl}" = "True" ]; then
|
/usr/local/bin/wiretrustee service install
|
||||||
printf "\033[32m Reload the service unit from disk\033[0m\n"
|
/usr/local/bin/wiretrustee service start
|
||||||
systemctl daemon-reload ||:
|
|
||||||
printf "\033[32m Unmask the service\033[0m\n"
|
|
||||||
systemctl unmask wiretrustee ||:
|
|
||||||
printf "\033[32m Set the preset flag for the service unit\033[0m\n"
|
|
||||||
systemctl preset wiretrustee ||:
|
|
||||||
printf "\033[32m Set the enabled flag for the service unit\033[0m\n"
|
|
||||||
systemctl enable wiretrustee ||:
|
|
||||||
systemctl restart wiretrustee ||:
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
upgrade() {
|
upgrade() {
|
||||||
printf "\033[32m Post Install of an upgrade\033[0m\n"
|
printf "\033[32m Post Install of an upgrade\033[0m\n"
|
||||||
if [ "${use_systemctl}" = "True" ]; then
|
if [ "${use_systemctl}" = "True" ]; then
|
||||||
printf "\033[32m Reload the service unit from disk\033[0m\n"
|
printf "\033[32m Stopping the service\033[0m\n"
|
||||||
systemctl daemon-reload ||:
|
systemctl stop wiretrustee 2> /dev/null || true
|
||||||
printf "\033[32m Restarting the service\033[0m\n"
|
|
||||||
systemctl restart wiretrustee ||:
|
|
||||||
fi
|
fi
|
||||||
|
if [ -e /lib/systemd/system/wiretrustee.service ]; then
|
||||||
|
rm -f /lib/systemd/system/wiretrustee.service
|
||||||
|
systemctl daemon-reload
|
||||||
|
fi
|
||||||
|
# will trow an error until everyone upgrade
|
||||||
|
/usr/local/bin/wiretrustee service uninstall 2> /dev/null || true
|
||||||
|
/usr/local/bin/wiretrustee service install
|
||||||
|
/usr/local/bin/wiretrustee service start
|
||||||
}
|
}
|
||||||
|
|
||||||
# Step 2, check if this is a clean install or an upgrade
|
# Check if this is a clean install or an upgrade
|
||||||
action="$1"
|
action="$1"
|
||||||
if [ "$1" = "configure" ] && [ -z "$2" ]; then
|
if [ "$1" = "configure" ] && [ -z "$2" ]; then
|
||||||
# Alpine linux does not pass args, and deb passes $1=configure
|
# Alpine linux does not pass args, and deb passes $1=configure
|
||||||
@@ -50,12 +47,9 @@ case "$action" in
|
|||||||
cleanInstall
|
cleanInstall
|
||||||
;;
|
;;
|
||||||
"2" | "upgrade")
|
"2" | "upgrade")
|
||||||
printf "\033[32m Post Install of an upgrade\033[0m\n"
|
|
||||||
upgrade
|
upgrade
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
# $1 == version being installed
|
|
||||||
printf "\033[32m install\033[0m"
|
|
||||||
cleanInstall
|
cleanInstall
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
43
release_files/pre_remove.sh
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# decide if we should use systemd or init/upstart
|
||||||
|
use_systemctl="True"
|
||||||
|
systemd_version=0
|
||||||
|
if ! command -V systemctl >/dev/null 2>&1; then
|
||||||
|
use_systemctl="False"
|
||||||
|
else
|
||||||
|
systemd_version=$(systemctl --version | head -1 | sed 's/systemd //g')
|
||||||
|
fi
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
printf "\033[32m Pre uninstall\033[0m\n"
|
||||||
|
|
||||||
|
if [ "${use_systemctl}" = "True" ]; then
|
||||||
|
printf "\033[32m Stopping the service\033[0m\n"
|
||||||
|
systemctl stop wiretrustee || true
|
||||||
|
|
||||||
|
if [ -e /lib/systemd/system/wiretrustee.service ]; then
|
||||||
|
rm -f /lib/systemd/system/wiretrustee.service
|
||||||
|
systemctl daemon-reload || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
printf "\033[32m Uninstalling the service\033[0m\n"
|
||||||
|
/usr/local/bin/wiretrustee service uninstall || true
|
||||||
|
|
||||||
|
|
||||||
|
if [ "${use_systemctl}" = "True" ]; then
|
||||||
|
printf "\n\033[32m running daemon reload\033[0m\n"
|
||||||
|
systemctl daemon-reload || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
action="$1"
|
||||||
|
|
||||||
|
case "$action" in
|
||||||
|
"0" | "remove")
|
||||||
|
remove
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"PrivateKey": "",
|
|
||||||
"Peers": [
|
|
||||||
{
|
|
||||||
"WgPubKey": "",
|
|
||||||
"WgAllowedIps": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"StunTurnURLs": [
|
|
||||||
{
|
|
||||||
"Scheme": 1,
|
|
||||||
"Host": "",
|
|
||||||
"Port": 3468,
|
|
||||||
"Username": "",
|
|
||||||
"Password": "",
|
|
||||||
"Proto": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Scheme": 3,
|
|
||||||
"Host": "",
|
|
||||||
"Port": 3468,
|
|
||||||
"Username": "",
|
|
||||||
"Password": "",
|
|
||||||
"Proto": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"SignalAddr": "",
|
|
||||||
"WgAddr": "",
|
|
||||||
"WgIface": ""
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Wiretrustee Service
|
|
||||||
After=multi-user.target network-online.target
|
|
||||||
Wants=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
ExecStart=/usr/local/bin/wiretrustee up --config /etc/wiretrustee/config.json --log-level debug
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
FROM gcr.io/distroless/base:debug
|
FROM gcr.io/distroless/base:debug
|
||||||
ENTRYPOINT [ "/go/bin/wiretrustee-signal","run" ]
|
ENTRYPOINT [ "/go/bin/wiretrustee-signal","run" ]
|
||||||
|
CMD ["--log-file", "console"]
|
||||||
COPY wiretrustee-signal /go/bin/wiretrustee-signal
|
COPY wiretrustee-signal /go/bin/wiretrustee-signal
|
||||||
@@ -18,6 +18,7 @@ Flags:
|
|||||||
|
|
||||||
Global Flags:
|
Global Flags:
|
||||||
--log-level string (default "info")
|
--log-level string (default "info")
|
||||||
|
--log-file string sets Wiretrustee log path. If console is specified the the log will be output to stdout (default "/var/log/wiretrustee/management.log")
|
||||||
```
|
```
|
||||||
## Running the Signal service (Docker)
|
## Running the Signal service (Docker)
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,20 @@ func NewClient(ctx context.Context, addr string, key wgtypes.Key, tlsEnabled boo
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//defaultBackoff is a basic backoff mechanism for general issues
|
||||||
|
func defaultBackoff(ctx context.Context) backoff.BackOff {
|
||||||
|
return backoff.WithContext(&backoff.ExponentialBackOff{
|
||||||
|
InitialInterval: 800 * time.Millisecond,
|
||||||
|
RandomizationFactor: backoff.DefaultRandomizationFactor,
|
||||||
|
Multiplier: backoff.DefaultMultiplier,
|
||||||
|
MaxInterval: 30 * time.Second,
|
||||||
|
MaxElapsedTime: 24 * 3 * time.Hour, //stop after 3 days trying
|
||||||
|
Stop: backoff.Stop,
|
||||||
|
Clock: backoff.SystemClock,
|
||||||
|
}, ctx)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Receive Connects to the Signal Exchange message stream and starts receiving messages.
|
// Receive Connects to the Signal Exchange message stream and starts receiving messages.
|
||||||
// The messages will be handled by msgHandler function provided.
|
// 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)
|
// This function runs a goroutine underneath and reconnects to the Signal Exchange if errors occur (e.g. Exchange restart)
|
||||||
@@ -83,20 +97,13 @@ func (c *Client) Receive(msgHandler func(msg *proto.Message) error) {
|
|||||||
c.connWg.Add(1)
|
c.connWg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
|
||||||
var backOff = &backoff.ExponentialBackOff{
|
var backOff = defaultBackoff(c.ctx)
|
||||||
InitialInterval: backoff.DefaultInitialInterval,
|
|
||||||
RandomizationFactor: backoff.DefaultRandomizationFactor,
|
|
||||||
Multiplier: backoff.DefaultMultiplier,
|
|
||||||
MaxInterval: 3 * time.Second,
|
|
||||||
MaxElapsedTime: time.Duration(0), //never stop
|
|
||||||
Stop: backoff.Stop,
|
|
||||||
Clock: backoff.SystemClock,
|
|
||||||
}
|
|
||||||
|
|
||||||
operation := func() error {
|
operation := func() error {
|
||||||
|
|
||||||
err := c.connect(c.key.PublicKey().String(), msgHandler)
|
err := c.connect(c.key.PublicKey().String(), msgHandler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("disconnected from the Signal Exchange due to an error %s. Retrying ... ", err)
|
log.Warnf("disconnected from the Signal Exchange due to an error: %v", err)
|
||||||
c.connWg.Add(1)
|
c.connWg.Add(1)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -107,7 +114,7 @@ func (c *Client) Receive(msgHandler func(msg *proto.Message) error) {
|
|||||||
|
|
||||||
err := backoff.Retry(operation, backOff)
|
err := backoff.Retry(operation, backOff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("error while communicating with the Signal Exchange %s ", err)
|
log.Errorf("exiting Signal Service connection retry loop due to unrecoverable error: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -14,7 +14,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logLevel string
|
logLevel string
|
||||||
|
defaultLogFile string
|
||||||
|
logFile string
|
||||||
|
|
||||||
rootCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
Use: "wiretrustee-signal",
|
Use: "wiretrustee-signal",
|
||||||
@@ -33,10 +35,14 @@ func Execute() error {
|
|||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
stopCh = make(chan int)
|
stopCh = make(chan int)
|
||||||
|
defaultLogFile = "/var/log/wiretrustee/signal.log"
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
defaultLogFile = os.Getenv("PROGRAMDATA") + "\\Wiretrustee\\" + "signal.log"
|
||||||
|
}
|
||||||
|
|
||||||
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "")
|
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "")
|
||||||
|
rootCmd.PersistentFlags().StringVar(&logFile, "log-file", defaultLogFile, "sets Wiretrustee log path. If console is specified the the log will be output to stdout")
|
||||||
rootCmd.AddCommand(runCmd)
|
rootCmd.AddCommand(runCmd)
|
||||||
InitLog(logLevel)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupCloseHandler handles SIGTERM signal and exits with success
|
// SetupCloseHandler handles SIGTERM signal and exits with success
|
||||||
@@ -50,13 +56,3 @@ func SetupCloseHandler() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitLog parses and sets log-level input
|
|
||||||
func InitLog(logLevel string) {
|
|
||||||
level, err := log.ParseLevel(logLevel)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed parsing log-level %s: %s", logLevel, err)
|
|
||||||
os.Exit(ExitSetupFailed)
|
|
||||||
}
|
|
||||||
log.SetLevel(level)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/wiretrustee/wiretrustee/encryption"
|
"github.com/wiretrustee/wiretrustee/encryption"
|
||||||
"github.com/wiretrustee/wiretrustee/signal/proto"
|
"github.com/wiretrustee/wiretrustee/signal/proto"
|
||||||
"github.com/wiretrustee/wiretrustee/signal/server"
|
"github.com/wiretrustee/wiretrustee/signal/server"
|
||||||
|
"github.com/wiretrustee/wiretrustee/util"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
@@ -39,6 +40,10 @@ var (
|
|||||||
Short: "start Wiretrustee Signal Server daemon",
|
Short: "start Wiretrustee Signal Server daemon",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
err := util.InitLog(logLevel, logFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed initializing log %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
var opts []grpc.ServerOption
|
var opts []grpc.ServerOption
|
||||||
if signalLetsencryptDomain != "" {
|
if signalLetsencryptDomain != "" {
|
||||||
|
|||||||
37
util/duration.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//Duration is used strictly for JSON requests/responses due to duration marshalling issues
|
||||||
|
type Duration struct {
|
||||||
|
time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Duration) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(d.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Duration) UnmarshalJSON(b []byte) error {
|
||||||
|
var v interface{}
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch value := v.(type) {
|
||||||
|
case float64:
|
||||||
|
d.Duration = time.Duration(value)
|
||||||
|
return nil
|
||||||
|
case string:
|
||||||
|
var err error
|
||||||
|
d.Duration, err = time.ParseDuration(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return errors.New("invalid duration")
|
||||||
|
}
|
||||||
|
}
|
||||||
39
util/log.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitLog parses and sets log-level input
|
||||||
|
func InitLog(logLevel string, logPath string) error {
|
||||||
|
level, err := log.ParseLevel(logLevel)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed parsing log-level %s: %s", logLevel, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if logPath != "" && logPath != "console" {
|
||||||
|
lumberjackLogger := &lumberjack.Logger{
|
||||||
|
// Log file absolute path, os agnostic
|
||||||
|
Filename: filepath.ToSlash(logPath),
|
||||||
|
MaxSize: 5, // MB
|
||||||
|
MaxBackups: 10,
|
||||||
|
MaxAge: 30, // days
|
||||||
|
Compress: true,
|
||||||
|
}
|
||||||
|
log.SetOutput(io.Writer(lumberjackLogger))
|
||||||
|
}
|
||||||
|
|
||||||
|
logFormatter := new(log.TextFormatter)
|
||||||
|
logFormatter.TimestampFormat = time.RFC3339 // or RFC3339
|
||||||
|
logFormatter.FullTimestamp = true
|
||||||
|
|
||||||
|
log.SetFormatter(logFormatter)
|
||||||
|
log.SetLevel(level)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||