mirror of
https://github.com/netbirdio/netbird.git
synced 2026-05-06 17:08:53 +00:00
Compare commits
14 Commits
v0.25.1
...
yury/add-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f1796e1c0 | ||
|
|
13c9765c6e | ||
|
|
b0729c5944 | ||
|
|
1de3bb5420 | ||
|
|
163933d429 | ||
|
|
875a2e2b63 | ||
|
|
fd8bba6aa3 | ||
|
|
86908eee58 | ||
|
|
c1caec3fcb | ||
|
|
b28b8fce50 | ||
|
|
f780f17f85 | ||
|
|
5903715a61 | ||
|
|
5469de53c5 | ||
|
|
bc3d647d6b |
15
.github/workflows/golang-test-darwin.yml
vendored
15
.github/workflows/golang-test-darwin.yml
vendored
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- yury/add-postgresql-store
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
@@ -14,8 +15,20 @@ jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
store: ['jsonfile', 'sqlite']
|
||||
store: ['jsonfile', 'sqlite', 'postgresql']
|
||||
runs-on: macos-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: integrations
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
ports:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
|
||||
15
.github/workflows/golang-test-linux.yml
vendored
15
.github/workflows/golang-test-linux.yml
vendored
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- yury/add-postgresql-store
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
@@ -15,8 +16,20 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: ['386','amd64']
|
||||
store: ['jsonfile', 'sqlite']
|
||||
store: ['jsonfile', 'sqlite', 'postgresql']
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: integrations
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
ports:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -20,7 +20,7 @@ on:
|
||||
- 'client/ui/**'
|
||||
|
||||
env:
|
||||
SIGN_PIPE_VER: "v0.0.10"
|
||||
SIGN_PIPE_VER: "v0.0.11"
|
||||
GORELEASER_VER: "v1.14.1"
|
||||
|
||||
concurrency:
|
||||
|
||||
@@ -189,6 +189,8 @@ CGO_ENABLED=0 go build .
|
||||
|
||||
> Windows clients have a Wireguard driver requirement. You can download the wintun driver from https://www.wintun.net/builds/wintun-0.14.1.zip, after decompressing, you can copy the file `windtun\bin\ARCH\wintun.dll` to the same path as your binary file or to `C:\Windows\System32\wintun.dll`.
|
||||
|
||||
> To test the client GUI application on Windows machines with RDP or vituralized environments (e.g. virtualbox or cloud), you need to download and extract the opengl32.dll from https://fdossena.com/?p=mesa/index.frag next to the built application.
|
||||
|
||||
To start NetBird the client in the foreground:
|
||||
|
||||
```
|
||||
|
||||
@@ -54,7 +54,7 @@ https://user-images.githubusercontent.com/700848/197345890-2e2cded5-7b7a-436f-a4
|
||||
| <ul><li> - \[x] Peer-to-peer connections </ul></li> | <ul><li> - \[x] Auto peer discovery and configuration </ul></li> | <ul><li> - \[x] [Setup keys for bulk network provisioning](https://docs.netbird.io/how-to/register-machines-using-setup-keys) </ul></li> | <ul><li> - \[x] Mac </ul></li> |
|
||||
| <ul><li> - \[x] Peer-to-peer encryption </ul></li> | <ul><li> - \[x] [IdP integrations](https://docs.netbird.io/selfhosted/identity-providers) </ul></li> | <ul><li> - \[x] [Self-hosting quickstart script](https://docs.netbird.io/selfhosted/selfhosted-quickstart) </ul></li> | <ul><li> - \[x] Windows </ul></li> |
|
||||
| <ul><li> - \[x] Connection relay fallback </ul></li> | <ul><li> - \[x] [SSO & MFA support](https://docs.netbird.io/how-to/installation#running-net-bird-with-sso-login) </ul></li> | <ul><li> - \[x] IdP groups sync with JWT </ul></li> | <ul><li> - \[x] Android </ul></li> |
|
||||
| <ul><li> - \[x] [Routes to external networks](https://docs.netbird.io/how-to/routing-traffic-to-private-networks) </ul></li> | <ul><li> - \[x] [Access control - groups & rules](https://docs.netbird.io/how-to/manage-network-access) </ul></li> | | <ul><li> - \[ ] iOS </ul></li> |
|
||||
| <ul><li> - \[x] [Routes to external networks](https://docs.netbird.io/how-to/routing-traffic-to-private-networks) </ul></li> | <ul><li> - \[x] [Access control - groups & rules](https://docs.netbird.io/how-to/manage-network-access) </ul></li> | | <ul><li> - \[x] iOS </ul></li> |
|
||||
| <ul><li> - \[x] NAT traversal with BPF </ul></li> | <ul><li> - \[x] [Private DNS](https://docs.netbird.io/how-to/manage-dns-in-your-network) </ul></li> | | <ul><li> - \[x] Docker </ul></li> |
|
||||
| | <ul><li> - \[x] [Multiuser support](https://docs.netbird.io/how-to/add-users-to-your-network) </ul></li> | | <ul><li> - \[x] OpenWRT </ul></li> |
|
||||
| | <ul><li> - \[x] [Activity logging](https://docs.netbird.io/how-to/monitor-system-and-network-activity) </ul></li> | | |
|
||||
|
||||
@@ -60,7 +60,7 @@ var loginCmd = &cobra.Command{
|
||||
return fmt.Errorf("get config file: %v", err)
|
||||
}
|
||||
|
||||
config, _ = internal.UpdateOldManagementPort(ctx, config, configPath)
|
||||
config, _ = internal.UpdateOldManagementURL(ctx, config, configPath)
|
||||
|
||||
err = foregroundLogin(ctx, cmd, config, setupKey)
|
||||
if err != nil {
|
||||
|
||||
@@ -95,7 +95,7 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
|
||||
return fmt.Errorf("get config file: %v", err)
|
||||
}
|
||||
|
||||
config, _ = internal.UpdateOldManagementPort(ctx, config, configPath)
|
||||
config, _ = internal.UpdateOldManagementURL(ctx, config, configPath)
|
||||
|
||||
err = foregroundLogin(ctx, cmd, config, setupKey)
|
||||
if err != nil {
|
||||
|
||||
@@ -193,6 +193,7 @@ Sleep 3000
|
||||
Delete "$INSTDIR\${UI_APP_EXE}"
|
||||
Delete "$INSTDIR\${MAIN_APP_EXE}"
|
||||
Delete "$INSTDIR\wintun.dll"
|
||||
Delete "$INSTDIR\opengl32.dll"
|
||||
RmDir /r "$INSTDIR"
|
||||
|
||||
SetShellVarContext all
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -12,16 +13,19 @@ import (
|
||||
|
||||
"github.com/netbirdio/netbird/client/ssh"
|
||||
"github.com/netbirdio/netbird/iface"
|
||||
mgm "github.com/netbirdio/netbird/management/client"
|
||||
"github.com/netbirdio/netbird/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// ManagementLegacyPort is the port that was used before by the Management gRPC server.
|
||||
// managementLegacyPortString is the port that was used before by the Management gRPC server.
|
||||
// It is used for backward compatibility now.
|
||||
// NB: hardcoded from github.com/netbirdio/netbird/management/cmd to avoid import
|
||||
ManagementLegacyPort = 33073
|
||||
managementLegacyPortString = "33073"
|
||||
// DefaultManagementURL points to the NetBird's cloud management endpoint
|
||||
DefaultManagementURL = "https://api.wiretrustee.com:443"
|
||||
DefaultManagementURL = "https://api.netbird.io:443"
|
||||
// oldDefaultManagementURL points to the NetBird's old cloud management endpoint
|
||||
oldDefaultManagementURL = "https://api.wiretrustee.com:443"
|
||||
// DefaultAdminURL points to NetBird's cloud management console
|
||||
DefaultAdminURL = "https://app.netbird.io:443"
|
||||
)
|
||||
@@ -302,3 +306,86 @@ func configFileIsExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
// UpdateOldManagementURL checks whether client can switch to the new Management URL with port 443 and the management domain.
|
||||
// If it can switch, then it updates the config and returns a new one. Otherwise, it returns the provided config.
|
||||
// The check is performed only for the NetBird's managed version.
|
||||
func UpdateOldManagementURL(ctx context.Context, config *Config, configPath string) (*Config, error) {
|
||||
|
||||
defaultManagementURL, err := parseURL("Management URL", DefaultManagementURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parsedOldDefaultManagementURL, err := parseURL("Management URL", oldDefaultManagementURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if config.ManagementURL.Hostname() != defaultManagementURL.Hostname() &&
|
||||
config.ManagementURL.Hostname() != parsedOldDefaultManagementURL.Hostname() {
|
||||
// only do the check for the NetBird's managed version
|
||||
return config, nil
|
||||
}
|
||||
|
||||
var mgmTlsEnabled bool
|
||||
if config.ManagementURL.Scheme == "https" {
|
||||
mgmTlsEnabled = true
|
||||
}
|
||||
|
||||
if !mgmTlsEnabled {
|
||||
// only do the check for HTTPs scheme (the hosted version of the Management service is always HTTPs)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
if config.ManagementURL.Port() != managementLegacyPortString &&
|
||||
config.ManagementURL.Hostname() == defaultManagementURL.Hostname() {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
newURL, err := parseURL("Management URL", fmt.Sprintf("%s://%s:%d",
|
||||
config.ManagementURL.Scheme, defaultManagementURL.Hostname(), 443))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// here we check whether we could switch from the legacy 33073 port to the new 443
|
||||
log.Infof("attempting to switch from the legacy Management URL %s to the new one %s",
|
||||
config.ManagementURL.String(), newURL.String())
|
||||
key, err := wgtypes.ParseKey(config.PrivateKey)
|
||||
if err != nil {
|
||||
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
||||
return config, err
|
||||
}
|
||||
|
||||
client, err := mgm.NewClient(ctx, newURL.Host, key, mgmTlsEnabled)
|
||||
if err != nil {
|
||||
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
||||
return config, err
|
||||
}
|
||||
defer func() {
|
||||
err = client.Close()
|
||||
if err != nil {
|
||||
log.Warnf("failed to close the Management service client %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// gRPC check
|
||||
_, err = client.GetServerPublicKey()
|
||||
if err != nil {
|
||||
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// everything is alright => update the config
|
||||
newConfig, err := UpdateConfig(ConfigInput{
|
||||
ManagementURL: newURL.String(),
|
||||
ConfigPath: configPath,
|
||||
})
|
||||
if err != nil {
|
||||
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
||||
return config, fmt.Errorf("failed updating config file: %v", err)
|
||||
}
|
||||
log.Infof("successfully switched to the new Management URL: %s", newURL.String())
|
||||
|
||||
return newConfig, nil
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/netbirdio/netbird/util"
|
||||
)
|
||||
@@ -120,3 +122,60 @@ func TestHiddenPreSharedKey(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateOldManagementURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
previousManagementURL string
|
||||
expectedManagementURL string
|
||||
fileShouldNotChange bool
|
||||
}{
|
||||
{
|
||||
name: "Update old management URL with legacy port",
|
||||
previousManagementURL: "https://api.wiretrustee.com:33073",
|
||||
expectedManagementURL: DefaultManagementURL,
|
||||
},
|
||||
{
|
||||
name: "Update old management URL",
|
||||
previousManagementURL: oldDefaultManagementURL,
|
||||
expectedManagementURL: DefaultManagementURL,
|
||||
},
|
||||
{
|
||||
name: "No update needed when management URL is up to date",
|
||||
previousManagementURL: DefaultManagementURL,
|
||||
expectedManagementURL: DefaultManagementURL,
|
||||
fileShouldNotChange: true,
|
||||
},
|
||||
{
|
||||
name: "No update needed when not using cloud management",
|
||||
previousManagementURL: "https://netbird.example.com:33073",
|
||||
expectedManagementURL: "https://netbird.example.com:33073",
|
||||
fileShouldNotChange: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
configPath := filepath.Join(tempDir, "config.json")
|
||||
config, err := UpdateOrCreateConfig(ConfigInput{
|
||||
ManagementURL: tt.previousManagementURL,
|
||||
ConfigPath: configPath,
|
||||
})
|
||||
require.NoError(t, err, "failed to create testing config")
|
||||
previousStats, err := os.Stat(configPath)
|
||||
require.NoError(t, err, "failed to create testing config stats")
|
||||
resultConfig, err := UpdateOldManagementURL(context.TODO(), config, configPath)
|
||||
require.NoError(t, err, "got error when updating old management url")
|
||||
require.Equal(t, tt.expectedManagementURL, resultConfig.ManagementURL.String())
|
||||
newStats, err := os.Stat(configPath)
|
||||
require.NoError(t, err, "failed to create testing config stats")
|
||||
switch tt.fileShouldNotChange {
|
||||
case true:
|
||||
require.Equal(t, previousStats.ModTime(), newStats.ModTime(), "file should not change")
|
||||
case false:
|
||||
require.NotEqual(t, previousStats.ModTime(), newStats.ModTime(), "file should have changed")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,83 +283,6 @@ func loginToManagement(ctx context.Context, client mgm.Client, pubSSHKey []byte)
|
||||
return loginResp, nil
|
||||
}
|
||||
|
||||
// UpdateOldManagementPort checks whether client can switch to the new Management port 443.
|
||||
// If it can switch, then it updates the config and returns a new one. Otherwise, it returns the provided config.
|
||||
// The check is performed only for the NetBird's managed version.
|
||||
func UpdateOldManagementPort(ctx context.Context, config *Config, configPath string) (*Config, error) {
|
||||
|
||||
defaultManagementURL, err := parseURL("Management URL", DefaultManagementURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if config.ManagementURL.Hostname() != defaultManagementURL.Hostname() {
|
||||
// only do the check for the NetBird's managed version
|
||||
return config, nil
|
||||
}
|
||||
|
||||
var mgmTlsEnabled bool
|
||||
if config.ManagementURL.Scheme == "https" {
|
||||
mgmTlsEnabled = true
|
||||
}
|
||||
|
||||
if !mgmTlsEnabled {
|
||||
// only do the check for HTTPs scheme (the hosted version of the Management service is always HTTPs)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
if mgmTlsEnabled && config.ManagementURL.Port() == fmt.Sprintf("%d", ManagementLegacyPort) {
|
||||
|
||||
newURL, err := parseURL("Management URL", fmt.Sprintf("%s://%s:%d",
|
||||
config.ManagementURL.Scheme, config.ManagementURL.Hostname(), 443))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// here we check whether we could switch from the legacy 33073 port to the new 443
|
||||
log.Infof("attempting to switch from the legacy Management URL %s to the new one %s",
|
||||
config.ManagementURL.String(), newURL.String())
|
||||
key, err := wgtypes.ParseKey(config.PrivateKey)
|
||||
if err != nil {
|
||||
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
||||
return config, err
|
||||
}
|
||||
|
||||
client, err := mgm.NewClient(ctx, newURL.Host, key, mgmTlsEnabled)
|
||||
if err != nil {
|
||||
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
||||
return config, err
|
||||
}
|
||||
defer func() {
|
||||
err = client.Close()
|
||||
if err != nil {
|
||||
log.Warnf("failed to close the Management service client %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// gRPC check
|
||||
_, err = client.GetServerPublicKey()
|
||||
if err != nil {
|
||||
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// everything is alright => update the config
|
||||
newConfig, err := UpdateConfig(ConfigInput{
|
||||
ManagementURL: newURL.String(),
|
||||
ConfigPath: configPath,
|
||||
})
|
||||
if err != nil {
|
||||
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
||||
return config, fmt.Errorf("failed updating config file: %v", err)
|
||||
}
|
||||
log.Infof("successfully switched to the new Management URL: %s", newURL.String())
|
||||
|
||||
return newConfig, nil
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func statusRecorderToMgmConnStateNotifier(statusRecorder *peer.Status) mgm.ConnStateNotifier {
|
||||
var sri interface{} = statusRecorder
|
||||
mgmNotifier, _ := sri.(mgm.ConnStateNotifier)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
"github.com/netbirdio/netbird/client/firewall/uspfilter"
|
||||
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||
@@ -250,11 +251,12 @@ func TestUpdateDNSServer(t *testing.T) {
|
||||
|
||||
for n, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
privKey, _ := wgtypes.GenerateKey()
|
||||
newNet, err := stdnet.NewNet(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wgIface, err := iface.NewWGIFace(fmt.Sprintf("utun230%d", n), fmt.Sprintf("100.66.100.%d/32", n+1), iface.DefaultMTU, nil, newNet)
|
||||
wgIface, err := iface.NewWGIFace(fmt.Sprintf("utun230%d", n), fmt.Sprintf("100.66.100.%d/32", n+1), 33100, privKey.String(), iface.DefaultMTU, newNet, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -331,7 +333,8 @@ func TestDNSFakeResolverHandleUpdates(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.1/32", iface.DefaultMTU, nil, newNet)
|
||||
privKey, _ := wgtypes.GeneratePrivateKey()
|
||||
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.1/32", 33100, privKey.String(), iface.DefaultMTU, newNet, nil)
|
||||
if err != nil {
|
||||
t.Errorf("build interface wireguard: %v", err)
|
||||
return
|
||||
@@ -782,7 +785,8 @@ func createWgInterfaceWithBind(t *testing.T) (*iface.WGIface, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.2/24", iface.DefaultMTU, nil, newNet)
|
||||
privKey, _ := wgtypes.GeneratePrivateKey()
|
||||
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.2/24", 33100, privKey.String(), iface.DefaultMTU, newNet, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("build interface wireguard: %v", err)
|
||||
return nil, err
|
||||
|
||||
@@ -3,7 +3,6 @@ package internal
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/netip"
|
||||
@@ -13,7 +12,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/ice/v3"
|
||||
"github.com/pion/stun/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
@@ -31,7 +31,6 @@ import (
|
||||
mgm "github.com/netbirdio/netbird/management/client"
|
||||
mgmProto "github.com/netbirdio/netbird/management/proto"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
"github.com/netbirdio/netbird/sharedsock"
|
||||
signal "github.com/netbirdio/netbird/signal/client"
|
||||
sProto "github.com/netbirdio/netbird/signal/proto"
|
||||
"github.com/netbirdio/netbird/util"
|
||||
@@ -95,9 +94,9 @@ type Engine struct {
|
||||
mobileDep MobileDependency
|
||||
|
||||
// STUNs is a list of STUN servers used by ICE
|
||||
STUNs []*ice.URL
|
||||
STUNs []*stun.URI
|
||||
// TURNs is a list of STUN servers used by ICE
|
||||
TURNs []*ice.URL
|
||||
TURNs []*stun.URI
|
||||
|
||||
cancel context.CancelFunc
|
||||
|
||||
@@ -106,8 +105,7 @@ type Engine struct {
|
||||
wgInterface *iface.WGIface
|
||||
wgProxyFactory *wgproxy.Factory
|
||||
|
||||
udpMux *bind.UniversalUDPMuxDefault
|
||||
udpMuxConn io.Closer
|
||||
udpMux *bind.UniversalUDPMuxDefault
|
||||
|
||||
// networkSerial is the latest CurrentSerial (state ID) of the network sent by the Management service
|
||||
networkSerial uint64
|
||||
@@ -146,8 +144,8 @@ func NewEngine(
|
||||
syncMsgMux: &sync.Mutex{},
|
||||
config: config,
|
||||
mobileDep: mobileDep,
|
||||
STUNs: []*ice.URL{},
|
||||
TURNs: []*ice.URL{},
|
||||
STUNs: []*stun.URI{},
|
||||
TURNs: []*stun.URI{},
|
||||
networkSerial: 0,
|
||||
sshServerFunc: nbssh.DefaultSSHServer,
|
||||
statusRecorder: statusRecorder,
|
||||
@@ -180,66 +178,26 @@ func (e *Engine) Start() error {
|
||||
e.syncMsgMux.Lock()
|
||||
defer e.syncMsgMux.Unlock()
|
||||
|
||||
wgIFaceName := e.config.WgIfaceName
|
||||
wgAddr := e.config.WgAddr
|
||||
myPrivateKey := e.config.WgPrivateKey
|
||||
var err error
|
||||
transportNet, err := e.newStdNet()
|
||||
wgIface, err := e.newWgIface()
|
||||
if err != nil {
|
||||
log.Errorf("failed to create pion's stdnet: %s", err)
|
||||
}
|
||||
|
||||
e.wgInterface, err = iface.NewWGIFace(wgIFaceName, wgAddr, iface.DefaultMTU, e.mobileDep.TunAdapter, transportNet)
|
||||
if err != nil {
|
||||
log.Errorf("failed creating wireguard interface instance %s: [%s]", wgIFaceName, err.Error())
|
||||
log.Errorf("failed creating wireguard interface instance %s: [%s]", e.config.WgIfaceName, err.Error())
|
||||
return err
|
||||
}
|
||||
e.wgInterface = wgIface
|
||||
|
||||
var routes []*route.Route
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "android":
|
||||
var dnsConfig *nbdns.Config
|
||||
routes, dnsConfig, err = e.readInitialSettings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.dnsServer == nil {
|
||||
e.dnsServer = dns.NewDefaultServerPermanentUpstream(e.ctx, e.wgInterface, e.mobileDep.HostDNSAddresses, *dnsConfig, e.mobileDep.NetworkChangeListener)
|
||||
go e.mobileDep.DnsReadyListener.OnReady()
|
||||
}
|
||||
case "ios":
|
||||
if e.dnsServer == nil {
|
||||
e.dnsServer = dns.NewDefaultServerIos(e.ctx, e.wgInterface, e.mobileDep.DnsManager)
|
||||
}
|
||||
default:
|
||||
if e.dnsServer == nil {
|
||||
e.dnsServer, err = dns.NewDefaultServer(e.ctx, e.wgInterface, e.config.CustomDNSAddress)
|
||||
if err != nil {
|
||||
e.close()
|
||||
return err
|
||||
}
|
||||
}
|
||||
initialRoutes, dnsServer, err := e.newDnsServer()
|
||||
if err != nil {
|
||||
e.close()
|
||||
return err
|
||||
}
|
||||
e.dnsServer = dnsServer
|
||||
|
||||
e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder, routes)
|
||||
e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder, initialRoutes)
|
||||
e.routeManager.SetRouteChangeListener(e.mobileDep.NetworkChangeListener)
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "android":
|
||||
err = e.wgInterface.CreateOnAndroid(iface.MobileIFaceArguments{
|
||||
Routes: e.routeManager.InitialRouteRange(),
|
||||
Dns: e.dnsServer.DnsIP(),
|
||||
SearchDomains: e.dnsServer.SearchDomains(),
|
||||
})
|
||||
case "ios":
|
||||
e.mobileDep.NetworkChangeListener.SetInterfaceIP(wgAddr)
|
||||
err = e.wgInterface.CreateOniOS(e.mobileDep.FileDescriptor)
|
||||
default:
|
||||
err = e.wgInterface.Create()
|
||||
}
|
||||
err = e.wgInterfaceCreate()
|
||||
if err != nil {
|
||||
log.Errorf("failed creating tunnel interface %s: [%s]", wgIFaceName, err.Error())
|
||||
log.Errorf("failed creating tunnel interface %s: [%s]", e.config.WgIfaceName, err.Error())
|
||||
e.close()
|
||||
return err
|
||||
}
|
||||
@@ -257,33 +215,13 @@ func (e *Engine) Start() error {
|
||||
}
|
||||
}
|
||||
|
||||
err = e.wgInterface.Configure(myPrivateKey.String(), e.config.WgPort)
|
||||
e.udpMux, err = e.wgInterface.Up()
|
||||
if err != nil {
|
||||
log.Errorf("failed configuring Wireguard interface [%s]: %s", wgIFaceName, err.Error())
|
||||
log.Errorf("failed to pull up wgInterface [%s]: %s", e.wgInterface.Name(), err.Error())
|
||||
e.close()
|
||||
return err
|
||||
}
|
||||
|
||||
if e.wgInterface.IsUserspaceBind() {
|
||||
iceBind := e.wgInterface.GetBind()
|
||||
udpMux, err := iceBind.GetICEMux()
|
||||
if err != nil {
|
||||
e.close()
|
||||
return err
|
||||
}
|
||||
e.udpMux = udpMux
|
||||
log.Infof("using userspace bind mode %s", udpMux.LocalAddr().String())
|
||||
} else {
|
||||
rawSock, err := sharedsock.Listen(e.config.WgPort, sharedsock.NewIncomingSTUNFilter())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mux := bind.NewUniversalUDPMuxDefault(bind.UniversalUDPMuxParams{UDPConn: rawSock, Net: transportNet})
|
||||
go mux.ReadFromConn(e.ctx)
|
||||
e.udpMuxConn = rawSock
|
||||
e.udpMux = mux
|
||||
}
|
||||
|
||||
if e.firewall != nil {
|
||||
e.acl = acl.NewDefaultManager(e.firewall)
|
||||
}
|
||||
@@ -575,10 +513,10 @@ func (e *Engine) updateSTUNs(stuns []*mgmProto.HostConfig) error {
|
||||
if len(stuns) == 0 {
|
||||
return nil
|
||||
}
|
||||
var newSTUNs []*ice.URL
|
||||
var newSTUNs []*stun.URI
|
||||
log.Debugf("got STUNs update from Management Service, updating")
|
||||
for _, stun := range stuns {
|
||||
url, err := ice.ParseURL(stun.Uri)
|
||||
for _, s := range stuns {
|
||||
url, err := stun.ParseURI(s.Uri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -593,10 +531,10 @@ func (e *Engine) updateTURNs(turns []*mgmProto.ProtectedHostConfig) error {
|
||||
if len(turns) == 0 {
|
||||
return nil
|
||||
}
|
||||
var newTURNs []*ice.URL
|
||||
var newTURNs []*stun.URI
|
||||
log.Debugf("got TURNs update from Management Service, updating")
|
||||
for _, turn := range turns {
|
||||
url, err := ice.ParseURL(turn.HostConfig.Uri)
|
||||
url, err := stun.ParseURI(turn.HostConfig.Uri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -846,7 +784,7 @@ func (e *Engine) peerExists(peerKey string) bool {
|
||||
|
||||
func (e *Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, error) {
|
||||
log.Debugf("creating peer connection %s", pubKey)
|
||||
var stunTurn []*ice.URL
|
||||
var stunTurn []*stun.URI
|
||||
stunTurn = append(stunTurn, e.STUNs...)
|
||||
stunTurn = append(stunTurn, e.TURNs...)
|
||||
|
||||
@@ -1041,18 +979,6 @@ func (e *Engine) close() {
|
||||
}
|
||||
}
|
||||
|
||||
if e.udpMux != nil {
|
||||
if err := e.udpMux.Close(); err != nil {
|
||||
log.Debugf("close udp mux: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if e.udpMuxConn != nil {
|
||||
if err := e.udpMuxConn.Close(); err != nil {
|
||||
log.Debugf("close udp mux connection: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if !isNil(e.sshServer) {
|
||||
err := e.sshServer.Stop()
|
||||
if err != nil {
|
||||
@@ -1086,6 +1012,68 @@ func (e *Engine) readInitialSettings() ([]*route.Route, *nbdns.Config, error) {
|
||||
return routes, &dnsCfg, nil
|
||||
}
|
||||
|
||||
func (e *Engine) newWgIface() (*iface.WGIface, error) {
|
||||
transportNet, err := e.newStdNet()
|
||||
if err != nil {
|
||||
log.Errorf("failed to create pion's stdnet: %s", err)
|
||||
}
|
||||
|
||||
var mArgs *iface.MobileIFaceArguments
|
||||
switch runtime.GOOS {
|
||||
case "android":
|
||||
mArgs = &iface.MobileIFaceArguments{
|
||||
TunAdapter: e.mobileDep.TunAdapter,
|
||||
TunFd: int(e.mobileDep.FileDescriptor),
|
||||
}
|
||||
case "ios":
|
||||
mArgs = &iface.MobileIFaceArguments{
|
||||
TunFd: int(e.mobileDep.FileDescriptor),
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
return iface.NewWGIFace(e.config.WgIfaceName, e.config.WgAddr, e.config.WgPort, e.config.WgPrivateKey.String(), iface.DefaultMTU, transportNet, mArgs)
|
||||
}
|
||||
|
||||
func (e *Engine) wgInterfaceCreate() (err error) {
|
||||
switch runtime.GOOS {
|
||||
case "android":
|
||||
err = e.wgInterface.CreateOnAndroid(e.routeManager.InitialRouteRange(), e.dnsServer.DnsIP(), e.dnsServer.SearchDomains())
|
||||
case "ios":
|
||||
e.mobileDep.NetworkChangeListener.SetInterfaceIP(e.config.WgAddr)
|
||||
err = e.wgInterface.Create()
|
||||
default:
|
||||
err = e.wgInterface.Create()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *Engine) newDnsServer() ([]*route.Route, dns.Server, error) {
|
||||
// due to tests where we are using a mocked version of the DNS server
|
||||
if e.dnsServer != nil {
|
||||
return nil, e.dnsServer, nil
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
case "android":
|
||||
routes, dnsConfig, err := e.readInitialSettings()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
dnsServer := dns.NewDefaultServerPermanentUpstream(e.ctx, e.wgInterface, e.mobileDep.HostDNSAddresses, *dnsConfig, e.mobileDep.NetworkChangeListener)
|
||||
go e.mobileDep.DnsReadyListener.OnReady()
|
||||
return routes, dnsServer, nil
|
||||
case "ios":
|
||||
dnsServer := dns.NewDefaultServerIos(e.ctx, e.wgInterface, e.mobileDep.DnsManager)
|
||||
return nil, dnsServer, nil
|
||||
default:
|
||||
dnsServer, err := dns.NewDefaultServer(e.ctx, e.wgInterface, e.config.CustomDNSAddress)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return nil, dnsServer, nil
|
||||
}
|
||||
}
|
||||
|
||||
func findIPFromInterfaceName(ifaceName string) (net.IP, error) {
|
||||
iface, err := net.InterfaceByName(ifaceName)
|
||||
if err != nil {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pion/transport/v2/stdnet"
|
||||
"github.com/pion/transport/v3/stdnet"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -213,7 +213,7 @@ func TestEngine_UpdateNetworkMap(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
engine.wgInterface, err = iface.NewWGIFace("utun102", "100.64.0.1/24", iface.DefaultMTU, nil, newNet)
|
||||
engine.wgInterface, err = iface.NewWGIFace("utun102", "100.64.0.1/24", engine.config.WgPort, key.String(), iface.DefaultMTU, newNet, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -567,7 +567,7 @@ func TestEngine_UpdateNetworkMapWithRoutes(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, iface.DefaultMTU, nil, newNet)
|
||||
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, engine.config.WgPort, key.String(), iface.DefaultMTU, newNet, nil)
|
||||
assert.NoError(t, err, "shouldn't return error")
|
||||
input := struct {
|
||||
inputSerial uint64
|
||||
@@ -736,7 +736,7 @@ func TestEngine_UpdateNetworkMapWithDNSUpdate(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, iface.DefaultMTU, nil, newNet)
|
||||
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, 33100, key.String(), iface.DefaultMTU, newNet, nil)
|
||||
assert.NoError(t, err, "shouldn't return error")
|
||||
|
||||
mockRouteManager := &routemanager.MockManager{
|
||||
|
||||
@@ -9,11 +9,14 @@ import (
|
||||
|
||||
// MobileDependency collect all dependencies for mobile platform
|
||||
type MobileDependency struct {
|
||||
// Android only
|
||||
TunAdapter iface.TunAdapter
|
||||
IFaceDiscover stdnet.ExternalIFaceDiscover
|
||||
NetworkChangeListener listener.NetworkChangeListener
|
||||
HostDNSAddresses []string
|
||||
DnsReadyListener dns.ReadyListener
|
||||
DnsManager dns.IosDnsManager
|
||||
FileDescriptor int32
|
||||
|
||||
// iOS only
|
||||
DnsManager dns.IosDnsManager
|
||||
FileDescriptor int32
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/ice/v3"
|
||||
"github.com/pion/stun/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
@@ -46,7 +47,7 @@ type ConnConfig struct {
|
||||
LocalKey string
|
||||
|
||||
// StunTurn is a list of STUN and TURN URLs
|
||||
StunTurn []*ice.URL
|
||||
StunTurn []*stun.URI
|
||||
|
||||
// InterfaceBlackList is a list of machine interfaces that should be filtered out by ICE Candidate gathering
|
||||
// (e.g. if eth0 is in the list, host candidate of this interface won't be used)
|
||||
@@ -142,7 +143,7 @@ func (conn *Conn) WgConfig() WgConfig {
|
||||
}
|
||||
|
||||
// UpdateStunTurn update the turn and stun addresses
|
||||
func (conn *Conn) UpdateStunTurn(turnStun []*ice.URL) {
|
||||
func (conn *Conn) UpdateStunTurn(turnStun []*stun.URI) {
|
||||
conn.config.StunTurn = turnStun
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/magiconair/properties/assert"
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/stun/v2"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||
"github.com/netbirdio/netbird/client/internal/wgproxy"
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
var connConf = ConnConfig{
|
||||
Key: "LLHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
||||
LocalKey: "RRHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
||||
StunTurn: []*ice.URL{},
|
||||
StunTurn: []*stun.URI{},
|
||||
InterfaceBlackList: nil,
|
||||
Timeout: time.Second,
|
||||
LocalWgPort: 51820,
|
||||
|
||||
@@ -7,7 +7,8 @@ import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/pion/transport/v2/stdnet"
|
||||
"github.com/pion/transport/v3/stdnet"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -399,12 +400,12 @@ func TestManagerUpdateRoutes(t *testing.T) {
|
||||
|
||||
for n, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
|
||||
peerPrivateKey, _ := wgtypes.GeneratePrivateKey()
|
||||
newNet, err := stdnet.NewNet()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun43%d", n), "100.65.65.2/24", iface.DefaultMTU, nil, newNet)
|
||||
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun43%d", n), "100.65.65.2/24", 33100, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
|
||||
require.NoError(t, err, "should create testing WGIface interface")
|
||||
defer wgInterface.Close()
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ func (n *notifier) onNewRoutes(idMap map[string][]*route.Route) {
|
||||
}
|
||||
|
||||
sort.Strings(newNets)
|
||||
if !n.hasDiff(n.routeRangers, newNets) {
|
||||
if !n.hasDiff(n.initialRouteRangers, newNets) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,10 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/pion/transport/v2/stdnet"
|
||||
"github.com/pion/transport/v3/stdnet"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
"github.com/netbirdio/netbird/iface"
|
||||
)
|
||||
@@ -41,11 +42,12 @@ func TestAddRemoveRoutes(t *testing.T) {
|
||||
|
||||
for n, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
peerPrivateKey, _ := wgtypes.GeneratePrivateKey()
|
||||
newNet, err := stdnet.NewNet()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", iface.DefaultMTU, nil, newNet)
|
||||
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", 33100, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
|
||||
require.NoError(t, err, "should create testing WGIface interface")
|
||||
defer wgInterface.Close()
|
||||
|
||||
@@ -175,11 +177,12 @@ func TestAddExistAndRemoveRouteNonAndroid(t *testing.T) {
|
||||
log.SetOutput(os.Stderr)
|
||||
}()
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
peerPrivateKey, _ := wgtypes.GeneratePrivateKey()
|
||||
newNet, err := stdnet.NewNet()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", iface.DefaultMTU, nil, newNet)
|
||||
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", 33100, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
|
||||
require.NoError(t, err, "should create testing WGIface interface")
|
||||
defer wgInterface.Close()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package stdnet
|
||||
|
||||
import "github.com/pion/transport/v2"
|
||||
import "github.com/pion/transport/v3"
|
||||
|
||||
// ExternalIFaceDiscover provide an option for external services (mobile)
|
||||
// to collect network interface information
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/transport/v2"
|
||||
"github.com/pion/transport/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package stdnet
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/pion/transport/v2"
|
||||
"github.com/pion/transport/v3"
|
||||
)
|
||||
|
||||
type pionDiscover struct {
|
||||
|
||||
@@ -6,8 +6,8 @@ package stdnet
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/transport/v2"
|
||||
"github.com/pion/transport/v2/stdnet"
|
||||
"github.com/pion/transport/v3"
|
||||
"github.com/pion/transport/v3/stdnet"
|
||||
)
|
||||
|
||||
// Net is an implementation of the net.Net interface
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<Shortcut Id="NetbirdStartMenuShortcut" Directory="StartMenuFolder" Name="NetBird" WorkingDirectory="NetbirdInstallDir" Icon="NetbirdIcon" />
|
||||
</File>
|
||||
<File ProcessorArchitecture="x64" Source=".\dist\netbird_windows_amd64\wintun.dll" />
|
||||
<File ProcessorArchitecture="x64" Source=".\dist\netbird_windows_amd64\opengl32.dll" />
|
||||
|
||||
<ServiceInstall
|
||||
Id="NetBirdService"
|
||||
|
||||
@@ -94,7 +94,7 @@ func (s *Server) Start() error {
|
||||
}
|
||||
|
||||
// if configuration exists, we just start connections.
|
||||
config, _ = internal.UpdateOldManagementPort(ctx, config, s.latestConfigInput.ConfigPath)
|
||||
config, _ = internal.UpdateOldManagementURL(ctx, config, s.latestConfigInput.ConfigPath)
|
||||
|
||||
s.config = config
|
||||
|
||||
@@ -197,7 +197,7 @@ func (s *Server) Login(callerCtx context.Context, msg *proto.LoginRequest) (*pro
|
||||
}
|
||||
|
||||
if msg.ManagementUrl == "" {
|
||||
config, _ = internal.UpdateOldManagementPort(ctx, config, s.latestConfigInput.ConfigPath)
|
||||
config, _ = internal.UpdateOldManagementURL(ctx, config, s.latestConfigInput.ConfigPath)
|
||||
s.config = config
|
||||
s.latestConfigInput.ManagementURL = config.ManagementURL.String()
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ func getOSNameAndVersion() (string, string) {
|
||||
query := wmi.CreateQuery(&dst, "")
|
||||
err := wmi.Query(query, &dst)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
log.Error(err)
|
||||
return "Windows", getBuildVersion()
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
|
||||
@@ -563,8 +563,8 @@ func (s *serviceClient) getSrvClient(timeout time.Duration) (proto.DaemonService
|
||||
|
||||
// getSrvConfig from the service to show it in the settings window.
|
||||
func (s *serviceClient) getSrvConfig() {
|
||||
s.managementURL = "https://api.wiretrustee.com:33073"
|
||||
s.adminURL = "https://app.netbird.io"
|
||||
s.managementURL = internal.DefaultManagementURL
|
||||
s.adminURL = internal.DefaultAdminURL
|
||||
|
||||
conn, err := s.getSrvClient(failFastTimeout)
|
||||
if err != nil {
|
||||
|
||||
33
go.mod
33
go.mod
@@ -6,12 +6,12 @@ require (
|
||||
github.com/cenkalti/backoff/v4 v4.1.3
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/golang/protobuf v1.5.3
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/google/uuid v1.3.1
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/gomega v1.18.1
|
||||
github.com/pion/ice/v2 v2.3.1
|
||||
github.com/pion/ice/v3 v3.0.2
|
||||
github.com/rs/cors v1.8.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/cobra v1.6.1
|
||||
@@ -44,6 +44,7 @@ require (
|
||||
github.com/google/nftables v0.0.0-20220808154552-2eca00135732
|
||||
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/libp2p/go-netroute v0.2.0
|
||||
github.com/magiconair/properties v1.8.5
|
||||
github.com/mattn/go-sqlite3 v1.14.17
|
||||
@@ -51,17 +52,18 @@ require (
|
||||
github.com/miekg/dns v1.1.43
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/nadoo/ipset v0.5.0
|
||||
github.com/netbirdio/management-integrations/additions v0.0.0-20231205113053-c462587ae695
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20231205113053-c462587ae695
|
||||
github.com/netbirdio/management-integrations/additions v0.0.0-20231230192609-a9dcce34ff86
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20231230192609-a9dcce34ff86
|
||||
github.com/okta/okta-sdk-golang/v2 v2.18.0
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pion/logging v0.2.2
|
||||
github.com/pion/stun v0.4.0
|
||||
github.com/pion/transport/v2 v2.0.2
|
||||
github.com/pion/stun/v2 v2.0.0
|
||||
github.com/pion/transport/v3 v3.0.1
|
||||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/rs/xid v1.3.0
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/things-go/go-socks5 v0.0.4
|
||||
github.com/yusufpapurcu/wmi v1.2.3
|
||||
go.opentelemetry.io/otel v1.11.1
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.33.0
|
||||
@@ -76,8 +78,9 @@ require (
|
||||
golang.org/x/term v0.13.0
|
||||
google.golang.org/api v0.126.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/driver/postgres v1.5.4
|
||||
gorm.io/driver/sqlite v1.5.3
|
||||
gorm.io/gorm v1.25.4
|
||||
gorm.io/gorm v1.25.5
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -109,11 +112,15 @@ require (
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/s2a-go v0.1.4 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.10.0 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.4.3 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/josharian/native v1.0.0 // indirect
|
||||
@@ -124,11 +131,11 @@ require (
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
|
||||
github.com/pegasus-kv/thrift v0.13.0 // indirect
|
||||
github.com/pion/dtls/v2 v2.2.6 // indirect
|
||||
github.com/pion/mdns v0.0.7 // indirect
|
||||
github.com/pion/dtls/v2 v2.2.7 // indirect
|
||||
github.com/pion/mdns v0.0.9 // indirect
|
||||
github.com/pion/randutil v0.1.0 // indirect
|
||||
github.com/pion/turn/v2 v2.1.0 // indirect
|
||||
github.com/pion/udp/v2 v2.0.1 // indirect
|
||||
github.com/pion/transport/v2 v2.2.1 // indirect
|
||||
github.com/pion/turn/v3 v3.0.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
@@ -149,11 +156,11 @@ require (
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 // indirect
|
||||
honnef.co/go/tools v0.2.2 // indirect
|
||||
k8s.io/apimachinery v0.23.5 // indirect
|
||||
)
|
||||
|
||||
84
go.sum
84
go.sum
@@ -340,8 +340,8 @@ github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkj
|
||||
github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
@@ -382,6 +382,12 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY=
|
||||
github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
|
||||
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
@@ -434,6 +440,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4nWRE=
|
||||
github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI=
|
||||
github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2OJReAmwzKivcYyremnibWGbK7WfftHzc=
|
||||
@@ -495,10 +503,10 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/nadoo/ipset v0.5.0 h1:5GJUAuZ7ITQQQGne5J96AmFjRtI8Avlbk6CabzYWVUc=
|
||||
github.com/nadoo/ipset v0.5.0/go.mod h1:rYF5DQLRGGoQ8ZSWeK+6eX5amAuPqwFkWjhQlEITGJQ=
|
||||
github.com/netbirdio/management-integrations/additions v0.0.0-20231205113053-c462587ae695 h1:c/Rvyy/mqbFoKo6FS8ihQ3/3y+TAl0qDEH0pO2tXayM=
|
||||
github.com/netbirdio/management-integrations/additions v0.0.0-20231205113053-c462587ae695/go.mod h1:31FhBNvQ+riHEIu6LSTmqr8IeuSIsGfQffqV4LFmbwA=
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20231205113053-c462587ae695 h1:9HRnqSosRuKyOZgVN/hJW3DG2zVyt5AARmiQlSuDPIc=
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20231205113053-c462587ae695/go.mod h1:B0nMS3es77gOvPYhc0K91fAzTkQLi/jRq5TffUN3klM=
|
||||
github.com/netbirdio/management-integrations/additions v0.0.0-20231230192609-a9dcce34ff86 h1:GJdFZA3DpCw8g/NI2j8ZtwWSjPeL95OaFTnXaZwqN/g=
|
||||
github.com/netbirdio/management-integrations/additions v0.0.0-20231230192609-a9dcce34ff86/go.mod h1:31FhBNvQ+riHEIu6LSTmqr8IeuSIsGfQffqV4LFmbwA=
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20231230192609-a9dcce34ff86 h1:Z5nohWjV/CE7RTLauciT0soYoUHS2TY24XRShIRdxQM=
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20231230192609-a9dcce34ff86/go.mod h1:B0nMS3es77gOvPYhc0K91fAzTkQLi/jRq5TffUN3klM=
|
||||
github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0 h1:hirFRfx3grVA/9eEyjME5/z3nxdJlN9kfQpvWWPk32g=
|
||||
github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
||||
github.com/netbirdio/systray v0.0.0-20231030152038-ef1ed2a27949 h1:xbWM9BU6mwZZLHxEjxIX/V8Hv3HurQt4mReIE4mY4DM=
|
||||
@@ -545,25 +553,24 @@ github.com/pegasus-kv/thrift v0.13.0 h1:4ESwaNoHImfbHa9RUGJiJZ4hrxorihZHk5aarYwY
|
||||
github.com/pegasus-kv/thrift v0.13.0/go.mod h1:Gl9NT/WHG6ABm6NsrbfE8LiJN0sAyneCrvB4qN4NPqQ=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pion/dtls/v2 v2.2.6 h1:yXMxKr0Skd+Ub6A8UqXTRLSywskx93ooMRHsQUtd+Z4=
|
||||
github.com/pion/dtls/v2 v2.2.6/go.mod h1:t8fWJCIquY5rlQZwA2yWxUS1+OCrAdXrhVKXB5oD/wY=
|
||||
github.com/pion/ice/v2 v2.3.1 h1:FQCmUfZe2Jpe7LYStVBOP6z1DiSzbIateih3TztgTjc=
|
||||
github.com/pion/ice/v2 v2.3.1/go.mod h1:aq2kc6MtYNcn4XmMhobAv6hTNJiHzvD0yXRz80+bnP8=
|
||||
github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
|
||||
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
|
||||
github.com/pion/ice/v3 v3.0.2 h1:dNQnKsjLvOWz+PaI4tw1VnLYTp9adihC1HIASFGajmI=
|
||||
github.com/pion/ice/v3 v3.0.2/go.mod h1:q3BDzTsxbqP0ySMSHrFuw2MYGUx/AC3WQfRGC5F/0Is=
|
||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pion/mdns v0.0.7 h1:P0UB4Sr6xDWEox0kTVxF0LmQihtCbSAdW0H2nEgkA3U=
|
||||
github.com/pion/mdns v0.0.7/go.mod h1:4iP2UbeFhLI/vWju/bw6ZfwjJzk0z8DNValjGxR/dD8=
|
||||
github.com/pion/mdns v0.0.9 h1:7Ue5KZsqq8EuqStnpPWV33vYYEH0+skdDN5L7EiEsI4=
|
||||
github.com/pion/mdns v0.0.9/go.mod h1:2JA5exfxwzXiCihmxpTKgFUpiQws2MnipoPK09vecIc=
|
||||
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||
github.com/pion/stun v0.4.0 h1:vgRrbBE2htWHy7l3Zsxckk7rkjnjOsSM7PHZnBwo8rk=
|
||||
github.com/pion/stun v0.4.0/go.mod h1:QPsh1/SbXASntw3zkkrIk3ZJVKz4saBY2G7S10P3wCw=
|
||||
github.com/pion/transport/v2 v2.0.0/go.mod h1:HS2MEBJTwD+1ZI2eSXSvHJx/HnzQqRy2/LXxt6eVMHc=
|
||||
github.com/pion/transport/v2 v2.0.2 h1:St+8o+1PEzPT51O9bv+tH/KYYLMNR5Vwm5Z3Qkjsywg=
|
||||
github.com/pion/transport/v2 v2.0.2/go.mod h1:vrz6bUbFr/cjdwbnxq8OdDDzHf7JJfGsIRkxfpZoTA0=
|
||||
github.com/pion/turn/v2 v2.1.0 h1:5wGHSgGhJhP/RpabkUb/T9PdsAjkGLS6toYz5HNzoSI=
|
||||
github.com/pion/turn/v2 v2.1.0/go.mod h1:yrT5XbXSGX1VFSF31A3c1kCNB5bBZgk/uu5LET162qs=
|
||||
github.com/pion/udp/v2 v2.0.1 h1:xP0z6WNux1zWEjhC7onRA3EwwSliXqu1ElUZAQhUP54=
|
||||
github.com/pion/udp/v2 v2.0.1/go.mod h1:B7uvTMP00lzWdyMr/1PVZXtV3wpPIxBRd4Wl6AksXn8=
|
||||
github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0=
|
||||
github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ=
|
||||
github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
|
||||
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
|
||||
github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM=
|
||||
github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
|
||||
github.com/pion/turn/v3 v3.0.1 h1:wLi7BTQr6/Q20R0vt/lHbjv6y4GChFtC33nkYbasoT8=
|
||||
github.com/pion/turn/v3 v3.0.1/go.mod h1:MrJDKgqryDyWy1/4NT9TWfXWGMC7UHT6pJIv1+gMeNE=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -661,9 +668,13 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/things-go/go-socks5 v0.0.4 h1:jMQjIc+qhD4z9cITOMnBiwo9dDmpGuXmBlkRFrl/qD0=
|
||||
github.com/things-go/go-socks5 v0.0.4/go.mod h1:sh4K6WHrmHZpjxLTCHyYtXYH8OUuD+yZun41NomR1IQ=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
@@ -730,8 +741,10 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -836,11 +849,12 @@ golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -961,20 +975,22 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.1-0.20230222185716-a3b23cc77e89/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -987,11 +1003,10 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -1172,7 +1187,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
@@ -1202,10 +1216,12 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo=
|
||||
gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0=
|
||||
gorm.io/driver/sqlite v1.5.3 h1:7/0dUgX28KAcopdfbRWWl68Rflh6osa4rDh+m51KL2g=
|
||||
gorm.io/driver/sqlite v1.5.3/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
|
||||
gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw=
|
||||
gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
|
||||
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
||||
gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 h1:Wobr37noukisGxpKo5jAsLREcpj61RxrWYzD8uwveOY=
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/stun"
|
||||
"github.com/pion/transport/v2"
|
||||
"github.com/pion/stun/v2"
|
||||
"github.com/pion/transport/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/ipv4"
|
||||
wgConn "golang.zx2c4.com/wireguard/conn"
|
||||
|
||||
@@ -7,13 +7,12 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/stun"
|
||||
"github.com/pion/transport/v2/stdnet"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/pion/ice/v3"
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/transport/v2"
|
||||
"github.com/pion/stun/v2"
|
||||
"github.com/pion/transport/v3"
|
||||
"github.com/pion/transport/v3/stdnet"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -224,6 +223,10 @@ func (m *UDPMuxDefault) GetListenAddresses() []net.Addr {
|
||||
// GetConn returns a PacketConn given the connection's ufrag and network address
|
||||
// creates the connection if an existing one can't be found
|
||||
func (m *UDPMuxDefault) GetConn(ufrag string, addr net.Addr) (net.PacketConn, error) {
|
||||
// don't check addr for mux using unspecified address
|
||||
if len(m.localAddrsForUnspecified) == 0 && m.params.UDPConn.LocalAddr().String() != addr.String() {
|
||||
return nil, fmt.Errorf("invalid address %s", addr.String())
|
||||
}
|
||||
|
||||
var isIPv6 bool
|
||||
if udpAddr, _ := addr.(*net.UDPAddr); udpAddr != nil && udpAddr.IP.To4() == nil {
|
||||
@@ -282,15 +285,7 @@ func (m *UDPMuxDefault) RemoveConnByUfrag(ufrag string) {
|
||||
for _, c := range removedConns {
|
||||
addresses := c.getAddresses()
|
||||
for _, addr := range addresses {
|
||||
if connList, ok := m.addressMap[addr]; ok {
|
||||
var newList []*udpMuxedConn
|
||||
for _, conn := range connList {
|
||||
if conn.params.Key != ufrag {
|
||||
newList = append(newList, conn)
|
||||
}
|
||||
}
|
||||
m.addressMap[addr] = newList
|
||||
}
|
||||
delete(m.addressMap, addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/stun"
|
||||
"github.com/pion/transport/v2"
|
||||
"github.com/pion/stun/v2"
|
||||
"github.com/pion/transport/v3"
|
||||
)
|
||||
|
||||
// UniversalUDPMuxDefault handles STUN and TURN servers packets by wrapping the original UDPConn
|
||||
@@ -80,13 +80,13 @@ func (m *UniversalUDPMuxDefault) ReadFromConn(ctx context.Context) {
|
||||
log.Debugf("stopped reading from the UDPConn due to finished context")
|
||||
return
|
||||
default:
|
||||
_, a, err := m.params.UDPConn.ReadFrom(buf)
|
||||
n, a, err := m.params.UDPConn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.Errorf("error while reading packet: %s", err)
|
||||
continue
|
||||
}
|
||||
msg := &stun.Message{
|
||||
Raw: buf,
|
||||
Raw: append([]byte{}, buf[:n]...),
|
||||
}
|
||||
err = msg.Decode()
|
||||
if err != nil {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/transport/v2/packetio"
|
||||
"github.com/pion/transport/v3/packetio"
|
||||
)
|
||||
|
||||
type udpMuxedConnParams struct {
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/netbirdio/netbird/iface/bind"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
"github.com/netbirdio/netbird/iface/bind"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -19,11 +19,12 @@ const (
|
||||
|
||||
// WGIface represents a interface instance
|
||||
type WGIface struct {
|
||||
tun *tunDevice
|
||||
configurer wGConfigurer
|
||||
mu sync.Mutex
|
||||
tun wgTunDevice
|
||||
userspaceBind bool
|
||||
filter PacketFilter
|
||||
mu sync.Mutex
|
||||
|
||||
configurer wgConfigurer
|
||||
filter PacketFilter
|
||||
}
|
||||
|
||||
// IsUserspaceBind indicates whether this interfaces is userspace with bind.ICEBind
|
||||
@@ -31,11 +32,6 @@ func (w *WGIface) IsUserspaceBind() bool {
|
||||
return w.userspaceBind
|
||||
}
|
||||
|
||||
// GetBind returns a userspace implementation of WireGuard Bind interface
|
||||
func (w *WGIface) GetBind() *bind.ICEBind {
|
||||
return w.tun.iceBind
|
||||
}
|
||||
|
||||
// Name returns the interface name
|
||||
func (w *WGIface) Name() string {
|
||||
return w.tun.DeviceName()
|
||||
@@ -46,13 +42,13 @@ func (w *WGIface) Address() WGAddress {
|
||||
return w.tun.WgAddress()
|
||||
}
|
||||
|
||||
// Configure configures a Wireguard interface
|
||||
// Up 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 {
|
||||
func (w *WGIface) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
log.Debugf("configuring Wireguard interface %s", w.tun.DeviceName())
|
||||
return w.configurer.configureInterface(privateKey, port)
|
||||
|
||||
return w.tun.Up()
|
||||
}
|
||||
|
||||
// UpdateAddr updates address of the interface
|
||||
@@ -74,7 +70,7 @@ func (w *WGIface) UpdatePeer(peerKey string, allowedIps string, keepAlive time.D
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
log.Debugf("updating interface %s peer %s, endpoint %s ", w.tun.DeviceName(), peerKey, endpoint)
|
||||
log.Debugf("updating interface %s peer %s, endpoint %s", w.tun.DeviceName(), peerKey, endpoint)
|
||||
return w.configurer.updatePeer(peerKey, allowedIps, keepAlive, endpoint, preSharedKey)
|
||||
}
|
||||
|
||||
@@ -117,14 +113,14 @@ func (w *WGIface) SetFilter(filter PacketFilter) error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
if w.tun.wrapper == nil {
|
||||
if w.tun.Wrapper() == nil {
|
||||
return fmt.Errorf("userspace packet filtering not handled on this device")
|
||||
}
|
||||
|
||||
w.filter = filter
|
||||
w.filter.SetNetwork(w.tun.address.Network)
|
||||
w.filter.SetNetwork(w.tun.WgAddress().Network)
|
||||
|
||||
w.tun.wrapper.SetFilter(filter)
|
||||
w.tun.Wrapper().SetFilter(filter)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -141,5 +137,5 @@ func (w *WGIface) GetDevice() *DeviceWrapper {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
return w.tun.wrapper
|
||||
return w.tun.Wrapper()
|
||||
}
|
||||
|
||||
@@ -2,47 +2,39 @@ package iface
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/transport/v2"
|
||||
"github.com/pion/transport/v3"
|
||||
)
|
||||
|
||||
// NewWGIFace Creates a new WireGuard interface instance
|
||||
func NewWGIFace(ifaceName string, address string, mtu int, tunAdapter TunAdapter, transportNet transport.Net) (*WGIface, error) {
|
||||
wgIFace := &WGIface{
|
||||
mu: sync.Mutex{},
|
||||
}
|
||||
|
||||
func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
|
||||
wgAddress, err := parseWGAddress(address)
|
||||
if err != nil {
|
||||
return wgIFace, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun := newTunDevice(wgAddress, mtu, tunAdapter, transportNet)
|
||||
wgIFace.tun = tun
|
||||
|
||||
wgIFace.configurer = newWGConfigurer(tun)
|
||||
|
||||
wgIFace.userspaceBind = !WireGuardModuleIsLoaded()
|
||||
|
||||
wgIFace := &WGIface{
|
||||
tun: newTunDevice(wgAddress, wgPort, wgPrivKey, mtu, transportNet, args.TunAdapter),
|
||||
userspaceBind: true,
|
||||
}
|
||||
return wgIFace, nil
|
||||
}
|
||||
|
||||
// CreateOnAndroid creates a new Wireguard interface, sets a given IP and brings it up.
|
||||
// Will reuse an existing one.
|
||||
func (w *WGIface) CreateOnAndroid(mIFaceArgs MobileIFaceArguments) error {
|
||||
func (w *WGIface) CreateOnAndroid(routes []string, dns string, searchDomains []string) error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return w.tun.Create(mIFaceArgs)
|
||||
}
|
||||
|
||||
// CreateOniOS creates a new Wireguard interface, sets a given IP and brings it up.
|
||||
// Will reuse an existing one.
|
||||
func (w *WGIface) CreateOniOS(tunFd int32) error {
|
||||
return fmt.Errorf("this function has not implemented on mobile")
|
||||
cfgr, err := w.tun.Create(routes, dns, searchDomains)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.configurer = cfgr
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create this function make sense on mobile only
|
||||
func (w *WGIface) Create() error {
|
||||
return fmt.Errorf("this function has not implemented on mobile")
|
||||
return fmt.Errorf("this function has not implemented on this platform")
|
||||
}
|
||||
|
||||
20
iface/iface_create.go
Normal file
20
iface/iface_create.go
Normal file
@@ -0,0 +1,20 @@
|
||||
//go:build !android
|
||||
// +build !android
|
||||
|
||||
package iface
|
||||
|
||||
// Create creates a new Wireguard interface, sets a given IP and brings it up.
|
||||
// Will reuse an existing one.
|
||||
// this function is different on Android
|
||||
func (w *WGIface) Create() error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
cfgr, err := w.tun.Create()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.configurer = cfgr
|
||||
return nil
|
||||
}
|
||||
38
iface/iface_darwin.go
Normal file
38
iface/iface_darwin.go
Normal file
@@ -0,0 +1,38 @@
|
||||
//go:build !ios
|
||||
// +build !ios
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/transport/v3"
|
||||
|
||||
"github.com/netbirdio/netbird/iface/netstack"
|
||||
)
|
||||
|
||||
// NewWGIFace Creates a new WireGuard interface instance
|
||||
func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
|
||||
wgAddress, err := parseWGAddress(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wgIFace := &WGIface{
|
||||
userspaceBind: true,
|
||||
}
|
||||
|
||||
if netstack.IsEnabled() {
|
||||
wgIFace.tun = newTunNetstackDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet, netstack.ListenAddr())
|
||||
return wgIFace, nil
|
||||
}
|
||||
|
||||
wgIFace.tun = newTunDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet)
|
||||
|
||||
return wgIFace, nil
|
||||
}
|
||||
|
||||
// CreateOnAndroid this function make sense on mobile only
|
||||
func (w *WGIface) CreateOnAndroid([]string, string, []string) error {
|
||||
return fmt.Errorf("this function has not implemented on this platform")
|
||||
}
|
||||
@@ -5,47 +5,25 @@ package iface
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/transport/v2"
|
||||
"github.com/pion/transport/v3"
|
||||
)
|
||||
|
||||
// NewWGIFace Creates a new WireGuard interface instance
|
||||
func NewWGIFace(ifaceName string, address string, mtu int, tunAdapter TunAdapter, transportNet transport.Net) (*WGIface, error) {
|
||||
wgIFace := &WGIface{
|
||||
mu: sync.Mutex{},
|
||||
}
|
||||
|
||||
func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
|
||||
wgAddress, err := parseWGAddress(address)
|
||||
if err != nil {
|
||||
return wgIFace, err
|
||||
return nil, err
|
||||
}
|
||||
wgIFace := &WGIface{
|
||||
tun: newTunDevice(iFaceName, wgAddress, wgPort, wgPrivKey, transportNet, args.TunFd),
|
||||
userspaceBind: true,
|
||||
}
|
||||
|
||||
tun := newTunDevice(ifaceName, wgAddress, mtu, tunAdapter, transportNet)
|
||||
wgIFace.tun = tun
|
||||
|
||||
wgIFace.configurer = newWGConfigurer(tun)
|
||||
|
||||
wgIFace.userspaceBind = !WireGuardModuleIsLoaded()
|
||||
|
||||
return wgIFace, nil
|
||||
}
|
||||
|
||||
// CreateOniOS creates a new Wireguard interface, sets a given IP and brings it up.
|
||||
// Will reuse an existing one.
|
||||
func (w *WGIface) CreateOniOS(tunFd int32) error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return w.tun.Create(tunFd)
|
||||
}
|
||||
|
||||
// CreateOnAndroid creates a new Wireguard interface, sets a given IP and brings it up.
|
||||
// Will reuse an existing one.
|
||||
func (w *WGIface) CreateOnAndroid(mIFaceArgs MobileIFaceArguments) error {
|
||||
return fmt.Errorf("this function has not implemented on mobile")
|
||||
}
|
||||
|
||||
// Create this function make sense on mobile only
|
||||
func (w *WGIface) Create() error {
|
||||
return fmt.Errorf("this function has not implemented on mobile")
|
||||
func (w *WGIface) CreateOnAndroid([]string, string, []string) error {
|
||||
return fmt.Errorf("this function has not implemented on this platform")
|
||||
}
|
||||
|
||||
48
iface/iface_linux.go
Normal file
48
iface/iface_linux.go
Normal file
@@ -0,0 +1,48 @@
|
||||
//go:build !android
|
||||
// +build !android
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/transport/v3"
|
||||
|
||||
"github.com/netbirdio/netbird/iface/netstack"
|
||||
)
|
||||
|
||||
// NewWGIFace Creates a new WireGuard interface instance
|
||||
func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
|
||||
wgAddress, err := parseWGAddress(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wgIFace := &WGIface{}
|
||||
|
||||
// move the kernel/usp/netstack preference evaluation to upper layer
|
||||
if netstack.IsEnabled() {
|
||||
wgIFace.tun = newTunNetstackDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet, netstack.ListenAddr())
|
||||
wgIFace.userspaceBind = true
|
||||
return wgIFace, nil
|
||||
}
|
||||
|
||||
if WireGuardModuleIsLoaded() {
|
||||
wgIFace.tun = newTunDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet)
|
||||
wgIFace.userspaceBind = false
|
||||
return wgIFace, nil
|
||||
}
|
||||
|
||||
if !tunModuleIsLoaded() {
|
||||
return nil, fmt.Errorf("couldn't check or load tun module")
|
||||
}
|
||||
wgIFace.tun = newTunUSPDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet)
|
||||
wgIFace.userspaceBind = true
|
||||
return wgIFace, nil
|
||||
|
||||
}
|
||||
|
||||
// CreateOnAndroid this function make sense on mobile only
|
||||
func (w *WGIface) CreateOnAndroid([]string, string, []string) error {
|
||||
return fmt.Errorf("this function has not implemented on this platform")
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
//go:build !android && !ios
|
||||
// +build !android,!ios
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/transport/v2"
|
||||
)
|
||||
|
||||
// NewWGIFace Creates a new WireGuard interface instance
|
||||
func NewWGIFace(iFaceName string, address string, mtu int, tunAdapter TunAdapter, transportNet transport.Net) (*WGIface, error) {
|
||||
wgIFace := &WGIface{
|
||||
mu: sync.Mutex{},
|
||||
}
|
||||
|
||||
wgAddress, err := parseWGAddress(address)
|
||||
if err != nil {
|
||||
return wgIFace, err
|
||||
}
|
||||
|
||||
wgIFace.tun = newTunDevice(iFaceName, wgAddress, mtu, transportNet)
|
||||
|
||||
wgIFace.configurer = newWGConfigurer(iFaceName)
|
||||
wgIFace.userspaceBind = !WireGuardModuleIsLoaded()
|
||||
return wgIFace, nil
|
||||
}
|
||||
|
||||
// CreateOnAndroid this function make sense on mobile only
|
||||
func (w *WGIface) CreateOnAndroid(mIFaceArgs MobileIFaceArguments) error {
|
||||
return fmt.Errorf("this function has not implemented on non mobile")
|
||||
}
|
||||
|
||||
// CreateOniOS this function make sense on mobile only
|
||||
func (w *WGIface) CreateOniOS(tunFd int32) error {
|
||||
return fmt.Errorf("this function has not implemented on non mobile")
|
||||
}
|
||||
|
||||
// Create creates a new Wireguard interface, sets a given IP and brings it up.
|
||||
// Will reuse an existing one.
|
||||
func (w *WGIface) Create() error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return w.tun.Create()
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pion/transport/v2/stdnet"
|
||||
"github.com/pion/transport/v3/stdnet"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.zx2c4.com/wireguard/wgctrl"
|
||||
@@ -34,12 +34,13 @@ func init() {
|
||||
func TestWGIface_UpdateAddr(t *testing.T) {
|
||||
ifaceName := fmt.Sprintf("utun%d", WgIntNumber+4)
|
||||
addr := "100.64.0.1/8"
|
||||
wgPort := 33100
|
||||
newNet, err := stdnet.NewNet()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
iface, err := NewWGIFace(ifaceName, addr, DefaultMTU, nil, newNet)
|
||||
iface, err := NewWGIFace(ifaceName, addr, wgPort, key, DefaultMTU, newNet, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -52,12 +53,10 @@ func TestWGIface_UpdateAddr(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
}()
|
||||
port, err := getListenPortByName(ifaceName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = iface.Configure(key, port)
|
||||
|
||||
_, err = iface.Up()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -103,7 +102,7 @@ func Test_CreateInterface(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
|
||||
iface, err := NewWGIFace(ifaceName, wgIP, 33100, key, DefaultMTU, newNet, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -132,11 +131,13 @@ func Test_CreateInterface(t *testing.T) {
|
||||
func Test_Close(t *testing.T) {
|
||||
ifaceName := fmt.Sprintf("utun%d", WgIntNumber+2)
|
||||
wgIP := "10.99.99.2/32"
|
||||
wgPort := 33100
|
||||
newNet, err := stdnet.NewNet()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
|
||||
|
||||
iface, err := NewWGIFace(ifaceName, wgIP, wgPort, key, DefaultMTU, newNet, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -164,11 +165,12 @@ func Test_Close(t *testing.T) {
|
||||
func Test_ConfigureInterface(t *testing.T) {
|
||||
ifaceName := fmt.Sprintf("utun%d", WgIntNumber+3)
|
||||
wgIP := "10.99.99.5/30"
|
||||
wgPort := 33100
|
||||
newNet, err := stdnet.NewNet()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
|
||||
iface, err := NewWGIFace(ifaceName, wgIP, wgPort, key, DefaultMTU, newNet, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -183,11 +185,7 @@ func Test_ConfigureInterface(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
port, err := getListenPortByName(ifaceName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = iface.Configure(key, port)
|
||||
_, err = iface.Up()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -219,7 +217,8 @@ func Test_UpdatePeer(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
|
||||
|
||||
iface, err := NewWGIFace(ifaceName, wgIP, 33100, key, DefaultMTU, newNet, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -233,11 +232,8 @@ func Test_UpdatePeer(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
port, err := getListenPortByName(ifaceName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = iface.Configure(key, port)
|
||||
|
||||
_, err = iface.Up()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -251,7 +247,7 @@ func Test_UpdatePeer(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
peer, err := iface.configurer.getPeer(ifaceName, peerPubKey)
|
||||
peer, err := getPeer(ifaceName, peerPubKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -282,7 +278,8 @@ func Test_RemovePeer(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
|
||||
|
||||
iface, err := NewWGIFace(ifaceName, wgIP, 33100, key, DefaultMTU, newNet, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -296,11 +293,8 @@ func Test_RemovePeer(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
port, err := getListenPortByName(ifaceName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = iface.Configure(key, port)
|
||||
|
||||
_, err = iface.Up()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -315,7 +309,8 @@ func Test_RemovePeer(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = iface.configurer.getPeer(ifaceName, peerPubKey)
|
||||
|
||||
_, err = getPeer(ifaceName, peerPubKey)
|
||||
if err.Error() != "peer not found" {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -325,17 +320,20 @@ func Test_ConnectPeers(t *testing.T) {
|
||||
peer1ifaceName := fmt.Sprintf("utun%d", WgIntNumber+400)
|
||||
peer1wgIP := "10.99.99.17/30"
|
||||
peer1Key, _ := wgtypes.GeneratePrivateKey()
|
||||
peer1wgPort := 33100
|
||||
|
||||
peer2ifaceName := "utun500"
|
||||
peer2wgIP := "10.99.99.18/30"
|
||||
peer2Key, _ := wgtypes.GeneratePrivateKey()
|
||||
peer2wgPort := 33200
|
||||
|
||||
keepAlive := 1 * time.Second
|
||||
newNet, err := stdnet.NewNet()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
iface1, err := NewWGIFace(peer1ifaceName, peer1wgIP, DefaultMTU, nil, newNet)
|
||||
|
||||
iface1, err := NewWGIFace(peer1ifaceName, peer1wgIP, peer1wgPort, peer1Key.String(), DefaultMTU, newNet, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -343,11 +341,13 @@ func Test_ConnectPeers(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
peer1Port, err := getListenPortByName(peer1ifaceName)
|
||||
|
||||
_, err = iface1.Up()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
peer1endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer1Port))
|
||||
|
||||
peer1endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer1wgPort))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -356,7 +356,7 @@ func Test_ConnectPeers(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
iface2, err := NewWGIFace(peer2ifaceName, peer2wgIP, DefaultMTU, nil, newNet)
|
||||
iface2, err := NewWGIFace(peer2ifaceName, peer2wgIP, peer2wgPort, peer2Key.String(), DefaultMTU, newNet, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -364,11 +364,13 @@ func Test_ConnectPeers(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
peer2Port, err := getListenPortByName(peer2ifaceName)
|
||||
|
||||
_, err = iface2.Up()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
peer2endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer2Port))
|
||||
|
||||
peer2endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer2wgPort))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -383,15 +385,6 @@ func Test_ConnectPeers(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
err = iface1.Configure(peer1Key.String(), peer1Port)
|
||||
if err != nil {
|
||||
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)
|
||||
@@ -403,13 +396,15 @@ func Test_ConnectPeers(t *testing.T) {
|
||||
// todo: investigate why in some tests execution we need 30s
|
||||
timeout := 30 * time.Second
|
||||
timeoutChannel := time.After(timeout)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-timeoutChannel:
|
||||
t.Fatalf("waiting for peer handshake timeout after %s", timeout.String())
|
||||
default:
|
||||
}
|
||||
peer, gpErr := iface1.configurer.getPeer(peer1ifaceName, peer2Key.PublicKey().String())
|
||||
|
||||
peer, gpErr := getPeer(peer1ifaceName, peer2Key.PublicKey().String())
|
||||
if gpErr != nil {
|
||||
t.Fatal(gpErr)
|
||||
}
|
||||
@@ -421,17 +416,26 @@ func Test_ConnectPeers(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func getListenPortByName(name string) (int, error) {
|
||||
func getPeer(ifaceName, peerPubKey string) (wgtypes.Peer, error) {
|
||||
wg, err := wgctrl.New()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return wgtypes.Peer{}, err
|
||||
}
|
||||
defer wg.Close()
|
||||
defer func() {
|
||||
err = wg.Close()
|
||||
if err != nil {
|
||||
log.Errorf("got error while closing wgctl: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
d, err := wg.Device(name)
|
||||
wgDevice, err := wg.Device(ifaceName)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return wgtypes.Peer{}, err
|
||||
}
|
||||
|
||||
return d.ListenPort, nil
|
||||
for _, peer := range wgDevice.Peers {
|
||||
if peer.PublicKey.String() == peerPubKey {
|
||||
return peer, nil
|
||||
}
|
||||
}
|
||||
return wgtypes.Peer{}, fmt.Errorf("peer not found")
|
||||
}
|
||||
|
||||
@@ -1,6 +1,39 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/transport/v3"
|
||||
|
||||
"github.com/netbirdio/netbird/iface/netstack"
|
||||
)
|
||||
|
||||
// NewWGIFace Creates a new WireGuard interface instance
|
||||
func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
|
||||
wgAddress, err := parseWGAddress(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wgIFace := &WGIface{
|
||||
userspaceBind: true,
|
||||
}
|
||||
|
||||
if netstack.IsEnabled() {
|
||||
wgIFace.tun = newTunNetstackDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet, netstack.ListenAddr())
|
||||
return wgIFace, nil
|
||||
}
|
||||
|
||||
wgIFace.tun = newTunDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet)
|
||||
return wgIFace, nil
|
||||
}
|
||||
|
||||
// CreateOnAndroid this function make sense on mobile only
|
||||
func (w *WGIface) CreateOnAndroid([]string, string, []string) error {
|
||||
return fmt.Errorf("this function has not implemented on non mobile")
|
||||
}
|
||||
|
||||
// GetInterfaceGUIDString returns an interface GUID. This is useful on Windows only
|
||||
func (w *WGIface) GetInterfaceGUIDString() (string, error) {
|
||||
return w.tun.getInterfaceGUIDString()
|
||||
return w.tun.(*tunDevice).getInterfaceGUIDString()
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
//go:build android || ios
|
||||
// +build android ios
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
func toWgUserspaceString(wgCfg wgtypes.Config) string {
|
||||
var sb strings.Builder
|
||||
if wgCfg.PrivateKey != nil {
|
||||
hexKey := hex.EncodeToString(wgCfg.PrivateKey[:])
|
||||
sb.WriteString(fmt.Sprintf("private_key=%s\n", hexKey))
|
||||
}
|
||||
|
||||
if wgCfg.ListenPort != nil {
|
||||
sb.WriteString(fmt.Sprintf("listen_port=%d\n", *wgCfg.ListenPort))
|
||||
}
|
||||
|
||||
if wgCfg.ReplacePeers {
|
||||
sb.WriteString("replace_peers=true\n")
|
||||
}
|
||||
|
||||
if wgCfg.FirewallMark != nil {
|
||||
sb.WriteString(fmt.Sprintf("fwmark=%d\n", *wgCfg.FirewallMark))
|
||||
}
|
||||
|
||||
for _, p := range wgCfg.Peers {
|
||||
hexKey := hex.EncodeToString(p.PublicKey[:])
|
||||
sb.WriteString(fmt.Sprintf("public_key=%s\n", hexKey))
|
||||
|
||||
if p.PresharedKey != nil {
|
||||
preSharedHexKey := hex.EncodeToString(p.PresharedKey[:])
|
||||
sb.WriteString(fmt.Sprintf("preshared_key=%s\n", preSharedHexKey))
|
||||
}
|
||||
|
||||
if p.Remove {
|
||||
sb.WriteString("remove=true")
|
||||
}
|
||||
|
||||
if p.ReplaceAllowedIPs {
|
||||
sb.WriteString("replace_allowed_ips=true\n")
|
||||
}
|
||||
|
||||
for _, aip := range p.AllowedIPs {
|
||||
sb.WriteString(fmt.Sprintf("allowed_ip=%s\n", aip.String()))
|
||||
}
|
||||
|
||||
if p.Endpoint != nil {
|
||||
sb.WriteString(fmt.Sprintf("endpoint=%s\n", p.Endpoint.String()))
|
||||
}
|
||||
|
||||
if p.PersistentKeepaliveInterval != nil {
|
||||
sb.WriteString(fmt.Sprintf("persistent_keepalive_interval=%d\n", int(p.PersistentKeepaliveInterval.Seconds())))
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
32
iface/netstack/dialer.go
Normal file
32
iface/netstack/dialer.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package netstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.zx2c4.com/wireguard/tun/netstack"
|
||||
)
|
||||
|
||||
type Dialer interface {
|
||||
Dial(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
}
|
||||
|
||||
type NSDialer struct {
|
||||
net *netstack.Net
|
||||
}
|
||||
|
||||
func NewNSDialer(net *netstack.Net) *NSDialer {
|
||||
return &NSDialer{
|
||||
net: net,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *NSDialer) Dial(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
log.Debugf("dialing %s %s", network, addr)
|
||||
conn, err := d.net.Dial(network, addr)
|
||||
if err != nil {
|
||||
log.Debugf("failed to deal connection: %s", err)
|
||||
}
|
||||
return conn, err
|
||||
}
|
||||
33
iface/netstack/env.go
Normal file
33
iface/netstack/env.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package netstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// IsEnabled todo: move these function to cmd layer
|
||||
func IsEnabled() bool {
|
||||
return os.Getenv("NB_USE_NETSTACK_MODE") == "true"
|
||||
}
|
||||
|
||||
func ListenAddr() string {
|
||||
sPort := os.Getenv("NB_SOCKS5_LISTENER_PORT")
|
||||
port, err := strconv.Atoi(sPort)
|
||||
if err != nil {
|
||||
log.Warnf("invalid socks5 listener port, unable to convert it to int, falling back to default: %d", DefaultSocks5Port)
|
||||
return listenAddr(DefaultSocks5Port)
|
||||
}
|
||||
if port < 1 || port > 65535 {
|
||||
log.Warnf("invalid socks5 listener port, it should be in the range 1-65535, falling back to default: %d", DefaultSocks5Port)
|
||||
return listenAddr(DefaultSocks5Port)
|
||||
}
|
||||
|
||||
return listenAddr(port)
|
||||
}
|
||||
|
||||
func listenAddr(port int) string {
|
||||
return fmt.Sprintf("0.0.0.0:%d", port)
|
||||
}
|
||||
65
iface/netstack/proxy.go
Normal file
65
iface/netstack/proxy.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package netstack
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/things-go/go-socks5"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultSocks5Port = 1080
|
||||
)
|
||||
|
||||
// Proxy todo close server
|
||||
type Proxy struct {
|
||||
server *socks5.Server
|
||||
|
||||
listener net.Listener
|
||||
closed bool
|
||||
}
|
||||
|
||||
func NewSocks5(dialer Dialer) (*Proxy, error) {
|
||||
server := socks5.NewServer(
|
||||
socks5.WithDial(dialer.Dial),
|
||||
)
|
||||
|
||||
return &Proxy{
|
||||
server: server,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Proxy) ListenAndServe(addr string) error {
|
||||
listener, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
log.Errorf("failed to create listener for socks5 proxy: %s", err)
|
||||
return err
|
||||
}
|
||||
s.listener = listener
|
||||
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
if s.closed {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := s.server.ServeConn(conn); err != nil {
|
||||
log.Errorf("failed to serve a connection: %s", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Proxy) Close() error {
|
||||
if s.listener == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.closed = true
|
||||
return s.listener.Close()
|
||||
}
|
||||
74
iface/netstack/tun.go
Normal file
74
iface/netstack/tun.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package netstack
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"golang.zx2c4.com/wireguard/tun/netstack"
|
||||
)
|
||||
|
||||
type NetStackTun struct {
|
||||
address string
|
||||
mtu int
|
||||
listenAddress string
|
||||
|
||||
proxy *Proxy
|
||||
tundev tun.Device
|
||||
}
|
||||
|
||||
func NewNetStackTun(listenAddress string, address string, mtu int) *NetStackTun {
|
||||
return &NetStackTun{
|
||||
address: address,
|
||||
mtu: mtu,
|
||||
listenAddress: listenAddress,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *NetStackTun) Create() (tun.Device, error) {
|
||||
nsTunDev, tunNet, err := netstack.CreateNetTUN(
|
||||
[]netip.Addr{netip.MustParseAddr(t.address)},
|
||||
[]netip.Addr{},
|
||||
t.mtu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.tundev = nsTunDev
|
||||
|
||||
dialer := NewNSDialer(tunNet)
|
||||
t.proxy, err = NewSocks5(dialer)
|
||||
if err != nil {
|
||||
_ = t.tundev.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := t.proxy.ListenAndServe(t.listenAddress)
|
||||
if err != nil {
|
||||
log.Errorf("error in socks5 proxy serving: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return nsTunDev, nil
|
||||
}
|
||||
|
||||
func (t *NetStackTun) Close() error {
|
||||
var err error
|
||||
if t.proxy != nil {
|
||||
pErr := t.proxy.Close()
|
||||
if pErr != nil {
|
||||
log.Errorf("failed to close socks5 proxy: %s", pErr)
|
||||
err = pErr
|
||||
}
|
||||
}
|
||||
|
||||
if t.tundev != nil {
|
||||
dErr := t.tundev.Close()
|
||||
if dErr != nil {
|
||||
log.Errorf("failed to close netstack tun device: %s", dErr)
|
||||
err = dErr
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
20
iface/tun.go
20
iface/tun.go
@@ -1,12 +1,18 @@
|
||||
//go:build !android
|
||||
// +build !android
|
||||
|
||||
package iface
|
||||
|
||||
type MobileIFaceArguments struct {
|
||||
Routes []string
|
||||
Dns string
|
||||
SearchDomains []string
|
||||
}
|
||||
import (
|
||||
"github.com/netbirdio/netbird/iface/bind"
|
||||
)
|
||||
|
||||
// NetInterface represents a generic network tunnel interface
|
||||
type NetInterface interface {
|
||||
type wgTunDevice interface {
|
||||
Create() (wgConfigurer, error)
|
||||
Up() (*bind.UniversalUDPMuxDefault, error)
|
||||
UpdateAddr(address WGAddress) error
|
||||
WgAddress() WGAddress
|
||||
DeviceName() string
|
||||
Close() error
|
||||
Wrapper() *DeviceWrapper // todo eliminate this function
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ package iface
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/pion/transport/v2"
|
||||
"github.com/pion/transport/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
@@ -15,42 +15,50 @@ import (
|
||||
"github.com/netbirdio/netbird/iface/bind"
|
||||
)
|
||||
|
||||
type tunDevice struct {
|
||||
// ignore the wgTunDevice interface on Android because the creation of the tun device is different on this platform
|
||||
type wgTunDevice struct {
|
||||
address WGAddress
|
||||
port int
|
||||
key string
|
||||
mtu int
|
||||
tunAdapter TunAdapter
|
||||
iceBind *bind.ICEBind
|
||||
tunAdapter TunAdapter
|
||||
|
||||
fd int
|
||||
name string
|
||||
device *device.Device
|
||||
wrapper *DeviceWrapper
|
||||
name string
|
||||
device *device.Device
|
||||
wrapper *DeviceWrapper
|
||||
udpMux *bind.UniversalUDPMuxDefault
|
||||
configurer wgConfigurer
|
||||
}
|
||||
|
||||
func newTunDevice(address WGAddress, mtu int, tunAdapter TunAdapter, transportNet transport.Net) *tunDevice {
|
||||
return &tunDevice{
|
||||
func newTunDevice(address WGAddress, port int, key string, mtu int, transportNet transport.Net, tunAdapter TunAdapter) wgTunDevice {
|
||||
return wgTunDevice{
|
||||
address: address,
|
||||
port: port,
|
||||
key: key,
|
||||
mtu: mtu,
|
||||
tunAdapter: tunAdapter,
|
||||
iceBind: bind.NewICEBind(transportNet),
|
||||
tunAdapter: tunAdapter,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tunDevice) Create(mIFaceArgs MobileIFaceArguments) error {
|
||||
func (t *wgTunDevice) Create(routes []string, dns string, searchDomains []string) (wgConfigurer, error) {
|
||||
log.Info("create tun interface")
|
||||
var err error
|
||||
routesString := t.routesToString(mIFaceArgs.Routes)
|
||||
searchDomainsToString := t.searchDomainsToString(mIFaceArgs.SearchDomains)
|
||||
t.fd, err = t.tunAdapter.ConfigureInterface(t.address.String(), t.mtu, mIFaceArgs.Dns, searchDomainsToString, routesString)
|
||||
|
||||
routesString := routesToString(routes)
|
||||
searchDomainsToString := searchDomainsToString(searchDomains)
|
||||
|
||||
fd, err := t.tunAdapter.ConfigureInterface(t.address.String(), t.mtu, dns, searchDomainsToString, routesString)
|
||||
if err != nil {
|
||||
log.Errorf("failed to create Android interface: %s", err)
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tunDevice, name, err := tun.CreateUnmonitoredTUNFromFD(t.fd)
|
||||
tunDevice, name, err := tun.CreateUnmonitoredTUNFromFD(fd)
|
||||
if err != nil {
|
||||
unix.Close(t.fd)
|
||||
return err
|
||||
_ = unix.Close(fd)
|
||||
log.Errorf("failed to create Android interface: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
t.name = name
|
||||
t.wrapper = newDeviceWrapper(tunDevice)
|
||||
@@ -61,44 +69,72 @@ func (t *tunDevice) Create(mIFaceArgs MobileIFaceArguments) error {
|
||||
// this helps with support for the older NetBird clients that had a hardcoded direct mode
|
||||
// t.device.DisableSomeRoamingForBrokenMobileSemantics()
|
||||
|
||||
err = t.device.Up()
|
||||
t.configurer = newWGUSPConfigurer(t.device, t.name)
|
||||
err = t.configurer.configureInterface(t.key, t.port)
|
||||
if err != nil {
|
||||
t.device.Close()
|
||||
return err
|
||||
t.configurer.close()
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("device is ready to use: %s", name)
|
||||
return nil
|
||||
return t.configurer, nil
|
||||
}
|
||||
func (t *wgTunDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||
err := t.device.Up()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
udpMux, err := t.iceBind.GetICEMux()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.udpMux = udpMux
|
||||
log.Debugf("device is ready to use: %s", t.name)
|
||||
return udpMux, nil
|
||||
}
|
||||
|
||||
func (t *tunDevice) Device() *device.Device {
|
||||
return t.device
|
||||
}
|
||||
|
||||
func (t *tunDevice) DeviceName() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *tunDevice) WgAddress() WGAddress {
|
||||
return t.address
|
||||
}
|
||||
|
||||
func (t *tunDevice) UpdateAddr(addr WGAddress) error {
|
||||
func (t *wgTunDevice) UpdateAddr(addr WGAddress) error {
|
||||
// todo implement
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tunDevice) Close() (err error) {
|
||||
if t.device != nil {
|
||||
t.device.Close()
|
||||
func (t *wgTunDevice) Close() error {
|
||||
if t.configurer != nil {
|
||||
t.configurer.close()
|
||||
}
|
||||
|
||||
return
|
||||
if t.device != nil {
|
||||
t.device.Close()
|
||||
t.device = nil
|
||||
}
|
||||
|
||||
if t.udpMux != nil {
|
||||
return t.udpMux.Close()
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tunDevice) routesToString(routes []string) string {
|
||||
func (t *wgTunDevice) Device() *device.Device {
|
||||
return t.device
|
||||
}
|
||||
|
||||
func (t *wgTunDevice) DeviceName() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *wgTunDevice) WgAddress() WGAddress {
|
||||
return t.address
|
||||
}
|
||||
|
||||
func (t *wgTunDevice) Wrapper() *DeviceWrapper {
|
||||
return t.wrapper
|
||||
}
|
||||
|
||||
func routesToString(routes []string) string {
|
||||
return strings.Join(routes, ";")
|
||||
}
|
||||
|
||||
func (t *tunDevice) searchDomainsToString(searchDomains []string) string {
|
||||
func searchDomainsToString(searchDomains []string) string {
|
||||
return strings.Join(searchDomains, ";")
|
||||
}
|
||||
|
||||
6
iface/tun_args.go
Normal file
6
iface/tun_args.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package iface
|
||||
|
||||
type MobileIFaceArguments struct {
|
||||
TunAdapter TunAdapter // only for Android
|
||||
TunFd int // only for iOS
|
||||
}
|
||||
@@ -6,32 +6,129 @@ package iface
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
"github.com/pion/transport/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
|
||||
"github.com/netbirdio/netbird/iface/bind"
|
||||
)
|
||||
|
||||
func (c *tunDevice) Create() error {
|
||||
var err error
|
||||
c.netInterface, err = c.createWithUserspace()
|
||||
type tunDevice struct {
|
||||
name string
|
||||
address WGAddress
|
||||
port int
|
||||
key string
|
||||
mtu int
|
||||
iceBind *bind.ICEBind
|
||||
|
||||
device *device.Device
|
||||
wrapper *DeviceWrapper
|
||||
udpMux *bind.UniversalUDPMuxDefault
|
||||
configurer wgConfigurer
|
||||
}
|
||||
|
||||
func newTunDevice(name string, address WGAddress, port int, key string, mtu int, transportNet transport.Net) wgTunDevice {
|
||||
return &tunDevice{
|
||||
name: name,
|
||||
address: address,
|
||||
port: port,
|
||||
key: key,
|
||||
mtu: mtu,
|
||||
iceBind: bind.NewICEBind(transportNet),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tunDevice) Create() (wgConfigurer, error) {
|
||||
tunDevice, err := tun.CreateTUN(t.name, t.mtu)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
t.wrapper = newDeviceWrapper(tunDevice)
|
||||
|
||||
// We need to create a wireguard-go device and listen to configuration requests
|
||||
t.device = device.NewDevice(
|
||||
t.wrapper,
|
||||
t.iceBind,
|
||||
device.NewLogger(device.LogLevelSilent, "[netbird] "),
|
||||
)
|
||||
|
||||
err = t.assignAddr()
|
||||
if err != nil {
|
||||
t.device.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.assignAddr()
|
||||
t.configurer = newWGUSPConfigurer(t.device, t.name)
|
||||
err = t.configurer.configureInterface(t.key, t.port)
|
||||
if err != nil {
|
||||
t.device.Close()
|
||||
t.configurer.close()
|
||||
return nil, err
|
||||
}
|
||||
return t.configurer, nil
|
||||
}
|
||||
|
||||
func (t *tunDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||
err := t.device.Up()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
udpMux, err := t.iceBind.GetICEMux()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.udpMux = udpMux
|
||||
log.Debugf("device is ready to use: %s", t.name)
|
||||
return udpMux, nil
|
||||
}
|
||||
|
||||
func (t *tunDevice) UpdateAddr(address WGAddress) error {
|
||||
t.address = address
|
||||
return t.assignAddr()
|
||||
}
|
||||
|
||||
func (t *tunDevice) Close() error {
|
||||
if t.configurer != nil {
|
||||
t.configurer.close()
|
||||
}
|
||||
|
||||
if t.device != nil {
|
||||
t.device.Close()
|
||||
t.device = nil
|
||||
}
|
||||
|
||||
if t.udpMux != nil {
|
||||
return t.udpMux.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tunDevice) WgAddress() WGAddress {
|
||||
return t.address
|
||||
}
|
||||
|
||||
func (t *tunDevice) DeviceName() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *tunDevice) Wrapper() *DeviceWrapper {
|
||||
return t.wrapper
|
||||
}
|
||||
|
||||
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided
|
||||
func (c *tunDevice) assignAddr() error {
|
||||
cmd := exec.Command("ifconfig", c.name, "inet", c.address.IP.String(), c.address.IP.String())
|
||||
func (t *tunDevice) assignAddr() error {
|
||||
cmd := exec.Command("ifconfig", t.name, "inet", t.address.IP.String(), t.address.IP.String())
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
log.Infof(`adding address command "%v" failed with output %s and error: `, cmd.String(), out)
|
||||
return err
|
||||
}
|
||||
|
||||
routeCmd := exec.Command("route", "add", "-net", c.address.Network.String(), "-interface", c.name)
|
||||
routeCmd := exec.Command("route", "add", "-net", t.address.Network.String(), "-interface", t.name)
|
||||
if out, err := routeCmd.CombinedOutput(); err != nil {
|
||||
log.Printf(`adding route command "%v" failed with output %s and error: `, routeCmd.String(), out)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ package iface
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/pion/transport/v2"
|
||||
"github.com/pion/transport/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
@@ -16,63 +16,82 @@ import (
|
||||
)
|
||||
|
||||
type tunDevice struct {
|
||||
address WGAddress
|
||||
mtu int
|
||||
tunAdapter TunAdapter
|
||||
iceBind *bind.ICEBind
|
||||
|
||||
fd int
|
||||
name string
|
||||
device *device.Device
|
||||
wrapper *DeviceWrapper
|
||||
address WGAddress
|
||||
port int
|
||||
key string
|
||||
iceBind *bind.ICEBind
|
||||
tunFd int
|
||||
|
||||
device *device.Device
|
||||
wrapper *DeviceWrapper
|
||||
udpMux *bind.UniversalUDPMuxDefault
|
||||
configurer wgConfigurer
|
||||
}
|
||||
|
||||
func newTunDevice(name string, address WGAddress, mtu int, tunAdapter TunAdapter, transportNet transport.Net) *tunDevice {
|
||||
func newTunDevice(name string, address WGAddress, port int, key string, transportNet transport.Net, tunFd int) *tunDevice {
|
||||
return &tunDevice{
|
||||
name: name,
|
||||
address: address,
|
||||
mtu: mtu,
|
||||
tunAdapter: tunAdapter,
|
||||
iceBind: bind.NewICEBind(transportNet),
|
||||
name: name,
|
||||
address: address,
|
||||
port: port,
|
||||
key: key,
|
||||
iceBind: bind.NewICEBind(transportNet),
|
||||
tunFd: tunFd,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tunDevice) Create(tunFd int32) error {
|
||||
func (t *tunDevice) Create() (wgConfigurer, error) {
|
||||
log.Infof("create tun interface")
|
||||
|
||||
dupTunFd, err := unix.Dup(int(tunFd))
|
||||
dupTunFd, err := unix.Dup(t.tunFd)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to dup tun fd: %v", err)
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = unix.SetNonblock(dupTunFd, true)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to set tun fd as non blocking: %v", err)
|
||||
unix.Close(dupTunFd)
|
||||
return err
|
||||
_ = unix.Close(dupTunFd)
|
||||
return nil, err
|
||||
}
|
||||
tun, err := tun.CreateTUNFromFile(os.NewFile(uintptr(dupTunFd), "/dev/tun"), 0)
|
||||
tunDevice, err := tun.CreateTUNFromFile(os.NewFile(uintptr(dupTunFd), "/dev/tun"), 0)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to create new tun device from fd: %v", err)
|
||||
unix.Close(dupTunFd)
|
||||
return err
|
||||
_ = unix.Close(dupTunFd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t.wrapper = newDeviceWrapper(tun)
|
||||
t.wrapper = newDeviceWrapper(tunDevice)
|
||||
log.Debug("Attaching to interface")
|
||||
t.device = device.NewDevice(t.wrapper, t.iceBind, device.NewLogger(device.LogLevelSilent, "[wiretrustee] "))
|
||||
// without this property mobile devices can discover remote endpoints if the configured one was wrong.
|
||||
// this helps with support for the older NetBird clients that had a hardcoded direct mode
|
||||
// t.device.DisableSomeRoamingForBrokenMobileSemantics()
|
||||
|
||||
err = t.device.Up()
|
||||
t.configurer = newWGUSPConfigurer(t.device, t.name)
|
||||
err = t.configurer.configureInterface(t.key, t.port)
|
||||
if err != nil {
|
||||
t.device.Close()
|
||||
return err
|
||||
t.configurer.close()
|
||||
return nil, err
|
||||
}
|
||||
return t.configurer, nil
|
||||
}
|
||||
|
||||
func (t *tunDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||
err := t.device.Up()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
udpMux, err := t.iceBind.GetICEMux()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.udpMux = udpMux
|
||||
log.Debugf("device is ready to use: %s", t.name)
|
||||
return nil
|
||||
return udpMux, nil
|
||||
}
|
||||
|
||||
func (t *tunDevice) Device() *device.Device {
|
||||
@@ -83,6 +102,23 @@ func (t *tunDevice) DeviceName() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *tunDevice) Close() error {
|
||||
if t.configurer != nil {
|
||||
t.configurer.close()
|
||||
}
|
||||
|
||||
if t.device != nil {
|
||||
t.device.Close()
|
||||
t.device = nil
|
||||
}
|
||||
|
||||
if t.udpMux != nil {
|
||||
return t.udpMux.Close()
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tunDevice) WgAddress() WGAddress {
|
||||
return t.address
|
||||
}
|
||||
@@ -92,10 +128,6 @@ func (t *tunDevice) UpdateAddr(addr WGAddress) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tunDevice) Close() (err error) {
|
||||
if t.device != nil {
|
||||
t.device.Close()
|
||||
}
|
||||
|
||||
return
|
||||
func (t *tunDevice) Wrapper() *DeviceWrapper {
|
||||
return t.wrapper
|
||||
}
|
||||
|
||||
209
iface/tun_kernel_linux.go
Normal file
209
iface/tun_kernel_linux.go
Normal file
@@ -0,0 +1,209 @@
|
||||
//go:build linux && !android
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/pion/transport/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
"github.com/netbirdio/netbird/iface/bind"
|
||||
"github.com/netbirdio/netbird/sharedsock"
|
||||
)
|
||||
|
||||
type tunKernelDevice struct {
|
||||
name string
|
||||
address WGAddress
|
||||
wgPort int
|
||||
key string
|
||||
mtu int
|
||||
ctx context.Context
|
||||
ctxCancel context.CancelFunc
|
||||
transportNet transport.Net
|
||||
|
||||
link *wgLink
|
||||
udpMuxConn net.PacketConn
|
||||
udpMux *bind.UniversalUDPMuxDefault
|
||||
}
|
||||
|
||||
func newTunDevice(name string, address WGAddress, wgPort int, key string, mtu int, transportNet transport.Net) wgTunDevice {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
return &tunKernelDevice{
|
||||
ctx: ctx,
|
||||
ctxCancel: cancel,
|
||||
name: name,
|
||||
address: address,
|
||||
wgPort: wgPort,
|
||||
key: key,
|
||||
mtu: mtu,
|
||||
transportNet: transportNet,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tunKernelDevice) Create() (wgConfigurer, error) {
|
||||
link := newWGLink(t.name)
|
||||
|
||||
// check if interface exists
|
||||
l, err := netlink.LinkByName(t.name)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case netlink.LinkNotFoundError:
|
||||
break
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// remove if interface exists
|
||||
if l != nil {
|
||||
err = netlink.LinkDel(link)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("adding device: %s", t.name)
|
||||
err = netlink.LinkAdd(link)
|
||||
if os.IsExist(err) {
|
||||
log.Infof("interface %s already exists. Will reuse.", t.name)
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t.link = link
|
||||
|
||||
err = t.assignAddr()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// todo do a discovery
|
||||
log.Debugf("setting MTU: %d interface: %s", t.mtu, t.name)
|
||||
err = netlink.LinkSetMTU(link, t.mtu)
|
||||
if err != nil {
|
||||
log.Errorf("error setting MTU on interface: %s", t.name)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
configurer := newWGConfigurer(t.name)
|
||||
err = configurer.configureInterface(t.key, t.wgPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return configurer, nil
|
||||
}
|
||||
|
||||
func (t *tunKernelDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||
if t.udpMux != nil {
|
||||
return t.udpMux, nil
|
||||
}
|
||||
|
||||
if t.link == nil {
|
||||
return nil, fmt.Errorf("device is not ready yet")
|
||||
}
|
||||
|
||||
log.Debugf("bringing up interface: %s", t.name)
|
||||
err := netlink.LinkSetUp(t.link)
|
||||
if err != nil {
|
||||
log.Errorf("error bringing up interface: %s", t.name)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawSock, err := sharedsock.Listen(t.wgPort, sharedsock.NewIncomingSTUNFilter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bindParams := bind.UniversalUDPMuxParams{
|
||||
UDPConn: rawSock,
|
||||
Net: t.transportNet,
|
||||
}
|
||||
mux := bind.NewUniversalUDPMuxDefault(bindParams)
|
||||
go mux.ReadFromConn(t.ctx)
|
||||
t.udpMuxConn = rawSock
|
||||
t.udpMux = mux
|
||||
|
||||
log.Debugf("device is ready to use: %s", t.name)
|
||||
return t.udpMux, nil
|
||||
}
|
||||
|
||||
func (t *tunKernelDevice) UpdateAddr(address WGAddress) error {
|
||||
t.address = address
|
||||
return t.assignAddr()
|
||||
}
|
||||
|
||||
func (t *tunKernelDevice) Close() error {
|
||||
if t.link == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
t.ctxCancel()
|
||||
|
||||
var closErr error
|
||||
if err := t.link.Close(); err != nil {
|
||||
log.Debugf("failed to close link: %s", err)
|
||||
closErr = err
|
||||
}
|
||||
|
||||
if t.udpMux != nil {
|
||||
if err := t.udpMux.Close(); err != nil {
|
||||
log.Debugf("failed to close udp mux: %s", err)
|
||||
closErr = err
|
||||
}
|
||||
|
||||
if err := t.udpMuxConn.Close(); err != nil {
|
||||
log.Debugf("failed to close udp mux connection: %s", err)
|
||||
closErr = err
|
||||
}
|
||||
}
|
||||
|
||||
return closErr
|
||||
}
|
||||
|
||||
func (t *tunKernelDevice) WgAddress() WGAddress {
|
||||
return t.address
|
||||
}
|
||||
|
||||
func (t *tunKernelDevice) DeviceName() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *tunKernelDevice) Wrapper() *DeviceWrapper {
|
||||
return nil
|
||||
}
|
||||
|
||||
// assignAddr Adds IP address to the tunnel interface
|
||||
func (t *tunKernelDevice) assignAddr() error {
|
||||
link := newWGLink(t.name)
|
||||
|
||||
//delete existing addresses
|
||||
list, err := netlink.AddrList(link, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(list) > 0 {
|
||||
for _, a := range list {
|
||||
addr := a
|
||||
err = netlink.AddrDel(link, &addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("adding address %s to interface: %s", t.address.String(), t.name)
|
||||
addr, _ := netlink.ParseAddr(t.address.String())
|
||||
err = netlink.AddrAdd(link, addr)
|
||||
if os.IsExist(err) {
|
||||
log.Infof("interface %s already has the address: %s", t.name, t.address.String())
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
// On linux, the link must be brought up
|
||||
err = netlink.LinkSetUp(link)
|
||||
return err
|
||||
}
|
||||
33
iface/tun_link_linux.go
Normal file
33
iface/tun_link_linux.go
Normal file
@@ -0,0 +1,33 @@
|
||||
//go:build linux && !android
|
||||
|
||||
package iface
|
||||
|
||||
import "github.com/vishvananda/netlink"
|
||||
|
||||
type wgLink struct {
|
||||
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
|
||||
func (l *wgLink) Attrs() *netlink.LinkAttrs {
|
||||
return l.attrs
|
||||
}
|
||||
|
||||
// Type returns the interface type
|
||||
func (l *wgLink) Type() string {
|
||||
return "wireguard"
|
||||
}
|
||||
|
||||
// Close deletes the link interface
|
||||
func (l *wgLink) Close() error {
|
||||
return netlink.LinkDel(l)
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
//go:build linux && !android
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
func (c *tunDevice) Create() error {
|
||||
if WireGuardModuleIsLoaded() {
|
||||
log.Infof("create tun interface with kernel WireGuard support: %s", c.DeviceName())
|
||||
return c.createWithKernel()
|
||||
}
|
||||
|
||||
if !tunModuleIsLoaded() {
|
||||
return fmt.Errorf("couldn't check or load tun module")
|
||||
}
|
||||
log.Infof("create tun interface with userspace WireGuard support: %s", c.DeviceName())
|
||||
var err error
|
||||
c.netInterface, err = c.createWithUserspace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.assignAddr()
|
||||
|
||||
}
|
||||
|
||||
// createWithKernel Creates a new WireGuard interface using kernel WireGuard module.
|
||||
// Works for Linux and offers much better network performance
|
||||
func (c *tunDevice) createWithKernel() error {
|
||||
|
||||
link := newWGLink(c.name)
|
||||
|
||||
// check if interface exists
|
||||
l, err := netlink.LinkByName(c.name)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case netlink.LinkNotFoundError:
|
||||
break
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// remove if interface exists
|
||||
if l != nil {
|
||||
err = netlink.LinkDel(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("adding device: %s", c.name)
|
||||
err = netlink.LinkAdd(link)
|
||||
if os.IsExist(err) {
|
||||
log.Infof("interface %s already exists. Will reuse.", c.name)
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.netInterface = link
|
||||
|
||||
err = c.assignAddr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// todo do a discovery
|
||||
log.Debugf("setting MTU: %d interface: %s", c.mtu, c.name)
|
||||
err = netlink.LinkSetMTU(link, c.mtu)
|
||||
if err != nil {
|
||||
log.Errorf("error setting MTU on interface: %s", c.name)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("bringing up interface: %s", c.name)
|
||||
err = netlink.LinkSetUp(link)
|
||||
if err != nil {
|
||||
log.Errorf("error bringing up interface: %s", c.name)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// assignAddr Adds IP address to the tunnel interface
|
||||
func (c *tunDevice) assignAddr() error {
|
||||
link := newWGLink(c.name)
|
||||
|
||||
//delete existing addresses
|
||||
list, err := netlink.AddrList(link, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(list) > 0 {
|
||||
for _, a := range list {
|
||||
addr := a
|
||||
err = netlink.AddrDel(link, &addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("adding address %s to interface: %s", c.address.String(), c.name)
|
||||
addr, _ := netlink.ParseAddr(c.address.String())
|
||||
err = netlink.AddrAdd(link, addr)
|
||||
if os.IsExist(err) {
|
||||
log.Infof("interface %s already has the address: %s", c.name, c.address.String())
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
// On linux, the link must be brought up
|
||||
err = netlink.LinkSetUp(link)
|
||||
return err
|
||||
}
|
||||
|
||||
type wgLink struct {
|
||||
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
|
||||
func (l *wgLink) Attrs() *netlink.LinkAttrs {
|
||||
return l.attrs
|
||||
}
|
||||
|
||||
// Type returns the interface type
|
||||
func (l *wgLink) Type() string {
|
||||
return "wireguard"
|
||||
}
|
||||
|
||||
// Close deletes the link interface
|
||||
func (l *wgLink) Close() error {
|
||||
return netlink.LinkDel(l)
|
||||
}
|
||||
119
iface/tun_netstack.go
Normal file
119
iface/tun_netstack.go
Normal file
@@ -0,0 +1,119 @@
|
||||
//go:build !android
|
||||
// +build !android
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/transport/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
|
||||
"github.com/netbirdio/netbird/iface/bind"
|
||||
"github.com/netbirdio/netbird/iface/netstack"
|
||||
)
|
||||
|
||||
type tunNetstackDevice struct {
|
||||
name string
|
||||
address WGAddress
|
||||
port int
|
||||
key string
|
||||
mtu int
|
||||
listenAddress string
|
||||
iceBind *bind.ICEBind
|
||||
|
||||
device *device.Device
|
||||
wrapper *DeviceWrapper
|
||||
nsTun *netstack.NetStackTun
|
||||
udpMux *bind.UniversalUDPMuxDefault
|
||||
configurer wgConfigurer
|
||||
}
|
||||
|
||||
func newTunNetstackDevice(name string, address WGAddress, wgPort int, key string, mtu int, transportNet transport.Net, listenAddress string) wgTunDevice {
|
||||
return &tunNetstackDevice{
|
||||
name: name,
|
||||
address: address,
|
||||
port: wgPort,
|
||||
key: key,
|
||||
mtu: mtu,
|
||||
listenAddress: listenAddress,
|
||||
iceBind: bind.NewICEBind(transportNet),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tunNetstackDevice) Create() (wgConfigurer, error) {
|
||||
log.Info("create netstack tun interface")
|
||||
t.nsTun = netstack.NewNetStackTun(t.listenAddress, t.address.IP.String(), t.mtu)
|
||||
tunIface, err := t.nsTun.Create()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.wrapper = newDeviceWrapper(tunIface)
|
||||
|
||||
t.device = device.NewDevice(
|
||||
t.wrapper,
|
||||
t.iceBind,
|
||||
device.NewLogger(device.LogLevelSilent, "[netbird] "),
|
||||
)
|
||||
|
||||
t.configurer = newWGUSPConfigurer(t.device, t.name)
|
||||
err = t.configurer.configureInterface(t.key, t.port)
|
||||
if err != nil {
|
||||
_ = tunIface.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugf("device has been created: %s", t.name)
|
||||
return t.configurer, nil
|
||||
}
|
||||
|
||||
func (t *tunNetstackDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||
if t.device == nil {
|
||||
return nil, fmt.Errorf("device is not ready yet")
|
||||
}
|
||||
|
||||
err := t.device.Up()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
udpMux, err := t.iceBind.GetICEMux()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.udpMux = udpMux
|
||||
log.Debugf("netstack device is ready to use")
|
||||
return udpMux, nil
|
||||
}
|
||||
|
||||
func (t *tunNetstackDevice) UpdateAddr(WGAddress) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tunNetstackDevice) Close() error {
|
||||
if t.configurer != nil {
|
||||
t.configurer.close()
|
||||
}
|
||||
|
||||
if t.device != nil {
|
||||
t.device.Close()
|
||||
}
|
||||
|
||||
if t.udpMux != nil {
|
||||
return t.udpMux.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tunNetstackDevice) WgAddress() WGAddress {
|
||||
return t.address
|
||||
}
|
||||
|
||||
func (t *tunNetstackDevice) DeviceName() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *tunNetstackDevice) Wrapper() *DeviceWrapper {
|
||||
return t.wrapper
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
//go:build (linux || darwin) && !android && !ios
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/pion/transport/v2"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
|
||||
"github.com/netbirdio/netbird/iface/bind"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
)
|
||||
|
||||
type tunDevice struct {
|
||||
name string
|
||||
address WGAddress
|
||||
mtu int
|
||||
netInterface NetInterface
|
||||
iceBind *bind.ICEBind
|
||||
uapi net.Listener
|
||||
wrapper *DeviceWrapper
|
||||
close chan struct{}
|
||||
}
|
||||
|
||||
func newTunDevice(name string, address WGAddress, mtu int, transportNet transport.Net) *tunDevice {
|
||||
return &tunDevice{
|
||||
name: name,
|
||||
address: address,
|
||||
mtu: mtu,
|
||||
iceBind: bind.NewICEBind(transportNet),
|
||||
close: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *tunDevice) UpdateAddr(address WGAddress) error {
|
||||
c.address = address
|
||||
return c.assignAddr()
|
||||
}
|
||||
|
||||
func (c *tunDevice) WgAddress() WGAddress {
|
||||
return c.address
|
||||
}
|
||||
|
||||
func (c *tunDevice) DeviceName() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func (c *tunDevice) Close() error {
|
||||
|
||||
select {
|
||||
case c.close <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
|
||||
var err1, err2, err3 error
|
||||
if c.netInterface != nil {
|
||||
err1 = c.netInterface.Close()
|
||||
}
|
||||
|
||||
if c.uapi != nil {
|
||||
err2 = c.uapi.Close()
|
||||
}
|
||||
|
||||
sockPath := "/var/run/wireguard/" + c.name + ".sock"
|
||||
if _, statErr := os.Stat(sockPath); statErr == nil {
|
||||
statErr = os.Remove(sockPath)
|
||||
if statErr != nil {
|
||||
err3 = statErr
|
||||
}
|
||||
}
|
||||
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
|
||||
return err3
|
||||
}
|
||||
|
||||
// createWithUserspace Creates a new Wireguard interface, using wireguard-go userspace implementation
|
||||
func (c *tunDevice) createWithUserspace() (NetInterface, error) {
|
||||
tunIface, err := tun.CreateTUN(c.name, c.mtu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.wrapper = newDeviceWrapper(tunIface)
|
||||
|
||||
// We need to create a wireguard-go device and listen to configuration requests
|
||||
tunDev := device.NewDevice(
|
||||
c.wrapper,
|
||||
c.iceBind,
|
||||
device.NewLogger(device.LogLevelSilent, "[netbird] "),
|
||||
)
|
||||
err = tunDev.Up()
|
||||
if err != nil {
|
||||
_ = tunIface.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.uapi, err = c.getUAPI(c.name)
|
||||
if err != nil {
|
||||
_ = tunIface.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c.close:
|
||||
log.Debugf("exit uapi.Accept()")
|
||||
return
|
||||
default:
|
||||
}
|
||||
uapiConn, uapiErr := c.uapi.Accept()
|
||||
if uapiErr != nil {
|
||||
log.Traceln("uapi Accept failed with error: ", uapiErr)
|
||||
continue
|
||||
}
|
||||
go func() {
|
||||
tunDev.IpcHandle(uapiConn)
|
||||
log.Debugf("exit tunDevice.IpcHandle")
|
||||
}()
|
||||
}
|
||||
}()
|
||||
|
||||
log.Debugln("UAPI listener started")
|
||||
return tunIface, nil
|
||||
}
|
||||
|
||||
// getUAPI returns a Listener
|
||||
func (c *tunDevice) getUAPI(iface string) (net.Listener, error) {
|
||||
tunSock, err := ipc.UAPIOpen(iface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ipc.UAPIListen(iface, tunSock)
|
||||
}
|
||||
157
iface/tun_usp_linux.go
Normal file
157
iface/tun_usp_linux.go
Normal file
@@ -0,0 +1,157 @@
|
||||
//go:build linux && !android
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/pion/transport/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
|
||||
"github.com/netbirdio/netbird/iface/bind"
|
||||
)
|
||||
|
||||
type tunUSPDevice struct {
|
||||
name string
|
||||
address WGAddress
|
||||
port int
|
||||
key string
|
||||
mtu int
|
||||
iceBind *bind.ICEBind
|
||||
|
||||
device *device.Device
|
||||
wrapper *DeviceWrapper
|
||||
udpMux *bind.UniversalUDPMuxDefault
|
||||
configurer wgConfigurer
|
||||
}
|
||||
|
||||
func newTunUSPDevice(name string, address WGAddress, port int, key string, mtu int, transportNet transport.Net) wgTunDevice {
|
||||
log.Infof("using userspace bind mode")
|
||||
return &tunUSPDevice{
|
||||
name: name,
|
||||
address: address,
|
||||
port: port,
|
||||
key: key,
|
||||
mtu: mtu,
|
||||
iceBind: bind.NewICEBind(transportNet),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tunUSPDevice) Create() (wgConfigurer, error) {
|
||||
log.Info("create tun interface")
|
||||
tunIface, err := tun.CreateTUN(t.name, t.mtu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.wrapper = newDeviceWrapper(tunIface)
|
||||
|
||||
// We need to create a wireguard-go device and listen to configuration requests
|
||||
t.device = device.NewDevice(
|
||||
t.wrapper,
|
||||
t.iceBind,
|
||||
device.NewLogger(device.LogLevelSilent, "[netbird] "),
|
||||
)
|
||||
|
||||
err = t.assignAddr()
|
||||
if err != nil {
|
||||
t.device.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t.configurer = newWGUSPConfigurer(t.device, t.name)
|
||||
err = t.configurer.configureInterface(t.key, t.port)
|
||||
if err != nil {
|
||||
t.device.Close()
|
||||
t.configurer.close()
|
||||
return nil, err
|
||||
}
|
||||
return t.configurer, nil
|
||||
}
|
||||
|
||||
func (t *tunUSPDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||
if t.device == nil {
|
||||
return nil, fmt.Errorf("device is not ready yet")
|
||||
}
|
||||
|
||||
err := t.device.Up()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
udpMux, err := t.iceBind.GetICEMux()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.udpMux = udpMux
|
||||
|
||||
log.Debugf("device is ready to use: %s", t.name)
|
||||
return udpMux, nil
|
||||
}
|
||||
|
||||
func (t *tunUSPDevice) UpdateAddr(address WGAddress) error {
|
||||
t.address = address
|
||||
return t.assignAddr()
|
||||
}
|
||||
|
||||
func (t *tunUSPDevice) Close() error {
|
||||
if t.configurer != nil {
|
||||
t.configurer.close()
|
||||
}
|
||||
|
||||
if t.device != nil {
|
||||
t.device.Close()
|
||||
}
|
||||
|
||||
if t.udpMux != nil {
|
||||
return t.udpMux.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tunUSPDevice) WgAddress() WGAddress {
|
||||
return t.address
|
||||
}
|
||||
|
||||
func (t *tunUSPDevice) DeviceName() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *tunUSPDevice) Wrapper() *DeviceWrapper {
|
||||
return t.wrapper
|
||||
}
|
||||
|
||||
// assignAddr Adds IP address to the tunnel interface
|
||||
func (t *tunUSPDevice) assignAddr() error {
|
||||
link := newWGLink(t.name)
|
||||
|
||||
//delete existing addresses
|
||||
list, err := netlink.AddrList(link, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(list) > 0 {
|
||||
for _, a := range list {
|
||||
addr := a
|
||||
err = netlink.AddrDel(link, &addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("adding address %s to interface: %s", t.address.String(), t.name)
|
||||
addr, _ := netlink.ParseAddr(t.address.String())
|
||||
err = netlink.AddrAdd(link, addr)
|
||||
if os.IsExist(err) {
|
||||
log.Infof("interface %s already has the address: %s", t.name, t.address.String())
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
// On linux, the link must be brought up
|
||||
err = netlink.LinkSetUp(link)
|
||||
return err
|
||||
}
|
||||
@@ -2,14 +2,12 @@ package iface
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/pion/transport/v2"
|
||||
"github.com/pion/transport/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
||||
|
||||
@@ -17,139 +15,131 @@ import (
|
||||
)
|
||||
|
||||
type tunDevice struct {
|
||||
name string
|
||||
address WGAddress
|
||||
netInterface NetInterface
|
||||
iceBind *bind.ICEBind
|
||||
mtu int
|
||||
uapi net.Listener
|
||||
wrapper *DeviceWrapper
|
||||
close chan struct{}
|
||||
name string
|
||||
address WGAddress
|
||||
port int
|
||||
key string
|
||||
mtu int
|
||||
iceBind *bind.ICEBind
|
||||
|
||||
device *device.Device
|
||||
nativeTunDevice *tun.NativeTun
|
||||
wrapper *DeviceWrapper
|
||||
udpMux *bind.UniversalUDPMuxDefault
|
||||
configurer wgConfigurer
|
||||
}
|
||||
|
||||
func newTunDevice(name string, address WGAddress, mtu int, transportNet transport.Net) *tunDevice {
|
||||
func newTunDevice(name string, address WGAddress, port int, key string, mtu int, transportNet transport.Net) wgTunDevice {
|
||||
return &tunDevice{
|
||||
name: name,
|
||||
address: address,
|
||||
port: port,
|
||||
key: key,
|
||||
mtu: mtu,
|
||||
iceBind: bind.NewICEBind(transportNet),
|
||||
close: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *tunDevice) Create() error {
|
||||
var err error
|
||||
c.netInterface, err = c.createWithUserspace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.assignAddr()
|
||||
}
|
||||
|
||||
// createWithUserspace Creates a new Wireguard interface, using wireguard-go userspace implementation
|
||||
func (c *tunDevice) createWithUserspace() (NetInterface, error) {
|
||||
tunIface, err := tun.CreateTUN(c.name, c.mtu)
|
||||
func (t *tunDevice) Create() (wgConfigurer, error) {
|
||||
tunDevice, err := tun.CreateTUN(t.name, t.mtu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.wrapper = newDeviceWrapper(tunIface)
|
||||
t.nativeTunDevice = tunDevice.(*tun.NativeTun)
|
||||
t.wrapper = newDeviceWrapper(tunDevice)
|
||||
|
||||
// We need to create a wireguard-go device and listen to configuration requests
|
||||
tunDev := device.NewDevice(c.wrapper, c.iceBind, device.NewLogger(device.LogLevelSilent, "[netbird] "))
|
||||
err = tunDev.Up()
|
||||
if err != nil {
|
||||
_ = tunIface.Close()
|
||||
return nil, err
|
||||
}
|
||||
t.device = device.NewDevice(
|
||||
t.wrapper,
|
||||
t.iceBind,
|
||||
device.NewLogger(device.LogLevelSilent, "[netbird] "),
|
||||
)
|
||||
|
||||
luid := winipcfg.LUID(tunIface.(*tun.NativeTun).LUID())
|
||||
luid := winipcfg.LUID(t.nativeTunDevice.LUID())
|
||||
|
||||
nbiface, err := luid.IPInterface(windows.AF_INET)
|
||||
if err != nil {
|
||||
_ = tunIface.Close()
|
||||
t.device.Close()
|
||||
return nil, fmt.Errorf("got error when getting ip interface %s", err)
|
||||
}
|
||||
|
||||
nbiface.NLMTU = uint32(c.mtu)
|
||||
nbiface.NLMTU = uint32(t.mtu)
|
||||
|
||||
err = nbiface.Set()
|
||||
if err != nil {
|
||||
_ = tunIface.Close()
|
||||
t.device.Close()
|
||||
return nil, fmt.Errorf("got error when getting setting the interface mtu: %s", err)
|
||||
}
|
||||
|
||||
c.uapi, err = c.getUAPI(c.name)
|
||||
err = t.assignAddr()
|
||||
if err != nil {
|
||||
_ = tunIface.Close()
|
||||
t.device.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c.close:
|
||||
log.Debugf("exit uapi.Accept()")
|
||||
return
|
||||
default:
|
||||
}
|
||||
uapiConn, uapiErr := c.uapi.Accept()
|
||||
if uapiErr != nil {
|
||||
log.Traceln("uapi Accept failed with error: ", uapiErr)
|
||||
continue
|
||||
}
|
||||
go func() {
|
||||
tunDev.IpcHandle(uapiConn)
|
||||
log.Debugf("exit tunDevice.IpcHandle")
|
||||
}()
|
||||
}
|
||||
}()
|
||||
|
||||
log.Debugln("UAPI listener started")
|
||||
return tunIface, nil
|
||||
t.configurer = newWGUSPConfigurer(t.device, t.name)
|
||||
err = t.configurer.configureInterface(t.key, t.port)
|
||||
if err != nil {
|
||||
t.device.Close()
|
||||
t.configurer.close()
|
||||
return nil, err
|
||||
}
|
||||
return t.configurer, nil
|
||||
}
|
||||
|
||||
func (c *tunDevice) UpdateAddr(address WGAddress) error {
|
||||
c.address = address
|
||||
return c.assignAddr()
|
||||
}
|
||||
|
||||
func (c *tunDevice) WgAddress() WGAddress {
|
||||
return c.address
|
||||
}
|
||||
|
||||
func (c *tunDevice) DeviceName() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func (c *tunDevice) Close() error {
|
||||
select {
|
||||
case c.close <- struct{}{}:
|
||||
default:
|
||||
func (t *tunDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||
err := t.device.Up()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var err1, err2 error
|
||||
if c.netInterface != nil {
|
||||
err1 = c.netInterface.Close()
|
||||
udpMux, err := t.iceBind.GetICEMux()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.uapi != nil {
|
||||
err2 = c.uapi.Close()
|
||||
}
|
||||
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
|
||||
return err2
|
||||
t.udpMux = udpMux
|
||||
log.Debugf("device is ready to use: %s", t.name)
|
||||
return udpMux, nil
|
||||
}
|
||||
|
||||
func (c *tunDevice) getInterfaceGUIDString() (string, error) {
|
||||
if c.netInterface == nil {
|
||||
func (t *tunDevice) UpdateAddr(address WGAddress) error {
|
||||
t.address = address
|
||||
return t.assignAddr()
|
||||
}
|
||||
|
||||
func (t *tunDevice) Close() error {
|
||||
if t.configurer != nil {
|
||||
t.configurer.close()
|
||||
}
|
||||
|
||||
if t.device != nil {
|
||||
t.device.Close()
|
||||
t.device = nil
|
||||
}
|
||||
|
||||
if t.udpMux != nil {
|
||||
return t.udpMux.Close()
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (t *tunDevice) WgAddress() WGAddress {
|
||||
return t.address
|
||||
}
|
||||
|
||||
func (t *tunDevice) DeviceName() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *tunDevice) Wrapper() *DeviceWrapper {
|
||||
return t.wrapper
|
||||
}
|
||||
|
||||
func (t *tunDevice) getInterfaceGUIDString() (string, error) {
|
||||
if t.nativeTunDevice == nil {
|
||||
return "", fmt.Errorf("interface has not been initialized yet")
|
||||
}
|
||||
windowsDevice := c.netInterface.(*tun.NativeTun)
|
||||
luid := winipcfg.LUID(windowsDevice.LUID())
|
||||
|
||||
luid := winipcfg.LUID(t.nativeTunDevice.LUID())
|
||||
guid, err := luid.GUID()
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -158,14 +148,8 @@ func (c *tunDevice) getInterfaceGUIDString() (string, error) {
|
||||
}
|
||||
|
||||
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided
|
||||
func (c *tunDevice) assignAddr() error {
|
||||
tunDev := c.netInterface.(*tun.NativeTun)
|
||||
luid := winipcfg.LUID(tunDev.LUID())
|
||||
log.Debugf("adding address %s to interface: %s", c.address.IP, c.name)
|
||||
return luid.SetIPAddresses([]netip.Prefix{netip.MustParsePrefix(c.address.String())})
|
||||
}
|
||||
|
||||
// getUAPI returns a Listener
|
||||
func (c *tunDevice) getUAPI(iface string) (net.Listener, error) {
|
||||
return ipc.UAPIListen(iface)
|
||||
func (t *tunDevice) assignAddr() error {
|
||||
luid := winipcfg.LUID(t.nativeTunDevice.LUID())
|
||||
log.Debugf("adding address %s to interface: %s", t.address.IP, t.name)
|
||||
return luid.SetIPAddresses([]netip.Prefix{netip.MustParsePrefix(t.address.String())})
|
||||
}
|
||||
|
||||
26
iface/uapi.go
Normal file
26
iface/uapi.go
Normal file
@@ -0,0 +1,26 @@
|
||||
//go:build !windows
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
)
|
||||
|
||||
func openUAPI(deviceName string) (net.Listener, error) {
|
||||
uapiSock, err := ipc.UAPIOpen(deviceName)
|
||||
if err != nil {
|
||||
log.Errorf("failed to open uapi socket: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
listener, err := ipc.UAPIListen(deviceName, uapiSock)
|
||||
if err != nil {
|
||||
log.Errorf("failed to listen on uapi socket: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return listener, nil
|
||||
}
|
||||
11
iface/uapi_windows.go
Normal file
11
iface/uapi_windows.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
)
|
||||
|
||||
func openUAPI(deviceName string) (net.Listener, error) {
|
||||
return ipc.UAPIListen(deviceName)
|
||||
}
|
||||
17
iface/wg_configurer.go
Normal file
17
iface/wg_configurer.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
type wgConfigurer interface {
|
||||
configureInterface(privateKey string, port int) error
|
||||
updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error
|
||||
removePeer(peerKey string) error
|
||||
addAllowedIP(peerKey string, allowedIP string) error
|
||||
removeAllowedIP(peerKey string, allowedIP string) error
|
||||
close()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !android && !ios
|
||||
//go:build linux && !android
|
||||
|
||||
package iface
|
||||
|
||||
@@ -12,17 +12,18 @@ import (
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
type wGConfigurer struct {
|
||||
type wgKernelConfigurer struct {
|
||||
deviceName string
|
||||
}
|
||||
|
||||
func newWGConfigurer(deviceName string) wGConfigurer {
|
||||
return wGConfigurer{
|
||||
func newWGConfigurer(deviceName string) wgConfigurer {
|
||||
wgc := &wgKernelConfigurer{
|
||||
deviceName: deviceName,
|
||||
}
|
||||
return wgc
|
||||
}
|
||||
|
||||
func (c *wGConfigurer) configureInterface(privateKey string, port int) error {
|
||||
func (c *wgKernelConfigurer) configureInterface(privateKey string, port int) error {
|
||||
log.Debugf("adding Wireguard private key")
|
||||
key, err := wgtypes.ParseKey(privateKey)
|
||||
if err != nil {
|
||||
@@ -43,7 +44,7 @@ func (c *wGConfigurer) configureInterface(privateKey string, port int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *wGConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
|
||||
func (c *wgKernelConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
|
||||
// parse allowed ips
|
||||
_, ipNet, err := net.ParseCIDR(allowedIps)
|
||||
if err != nil {
|
||||
@@ -73,7 +74,7 @@ func (c *wGConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *wGConfigurer) removePeer(peerKey string) error {
|
||||
func (c *wgKernelConfigurer) removePeer(peerKey string) error {
|
||||
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -94,7 +95,7 @@ func (c *wGConfigurer) removePeer(peerKey string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *wGConfigurer) addAllowedIP(peerKey string, allowedIP string) error {
|
||||
func (c *wgKernelConfigurer) addAllowedIP(peerKey string, allowedIP string) error {
|
||||
_, ipNet, err := net.ParseCIDR(allowedIP)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -121,7 +122,7 @@ func (c *wGConfigurer) addAllowedIP(peerKey string, allowedIP string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *wGConfigurer) removeAllowedIP(peerKey string, allowedIP string) error {
|
||||
func (c *wgKernelConfigurer) removeAllowedIP(peerKey string, allowedIP string) error {
|
||||
_, ipNet, err := net.ParseCIDR(allowedIP)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -163,7 +164,7 @@ func (c *wGConfigurer) removeAllowedIP(peerKey string, allowedIP string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *wGConfigurer) getPeer(ifaceName, peerPubKey string) (wgtypes.Peer, error) {
|
||||
func (c *wgKernelConfigurer) getPeer(ifaceName, peerPubKey string) (wgtypes.Peer, error) {
|
||||
wg, err := wgctrl.New()
|
||||
if err != nil {
|
||||
return wgtypes.Peer{}, err
|
||||
@@ -187,7 +188,7 @@ func (c *wGConfigurer) getPeer(ifaceName, peerPubKey string) (wgtypes.Peer, erro
|
||||
return wgtypes.Peer{}, fmt.Errorf("peer not found")
|
||||
}
|
||||
|
||||
func (c *wGConfigurer) configure(config wgtypes.Config) error {
|
||||
func (c *wgKernelConfigurer) configure(config wgtypes.Config) error {
|
||||
wg, err := wgctrl.New()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -203,3 +204,6 @@ func (c *wGConfigurer) configure(config wgtypes.Config) error {
|
||||
|
||||
return wg.ConfigureDevice(c.deviceName, config)
|
||||
}
|
||||
|
||||
func (c *wgKernelConfigurer) close() {
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
//go:build ios || android
|
||||
// +build ios android
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
var (
|
||||
errFuncNotImplemented = errors.New("function not implemented")
|
||||
)
|
||||
|
||||
type wGConfigurer struct {
|
||||
tunDevice *tunDevice
|
||||
}
|
||||
|
||||
func newWGConfigurer(tunDevice *tunDevice) wGConfigurer {
|
||||
return wGConfigurer{
|
||||
tunDevice: tunDevice,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *wGConfigurer) configureInterface(privateKey string, port int) error {
|
||||
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 c.tunDevice.Device().IpcSet(toWgUserspaceString(config))
|
||||
}
|
||||
|
||||
func (c *wGConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
|
||||
// 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 c.tunDevice.Device().IpcSet(toWgUserspaceString(config))
|
||||
}
|
||||
|
||||
func (c *wGConfigurer) removePeer(peerKey string) error {
|
||||
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 c.tunDevice.Device().IpcSet(toWgUserspaceString(config))
|
||||
}
|
||||
|
||||
func (c *wGConfigurer) addAllowedIP(peerKey string, allowedIP string) error {
|
||||
_, ipNet, err := net.ParseCIDR(allowedIP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
peer := wgtypes.PeerConfig{
|
||||
PublicKey: peerKeyParsed,
|
||||
UpdateOnly: true,
|
||||
ReplaceAllowedIPs: false,
|
||||
AllowedIPs: []net.IPNet{*ipNet},
|
||||
}
|
||||
|
||||
config := wgtypes.Config{
|
||||
Peers: []wgtypes.PeerConfig{peer},
|
||||
}
|
||||
|
||||
return c.tunDevice.Device().IpcSet(toWgUserspaceString(config))
|
||||
}
|
||||
|
||||
func (c *wGConfigurer) removeAllowedIP(peerKey string, ip string) error {
|
||||
ipc, err := c.tunDevice.Device().IpcGet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||
hexKey := hex.EncodeToString(peerKeyParsed[:])
|
||||
|
||||
lines := strings.Split(ipc, "\n")
|
||||
|
||||
output := ""
|
||||
foundPeer := false
|
||||
removedAllowedIP := false
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
// If we're within the details of the found peer and encounter another public key,
|
||||
// this means we're starting another peer's details. So, reset the flag.
|
||||
if strings.HasPrefix(line, "public_key=") && foundPeer {
|
||||
foundPeer = false
|
||||
}
|
||||
|
||||
// Identify the peer with the specific public key
|
||||
if line == fmt.Sprintf("public_key=%s", hexKey) {
|
||||
foundPeer = true
|
||||
}
|
||||
|
||||
// If we're within the details of the found peer and find the specific allowed IP, skip this line
|
||||
if foundPeer && line == "allowed_ip="+ip {
|
||||
removedAllowedIP = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Append the line to the output string
|
||||
if strings.HasPrefix(line, "private_key=") || strings.HasPrefix(line, "listen_port=") ||
|
||||
strings.HasPrefix(line, "public_key=") || strings.HasPrefix(line, "preshared_key=") ||
|
||||
strings.HasPrefix(line, "endpoint=") || strings.HasPrefix(line, "persistent_keepalive_interval=") ||
|
||||
strings.HasPrefix(line, "allowed_ip=") {
|
||||
output += line + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
if !removedAllowedIP {
|
||||
return fmt.Errorf("allowedIP not found")
|
||||
} else {
|
||||
return c.tunDevice.Device().IpcSet(output)
|
||||
}
|
||||
}
|
||||
259
iface/wg_configurer_usp.go
Normal file
259
iface/wg_configurer_usp.go
Normal file
@@ -0,0 +1,259 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
type wgUSPConfigurer struct {
|
||||
device *device.Device
|
||||
deviceName string
|
||||
|
||||
uapiListener net.Listener
|
||||
}
|
||||
|
||||
func newWGUSPConfigurer(device *device.Device, deviceName string) wgConfigurer {
|
||||
wgCfg := &wgUSPConfigurer{
|
||||
device: device,
|
||||
deviceName: deviceName,
|
||||
}
|
||||
wgCfg.startUAPI()
|
||||
return wgCfg
|
||||
}
|
||||
|
||||
func (c *wgUSPConfigurer) configureInterface(privateKey string, port int) error {
|
||||
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 c.device.IpcSet(toWgUserspaceString(config))
|
||||
}
|
||||
|
||||
func (c *wgUSPConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
|
||||
// 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 c.device.IpcSet(toWgUserspaceString(config))
|
||||
}
|
||||
|
||||
func (c *wgUSPConfigurer) removePeer(peerKey string) error {
|
||||
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 c.device.IpcSet(toWgUserspaceString(config))
|
||||
}
|
||||
|
||||
func (c *wgUSPConfigurer) addAllowedIP(peerKey string, allowedIP string) error {
|
||||
_, ipNet, err := net.ParseCIDR(allowedIP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
peer := wgtypes.PeerConfig{
|
||||
PublicKey: peerKeyParsed,
|
||||
UpdateOnly: true,
|
||||
ReplaceAllowedIPs: false,
|
||||
AllowedIPs: []net.IPNet{*ipNet},
|
||||
}
|
||||
|
||||
config := wgtypes.Config{
|
||||
Peers: []wgtypes.PeerConfig{peer},
|
||||
}
|
||||
|
||||
return c.device.IpcSet(toWgUserspaceString(config))
|
||||
}
|
||||
|
||||
func (c *wgUSPConfigurer) removeAllowedIP(peerKey string, ip string) error {
|
||||
ipc, err := c.device.IpcGet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hexKey := hex.EncodeToString(peerKeyParsed[:])
|
||||
|
||||
lines := strings.Split(ipc, "\n")
|
||||
|
||||
output := ""
|
||||
foundPeer := false
|
||||
removedAllowedIP := false
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
// If we're within the details of the found peer and encounter another public key,
|
||||
// this means we're starting another peer's details. So, reset the flag.
|
||||
if strings.HasPrefix(line, "public_key=") && foundPeer {
|
||||
foundPeer = false
|
||||
}
|
||||
|
||||
// Identify the peer with the specific public key
|
||||
if line == fmt.Sprintf("public_key=%s", hexKey) {
|
||||
foundPeer = true
|
||||
}
|
||||
|
||||
// If we're within the details of the found peer and find the specific allowed IP, skip this line
|
||||
if foundPeer && line == "allowed_ip="+ip {
|
||||
removedAllowedIP = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Append the line to the output string
|
||||
if strings.HasPrefix(line, "private_key=") || strings.HasPrefix(line, "listen_port=") ||
|
||||
strings.HasPrefix(line, "public_key=") || strings.HasPrefix(line, "preshared_key=") ||
|
||||
strings.HasPrefix(line, "endpoint=") || strings.HasPrefix(line, "persistent_keepalive_interval=") ||
|
||||
strings.HasPrefix(line, "allowed_ip=") {
|
||||
output += line + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
if !removedAllowedIP {
|
||||
return fmt.Errorf("allowedIP not found")
|
||||
} else {
|
||||
return c.device.IpcSet(output)
|
||||
}
|
||||
}
|
||||
|
||||
// startUAPI starts the UAPI listener for managing the WireGuard interface via external tool
|
||||
func (t *wgUSPConfigurer) startUAPI() {
|
||||
var err error
|
||||
t.uapiListener, err = openUAPI(t.deviceName)
|
||||
if err != nil {
|
||||
log.Errorf("failed to open uapi listener: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
go func(uapi net.Listener) {
|
||||
for {
|
||||
uapiConn, uapiErr := uapi.Accept()
|
||||
if uapiErr != nil {
|
||||
log.Tracef("%s", uapiErr)
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
t.device.IpcHandle(uapiConn)
|
||||
}()
|
||||
}
|
||||
}(t.uapiListener)
|
||||
}
|
||||
|
||||
func (t *wgUSPConfigurer) close() {
|
||||
if t.uapiListener != nil {
|
||||
err := t.uapiListener.Close()
|
||||
if err != nil {
|
||||
log.Errorf("failed to close uapi listener: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
sockPath := "/var/run/wireguard/" + t.deviceName + ".sock"
|
||||
if _, statErr := os.Stat(sockPath); statErr == nil {
|
||||
_ = os.Remove(sockPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toWgUserspaceString(wgCfg wgtypes.Config) string {
|
||||
var sb strings.Builder
|
||||
if wgCfg.PrivateKey != nil {
|
||||
hexKey := hex.EncodeToString(wgCfg.PrivateKey[:])
|
||||
sb.WriteString(fmt.Sprintf("private_key=%s\n", hexKey))
|
||||
}
|
||||
|
||||
if wgCfg.ListenPort != nil {
|
||||
sb.WriteString(fmt.Sprintf("listen_port=%d\n", *wgCfg.ListenPort))
|
||||
}
|
||||
|
||||
if wgCfg.ReplacePeers {
|
||||
sb.WriteString("replace_peers=true\n")
|
||||
}
|
||||
|
||||
if wgCfg.FirewallMark != nil {
|
||||
sb.WriteString(fmt.Sprintf("fwmark=%d\n", *wgCfg.FirewallMark))
|
||||
}
|
||||
|
||||
for _, p := range wgCfg.Peers {
|
||||
hexKey := hex.EncodeToString(p.PublicKey[:])
|
||||
sb.WriteString(fmt.Sprintf("public_key=%s\n", hexKey))
|
||||
|
||||
if p.PresharedKey != nil {
|
||||
preSharedHexKey := hex.EncodeToString(p.PresharedKey[:])
|
||||
sb.WriteString(fmt.Sprintf("preshared_key=%s\n", preSharedHexKey))
|
||||
}
|
||||
|
||||
if p.Remove {
|
||||
sb.WriteString("remove=true")
|
||||
}
|
||||
|
||||
if p.ReplaceAllowedIPs {
|
||||
sb.WriteString("replace_allowed_ips=true\n")
|
||||
}
|
||||
|
||||
for _, aip := range p.AllowedIPs {
|
||||
sb.WriteString(fmt.Sprintf("allowed_ip=%s\n", aip.String()))
|
||||
}
|
||||
|
||||
if p.Endpoint != nil {
|
||||
sb.WriteString(fmt.Sprintf("endpoint=%s\n", p.Endpoint.String()))
|
||||
}
|
||||
|
||||
if p.PersistentKeepaliveInterval != nil {
|
||||
sb.WriteString(fmt.Sprintf("persistent_keepalive_interval=%d\n", int(p.PersistentKeepaliveInterval.Seconds())))
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
@@ -312,7 +312,7 @@ delete_auto_service_user() {
|
||||
|
||||
init_zitadel() {
|
||||
echo -e "\nInitializing Zitadel with NetBird's applications\n"
|
||||
INSTANCE_URL="$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT"
|
||||
INSTANCE_URL="$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN"
|
||||
|
||||
TOKEN_PATH=./machinekey/zitadel-admin-sa.token
|
||||
|
||||
@@ -472,7 +472,7 @@ initEnvironment() {
|
||||
echo -e "\nStarting NetBird services\n"
|
||||
$DOCKER_COMPOSE_COMMAND up -d
|
||||
echo -e "\nDone!\n"
|
||||
echo "You can access the NetBird dashboard at $NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT"
|
||||
echo "You can access the NetBird dashboard at $NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN"
|
||||
echo "Login with the following credentials:"
|
||||
echo "Username: $ZITADEL_ADMIN_USERNAME" | tee .env
|
||||
echo "Password: $ZITADEL_ADMIN_PASSWORD" | tee -a .env
|
||||
@@ -608,14 +608,14 @@ renderManagementJson() {
|
||||
"IdpManagerConfig": {
|
||||
"ManagerType": "zitadel",
|
||||
"ClientConfig": {
|
||||
"Issuer": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT",
|
||||
"TokenEndpoint": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT/oauth/v2/token",
|
||||
"Issuer": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN",
|
||||
"TokenEndpoint": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN/oauth/v2/token",
|
||||
"ClientID": "$NETBIRD_IDP_MGMT_CLIENT_ID",
|
||||
"ClientSecret": "$NETBIRD_IDP_MGMT_CLIENT_SECRET",
|
||||
"GrantType": "client_credentials"
|
||||
},
|
||||
"ExtraConfig": {
|
||||
"ManagementEndpoint": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT/management/v1"
|
||||
"ManagementEndpoint": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN/management/v1"
|
||||
}
|
||||
},
|
||||
"PKCEAuthorizationFlow": {
|
||||
@@ -633,12 +633,12 @@ EOF
|
||||
renderDashboardEnv() {
|
||||
cat <<EOF
|
||||
# Endpoints
|
||||
NETBIRD_MGMT_API_ENDPOINT=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT
|
||||
NETBIRD_MGMT_GRPC_API_ENDPOINT=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT
|
||||
NETBIRD_MGMT_API_ENDPOINT=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN
|
||||
NETBIRD_MGMT_GRPC_API_ENDPOINT=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN
|
||||
# OIDC
|
||||
AUTH_AUDIENCE=$NETBIRD_AUTH_CLIENT_ID
|
||||
AUTH_CLIENT_ID=$NETBIRD_AUTH_CLIENT_ID
|
||||
AUTH_AUTHORITY=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT
|
||||
AUTH_AUTHORITY=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN
|
||||
USE_AUTH0=false
|
||||
AUTH_SUPPORTED_SCOPES="openid profile email offline_access"
|
||||
AUTH_REDIRECT_URI=/nb-auth
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/netbirdio/management-integrations/integrations"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net"
|
||||
@@ -31,8 +32,6 @@ import (
|
||||
"github.com/netbirdio/netbird/encryption"
|
||||
mgmtProto "github.com/netbirdio/netbird/management/proto"
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/activity/sqlite"
|
||||
httpapi "github.com/netbirdio/netbird/management/server/http"
|
||||
"github.com/netbirdio/netbird/management/server/idp"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
@@ -146,7 +145,7 @@ var (
|
||||
if disableSingleAccMode {
|
||||
mgmtSingleAccModeDomain = ""
|
||||
}
|
||||
eventStore, key, err := initEventStore(config.Datadir, config.DataStoreEncryptionKey)
|
||||
eventStore, key, err := integrations.InitEventStore(config.Datadir, config.DataStoreEncryptionKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize database: %s", err)
|
||||
}
|
||||
@@ -301,20 +300,6 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func initEventStore(dataDir string, key string) (activity.Store, string, error) {
|
||||
var err error
|
||||
if key == "" {
|
||||
log.Debugf("generate new activity store encryption key")
|
||||
key, err = sqlite.GenerateKey()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
store, err := sqlite.NewSQLiteStore(dataDir, key)
|
||||
return store, key, err
|
||||
|
||||
}
|
||||
|
||||
func notifyStop(msg string) {
|
||||
select {
|
||||
case stopCh <- 1:
|
||||
|
||||
@@ -1299,6 +1299,22 @@ func (am *DefaultAccountManager) lookupCache(accountUsers map[string]struct{}, a
|
||||
return data, err
|
||||
}
|
||||
|
||||
func (am *DefaultAccountManager) removeUserFromCache(accountID, userID string) error {
|
||||
data, err := am.getAccountFromCache(accountID, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, datum := range data {
|
||||
if datum.ID == userID {
|
||||
data = append(data[:i], data[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return am.cacheManager.Set(am.ctx, accountID, data, cacheStore.WithExpiration(cacheEntryExpiration()))
|
||||
}
|
||||
|
||||
// updateAccountDomainAttributes updates the account domain attributes and then, saves the account
|
||||
func (am *DefaultAccountManager) updateAccountDomainAttributes(account *Account, claims jwtclaims.AuthorizationClaims,
|
||||
primaryDomain bool,
|
||||
|
||||
534
management/server/postgresql_store.go
Normal file
534
management/server/postgresql_store.go
Normal file
@@ -0,0 +1,534 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/logger"
|
||||
"gorm.io/gorm/schema"
|
||||
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/server/account"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
)
|
||||
|
||||
// PostgresqlStore represents an account storage backed by a Postgres DB persisted to disk
|
||||
type PostgresqlStore struct {
|
||||
db *gorm.DB
|
||||
dsn string
|
||||
accountLocks sync.Map
|
||||
globalAccountLock sync.Mutex
|
||||
metrics telemetry.AppMetrics
|
||||
installationPK int
|
||||
}
|
||||
|
||||
// GobSerializer gob serializer
|
||||
type GobBase64Serializer struct{}
|
||||
|
||||
// Scan implements serializer interface with base64 encoding
|
||||
func (GobBase64Serializer) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue interface{}) (err error) {
|
||||
fieldValue := reflect.New(field.FieldType)
|
||||
|
||||
if dbValue != nil {
|
||||
var bytesValue []byte
|
||||
switch v := dbValue.(type) {
|
||||
case []byte:
|
||||
bytesValue = v
|
||||
case string:
|
||||
bytesValue = []byte(v)
|
||||
default:
|
||||
return fmt.Errorf("failed to unmarshal gob value: %#v", dbValue)
|
||||
}
|
||||
if len(bytesValue) > 0 {
|
||||
var decoded []byte
|
||||
decoded, err = base64.StdEncoding.DecodeString(string(bytesValue))
|
||||
if err == nil {
|
||||
decoder := gob.NewDecoder(bytes.NewBuffer(decoded))
|
||||
err = decoder.Decode(fieldValue.Interface())
|
||||
}
|
||||
}
|
||||
}
|
||||
field.ReflectValueOf(ctx, dst).Set(fieldValue.Elem())
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements serializer interface
|
||||
func (GobBase64Serializer) Value(ctx context.Context, field *schema.Field, dst reflect.Value, fieldValue interface{}) (interface{}, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
err := gob.NewEncoder(buf).Encode(fieldValue)
|
||||
return base64.StdEncoding.EncodeToString(buf.Bytes()), err
|
||||
}
|
||||
|
||||
// NewPostgresqlStore restores a store from the file located in the datadir
|
||||
func NewPostgresqlStore(dsn string, metrics telemetry.AppMetrics) (*PostgresqlStore, error) {
|
||||
schema.RegisterSerializer("gob", GobBase64Serializer{})
|
||||
|
||||
sqlDB, err := sql.Open("postgres", dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db, err := gorm.Open(postgres.New(postgres.Config{
|
||||
Conn: sqlDB,
|
||||
}), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
PrepareStmt: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//sql, err := db.DB()
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
conns := runtime.NumCPU()
|
||||
sqlDB.SetMaxOpenConns(conns) // TODO: make it configurable
|
||||
|
||||
err = db.AutoMigrate(
|
||||
&Account{}, &SetupKey{}, &nbpeer.Peer{}, &User{}, &PersonalAccessToken{}, &Group{}, &Rule{},
|
||||
&Policy{}, &PolicyRule{}, &route.Route{}, &nbdns.NameServerGroup{},
|
||||
&installation{}, &account.ExtraSettings{},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &PostgresqlStore{db: db, dsn: dsn, metrics: metrics, installationPK: 1}, nil
|
||||
}
|
||||
|
||||
// NewPostgresqlStoreFromFileStore restores a store from FileStore and stores PostgreSQL DB in the file located in datadir
|
||||
func NewPostgresqlStoreFromFileStore(filestore *FileStore, dataDir string, metrics telemetry.AppMetrics) (*PostgresqlStore, error) {
|
||||
store, err := NewPostgresqlStore(dataDir, metrics)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = store.SaveInstallationID(filestore.InstallationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, account := range filestore.GetAllAccounts() {
|
||||
err := store.SaveAccount(account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return store, nil
|
||||
}
|
||||
|
||||
// AcquireGlobalLock acquires global lock across all the accounts and returns a function that releases the lock
|
||||
func (s *PostgresqlStore) AcquireGlobalLock() (unlock func()) {
|
||||
log.Debugf("acquiring global lock")
|
||||
start := time.Now()
|
||||
s.globalAccountLock.Lock()
|
||||
|
||||
unlock = func() {
|
||||
s.globalAccountLock.Unlock()
|
||||
log.Debugf("released global lock in %v", time.Since(start))
|
||||
}
|
||||
|
||||
took := time.Since(start)
|
||||
log.Debugf("took %v to acquire global lock", took)
|
||||
if s.metrics != nil {
|
||||
s.metrics.StoreMetrics().CountGlobalLockAcquisitionDuration(took)
|
||||
}
|
||||
|
||||
return unlock
|
||||
}
|
||||
|
||||
func (s *PostgresqlStore) AcquireAccountLock(accountID string) (unlock func()) {
|
||||
log.Debugf("acquiring lock for account %s", accountID)
|
||||
|
||||
start := time.Now()
|
||||
value, _ := s.accountLocks.LoadOrStore(accountID, &sync.Mutex{})
|
||||
mtx := value.(*sync.Mutex)
|
||||
mtx.Lock()
|
||||
|
||||
unlock = func() {
|
||||
mtx.Unlock()
|
||||
log.Debugf("released lock for account %s in %v", accountID, time.Since(start))
|
||||
}
|
||||
|
||||
return unlock
|
||||
}
|
||||
|
||||
func (s *PostgresqlStore) SaveAccount(account *Account) error {
|
||||
start := time.Now()
|
||||
|
||||
for _, key := range account.SetupKeys {
|
||||
account.SetupKeysG = append(account.SetupKeysG, *key)
|
||||
}
|
||||
|
||||
for id, peer := range account.Peers {
|
||||
peer.ID = id
|
||||
account.PeersG = append(account.PeersG, *peer)
|
||||
}
|
||||
|
||||
for id, user := range account.Users {
|
||||
user.Id = id
|
||||
for id, pat := range user.PATs {
|
||||
pat.ID = id
|
||||
user.PATsG = append(user.PATsG, *pat)
|
||||
}
|
||||
account.UsersG = append(account.UsersG, *user)
|
||||
}
|
||||
|
||||
for id, group := range account.Groups {
|
||||
group.ID = id
|
||||
account.GroupsG = append(account.GroupsG, *group)
|
||||
}
|
||||
|
||||
for id, rule := range account.Rules {
|
||||
rule.ID = id
|
||||
account.RulesG = append(account.RulesG, *rule)
|
||||
}
|
||||
|
||||
for id, route := range account.Routes {
|
||||
route.ID = id
|
||||
account.RoutesG = append(account.RoutesG, *route)
|
||||
}
|
||||
|
||||
for id, ns := range account.NameServerGroups {
|
||||
ns.ID = id
|
||||
account.NameServerGroupsG = append(account.NameServerGroupsG, *ns)
|
||||
}
|
||||
|
||||
err := s.db.Transaction(func(tx *gorm.DB) error {
|
||||
result := tx.Select(clause.Associations).Delete(account.Policies, "account_id = ?", account.Id)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
result = tx.Select(clause.Associations).Delete(account.UsersG, "account_id = ?", account.Id)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
result = tx.Select(clause.Associations).Delete(account)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
result = tx.
|
||||
Session(&gorm.Session{FullSaveAssociations: true}).
|
||||
Clauses(clause.OnConflict{UpdateAll: true}).Create(account)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
took := time.Since(start)
|
||||
if s.metrics != nil {
|
||||
s.metrics.StoreMetrics().CountPersistenceDuration(took)
|
||||
}
|
||||
log.Debugf("took %d ms to persist an account to the PostgreSQL", took.Milliseconds())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PostgresqlStore) DeleteAccount(account *Account) error {
|
||||
start := time.Now()
|
||||
|
||||
err := s.db.Transaction(func(tx *gorm.DB) error {
|
||||
result := tx.Select(clause.Associations).Delete(account.Policies, "account_id = ?", account.Id)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
result = tx.Select(clause.Associations).Delete(account.UsersG, "account_id = ?", account.Id)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
result = tx.Select(clause.Associations).Delete(account)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
took := time.Since(start)
|
||||
if s.metrics != nil {
|
||||
s.metrics.StoreMetrics().CountPersistenceDuration(took)
|
||||
}
|
||||
log.Debugf("took %d ms to delete an account to the PostgreSQL", took.Milliseconds())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PostgresqlStore) SaveInstallationID(ID string) error {
|
||||
installation := installation{InstallationIDValue: ID}
|
||||
installation.ID = uint(s.installationPK)
|
||||
|
||||
return s.db.Clauses(clause.OnConflict{UpdateAll: true}).Create(&installation).Error
|
||||
}
|
||||
|
||||
func (s *PostgresqlStore) GetInstallationID() string {
|
||||
var installation installation
|
||||
|
||||
if result := s.db.First(&installation, "id = ?", s.installationPK); result.Error != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return installation.InstallationIDValue
|
||||
}
|
||||
|
||||
func (s *PostgresqlStore) SavePeerStatus(accountID, peerID string, peerStatus nbpeer.PeerStatus) error {
|
||||
var peer nbpeer.Peer
|
||||
|
||||
result := s.db.First(&peer, "account_id = ? and id = ?", accountID, peerID)
|
||||
if result.Error != nil {
|
||||
return status.Errorf(status.NotFound, "peer %s not found", peerID)
|
||||
}
|
||||
|
||||
peer.Status = &peerStatus
|
||||
|
||||
return s.db.Save(peer).Error
|
||||
}
|
||||
|
||||
// DeleteHashedPAT2TokenIDIndex is noop in PostgreSQL
|
||||
func (s *PostgresqlStore) DeleteHashedPAT2TokenIDIndex(hashedToken string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTokenID2UserIDIndex is noop in PostgreSQL
|
||||
func (s *PostgresqlStore) DeleteTokenID2UserIDIndex(tokenID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PostgresqlStore) GetAccountByPrivateDomain(domain string) (*Account, error) {
|
||||
var account Account
|
||||
|
||||
result := s.db.First(&account, "domain = ? and is_domain_primary_account = ? and domain_category = ?",
|
||||
strings.ToLower(domain), true, PrivateCategory)
|
||||
if result.Error != nil {
|
||||
return nil, status.Errorf(status.NotFound, "account not found: provided domain is not registered or is not private")
|
||||
}
|
||||
|
||||
// TODO: rework to not call GetAccount
|
||||
return s.GetAccount(account.Id)
|
||||
}
|
||||
|
||||
func (s *PostgresqlStore) GetAccountBySetupKey(setupKey string) (*Account, error) {
|
||||
var key SetupKey
|
||||
result := s.db.Select("account_id").First(&key, "key = ?", strings.ToUpper(setupKey))
|
||||
if result.Error != nil {
|
||||
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
||||
}
|
||||
|
||||
if key.AccountID == "" {
|
||||
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
||||
}
|
||||
|
||||
return s.GetAccount(key.AccountID)
|
||||
}
|
||||
|
||||
func (s *PostgresqlStore) GetTokenIDByHashedToken(hashedToken string) (string, error) {
|
||||
var token PersonalAccessToken
|
||||
result := s.db.First(&token, "hashed_token = ?", hashedToken)
|
||||
if result.Error != nil {
|
||||
return "", status.Errorf(status.NotFound, "account not found: index lookup failed")
|
||||
}
|
||||
|
||||
return token.ID, nil
|
||||
}
|
||||
|
||||
func (s *PostgresqlStore) GetUserByTokenID(tokenID string) (*User, error) {
|
||||
var token PersonalAccessToken
|
||||
result := s.db.First(&token, "id = ?", tokenID)
|
||||
if result.Error != nil {
|
||||
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
||||
}
|
||||
|
||||
if token.UserID == "" {
|
||||
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
||||
}
|
||||
|
||||
var user User
|
||||
result = s.db.Preload("PATsG").First(&user, "id = ?", token.UserID)
|
||||
if result.Error != nil {
|
||||
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
||||
}
|
||||
|
||||
user.PATs = make(map[string]*PersonalAccessToken, len(user.PATsG))
|
||||
for _, pat := range user.PATsG {
|
||||
user.PATs[pat.ID] = pat.Copy()
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (s *PostgresqlStore) GetAllAccounts() (all []*Account) {
|
||||
var accounts []Account
|
||||
result := s.db.Find(&accounts)
|
||||
if result.Error != nil {
|
||||
return all
|
||||
}
|
||||
|
||||
for _, account := range accounts {
|
||||
if acc, err := s.GetAccount(account.Id); err == nil {
|
||||
all = append(all, acc)
|
||||
}
|
||||
}
|
||||
|
||||
return all
|
||||
}
|
||||
|
||||
func (s *PostgresqlStore) GetAccount(accountID string) (*Account, error) {
|
||||
var account Account
|
||||
|
||||
result := s.db.Model(&account).
|
||||
Preload("UsersG.PATsG"). // have to be specifies as this is nester reference
|
||||
Preload(clause.Associations).
|
||||
First(&account, "id = ?", accountID)
|
||||
if result.Error != nil {
|
||||
return nil, status.Errorf(status.NotFound, "account not found")
|
||||
}
|
||||
|
||||
// we have to manually preload policy rules as it seems that gorm preloading doesn't do it for us
|
||||
for i, policy := range account.Policies {
|
||||
var rules []*PolicyRule
|
||||
err := s.db.Model(&PolicyRule{}).Find(&rules, "policy_id = ?", policy.ID).Error
|
||||
if err != nil {
|
||||
return nil, status.Errorf(status.NotFound, "rule not found")
|
||||
}
|
||||
account.Policies[i].Rules = rules
|
||||
}
|
||||
|
||||
account.SetupKeys = make(map[string]*SetupKey, len(account.SetupKeysG))
|
||||
for _, key := range account.SetupKeysG {
|
||||
account.SetupKeys[key.Key] = key.Copy()
|
||||
}
|
||||
account.SetupKeysG = nil
|
||||
|
||||
account.Peers = make(map[string]*nbpeer.Peer, len(account.PeersG))
|
||||
for _, peer := range account.PeersG {
|
||||
account.Peers[peer.ID] = peer.Copy()
|
||||
}
|
||||
account.PeersG = nil
|
||||
|
||||
account.Users = make(map[string]*User, len(account.UsersG))
|
||||
for _, user := range account.UsersG {
|
||||
user.PATs = make(map[string]*PersonalAccessToken, len(user.PATs))
|
||||
for _, pat := range user.PATsG {
|
||||
user.PATs[pat.ID] = pat.Copy()
|
||||
}
|
||||
account.Users[user.Id] = user.Copy()
|
||||
}
|
||||
account.UsersG = nil
|
||||
|
||||
account.Groups = make(map[string]*Group, len(account.GroupsG))
|
||||
for _, group := range account.GroupsG {
|
||||
account.Groups[group.ID] = group.Copy()
|
||||
}
|
||||
account.GroupsG = nil
|
||||
|
||||
account.Rules = make(map[string]*Rule, len(account.RulesG))
|
||||
for _, rule := range account.RulesG {
|
||||
account.Rules[rule.ID] = rule.Copy()
|
||||
}
|
||||
account.RulesG = nil
|
||||
|
||||
account.Routes = make(map[string]*route.Route, len(account.RoutesG))
|
||||
for _, route := range account.RoutesG {
|
||||
account.Routes[route.ID] = route.Copy()
|
||||
}
|
||||
account.RoutesG = nil
|
||||
|
||||
account.NameServerGroups = make(map[string]*nbdns.NameServerGroup, len(account.NameServerGroupsG))
|
||||
for _, ns := range account.NameServerGroupsG {
|
||||
account.NameServerGroups[ns.ID] = ns.Copy()
|
||||
}
|
||||
account.NameServerGroupsG = nil
|
||||
|
||||
return &account, nil
|
||||
}
|
||||
|
||||
func (s *PostgresqlStore) GetAccountByUser(userID string) (*Account, error) {
|
||||
var user User
|
||||
result := s.db.Select("account_id").First(&user, "id = ?", userID)
|
||||
if result.Error != nil {
|
||||
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
||||
}
|
||||
|
||||
if user.AccountID == "" {
|
||||
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
||||
}
|
||||
|
||||
return s.GetAccount(user.AccountID)
|
||||
}
|
||||
|
||||
func (s *PostgresqlStore) GetAccountByPeerID(peerID string) (*Account, error) {
|
||||
var peer nbpeer.Peer
|
||||
result := s.db.Select("account_id").First(&peer, "id = ?", peerID)
|
||||
if result.Error != nil {
|
||||
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
||||
}
|
||||
|
||||
if peer.AccountID == "" {
|
||||
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
||||
}
|
||||
|
||||
return s.GetAccount(peer.AccountID)
|
||||
}
|
||||
|
||||
func (s *PostgresqlStore) GetAccountByPeerPubKey(peerKey string) (*Account, error) {
|
||||
var peer nbpeer.Peer
|
||||
|
||||
result := s.db.Select("account_id").First(&peer, "key = ?", peerKey)
|
||||
if result.Error != nil {
|
||||
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
||||
}
|
||||
|
||||
if peer.AccountID == "" {
|
||||
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
||||
}
|
||||
|
||||
return s.GetAccount(peer.AccountID)
|
||||
}
|
||||
|
||||
// SaveUserLastLogin stores the last login time for a user in DB.
|
||||
func (s *PostgresqlStore) SaveUserLastLogin(accountID, userID string, lastLogin time.Time) error {
|
||||
var user User
|
||||
|
||||
result := s.db.First(&user, "account_id = ? and id = ?", accountID, userID)
|
||||
if result.Error != nil {
|
||||
return status.Errorf(status.NotFound, "user %s not found", userID)
|
||||
}
|
||||
|
||||
user.LastLogin = lastLogin
|
||||
|
||||
return s.db.Save(user).Error
|
||||
}
|
||||
|
||||
// Close is noop in PostgreSQL
|
||||
func (s *PostgresqlStore) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetStoreEngine returns PostgresqlStoreEngine
|
||||
func (s *PostgresqlStore) GetStoreEngine() StoreEngine {
|
||||
return PostgresqlStoreEngine
|
||||
}
|
||||
354
management/server/postgresql_store_test.go
Normal file
354
management/server/postgresql_store_test.go
Normal file
@@ -0,0 +1,354 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/util"
|
||||
)
|
||||
|
||||
func TestPostgresql_NewStore(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("The PostgreSQL store is not properly supported by Windows yet")
|
||||
}
|
||||
|
||||
store, cleanup := newPostgresqlStore(t)
|
||||
defer cleanup()
|
||||
|
||||
if len(store.GetAllAccounts()) != 0 {
|
||||
t.Errorf("expected to create a new empty Accounts map when creating a new FileStore")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostgresql_SaveAccount(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("The PostgreSQL store is not properly supported by Windows yet")
|
||||
}
|
||||
|
||||
store, cleanup := newPostgresqlStore(t)
|
||||
defer cleanup()
|
||||
|
||||
account := newAccountWithId("account_id", "testuser", "")
|
||||
setupKey := GenerateDefaultSetupKey()
|
||||
account.SetupKeys[setupKey.Key] = setupKey
|
||||
account.Peers["testpeer"] = &nbpeer.Peer{
|
||||
Key: "peerkey",
|
||||
SetupKey: "peerkeysetupkey",
|
||||
IP: net.IP{127, 0, 0, 1},
|
||||
Meta: nbpeer.PeerSystemMeta{},
|
||||
Name: "peer name",
|
||||
Status: &nbpeer.PeerStatus{Connected: true, LastSeen: time.Now().UTC()},
|
||||
}
|
||||
|
||||
err := store.SaveAccount(account)
|
||||
require.NoError(t, err)
|
||||
|
||||
account2 := newAccountWithId("account_id2", "testuser2", "")
|
||||
setupKey = GenerateDefaultSetupKey()
|
||||
account2.SetupKeys[setupKey.Key] = setupKey
|
||||
account2.Peers["testpeer2"] = &nbpeer.Peer{
|
||||
Key: "peerkey2",
|
||||
SetupKey: "peerkeysetupkey2",
|
||||
IP: net.IP{127, 0, 0, 2},
|
||||
Meta: nbpeer.PeerSystemMeta{},
|
||||
Name: "peer name 2",
|
||||
Status: &nbpeer.PeerStatus{Connected: true, LastSeen: time.Now().UTC()},
|
||||
}
|
||||
|
||||
err = store.SaveAccount(account2)
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(store.GetAllAccounts()) != 2 {
|
||||
t.Errorf("expecting 2 Accounts to be stored after SaveAccount()")
|
||||
}
|
||||
|
||||
a, err := store.GetAccount(account.Id)
|
||||
if a == nil {
|
||||
t.Errorf("expecting Account to be stored after SaveAccount(): %v", err)
|
||||
}
|
||||
|
||||
if a != nil && len(a.Policies) != 1 {
|
||||
t.Errorf("expecting Account to have one policy stored after SaveAccount(), got %d", len(a.Policies))
|
||||
}
|
||||
|
||||
if a != nil && len(a.Policies[0].Rules) != 1 {
|
||||
t.Errorf("expecting Account to have one policy rule stored after SaveAccount(), got %d", len(a.Policies[0].Rules))
|
||||
return
|
||||
}
|
||||
|
||||
if a, err := store.GetAccountByPeerPubKey("peerkey"); a == nil {
|
||||
t.Errorf("expecting PeerKeyID2AccountID index updated after SaveAccount(): %v", err)
|
||||
}
|
||||
|
||||
if a, err := store.GetAccountByUser("testuser"); a == nil {
|
||||
t.Errorf("expecting UserID2AccountID index updated after SaveAccount(): %v", err)
|
||||
}
|
||||
|
||||
if a, err := store.GetAccountByPeerID("testpeer"); a == nil {
|
||||
t.Errorf("expecting PeerID2AccountID index updated after SaveAccount(): %v", err)
|
||||
}
|
||||
|
||||
if a, err := store.GetAccountBySetupKey(setupKey.Key); a == nil {
|
||||
t.Errorf("expecting SetupKeyID2AccountID index updated after SaveAccount(): %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostgresql_DeleteAccount(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("The PostgreSQL store is not properly supported by Windows yet")
|
||||
}
|
||||
|
||||
store, cleanup := newPostgresqlStore(t)
|
||||
defer cleanup()
|
||||
|
||||
testUserID := "testuser"
|
||||
user := NewAdminUser(testUserID)
|
||||
user.PATs = map[string]*PersonalAccessToken{"testtoken": {
|
||||
ID: "testtoken",
|
||||
Name: "test token",
|
||||
}}
|
||||
|
||||
account := newAccountWithId("account_id", testUserID, "")
|
||||
setupKey := GenerateDefaultSetupKey()
|
||||
account.SetupKeys[setupKey.Key] = setupKey
|
||||
account.Peers["testpeer"] = &nbpeer.Peer{
|
||||
Key: "peerkey",
|
||||
SetupKey: "peerkeysetupkey",
|
||||
IP: net.IP{127, 0, 0, 1},
|
||||
Meta: nbpeer.PeerSystemMeta{},
|
||||
Name: "peer name",
|
||||
Status: &nbpeer.PeerStatus{Connected: true, LastSeen: time.Now().UTC()},
|
||||
}
|
||||
account.Users[testUserID] = user
|
||||
|
||||
err := store.SaveAccount(account)
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(store.GetAllAccounts()) != 1 {
|
||||
t.Errorf("expecting 1 Accounts to be stored after SaveAccount()")
|
||||
}
|
||||
|
||||
err = store.DeleteAccount(account)
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(store.GetAllAccounts()) != 0 {
|
||||
t.Errorf("expecting 0 Accounts to be stored after DeleteAccount()")
|
||||
}
|
||||
|
||||
_, err = store.GetAccountByPeerPubKey("peerkey")
|
||||
require.Error(t, err, "expecting error after removing DeleteAccount when getting account by peer public key")
|
||||
|
||||
_, err = store.GetAccountByUser("testuser")
|
||||
require.Error(t, err, "expecting error after removing DeleteAccount when getting account by user")
|
||||
|
||||
_, err = store.GetAccountByPeerID("testpeer")
|
||||
require.Error(t, err, "expecting error after removing DeleteAccount when getting account by peer id")
|
||||
|
||||
_, err = store.GetAccountBySetupKey(setupKey.Key)
|
||||
require.Error(t, err, "expecting error after removing DeleteAccount when getting account by setup key")
|
||||
|
||||
_, err = store.GetAccount(account.Id)
|
||||
require.Error(t, err, "expecting error after removing DeleteAccount when getting account by id")
|
||||
|
||||
for _, policy := range account.Policies {
|
||||
var rules []*PolicyRule
|
||||
err = store.db.Model(&PolicyRule{}).Find(&rules, "policy_id = ?", policy.ID).Error
|
||||
require.NoError(t, err, "expecting no error after removing DeleteAccount when searching for policy rules")
|
||||
require.Len(t, rules, 0, "expecting no policy rules to be found after removing DeleteAccount")
|
||||
|
||||
}
|
||||
|
||||
for _, accountUser := range account.Users {
|
||||
var pats []*PersonalAccessToken
|
||||
err = store.db.Model(&PersonalAccessToken{}).Find(&pats, "user_id = ?", accountUser.Id).Error
|
||||
require.NoError(t, err, "expecting no error after removing DeleteAccount when searching for personal access token")
|
||||
require.Len(t, pats, 0, "expecting no personal access token to be found after removing DeleteAccount")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPostgresql_SavePeerStatus(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("The PostgreSQL store is not properly supported by Windows yet")
|
||||
}
|
||||
|
||||
store, cleanup := newPostgresqlStoreFromFile(t, "testdata/store.json")
|
||||
defer cleanup()
|
||||
|
||||
account, err := store.GetAccount("bf1c8084-ba50-4ce7-9439-34653001fc3b")
|
||||
require.NoError(t, err)
|
||||
|
||||
// save status of non-existing peer
|
||||
newStatus := nbpeer.PeerStatus{Connected: true, LastSeen: time.Now().UTC()}
|
||||
err = store.SavePeerStatus(account.Id, "non-existing-peer", newStatus)
|
||||
assert.Error(t, err)
|
||||
|
||||
// save new status of existing peer
|
||||
account.Peers["testpeer"] = &nbpeer.Peer{
|
||||
Key: "peerkey",
|
||||
ID: "testpeer",
|
||||
SetupKey: "peerkeysetupkey",
|
||||
IP: net.IP{127, 0, 0, 1},
|
||||
Meta: nbpeer.PeerSystemMeta{},
|
||||
Name: "peer name",
|
||||
Status: &nbpeer.PeerStatus{Connected: false, LastSeen: time.Now().UTC()},
|
||||
}
|
||||
|
||||
err = store.SaveAccount(account)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = store.SavePeerStatus(account.Id, "testpeer", newStatus)
|
||||
require.NoError(t, err)
|
||||
|
||||
account, err = store.GetAccount(account.Id)
|
||||
require.NoError(t, err)
|
||||
|
||||
actual := account.Peers["testpeer"].Status
|
||||
assert.Equal(t, newStatus.Connected, actual.Connected)
|
||||
// TODO: fix later
|
||||
//assert.True(t, newStatus.LastSeen.Equal(actual.LastSeen))
|
||||
}
|
||||
|
||||
func TestPostgresql_TestGetAccountByPrivateDomain(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("The PostgreSQL store is not properly supported by Windows yet")
|
||||
}
|
||||
|
||||
store, cleanup := newPostgresqlStoreFromFile(t, "testdata/store.json")
|
||||
defer cleanup()
|
||||
|
||||
existingDomain := "test.com"
|
||||
|
||||
account, err := store.GetAccountByPrivateDomain(existingDomain)
|
||||
require.NoError(t, err, "should found account")
|
||||
require.Equal(t, existingDomain, account.Domain, "domains should match")
|
||||
|
||||
_, err = store.GetAccountByPrivateDomain("missing-domain.com")
|
||||
require.Error(t, err, "should return error on domain lookup")
|
||||
}
|
||||
|
||||
func TestPostgresql_GetTokenIDByHashedToken(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("The PostgreSQL store is not properly supported by Windows yet")
|
||||
}
|
||||
|
||||
store, cleanup := newPostgresqlStoreFromFile(t, "testdata/store.json")
|
||||
defer cleanup()
|
||||
|
||||
hashed := "SoMeHaShEdToKeN"
|
||||
id := "9dj38s35-63fb-11ec-90d6-0242ac120003"
|
||||
|
||||
token, err := store.GetTokenIDByHashedToken(hashed)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, id, token)
|
||||
}
|
||||
|
||||
func TestPostgresql_GetUserByTokenID(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("The PostgreSQL store is not properly supported by Windows yet")
|
||||
}
|
||||
|
||||
store, cleanup := newPostgresqlStoreFromFile(t, "testdata/store.json")
|
||||
defer cleanup()
|
||||
|
||||
id := "9dj38s35-63fb-11ec-90d6-0242ac120003"
|
||||
|
||||
user, err := store.GetUserByTokenID(id)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, id, user.PATs[id].ID)
|
||||
}
|
||||
|
||||
func newPostgresqlStore(t *testing.T) (*PostgresqlStore, func()) {
|
||||
t.Helper()
|
||||
|
||||
dbName := "store_" + randString(10)
|
||||
postgresDsn := "host=localhost user=postgres password=postgres port=5432 sslmode=disable"
|
||||
db, _ := gorm.Open(postgres.Open(postgresDsn), &gorm.Config{})
|
||||
result := db.Exec(fmt.Sprintf("CREATE DATABASE %s ENCODING = 'UTF8'", dbName))
|
||||
if result.Error != nil {
|
||||
t.Fatalf("could not initialize postgresql store: %s", result.Error)
|
||||
}
|
||||
postgresDsn = fmt.Sprintf("%s dbname=%s ", postgresDsn, dbName)
|
||||
cleanup := func() {
|
||||
db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s WITH (FORCE)", dbName))
|
||||
}
|
||||
|
||||
store, err := NewPostgresqlStore(postgresDsn, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("could not initialize postgresql store: %s", err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, store)
|
||||
|
||||
return store, cleanup
|
||||
}
|
||||
|
||||
func randString(n int) string {
|
||||
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz1234567890")
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = letterRunes[rand.Intn(len(letterRunes))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func newPostgresqlStoreFromFile(t *testing.T, filename string) (*PostgresqlStore, func()) {
|
||||
t.Helper()
|
||||
|
||||
storeDir := t.TempDir()
|
||||
|
||||
err := util.CopyFileContents(filename, filepath.Join(storeDir, "store.json"))
|
||||
require.NoError(t, err)
|
||||
|
||||
fStore, err := NewFileStore(storeDir, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
dbName := "store_" + randString(10)
|
||||
postgresDsn := "host=localhost user=postgres password=postgres port=5432 sslmode=disable"
|
||||
db, _ := gorm.Open(postgres.Open(postgresDsn), &gorm.Config{})
|
||||
result := db.Exec(fmt.Sprintf("CREATE DATABASE %s ENCODING = 'UTF8'", dbName))
|
||||
if result.Error != nil {
|
||||
t.Fatalf("could not initialize postgresql store: %s", result.Error)
|
||||
}
|
||||
postgresDsn = fmt.Sprintf("%s dbname=%s ", postgresDsn, dbName)
|
||||
cleanup := func() {
|
||||
db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s WITH (FORCE)", dbName))
|
||||
}
|
||||
|
||||
store, err := NewPostgresqlStoreFromFileStore(fStore, postgresDsn, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, store)
|
||||
|
||||
return store, cleanup
|
||||
}
|
||||
|
||||
/*
|
||||
func newAccount(store Store, id int) error {
|
||||
str := fmt.Sprintf("%s-%d", uuid.New().String(), id)
|
||||
account := newAccountWithId(str, str+"-testuser", "example.com")
|
||||
setupKey := GenerateDefaultSetupKey()
|
||||
account.SetupKeys[setupKey.Key] = setupKey
|
||||
account.Peers["p"+str] = &nbpeer.Peer{
|
||||
Key: "peerkey" + str,
|
||||
SetupKey: "peerkeysetupkey",
|
||||
IP: net.IP{127, 0, 0, 1},
|
||||
Meta: nbpeer.PeerSystemMeta{},
|
||||
Name: "peer name",
|
||||
Status: &nbpeer.PeerStatus{Connected: true, LastSeen: time.Now().UTC()},
|
||||
}
|
||||
|
||||
return store.SaveAccount(account)
|
||||
}
|
||||
*/
|
||||
@@ -44,8 +44,9 @@ type Store interface {
|
||||
type StoreEngine string
|
||||
|
||||
const (
|
||||
FileStoreEngine StoreEngine = "jsonfile"
|
||||
SqliteStoreEngine StoreEngine = "sqlite"
|
||||
FileStoreEngine StoreEngine = "jsonfile"
|
||||
SqliteStoreEngine StoreEngine = "sqlite"
|
||||
PostgresqlStoreEngine StoreEngine = "postgresql"
|
||||
)
|
||||
|
||||
func getStoreEngineFromEnv() StoreEngine {
|
||||
@@ -76,6 +77,9 @@ func NewStore(kind StoreEngine, dataDir string, metrics telemetry.AppMetrics) (S
|
||||
case SqliteStoreEngine:
|
||||
log.Info("using SQLite store engine")
|
||||
return NewSqliteStore(dataDir, metrics)
|
||||
case PostgresqlStoreEngine:
|
||||
log.Info("using PostgreSQL store engine")
|
||||
return NewPostgresqlStore(dataDir, metrics) // dataDir is dsn
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported kind of store %s", kind)
|
||||
}
|
||||
@@ -94,6 +98,8 @@ func NewStoreFromJson(dataDir string, metrics telemetry.AppMetrics) (Store, erro
|
||||
return fstore, nil
|
||||
case SqliteStoreEngine:
|
||||
return NewSqliteStoreFromFileStore(fstore, dataDir, metrics)
|
||||
case PostgresqlStoreEngine:
|
||||
return NewPostgresqlStoreFromFileStore(fstore, dataDir, metrics) // dataDir is dsn
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported store engine %s", kind)
|
||||
}
|
||||
|
||||
@@ -1073,11 +1073,10 @@ func (am *DefaultAccountManager) deleteUserFromIDP(targetUserID, accountID strin
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to remove user %s app metadata in IdP: %s", targetUserID, err)
|
||||
}
|
||||
|
||||
_, err = am.refreshCache(accountID)
|
||||
if err != nil {
|
||||
log.Errorf("refresh account (%q) cache: %v", accountID, err)
|
||||
}
|
||||
}
|
||||
err := am.removeUserFromCache(accountID, targetUserID)
|
||||
if err != nil {
|
||||
log.Errorf("remove user from account (%q) cache failed with error: %v", accountID, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pion/stun"
|
||||
"github.com/pion/stun/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
Reference in New Issue
Block a user