Refactor Interface package and update windows driver (#192)

* script to generate syso files

* test wireguard-windows driver package

* set int log

* add windows test

* add windows test

* verbose bash

* use cd

* move checkout

* exit 0

* removed tty flag

* artifact path

* fix tags and add cache

* fix cache

* fix cache

* test dir

* restore artifacts in the root

* try dll file

* try dll file

* copy dll

* typo in copy dll

* compile test

* checkout first

* updated cicd

* fix add address issue and gen GUID

* psexec typo

* accept eula

* mod tidy before tests

* regular test exec and verbose test with psexec

* test all

* return WGInterface Interface

* use WgIfaceName and timeout after 30 seconds

* different ports and validate connect 2 peers

* Use time.After for timeout and close interface

* Use time.After for testing connect peers

* WG Interface struct

* Update engine and parse address

* refactor Linux create and assignAddress

* NewWGIface and configuration methods

* Update proxy with interface methods

* update up command test

* resolve lint warnings

* remove psexec test

* close copied files

* add goos before build

* run tests on mac,windows and linux

* cache by testing os

* run on push

* fix indentation

* adjust test timeouts

* remove parallel flag

* mod tidy before test

* ignore syso files

* removed functions and renamed vars

* different IPs for connect peers test

* Generate syso with DLL

* Single Close method

* use port from test constant

* test: remove wireguard interfaces after finishing engine test

* use load_wgnt_from_rsrc

Co-authored-by: braginini <bangvalo@gmail.com>
This commit is contained in:
Maycon Santos 2022-01-17 13:01:58 +00:00 committed by GitHub
parent afb302d5e7
commit 64f2d295a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 687 additions and 445 deletions

View File

@ -1,9 +1,5 @@
on:
push:
branches:
- main
pull_request:
name: Test Build On Platforms name: Test Build On Platforms
on: push
jobs: jobs:
test_build: test_build:
strategy: strategy:
@ -21,15 +17,15 @@ jobs:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
- name: Cache Go modules - name: Cache Go modules
uses: actions/cache@v1 uses: actions/cache@v2
with: with:
path: ~/go/pkg/mod path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go-test-${{ matrix.os }}-${{ hashFiles('**/go.sum') }}
restore-keys: | restore-keys: |
${{ runner.os }}-go- ${{ runner.os }}-go-
- name: Install modules - name: Install modules
run: go mod tidy run: GOOS=${{ matrix.os }} go mod tidy
- name: run build client - name: run build client
run: GOOS=${{ matrix.os }} go build . run: GOOS=${{ matrix.os }} go build .

View File

@ -0,0 +1,29 @@
name: Test Code Darwin
on: push
jobs:
test:
strategy:
matrix:
go-version: [1.17.x]
runs-on: macos-latest
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Cache Go modules
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: macos-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
macos-go-
- name: Install modules
run: go mod tidy
- name: Test
run: GOBIN=$(which go) && sudo --preserve-env=GOROOT $GOBIN test ./...

View File

@ -1,9 +1,5 @@
on: name: Test Code Linux
push: on: push
branches:
- main
pull_request:
name: Test Code
jobs: jobs:
test: test:
strategy: strategy:
@ -27,7 +23,20 @@ jobs:
$(whoami) soft nofile 65535 $(whoami) soft nofile 65535
$(whoami) hard nofile 65535 $(whoami) hard nofile 65535
EOF EOF
- name: Cache Go modules
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Install modules
run: go mod tidy
- name: Test - name: Test
run: GOBIN=$(which go) && sudo --preserve-env=GOROOT $GOBIN test -p 1 ./... run: GOBIN=$(which go) && sudo --preserve-env=GOROOT $GOBIN test ./...

View File

@ -0,0 +1,51 @@
name: Test Code Windows
on: push
jobs:
pre:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- run: bash -x wireguard_nt.sh
working-directory: client
- uses: actions/upload-artifact@v2
with:
name: syso
path: client/*.syso
retention-days: 1
test:
needs: pre
strategy:
matrix:
go-version: [1.17.x]
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- uses: actions/cache@v2
with:
path: |
%LocalAppData%\go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- uses: actions/download-artifact@v2
with:
name: syso
path: iface\
- name: Install modules
run: go mod tidy
- name: Test build
run: go test -tags=load_wgnt_from_rsrc ./...

View File

@ -1,9 +1,5 @@
name: golangci-lint name: golangci-lint
on: on: push
push:
branches:
- main
pull_request:
jobs: jobs:
golangci: golangci:
name: lint name: lint

View File

@ -14,6 +14,10 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
fetch-depth: 0 # It is required for GoReleaser to work properly fetch-depth: 0 # It is required for GoReleaser to work properly
- name: Generate syso with DLL
run: bash -x wireguard_nt.sh
working-directory: client
- -
name: Set up Go name: Set up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2

3
.gitignore vendored
View File

@ -5,4 +5,5 @@ dist/
conf.json conf.json
http-cmds.sh http-cmds.sh
infrastructure_files/management.json infrastructure_files/management.json
infrastructure_files/docker-compose.yml infrastructure_files/docker-compose.yml
*.syso

View File

@ -26,7 +26,7 @@ builds:
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser - -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
mod_timestamp: '{{ .CommitTimestamp }}' mod_timestamp: '{{ .CommitTimestamp }}'
tags: tags:
- load_wintun_from_rsrc - load_wgnt_from_rsrc
- id: wiretrustee-mgmt - id: wiretrustee-mgmt
dir: management dir: management

View File

@ -60,7 +60,7 @@ func TestLogin(t *testing.T) {
} }
if actualConf.WgIface != iface.WgInterfaceDefault { if actualConf.WgIface != iface.WgInterfaceDefault {
t.Errorf("expected WgIface %s got %s", iface.WgInterfaceDefault, actualConf.WgIface) t.Errorf("expected WgIfaceName %s got %s", iface.WgInterfaceDefault, actualConf.WgIface)
} }
if len(actualConf.PrivateKey) == 0 { if len(actualConf.PrivateKey) == 0 {

View File

@ -64,7 +64,7 @@ func createEngineConfig(key wgtypes.Key, config *internal.Config, peerConfig *mg
} }
engineConf := &internal.EngineConfig{ engineConf := &internal.EngineConfig{
WgIface: config.WgIface, WgIfaceName: config.WgIface,
WgAddr: peerConfig.Address, WgAddr: peerConfig.Address,
IFaceBlackList: iFaceBlackList, IFaceBlackList: iFaceBlackList,
WgPrivateKey: key, WgPrivateKey: key,

View File

@ -36,7 +36,7 @@ func TestUp_Start(t *testing.T) {
func TestUp(t *testing.T) { func TestUp(t *testing.T) {
defer iface.Close("wt0") //defer iface.Close("wt0")
tempDir := t.TempDir() tempDir := t.TempDir()
confPath := tempDir + "/config.json" confPath := tempDir + "/config.json"
@ -53,6 +53,8 @@ func TestUp(t *testing.T) {
"A2C8E62B-38F5-4553-B31E-DD66C696CEBB", "A2C8E62B-38F5-4553-B31E-DD66C696CEBB",
"--management-url", "--management-url",
mgmtURL.String(), mgmtURL.String(),
"--log-level",
"debug",
"--log-file", "--log-file",
"console", "console",
}) })
@ -63,20 +65,23 @@ func TestUp(t *testing.T) {
} }
}() }()
exists := false timeout := 15 * time.Second
for start := time.Now(); time.Since(start) < 15*time.Second; { timeoutChannel := time.After(timeout)
for {
select {
case <-timeoutChannel:
t.Fatalf("expected wireguard interface %s to be created before %s", iface.WgInterfaceDefault, timeout.String())
default:
}
e, err := iface.Exists(iface.WgInterfaceDefault) e, err := iface.Exists(iface.WgInterfaceDefault)
if err != nil { if err != nil {
continue continue
} }
if err != nil {
continue
}
if *e { if *e {
exists = true
break break
} }
}
if !exists {
t.Errorf("expected wireguard interface %s to be created", iface.WgInterfaceDefault)
} }
} }

View File

@ -21,6 +21,7 @@ import (
// PeerConnectionTimeoutMax is a timeout of an initial connection attempt to a remote peer. // PeerConnectionTimeoutMax is a timeout of an initial connection attempt to a remote peer.
// E.g. this peer will wait PeerConnectionTimeoutMax for the remote peer to respond, if not successful then it will retry the connection attempt. // E.g. this peer will wait PeerConnectionTimeoutMax for the remote peer to respond, if not successful then it will retry the connection attempt.
// Todo pass timeout at EnginConfig
const PeerConnectionTimeoutMax = 45000 //ms const PeerConnectionTimeoutMax = 45000 //ms
const PeerConnectionTimeoutMin = 30000 //ms const PeerConnectionTimeoutMin = 30000 //ms
@ -28,8 +29,8 @@ const WgPort = 51820
// EngineConfig is a config for the Engine // EngineConfig is a config for the Engine
type EngineConfig struct { type EngineConfig struct {
WgPort int WgPort int
WgIface string WgIfaceName 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)
@ -61,6 +62,8 @@ type Engine struct {
cancel context.CancelFunc cancel context.CancelFunc
ctx context.Context ctx context.Context
wgInterface iface.WGIface
} }
// Peer is an instance of the Connection Peer // Peer is an instance of the Connection Peer
@ -93,11 +96,13 @@ func (e *Engine) Stop() error {
return err return err
} }
log.Debugf("removing Wiretrustee interface %s", e.config.WgIface) log.Debugf("removing Wiretrustee interface %s", e.config.WgIfaceName)
err = iface.Close(e.config.WgIface) if e.wgInterface.Interface != nil {
if err != nil { err = e.wgInterface.Close()
log.Errorf("failed closing Wiretrustee interface %s %v", e.config.WgIface, err) if err != nil {
return err log.Errorf("failed closing Wiretrustee interface %s %v", e.config.WgIfaceName, err)
return err
}
} }
log.Infof("stopped Wiretrustee Engine") log.Infof("stopped Wiretrustee Engine")
@ -112,19 +117,26 @@ func (e *Engine) Start() error {
e.syncMsgMux.Lock() e.syncMsgMux.Lock()
defer e.syncMsgMux.Unlock() defer e.syncMsgMux.Unlock()
wgIface := e.config.WgIface wgIfaceName := e.config.WgIfaceName
wgAddr := e.config.WgAddr wgAddr := e.config.WgAddr
myPrivateKey := e.config.WgPrivateKey myPrivateKey := e.config.WgPrivateKey
var err error
err := iface.Create(wgIface, wgAddr) e.wgInterface, err = iface.NewWGIface(wgIfaceName, wgAddr, iface.DefaultMTU)
if err != nil { if err != nil {
log.Errorf("failed creating interface %s: [%s]", wgIface, err.Error()) log.Errorf("failed creating wireguard interface instance %s: [%s]", wgIfaceName, err.Error())
return err return err
} }
err = iface.Configure(wgIface, myPrivateKey.String(), e.config.WgPort) err = e.wgInterface.Create()
if err != nil { if err != nil {
log.Errorf("failed configuring Wireguard interface [%s]: %s", wgIface, err.Error()) log.Errorf("failed creating tunnel interface %s: [%s]", wgIfaceName, err.Error())
return err
}
err = e.wgInterface.Configure(myPrivateKey.String(), e.config.WgPort)
if err != nil {
log.Errorf("failed configuring Wireguard interface [%s]: %s", wgIfaceName, err.Error())
return err return err
} }
@ -399,7 +411,7 @@ func (e Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, er
proxyConfig := proxy.Config{ proxyConfig := proxy.Config{
RemoteKey: pubKey, RemoteKey: pubKey,
WgListenAddr: fmt.Sprintf("127.0.0.1:%d", e.config.WgPort), WgListenAddr: fmt.Sprintf("127.0.0.1:%d", e.config.WgPort),
WgInterface: e.config.WgIface, WgInterface: e.wgInterface,
AllowedIps: allowedIPs, AllowedIps: allowedIPs,
PreSharedKey: e.config.PreSharedKey, PreSharedKey: e.config.PreSharedKey,
} }

View File

@ -48,7 +48,11 @@ func TestEngine_MultiplePeers(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer func() {
os.Remove(filepath.Join(dir, "store.json")) //nolint err = os.Remove(filepath.Join(dir, "store.json")) //nolint
if err != nil {
t.Fatal(err)
return
}
}() }()
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -102,25 +106,43 @@ func TestEngine_MultiplePeers(t *testing.T) {
// wait until all have been created and started // wait until all have been created and started
wg.Wait() wg.Wait()
// check whether all the peer have expected peers connected // check whether all the peer have expected peers connected
expectedConnected := numPeers * (numPeers - 1) expectedConnected := numPeers * (numPeers - 1)
// adjust according to timeouts
timeout := 50 * time.Second
timeoutChan := time.After(timeout)
for { for {
select {
case <-timeoutChan:
t.Fatalf("waiting for expected connections timeout after %s", timeout.String())
return
default:
}
time.Sleep(time.Second) time.Sleep(time.Second)
totalConnected := 0 totalConnected := 0
for _, engine := range engines { for _, engine := range engines {
totalConnected = totalConnected + len(engine.GetConnectedPeers()) totalConnected = totalConnected + len(engine.GetConnectedPeers())
} }
if totalConnected == expectedConnected { if totalConnected == expectedConnected {
log.Debugf("total connected=%d", totalConnected)
break break
} }
log.Infof("total connected=%d", totalConnected) log.Infof("total connected=%d", totalConnected)
} }
// cleanup test
for _, peerEngine := range engines {
errStop := peerEngine.Stop()
if errStop != nil {
log.Infoln("got error trying to close testing peers engine: ", errStop)
}
}
} }
func createEngine(ctx context.Context, cancel context.CancelFunc, setupKey string, i int) (*Engine, error) { func createEngine(ctx context.Context, cancel context.CancelFunc, setupKey string, i int) (*Engine, error) {
key, err := wgtypes.GenerateKey() key, err := wgtypes.GeneratePrivateKey()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -151,7 +173,7 @@ func createEngine(ctx context.Context, cancel context.CancelFunc, setupKey strin
} }
conf := &EngineConfig{ conf := &EngineConfig{
WgIface: ifaceName, WgIfaceName: ifaceName,
WgAddr: resp.PeerConfig.Address, WgAddr: resp.PeerConfig.Address,
WgPrivateKey: key, WgPrivateKey: key,
WgPort: 33100 + i, WgPort: 33100 + i,

View File

@ -1,6 +1,7 @@
package proxy package proxy
import ( import (
"github.com/wiretrustee/wiretrustee/iface"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"io" "io"
"net" "net"
@ -12,7 +13,7 @@ const DefaultWgKeepAlive = 25 * time.Second
type Config struct { type Config struct {
WgListenAddr string WgListenAddr string
RemoteKey string RemoteKey string
WgInterface string WgInterface iface.WGIface
AllowedIps string AllowedIps string
PreSharedKey *wgtypes.Key PreSharedKey *wgtypes.Key
} }

View File

@ -3,7 +3,6 @@ package proxy
import ( import (
"context" "context"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/wiretrustee/wiretrustee/iface"
"net" "net"
) )
@ -25,9 +24,13 @@ func NewWireguardProxy(config Config) *WireguardProxy {
} }
func (p *WireguardProxy) updateEndpoint() error { func (p *WireguardProxy) updateEndpoint() error {
udpAddr, err := net.ResolveUDPAddr(p.localConn.LocalAddr().Network(), p.localConn.LocalAddr().String())
if err != nil {
return err
}
// add local proxy connection as a Wireguard peer // add local proxy connection as a Wireguard peer
err := iface.UpdatePeer(p.config.WgInterface, p.config.RemoteKey, p.config.AllowedIps, DefaultWgKeepAlive, err = p.config.WgInterface.UpdatePeer(p.config.RemoteKey, p.config.AllowedIps, DefaultWgKeepAlive,
p.localConn.LocalAddr().String(), p.config.PreSharedKey) udpAddr, p.config.PreSharedKey)
if err != nil { if err != nil {
return err return err
} }
@ -65,7 +68,7 @@ func (p *WireguardProxy) Close() error {
return err return err
} }
} }
err := iface.RemovePeer(p.config.WgInterface, p.config.RemoteKey) err := p.config.WgInterface.RemovePeer(p.config.RemoteKey)
if err != nil { if err != nil {
return err return err
} }

View File

@ -5,5 +5,5 @@
#define STRINGIZE(x) #x #define STRINGIZE(x) #x
#define EXPAND(x) STRINGIZE(x) #define EXPAND(x) STRINGIZE(x)
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST manifest.xml CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST manifest.xml
wintun.dll RCDATA wintun.dll 7 ICON ui/wiretrustee.ico
wireguard.dll RCDATA wireguard.dll

Binary file not shown.

27
client/wireguard_nt.sh Normal file
View File

@ -0,0 +1,27 @@
#!/bin/bash
ldir=$PWD
tmp_dir_path=$ldir/.distfiles
winnt=wireguard-nt.zip
download_file_path=$tmp_dir_path/$winnt
download_url=https://download.wireguard.com/wireguard-nt/wireguard-nt-0.10.1.zip
download_sha=772c0b1463d8d2212716f43f06f4594d880dea4f735165bd68e388fc41b81605
function resources_windows(){
cmd=$1
arch=$2
out=$3
docker run -i --rm -v $PWD:$PWD -w $PWD mstorsjo/llvm-mingw:latest $cmd -O coff -c 65001 -I $tmp_dir_path/wireguard-nt/bin/$arch -i resources.rc -o $out
}
mkdir -p $tmp_dir_path
curl -L#o $download_file_path.unverified $download_url
echo "$download_sha $download_file_path.unverified" | sha256sum -c
mv $download_file_path.unverified $download_file_path
mkdir -p .deps
unzip $download_file_path -d $tmp_dir_path
resources_windows i686-w64-mingw32-windres x86 resources_windows_386.syso
resources_windows aarch64-w64-mingw32-windres arm64 resources_windows_arm64.syso
resources_windows x86_64-w64-mingw32-windres amd64 resources_windows_amd64.syso

122
iface/configuration.go Normal file
View File

@ -0,0 +1,122 @@
package iface
import (
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"net"
"time"
)
// configureDevice configures the wireguard device
func (w *WGIface) configureDevice(config wgtypes.Config) error {
wg, err := wgctrl.New()
if err != nil {
return err
}
defer wg.Close()
// validate if device with name exists
_, err = wg.Device(w.Name)
if err != nil {
return err
}
log.Debugf("got Wireguard device %s", w.Name)
return wg.ConfigureDevice(w.Name, config)
}
// Configure configures a Wireguard interface
// The interface must exist before calling this method (e.g. call interface.Create() before)
func (w *WGIface) Configure(privateKey string, port int) error {
log.Debugf("configuring Wireguard interface %s", w.Name)
log.Debugf("adding Wireguard private key")
key, err := wgtypes.ParseKey(privateKey)
if err != nil {
return err
}
fwmark := 0
config := wgtypes.Config{
PrivateKey: &key,
ReplacePeers: true,
FirewallMark: &fwmark,
ListenPort: &port,
}
return w.configureDevice(config)
}
// GetListenPort returns the listening port of the Wireguard endpoint
func (w *WGIface) GetListenPort() (*int, error) {
log.Debugf("getting Wireguard listen port of interface %s", w.Name)
//discover Wireguard current configuration
wg, err := wgctrl.New()
if err != nil {
return nil, err
}
defer wg.Close()
d, err := wg.Device(w.Name)
if err != nil {
return nil, err
}
log.Debugf("got Wireguard device listen port %s, %d", w.Name, d.ListenPort)
return &d.ListenPort, nil
}
// UpdatePeer updates existing Wireguard Peer or creates a new one if doesn't exist
// Endpoint is optional
func (w *WGIface) UpdatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
log.Debugf("updating interface %s peer %s: endpoint %s ", w.Name, peerKey, endpoint)
//parse allowed ips
_, ipNet, err := net.ParseCIDR(allowedIps)
if err != nil {
return err
}
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
if err != nil {
return err
}
peer := wgtypes.PeerConfig{
PublicKey: peerKeyParsed,
ReplaceAllowedIPs: true,
AllowedIPs: []net.IPNet{*ipNet},
PersistentKeepaliveInterval: &keepAlive,
PresharedKey: preSharedKey,
Endpoint: endpoint,
}
config := wgtypes.Config{
Peers: []wgtypes.PeerConfig{peer},
}
return w.configureDevice(config)
}
// RemovePeer removes a Wireguard Peer from the interface iface
func (w *WGIface) RemovePeer(peerKey string) error {
log.Debugf("Removing peer %s from interface %s ", peerKey, w.Name)
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
if err != nil {
return err
}
peer := wgtypes.PeerConfig{
PublicKey: peerKeyParsed,
Remove: true,
}
config := wgtypes.Config{
Peers: []wgtypes.PeerConfig{peer},
}
return w.configureDevice(config)
}

View File

@ -1,78 +1,51 @@
package iface package iface
import ( import (
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/wgctrl" "golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"net" "net"
"time" "os"
"runtime"
) )
const ( const (
defaultMTU = 1280 DefaultMTU = 1280
) )
var ( // WGIface represents a interface instance
tunIface tun.Device type WGIface struct {
) Name string
Port int
// CreateWithUserspace Creates a new Wireguard interface, using wireguard-go userspace implementation MTU int
func CreateWithUserspace(iface string, address string) error { Address WGAddress
var err error Interface NetInterface
tunIface, err = tun.CreateTUN(iface, defaultMTU)
if err != nil {
return err
}
// We need to create a wireguard-go device and listen to configuration requests
tunDevice := device.NewDevice(tunIface, conn.NewDefaultBind(), device.NewLogger(device.LogLevelSilent, "[wiretrustee] "))
err = tunDevice.Up()
if err != nil {
return err
}
uapi, err := getUAPI(iface)
if err != nil {
return err
}
go func() {
for {
uapiConn, err := uapi.Accept()
if err != nil {
log.Debugln("uapi Accept failed with error: ", err)
continue
}
go tunDevice.IpcHandle(uapiConn)
}
}()
log.Debugln("UAPI listener started")
err = assignAddr(address, iface)
if err != nil {
return err
}
return nil
} }
// configure peer for the wireguard device // WGAddress Wireguard parsed address
func configureDevice(iface string, config wgtypes.Config) error { type WGAddress struct {
wg, err := wgctrl.New() IP net.IP
if err != nil { Network *net.IPNet
return err }
}
defer wg.Close()
_, err = wg.Device(iface) // NetInterface represents a generic network tunnel interface
if err != nil { type NetInterface interface {
return err Close() error
} }
log.Debugf("got Wireguard device %s", iface)
return wg.ConfigureDevice(iface, config) // NewWGIface Creates a new Wireguard interface instance
func NewWGIface(iface string, address string, mtu int) (WGIface, error) {
wgIface := WGIface{
Name: iface,
MTU: mtu,
}
wgAddress, err := parseAddress(address)
if err != nil {
return wgIface, err
}
wgIface.Address = wgAddress
return wgIface, nil
} }
// Exists checks whether specified Wireguard device exists or not // Exists checks whether specified Wireguard device exists or not
@ -99,140 +72,35 @@ func Exists(iface string) (*bool, error) {
return &exists, nil return &exists, nil
} }
// Configure configures a Wireguard interface // parseAddress parse a string ("1.2.3.4/24") address to WG Address
// The interface must exist before calling this method (e.g. call interface.Create() before) func parseAddress(address string) (WGAddress, error) {
func Configure(iface string, privateKey string, port int) error { ip, network, err := net.ParseCIDR(address)
log.Debugf("configuring Wireguard interface %s", iface)
log.Debugf("adding Wireguard private key")
key, err := wgtypes.ParseKey(privateKey)
if err != nil { if err != nil {
return err return WGAddress{}, err
} }
fwmark := 0 return WGAddress{
config := wgtypes.Config{ IP: ip,
PrivateKey: &key, Network: network,
ReplacePeers: false, }, nil
FirewallMark: &fwmark,
ListenPort: &port,
}
return configureDevice(iface, config)
} }
// GetListenPort returns the listening port of the Wireguard endpoint // Closes the tunnel interface
func GetListenPort(iface string) (*int, error) { func (w *WGIface) Close() error {
log.Debugf("getting Wireguard listen port of interface %s", iface)
//discover Wireguard current configuration err := w.Interface.Close()
wg, err := wgctrl.New()
if err != nil {
return nil, err
}
defer wg.Close()
d, err := wg.Device(iface)
if err != nil {
return nil, err
}
log.Debugf("got Wireguard device listen port %s, %d", iface, d.ListenPort)
return &d.ListenPort, nil
}
// UpdatePeer updates existing Wireguard Peer or creates a new one if doesn't exist
// Endpoint is optional
func UpdatePeer(iface string, peerKey string, allowedIps string, keepAlive time.Duration, endpoint string, preSharedKey *wgtypes.Key) error {
log.Debugf("updating interface %s peer %s: endpoint %s ", iface, peerKey, endpoint)
//parse allowed ips
_, ipNet, err := net.ParseCIDR(allowedIps)
if err != nil { if err != nil {
return err return err
} }
peerKeyParsed, err := wgtypes.ParseKey(peerKey) if runtime.GOOS == "darwin" {
if err != nil { sockPath := "/var/run/wireguard/" + w.Name + ".sock"
return err if _, statErr := os.Stat(sockPath); statErr == nil {
} statErr = os.Remove(sockPath)
peer := wgtypes.PeerConfig{ if statErr != nil {
PublicKey: peerKeyParsed, return statErr
ReplaceAllowedIPs: true, }
AllowedIPs: []net.IPNet{*ipNet}, }
PersistentKeepaliveInterval: &keepAlive,
PresharedKey: preSharedKey,
}
config := wgtypes.Config{
Peers: []wgtypes.PeerConfig{peer},
}
err = configureDevice(iface, config)
if err != nil {
return err
}
if endpoint != "" {
return UpdatePeerEndpoint(iface, peerKey, endpoint)
} }
return nil return nil
} }
// UpdatePeerEndpoint updates a Wireguard interface Peer with the new endpoint
// Used when NAT hole punching was successful and an update of the remote peer endpoint is required
func UpdatePeerEndpoint(iface string, peerKey string, newEndpoint string) error {
log.Debugf("updating peer %s endpoint %s ", peerKey, newEndpoint)
peerAddr, err := net.ResolveUDPAddr("udp4", newEndpoint)
if err != nil {
return err
}
log.Debugf("parsed peer endpoint [%s]", peerAddr.String())
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
if err != nil {
return err
}
peer := wgtypes.PeerConfig{
PublicKey: peerKeyParsed,
ReplaceAllowedIPs: false,
UpdateOnly: true,
Endpoint: peerAddr,
}
config := wgtypes.Config{
Peers: []wgtypes.PeerConfig{peer},
}
return configureDevice(iface, config)
}
// RemovePeer removes a Wireguard Peer from the interface iface
func RemovePeer(iface string, peerKey string) error {
log.Debugf("Removing peer %s from interface %s ", peerKey, iface)
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
if err != nil {
return err
}
peer := wgtypes.PeerConfig{
PublicKey: peerKeyParsed,
Remove: true,
}
config := wgtypes.Config{
Peers: []wgtypes.PeerConfig{peer},
}
return configureDevice(iface, config)
}
// CloseWithUserspace closes the User Space tunnel interface
func CloseWithUserspace() error {
return tunIface.Close()
}

View File

@ -2,62 +2,31 @@ package iface
import ( import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"net"
"os"
"os/exec" "os/exec"
"strings"
) )
// Create Creates a new Wireguard interface, sets a given IP and brings it up. // Create Creates a new Wireguard interface, sets a given IP and brings it up.
func Create(iface string, address string) error { func (w *WGIface) Create() error {
return CreateWithUserspace(iface, address) return w.CreateWithUserspace()
} }
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided // assignAddr Adds IP address to the tunnel interface and network route based on the range provided
func assignAddr(address string, ifaceName string) error { func (w *WGIface) assignAddr() error {
ip := strings.Split(address, "/") //mask,_ := w.Address.Network.Mask.Size()
cmd := exec.Command("ifconfig", ifaceName, "inet", address, ip[0]) //
//address := fmt.Sprintf("%s/%d",w.Address.IP.String() , mask)
cmd := exec.Command("ifconfig", w.Name, "inet", w.Address.IP.String(), w.Address.IP.String())
if out, err := cmd.CombinedOutput(); err != nil { if out, err := cmd.CombinedOutput(); err != nil {
log.Infof("Command: %v failed with output %s and error: ", cmd.String(), out) log.Infof("adding addreess command \"%v\" failed with output %s and error: ", cmd.String(), out)
return err return err
} }
_, resolvedNet, err := net.ParseCIDR(address)
err = addRoute(ifaceName, resolvedNet) routeCmd := exec.Command("route", "add", "-net", w.Address.Network.String(), "-interface", w.Name)
if err != nil { if out, err := routeCmd.CombinedOutput(); err != nil {
log.Infoln("Adding route failed with error:", err) log.Printf("adding route command \"%v\" failed with output %s and error: ", routeCmd.String(), out)
} return err
return nil
}
// addRoute Adds network route based on the range provided
func addRoute(iface string, ipNet *net.IPNet) error {
cmd := exec.Command("route", "add", "-net", ipNet.String(), "-interface", iface)
if out, err := cmd.CombinedOutput(); err != nil {
log.Printf("Command: %v failed with output %s and error: ", cmd.String(), out)
return err
}
return nil
}
// Closes the tunnel interface
func Close(iFace string) error {
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 return nil
} }

View File

@ -1,35 +1,36 @@
package iface package iface
import ( import (
"fmt"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
"os" "os"
) )
type NativeLink struct {
Link *netlink.Link
}
// Create Creates a new Wireguard interface, sets a given IP and brings it up. // Create Creates a new Wireguard interface, sets a given IP and brings it up.
// Will reuse an existing one. // Will reuse an existing one.
func Create(iface string, address string) error { func (w *WGIface) Create() error {
if WireguardModExists() { if WireguardModExists() {
log.Debug("using kernel Wireguard module") log.Debug("using kernel Wireguard module")
return CreateWithKernel(iface, address) return w.CreateWithKernel()
} else { } else {
return CreateWithUserspace(iface, address) return w.CreateWithUserspace()
} }
} }
// CreateWithKernel Creates a new Wireguard interface using kernel Wireguard module. // CreateWithKernel Creates a new Wireguard interface using kernel Wireguard module.
// Works for Linux and offers much better network performance // Works for Linux and offers much better network performance
func CreateWithKernel(iface string, address string) error { func (w *WGIface) CreateWithKernel() error {
attrs := netlink.NewLinkAttrs()
attrs.Name = iface
link := wgLink{ link := newWGLink(w.Name)
attrs: &attrs,
}
// check if interface exists // check if interface exists
l, err := netlink.LinkByName(iface) l, err := netlink.LinkByName(w.Name)
if err != nil { if err != nil {
switch err.(type) { switch err.(type) {
case netlink.LinkNotFoundError: case netlink.LinkNotFoundError:
@ -41,37 +42,39 @@ func CreateWithKernel(iface string, address string) error {
// remove if interface exists // remove if interface exists
if l != nil { if l != nil {
err = netlink.LinkDel(&link) err = netlink.LinkDel(link)
if err != nil { if err != nil {
return err return err
} }
} }
log.Debugf("adding device: %s", iface) log.Debugf("adding device: %s", w.Name)
err = netlink.LinkAdd(&link) err = netlink.LinkAdd(link)
if os.IsExist(err) { if os.IsExist(err) {
log.Infof("interface %s already exists. Will reuse.", iface) log.Infof("interface %s already exists. Will reuse.", w.Name)
} else if err != nil { } else if err != nil {
return err return err
} }
err = assignAddr(address, iface) w.Interface = link
err = w.assignAddr()
if err != nil { if err != nil {
return err return err
} }
// todo do a discovery // todo do a discovery
log.Debugf("setting MTU: %d interface: %s", defaultMTU, iface) log.Debugf("setting MTU: %d interface: %s", w.MTU, w.Name)
err = netlink.LinkSetMTU(&link, defaultMTU) err = netlink.LinkSetMTU(link, w.MTU)
if err != nil { if err != nil {
log.Errorf("error setting MTU on interface: %s", iface) log.Errorf("error setting MTU on interface: %s", w.Name)
return err return err
} }
log.Debugf("bringing up interface: %s", iface) log.Debugf("bringing up interface: %s", w.Name)
err = netlink.LinkSetUp(&link) err = netlink.LinkSetUp(link)
if err != nil { if err != nil {
log.Errorf("error bringing up interface: %s", iface) log.Errorf("error bringing up interface: %s", w.Name)
return err return err
} }
@ -79,39 +82,37 @@ func CreateWithKernel(iface string, address string) error {
} }
// assignAddr Adds IP address to the tunnel interface // assignAddr Adds IP address to the tunnel interface
func assignAddr(address, name string) error { func (w *WGIface) assignAddr() error {
var err error
attrs := netlink.NewLinkAttrs()
attrs.Name = name
link := wgLink{ mask, _ := w.Address.Network.Mask.Size()
attrs: &attrs, address := fmt.Sprintf("%s/%d", w.Address.IP.String(), mask)
}
link := newWGLink(w.Name)
//delete existing addresses //delete existing addresses
list, err := netlink.AddrList(&link, 0) list, err := netlink.AddrList(link, 0)
if err != nil { if err != nil {
return err return err
} }
if len(list) > 0 { if len(list) > 0 {
for _, a := range list { for _, a := range list {
err = netlink.AddrDel(&link, &a) err = netlink.AddrDel(link, &a)
if err != nil { if err != nil {
return err return err
} }
} }
} }
log.Debugf("adding address %s to interface: %s", address, attrs.Name) log.Debugf("adding address %s to interface: %s", address, w.Name)
addr, _ := netlink.ParseAddr(address) addr, _ := netlink.ParseAddr(address)
err = netlink.AddrAdd(&link, addr) err = netlink.AddrAdd(link, addr)
if os.IsExist(err) { if os.IsExist(err) {
log.Infof("interface %s already has the address: %s", attrs.Name, address) log.Infof("interface %s already has the address: %s", w.Name, address)
} else if err != nil { } else if err != nil {
return err return err
} }
// On linux, the link must be brought up // On linux, the link must be brought up
err = netlink.LinkSetUp(&link) err = netlink.LinkSetUp(link)
return err return err
} }
@ -119,28 +120,26 @@ type wgLink struct {
attrs *netlink.LinkAttrs attrs *netlink.LinkAttrs
} }
func newWGLink(name string) *wgLink {
attrs := netlink.NewLinkAttrs()
attrs.Name = name
return &wgLink{
attrs: &attrs,
}
}
// Attrs returns the Wireguard's default attributes // Attrs returns the Wireguard's default attributes
func (w *wgLink) Attrs() *netlink.LinkAttrs { func (l *wgLink) Attrs() *netlink.LinkAttrs {
return w.attrs return l.attrs
} }
// Type returns the interface type // Type returns the interface type
func (w *wgLink) Type() string { func (l *wgLink) Type() string {
return "wireguard" return "wireguard"
} }
// Close closes the tunnel interface // Close deletes the link interface
func Close(iFace string) error { func (l *wgLink) Close() error {
return netlink.LinkDel(l)
if tunIface != nil {
return CloseWithUserspace()
} else {
attrs := netlink.NewLinkAttrs()
attrs.Name = iFace
link := wgLink{
attrs: &attrs,
}
return netlink.LinkDel(&link)
}
} }

View File

@ -12,25 +12,36 @@ import (
// keep darwin compability // keep darwin compability
const ( const (
key = "0PMI6OkB5JmB+Jj/iWWHekuQRx+bipZirWCWKFXexHc=" WgPort = 51000
peerPubKey = "Ok0mC0qlJyXEPKh2UFIpsI2jG0L7LRpC3sLAusSJ5CQ=" )
WgPort = 51820
var (
key string
peerPubKey string
) )
func init() { func init() {
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
privateKey, _ := wgtypes.GeneratePrivateKey()
key = privateKey.String()
peerPrivateKey, _ := wgtypes.GeneratePrivateKey()
peerPubKey = peerPrivateKey.PublicKey().String()
} }
// //
func Test_CreateInterface(t *testing.T) { func Test_CreateInterface(t *testing.T) {
ifaceName := "utun999" ifaceName := "utun999"
wgIP := "10.99.99.1/24" wgIP := "10.99.99.1/24"
err := Create(ifaceName, wgIP) iface, err := NewWGIface(ifaceName, wgIP, DefaultMTU)
if err != nil {
t.Fatal(err)
}
err = iface.Create()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer func() {
err = Close(ifaceName) err = iface.Close()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -46,21 +57,54 @@ func Test_CreateInterface(t *testing.T) {
} }
}() }()
} }
func Test_ConfigureInterface(t *testing.T) {
ifaceName := "utun1000" func Test_Close(t *testing.T) {
wgIP := "10.99.99.10/24" ifaceName := "utun1004"
err := Create(ifaceName, wgIP) wgIP := "10.99.99.50/24"
iface, err := NewWGIface(ifaceName, wgIP, DefaultMTU)
if err != nil {
t.Fatal(err)
}
err = iface.Create()
if err != nil {
t.Fatal(err)
}
wg, err := wgctrl.New()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer func() {
err = Close(ifaceName) err = wg.Close()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
}() }()
err = Configure(ifaceName, key, WgPort) err = iface.Close()
if err != nil {
t.Fatal(err)
}
}
func Test_ConfigureInterface(t *testing.T) {
ifaceName := "utun1000"
wgIP := "10.99.99.10/24"
iface, err := NewWGIface(ifaceName, wgIP, DefaultMTU)
if err != nil {
t.Fatal(err)
}
err = iface.Create()
if err != nil {
t.Fatal(err)
}
defer func() {
err = iface.Close()
if err != nil {
t.Error(err)
}
}()
err = iface.Configure(key, WgPort+1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -88,28 +132,35 @@ func Test_ConfigureInterface(t *testing.T) {
func Test_UpdatePeer(t *testing.T) { func Test_UpdatePeer(t *testing.T) {
ifaceName := "utun1001" ifaceName := "utun1001"
wgIP := "10.99.99.20/24" wgIP := "10.99.99.20/24"
err := Create(ifaceName, wgIP) iface, err := NewWGIface(ifaceName, wgIP, DefaultMTU)
if err != nil {
t.Fatal(err)
}
err = iface.Create()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer func() {
err = Close(ifaceName) err = iface.Close()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
}() }()
err = Configure(ifaceName, key, WgPort) err = iface.Configure(key, WgPort+2)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
keepAlive := 15 * time.Second keepAlive := 15 * time.Second
allowedIP := "10.99.99.2/32" allowedIP := "10.99.99.2/32"
endpoint := "127.0.0.1:9900" endpoint, err := net.ResolveUDPAddr("udp", "127.0.0.1:9900")
err = UpdatePeer(ifaceName, peerPubKey, allowedIP, keepAlive, endpoint, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
peer, err := getPeer(ifaceName, t) err = iface.UpdatePeer(peerPubKey, allowedIP, keepAlive, endpoint, nil)
if err != nil {
t.Fatal(err)
}
peer, err := getPeer(ifaceName, peerPubKey, t)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -117,11 +168,7 @@ func Test_UpdatePeer(t *testing.T) {
t.Fatal("configured peer with mismatched keepalive interval value") t.Fatal("configured peer with mismatched keepalive interval value")
} }
resolvedEndpoint, err := net.ResolveUDPAddr("udp", endpoint) if peer.Endpoint.String() != endpoint.String() {
if err != nil {
t.Fatal(err)
}
if peer.Endpoint.String() != resolvedEndpoint.String() {
t.Fatal("configured peer with mismatched endpoint") t.Fatal("configured peer with mismatched endpoint")
} }
@ -137,104 +184,132 @@ func Test_UpdatePeer(t *testing.T) {
} }
} }
func Test_UpdatePeerEndpoint(t *testing.T) {
ifaceName := "utun1002"
wgIP := "10.99.99.30/24"
err := Create(ifaceName, wgIP)
if err != nil {
t.Fatal(err)
}
defer func() {
err = Close(ifaceName)
if err != nil {
t.Error(err)
}
}()
err = Configure(ifaceName, key, WgPort)
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, nil)
if err != nil {
t.Fatal(err)
}
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 {
t.Fatal(err)
}
if peer.Endpoint.String() != newEndpoint {
t.Fatal("configured peer with mismatched endpoint")
}
}
func Test_RemovePeer(t *testing.T) { func Test_RemovePeer(t *testing.T) {
ifaceName := "utun1003" ifaceName := "utun1003"
wgIP := "10.99.99.40/24" wgIP := "10.99.99.40/24"
err := Create(ifaceName, wgIP) iface, err := NewWGIface(ifaceName, wgIP, DefaultMTU)
if err != nil {
t.Fatal(err)
}
err = iface.Create()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer func() {
err = Close(ifaceName) err = iface.Close()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
}() }()
err = Configure(ifaceName, key, WgPort) err = iface.Configure(key, WgPort+3)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
keepAlive := 15 * time.Second keepAlive := 15 * time.Second
allowedIP := "10.99.99.2/32" allowedIP := "10.99.99.2/32"
endpoint := "127.0.0.1:9900"
err = UpdatePeer(ifaceName, peerPubKey, allowedIP, keepAlive, endpoint, nil) err = iface.UpdatePeer(peerPubKey, allowedIP, keepAlive, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = RemovePeer(ifaceName, peerPubKey) err = iface.RemovePeer(peerPubKey)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, err = getPeer(ifaceName, t) _, err = getPeer(ifaceName, peerPubKey, 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) {
ifaceName := "utun1004" func Test_ConnectPeers(t *testing.T) {
wgIP := "10.99.99.50/24" peer1ifaceName := fmt.Sprintf("utun%d", 400)
err := Create(ifaceName, wgIP) peer1wgIP := "10.99.99.100/24"
peer1Key, _ := wgtypes.GeneratePrivateKey()
peer1Port := WgPort + 4
peer1endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer1Port))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
wg, err := wgctrl.New()
peer2ifaceName := fmt.Sprintf("utun%d", 500)
peer2wgIP := "10.99.99.200/24"
peer2Key, _ := wgtypes.GeneratePrivateKey()
peer2Port := WgPort + 5
peer2endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer2Port))
if err != nil {
t.Fatal(err)
}
keepAlive := 1 * time.Second
iface1, err := NewWGIface(peer1ifaceName, peer1wgIP, DefaultMTU)
if err != nil {
t.Fatal(err)
}
err = iface1.Create()
if err != nil {
t.Fatal(err)
}
iface2, err := NewWGIface(peer2ifaceName, peer2wgIP, DefaultMTU)
if err != nil {
t.Fatal(err)
}
err = iface2.Create()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer func() {
err = wg.Close() err = iface1.Close()
if err != nil {
t.Error(err)
}
err = iface2.Close()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
}() }()
err = iface1.Configure(peer1Key.String(), peer1Port)
err = Close(ifaceName)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = iface2.Configure(peer2Key.String(), peer2Port)
if err != nil {
t.Fatal(err)
}
err = iface1.UpdatePeer(peer2Key.PublicKey().String(), peer2wgIP, keepAlive, peer2endpoint, nil)
if err != nil {
t.Fatal(err)
}
err = iface2.UpdatePeer(peer1Key.PublicKey().String(), peer1wgIP, keepAlive, peer1endpoint, nil)
if err != nil {
t.Fatal(err)
}
timeout := 10 * time.Second
timeoutChannel := time.After(timeout)
for {
select {
case <-timeoutChannel:
t.Fatalf("waiting for peer handshake timeout after %s", timeout.String())
default:
}
peer, gpErr := getPeer(peer1ifaceName, peer2Key.PublicKey().String(), t)
if gpErr != nil {
t.Fatal(gpErr)
}
if !peer.LastHandshakeTime.IsZero() {
t.Log("peers successfully handshake")
break
}
}
} }
func getPeer(ifaceName string, t *testing.T) (wgtypes.Peer, error) {
func getPeer(ifaceName, peerPubKey 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 {

View File

@ -1,12 +1,58 @@
//go:build linux || darwin
// +build linux darwin // +build linux darwin
package iface package iface
import ( import (
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/ipc" "golang.zx2c4.com/wireguard/ipc"
"golang.zx2c4.com/wireguard/tun"
"net" "net"
) )
// CreateWithUserspace Creates a new Wireguard interface, using wireguard-go userspace implementation
func (w *WGIface) CreateWithUserspace() error {
tunIface, err := tun.CreateTUN(w.Name, w.MTU)
if err != nil {
return err
}
w.Interface = tunIface
// We need to create a wireguard-go device and listen to configuration requests
tunDevice := device.NewDevice(tunIface, conn.NewDefaultBind(), device.NewLogger(device.LogLevelSilent, "[wiretrustee] "))
err = tunDevice.Up()
if err != nil {
return err
}
uapi, err := getUAPI(w.Name)
if err != nil {
return err
}
go func() {
for {
uapiConn, uapiErr := uapi.Accept()
if uapiErr != nil {
log.Traceln("uapi Accept failed with error: ", uapiErr)
continue
}
go tunDevice.IpcHandle(uapiConn)
}
}()
log.Debugln("UAPI listener started")
err = w.assignAddr()
if err != nil {
return err
}
return nil
}
// getUAPI returns a Listener // getUAPI returns a Listener
func getUAPI(iface string) (net.Listener, error) { func getUAPI(iface string) (net.Listener, error) {
tunSock, err := ipc.UAPIOpen(iface) tunSock, err := ipc.UAPIOpen(iface)

View File

@ -1,46 +1,47 @@
package iface package iface
import ( import (
"fmt"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/ipc" "golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/windows/driver"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"net" "net"
) )
// Create Creates a new Wireguard interface, sets a given IP and brings it up. // Create Creates a new Wireguard interface, sets a given IP and brings it up.
func Create(iface string, address string) error { func (w *WGIface) Create() error {
return CreateWithUserspace(iface, address)
WintunStaticRequestedGUID, _ := windows.GenerateGUID()
adapter, err := driver.CreateAdapter(w.Name, "WireGuard", &WintunStaticRequestedGUID)
if err != nil {
err = fmt.Errorf("error creating adapter: %w", err)
return err
}
w.Interface = adapter
luid := adapter.LUID()
err = adapter.SetLogging(driver.AdapterLogOn)
if err != nil {
err = fmt.Errorf("Error enabling adapter logging: %w", err)
return err
}
err = adapter.SetAdapterState(driver.AdapterStateUp)
if err != nil {
return err
}
state, _ := luid.GUID()
log.Debugln("device guid: ", state.String())
return w.assignAddr(luid)
} }
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided // assignAddr Adds IP address to the tunnel interface and network route based on the range provided
func assignAddr(address string, ifaceName string) error { func (w *WGIface) assignAddr(luid winipcfg.LUID) error {
nativeTunDevice := tunIface.(*tun.NativeTun) log.Debugf("adding address %s to interface: %s", w.Address.IP, w.Name)
luid := winipcfg.LUID(nativeTunDevice.LUID()) err := luid.SetIPAddresses([]net.IPNet{{w.Address.IP, w.Address.Network.Mask}})
ip, ipnet, _ := net.ParseCIDR(address)
log.Debugf("adding address %s to interface: %s", address, ifaceName)
err := luid.SetIPAddresses([]net.IPNet{{ip, ipnet.Mask}})
if err != nil { if err != nil {
return err return err
} }
log.Debugf("adding Routes to interface: %s", ifaceName)
err = luid.SetRoutes([]*winipcfg.RouteData{{*ipnet, ipnet.IP, 0}})
if err != nil {
return err
}
return nil return nil
} }
// getUAPI returns a Listener
func getUAPI(iface string) (net.Listener, error) {
return ipc.UAPIListen(iface)
}
// Closes the tunnel interface
func Close(iFace string) error {
return CloseWithUserspace()
}

View File

@ -93,6 +93,12 @@ var _ = Describe("Client", func() {
_, err = io.Copy(hashDst, dstFile) _, err = io.Copy(hashDst, dstFile)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
err = srcFile.Close()
Expect(err).NotTo(HaveOccurred())
err = dstFile.Close()
Expect(err).NotTo(HaveOccurred())
Expect(hex.EncodeToString(hashSrc.Sum(nil)[:16])).To(BeEquivalentTo(hex.EncodeToString(hashDst.Sum(nil)[:16]))) Expect(hex.EncodeToString(hashSrc.Sum(nil)[:16])).To(BeEquivalentTo(hex.EncodeToString(hashDst.Sum(nil)[:16])))
}) })
}) })