diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index e4875bf390..41b00e8888 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -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:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8fe1a5255a..9e658b51b2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -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:
```
diff --git a/README.md b/README.md
index ef391e90fc..fab580dbbb 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@ https://user-images.githubusercontent.com/700848/197345890-2e2cded5-7b7a-436f-a4
|
- - \[x] Peer-to-peer connections
| - - \[x] Auto peer discovery and configuration
| - - \[x] [Setup keys for bulk network provisioning](https://docs.netbird.io/how-to/register-machines-using-setup-keys)
| |
| - - \[x] Peer-to-peer encryption
| - - \[x] [IdP integrations](https://docs.netbird.io/selfhosted/identity-providers)
| - - \[x] [Self-hosting quickstart script](https://docs.netbird.io/selfhosted/selfhosted-quickstart)
| |
| - - \[x] Connection relay fallback
| - - \[x] [SSO & MFA support](https://docs.netbird.io/how-to/installation#running-net-bird-with-sso-login)
| - - \[x] IdP groups sync with JWT
| |
-| - - \[x] [Routes to external networks](https://docs.netbird.io/how-to/routing-traffic-to-private-networks)
| - - \[x] [Access control - groups & rules](https://docs.netbird.io/how-to/manage-network-access)
| | |
+| - - \[x] [Routes to external networks](https://docs.netbird.io/how-to/routing-traffic-to-private-networks)
| - - \[x] [Access control - groups & rules](https://docs.netbird.io/how-to/manage-network-access)
| | |
| - - \[x] NAT traversal with BPF
| - - \[x] [Private DNS](https://docs.netbird.io/how-to/manage-dns-in-your-network)
| | |
| | - - \[x] [Multiuser support](https://docs.netbird.io/how-to/add-users-to-your-network)
| | |
| | - - \[x] [Activity logging](https://docs.netbird.io/how-to/monitor-system-and-network-activity)
| | |
diff --git a/client/cmd/login.go b/client/cmd/login.go
index 5af8c17750..71153906cc 100644
--- a/client/cmd/login.go
+++ b/client/cmd/login.go
@@ -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 {
diff --git a/client/cmd/up.go b/client/cmd/up.go
index ebfcb2b9d0..e895b2f221 100644
--- a/client/cmd/up.go
+++ b/client/cmd/up.go
@@ -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 {
diff --git a/client/installer.nsis b/client/installer.nsis
index fbffa326d8..af942a868e 100644
--- a/client/installer.nsis
+++ b/client/installer.nsis
@@ -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
diff --git a/client/internal/config.go b/client/internal/config.go
index 8f433a0419..fdc6385f40 100644
--- a/client/internal/config.go
+++ b/client/internal/config.go
@@ -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
+}
diff --git a/client/internal/config_test.go b/client/internal/config_test.go
index eeec9b516b..7453c8fdf8 100644
--- a/client/internal/config_test.go
+++ b/client/internal/config_test.go
@@ -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")
+ }
+ })
+ }
+}
diff --git a/client/internal/connect.go b/client/internal/connect.go
index d8784c0c86..6c654ec491 100644
--- a/client/internal/connect.go
+++ b/client/internal/connect.go
@@ -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)
diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go
index 67d411df5a..5f67a6ece5 100644
--- a/client/internal/dns/server_test.go
+++ b/client/internal/dns/server_test.go
@@ -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
diff --git a/client/internal/engine.go b/client/internal/engine.go
index d811ad48cf..3c421aabd1 100644
--- a/client/internal/engine.go
+++ b/client/internal/engine.go
@@ -3,7 +3,6 @@ package internal
import (
"context"
"fmt"
- "io"
"math/rand"
"net"
"net/netip"
@@ -32,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"
@@ -107,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
@@ -181,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)
+ log.Errorf("failed creating wireguard interface instance %s: [%s]", e.config.WgIfaceName, err.Error())
+ return err
}
+ e.wgInterface = wgIface
- e.wgInterface, err = iface.NewWGIFace(wgIFaceName, wgAddr, iface.DefaultMTU, e.mobileDep.TunAdapter, transportNet)
+ initialRoutes, dnsServer, err := e.newDnsServer()
if err != nil {
- log.Errorf("failed creating wireguard interface instance %s: [%s]", wgIFaceName, err.Error())
+ e.close()
return err
}
+ e.dnsServer = dnsServer
- 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
- }
- }
- }
-
- 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
}
@@ -258,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)
}
@@ -1042,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 {
@@ -1087,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 {
diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go
index 2de9b29f04..5dfc171a63 100644
--- a/client/internal/engine_test.go
+++ b/client/internal/engine_test.go
@@ -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{
diff --git a/client/internal/mobile_dependency.go b/client/internal/mobile_dependency.go
index 1a2a4c2b2f..2355c67c3b 100644
--- a/client/internal/mobile_dependency.go
+++ b/client/internal/mobile_dependency.go
@@ -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
}
diff --git a/client/internal/routemanager/manager_test.go b/client/internal/routemanager/manager_test.go
index 1aa58c16b7..2e5cf6649d 100644
--- a/client/internal/routemanager/manager_test.go
+++ b/client/internal/routemanager/manager_test.go
@@ -8,6 +8,7 @@ import (
"testing"
"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()
diff --git a/client/internal/routemanager/notifier.go b/client/internal/routemanager/notifier.go
index e27d08db57..ede8f02c4f 100644
--- a/client/internal/routemanager/notifier.go
+++ b/client/internal/routemanager/notifier.go
@@ -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
}
diff --git a/client/internal/routemanager/systemops_nonandroid_test.go b/client/internal/routemanager/systemops_nonandroid_test.go
index f43a88eec6..6f32d9634b 100644
--- a/client/internal/routemanager/systemops_nonandroid_test.go
+++ b/client/internal/routemanager/systemops_nonandroid_test.go
@@ -14,6 +14,7 @@ import (
"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()
diff --git a/client/netbird.wxs b/client/netbird.wxs
index f9b2449bac..0e2be7b3ca 100644
--- a/client/netbird.wxs
+++ b/client/netbird.wxs
@@ -20,6 +20,7 @@
+
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)
+}
diff --git a/iface/netstack/proxy.go b/iface/netstack/proxy.go
new file mode 100644
index 0000000000..a2120c642f
--- /dev/null
+++ b/iface/netstack/proxy.go
@@ -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()
+}
diff --git a/iface/netstack/tun.go b/iface/netstack/tun.go
new file mode 100644
index 0000000000..8c7c3a8ff5
--- /dev/null
+++ b/iface/netstack/tun.go
@@ -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
+}
diff --git a/iface/tun.go b/iface/tun.go
index ec8af4c322..b3c0f9d803 100644
--- a/iface/tun.go
+++ b/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
}
diff --git a/iface/tun_android.go b/iface/tun_android.go
index 3600001bad..834b2cb42d 100644
--- a/iface/tun_android.go
+++ b/iface/tun_android.go
@@ -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
-}
-
-func (t *tunDevice) Device() *device.Device {
- return t.device
-}
-
-func (t *tunDevice) DeviceName() string {
- return t.name
+ return t.configurer, nil
}
+func (t *wgTunDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
+ err := t.device.Up()
+ if err != nil {
+ return nil, err
+ }
-func (t *tunDevice) WgAddress() WGAddress {
- return t.address
+ 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(addr WGAddress) error {
+func (t *wgTunDevice) UpdateAddr(addr WGAddress) error {
// todo implement
return nil
}
-func (t *tunDevice) Close() (err error) {
+func (t *wgTunDevice) Close() error {
+ if t.configurer != nil {
+ t.configurer.close()
+ }
+
if t.device != nil {
t.device.Close()
+ t.device = nil
}
- return
+ if t.udpMux != nil {
+ return t.udpMux.Close()
+
+ }
+ return nil
+}
+
+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 (t *tunDevice) routesToString(routes []string) string {
+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, ";")
}
diff --git a/iface/tun_args.go b/iface/tun_args.go
new file mode 100644
index 0000000000..0eac2c4c0e
--- /dev/null
+++ b/iface/tun_args.go
@@ -0,0 +1,6 @@
+package iface
+
+type MobileIFaceArguments struct {
+ TunAdapter TunAdapter // only for Android
+ TunFd int // only for iOS
+}
diff --git a/iface/tun_darwin.go b/iface/tun_darwin.go
index 6e917e3748..bac14986f1 100644
--- a/iface/tun_darwin.go
+++ b/iface/tun_darwin.go
@@ -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
+ }
+
+ 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
}
- return c.assignAddr()
+ 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
}
diff --git a/iface/tun_ios.go b/iface/tun_ios.go
index 7a9ce5622e..ea980818d7 100644
--- a/iface/tun_ios.go
+++ b/iface/tun_ios.go
@@ -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
}
diff --git a/iface/tun_kernel_linux.go b/iface/tun_kernel_linux.go
new file mode 100644
index 0000000000..12adcdf737
--- /dev/null
+++ b/iface/tun_kernel_linux.go
@@ -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
+}
diff --git a/iface/tun_link_linux.go b/iface/tun_link_linux.go
new file mode 100644
index 0000000000..ab28b7e387
--- /dev/null
+++ b/iface/tun_link_linux.go
@@ -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)
+}
diff --git a/iface/tun_linux.go b/iface/tun_linux.go
deleted file mode 100644
index 1a3537394a..0000000000
--- a/iface/tun_linux.go
+++ /dev/null
@@ -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)
-}
diff --git a/iface/tun_netstack.go b/iface/tun_netstack.go
new file mode 100644
index 0000000000..e1d01ecc90
--- /dev/null
+++ b/iface/tun_netstack.go
@@ -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
+}
diff --git a/iface/tun_unix.go b/iface/tun_unix.go
deleted file mode 100644
index bc2d8d0196..0000000000
--- a/iface/tun_unix.go
+++ /dev/null
@@ -1,145 +0,0 @@
-//go:build (linux || darwin) && !android && !ios
-
-package iface
-
-import (
- "net"
- "os"
-
- "github.com/pion/transport/v3"
- "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)
-}
diff --git a/iface/tun_usp_linux.go b/iface/tun_usp_linux.go
new file mode 100644
index 0000000000..3ed518d522
--- /dev/null
+++ b/iface/tun_usp_linux.go
@@ -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
+}
diff --git a/iface/tun_windows.go b/iface/tun_windows.go
index a4ddf1d859..900e62fc3e 100644
--- a/iface/tun_windows.go
+++ b/iface/tun_windows.go
@@ -2,14 +2,12 @@ package iface
import (
"fmt"
- "net"
"net/netip"
"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 (t *tunDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
+ err := t.device.Up()
+ if err != nil {
+ return nil, err
+ }
-func (c *tunDevice) WgAddress() WGAddress {
- return c.address
+ 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 (c *tunDevice) DeviceName() string {
- return c.name
+func (t *tunDevice) UpdateAddr(address WGAddress) error {
+ t.address = address
+ return t.assignAddr()
}
-func (c *tunDevice) Close() error {
- select {
- case c.close <- struct{}{}:
- default:
+func (t *tunDevice) Close() error {
+ if t.configurer != nil {
+ t.configurer.close()
}
- var err1, err2 error
- if c.netInterface != nil {
- err1 = c.netInterface.Close()
+ if t.device != nil {
+ t.device.Close()
+ t.device = nil
}
- if c.uapi != nil {
- err2 = c.uapi.Close()
- }
+ if t.udpMux != nil {
+ return t.udpMux.Close()
- if err1 != nil {
- return err1
}
+ return nil
+}
+func (t *tunDevice) WgAddress() WGAddress {
+ return t.address
+}
+
+func (t *tunDevice) DeviceName() string {
+ return t.name
+}
- return err2
+func (t *tunDevice) Wrapper() *DeviceWrapper {
+ return t.wrapper
}
-func (c *tunDevice) getInterfaceGUIDString() (string, error) {
- if c.netInterface == nil {
+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())})
}
diff --git a/iface/uapi.go b/iface/uapi.go
new file mode 100644
index 0000000000..d7ff52e7b3
--- /dev/null
+++ b/iface/uapi.go
@@ -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
+}
diff --git a/iface/uapi_windows.go b/iface/uapi_windows.go
new file mode 100644
index 0000000000..e1f4663642
--- /dev/null
+++ b/iface/uapi_windows.go
@@ -0,0 +1,11 @@
+package iface
+
+import (
+ "net"
+
+ "golang.zx2c4.com/wireguard/ipc"
+)
+
+func openUAPI(deviceName string) (net.Listener, error) {
+ return ipc.UAPIListen(deviceName)
+}
diff --git a/iface/wg_configurer.go b/iface/wg_configurer.go
new file mode 100644
index 0000000000..b56d75084f
--- /dev/null
+++ b/iface/wg_configurer.go
@@ -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()
+}
diff --git a/iface/wg_configurer_nonmobile.go b/iface/wg_configurer_kernel.go
similarity index 83%
rename from iface/wg_configurer_nonmobile.go
rename to iface/wg_configurer_kernel.go
index c09dda9adf..3192f5a2b5 100644
--- a/iface/wg_configurer_nonmobile.go
+++ b/iface/wg_configurer_kernel.go
@@ -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() {
+}
diff --git a/iface/wg_configurer_mobile.go b/iface/wg_configurer_mobile.go
deleted file mode 100644
index 7f6e5595da..0000000000
--- a/iface/wg_configurer_mobile.go
+++ /dev/null
@@ -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)
- }
-}
diff --git a/iface/wg_configurer_usp.go b/iface/wg_configurer_usp.go
new file mode 100644
index 0000000000..cf12b99004
--- /dev/null
+++ b/iface/wg_configurer_usp.go
@@ -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()
+}
diff --git a/infrastructure_files/getting-started-with-zitadel.sh b/infrastructure_files/getting-started-with-zitadel.sh
index 330a47a2d7..90f623cbaf 100644
--- a/infrastructure_files/getting-started-with-zitadel.sh
+++ b/infrastructure_files/getting-started-with-zitadel.sh
@@ -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
@@ -569,7 +569,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
@@ -709,14 +709,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": {
@@ -734,12 +734,12 @@ EOF
renderDashboardEnv() {
cat <