Compare commits

..

10 Commits

Author SHA1 Message Date
bcmmbaga
ea01ef79f7 Implement posture check execution in policy 2023-12-22 14:25:27 +03:00
bcmmbaga
3a717368f7 Refactor version checks using go-version library 2023-12-22 14:25:12 +03:00
bcmmbaga
26e4ad7698 Migrate posture checks 2023-12-22 00:23:49 +03:00
bcmmbaga
3d9d93e15c implement posture checks update and request validation 2023-12-22 00:23:32 +03:00
bcmmbaga
c0e51377af go mod tidy 2023-12-21 15:25:25 +03:00
bcmmbaga
e765c1dc0d Merge branch 'main' into posture-checks-poc
# Conflicts:
#	go.sum
2023-12-21 15:24:56 +03:00
bcmmbaga
4989e1f6ca Include posture in policy api response 2023-12-21 15:22:37 +03:00
bcmmbaga
26e931b5fc add policy posture checks 2023-12-21 15:21:18 +03:00
bcmmbaga
e69039f68e go mod tidy 2023-12-21 15:20:43 +03:00
bcmmbaga
4efe664018 Add posture checks to open api doc 2023-12-21 15:20:30 +03:00
65 changed files with 1380 additions and 1973 deletions

View File

@@ -20,7 +20,7 @@ on:
- 'client/ui/**'
env:
SIGN_PIPE_VER: "v0.0.11"
SIGN_PIPE_VER: "v0.0.10"
GORELEASER_VER: "v1.14.1"
concurrency:

View File

@@ -189,8 +189,6 @@ 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:
```

View File

@@ -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> - \[x] 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> - \[ ] 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> | | |

View File

@@ -60,7 +60,7 @@ var loginCmd = &cobra.Command{
return fmt.Errorf("get config file: %v", err)
}
config, _ = internal.UpdateOldManagementURL(ctx, config, configPath)
config, _ = internal.UpdateOldManagementPort(ctx, config, configPath)
err = foregroundLogin(ctx, cmd, config, setupKey)
if err != nil {

View File

@@ -95,7 +95,7 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
return fmt.Errorf("get config file: %v", err)
}
config, _ = internal.UpdateOldManagementURL(ctx, config, configPath)
config, _ = internal.UpdateOldManagementPort(ctx, config, configPath)
err = foregroundLogin(ctx, cmd, config, setupKey)
if err != nil {

View File

@@ -179,11 +179,14 @@ func (r *router) InsertRoutingRules(pair manager.RouterPair) error {
// insertRoutingRule inserts a nftable rule to the conn client flush queue
func (r *router) insertRoutingRule(format, chainName string, pair manager.RouterPair, isNat bool) error {
expression := generateCIDRMatcherExpressions(true, pair.Source)
sourceExp := generateCIDRMatcherExpressions(true, pair.Source)
destExp := generateCIDRMatcherExpressions(false, pair.Destination)
var expression []expr.Any
if isNat {
expression = append(expression, &expr.Counter{}, &expr.Masq{}) // nolint:gocritic
expression = append(sourceExp, append(destExp, &expr.Counter{}, &expr.Masq{})...) // nolint:gocritic
} else {
expression = append(expression, exprCounterAccept...) // nolint:gocritic
expression = append(sourceExp, append(destExp, exprCounterAccept...)...) // nolint:gocritic
}
ruleKey := manager.GenKey(format, pair.ID)

View File

@@ -193,7 +193,6 @@ 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

View File

@@ -1,7 +1,6 @@
package internal
import (
"context"
"fmt"
"net/url"
"os"
@@ -13,19 +12,16 @@ 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 (
// managementLegacyPortString is the port that was used before by the Management gRPC server.
// ManagementLegacyPort 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
managementLegacyPortString = "33073"
ManagementLegacyPort = 33073
// DefaultManagementURL points to the NetBird's cloud management endpoint
DefaultManagementURL = "https://api.netbird.io:443"
// oldDefaultManagementURL points to the NetBird's old cloud management endpoint
oldDefaultManagementURL = "https://api.wiretrustee.com:443"
DefaultManagementURL = "https://api.wiretrustee.com:443"
// DefaultAdminURL points to NetBird's cloud management console
DefaultAdminURL = "https://app.netbird.io:443"
)
@@ -306,86 +302,3 @@ 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
}

View File

@@ -1,14 +1,12 @@
package internal
import (
"context"
"errors"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/util"
)
@@ -122,60 +120,3 @@ 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")
}
})
}
}

View File

@@ -283,6 +283,83 @@ 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)

View File

@@ -12,7 +12,6 @@ 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"
@@ -251,12 +250,11 @@ 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), 33100, privKey.String(), iface.DefaultMTU, newNet, nil)
wgIface, err := iface.NewWGIFace(fmt.Sprintf("utun230%d", n), fmt.Sprintf("100.66.100.%d/32", n+1), iface.DefaultMTU, nil, newNet)
if err != nil {
t.Fatal(err)
}
@@ -333,8 +331,7 @@ func TestDNSFakeResolverHandleUpdates(t *testing.T) {
return
}
privKey, _ := wgtypes.GeneratePrivateKey()
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.1/32", 33100, privKey.String(), iface.DefaultMTU, newNet, nil)
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.1/32", iface.DefaultMTU, nil, newNet)
if err != nil {
t.Errorf("build interface wireguard: %v", err)
return
@@ -785,8 +782,7 @@ func createWgInterfaceWithBind(t *testing.T) (*iface.WGIface, error) {
return nil, err
}
privKey, _ := wgtypes.GeneratePrivateKey()
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.2/24", 33100, privKey.String(), iface.DefaultMTU, newNet, nil)
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.2/24", iface.DefaultMTU, nil, newNet)
if err != nil {
t.Fatalf("build interface wireguard: %v", err)
return nil, err

View File

@@ -3,6 +3,7 @@ package internal
import (
"context"
"fmt"
"io"
"math/rand"
"net"
"net/netip"
@@ -31,6 +32,7 @@ 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"
@@ -105,7 +107,8 @@ type Engine struct {
wgInterface *iface.WGIface
wgProxyFactory *wgproxy.Factory
udpMux *bind.UniversalUDPMuxDefault
udpMux *bind.UniversalUDPMuxDefault
udpMuxConn io.Closer
// networkSerial is the latest CurrentSerial (state ID) of the network sent by the Management service
networkSerial uint64
@@ -178,26 +181,66 @@ func (e *Engine) Start() error {
e.syncMsgMux.Lock()
defer e.syncMsgMux.Unlock()
wgIface, err := e.newWgIface()
wgIFaceName := e.config.WgIfaceName
wgAddr := e.config.WgAddr
myPrivateKey := e.config.WgPrivateKey
var err error
transportNet, err := e.newStdNet()
if err != nil {
log.Errorf("failed creating wireguard interface instance %s: [%s]", e.config.WgIfaceName, err.Error())
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())
return err
}
e.wgInterface = wgIface
initialRoutes, dnsServer, err := e.newDnsServer()
if err != nil {
e.close()
return err
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.dnsServer = dnsServer
e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder, initialRoutes)
e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder, routes)
e.routeManager.SetRouteChangeListener(e.mobileDep.NetworkChangeListener)
err = e.wgInterfaceCreate()
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()
}
if err != nil {
log.Errorf("failed creating tunnel interface %s: [%s]", e.config.WgIfaceName, err.Error())
log.Errorf("failed creating tunnel interface %s: [%s]", wgIFaceName, err.Error())
e.close()
return err
}
@@ -215,13 +258,33 @@ func (e *Engine) Start() error {
}
}
e.udpMux, err = e.wgInterface.Up()
err = e.wgInterface.Configure(myPrivateKey.String(), e.config.WgPort)
if err != nil {
log.Errorf("failed to pull up wgInterface [%s]: %s", e.wgInterface.Name(), err.Error())
log.Errorf("failed configuring Wireguard interface [%s]: %s", wgIFaceName, 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)
}
@@ -979,6 +1042,18 @@ 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 {
@@ -1012,68 +1087,6 @@ 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 {

View File

@@ -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", engine.config.WgPort, key.String(), iface.DefaultMTU, newNet, nil)
engine.wgInterface, err = iface.NewWGIFace("utun102", "100.64.0.1/24", iface.DefaultMTU, nil, newNet)
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, engine.config.WgPort, key.String(), iface.DefaultMTU, newNet, nil)
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, iface.DefaultMTU, nil, newNet)
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, 33100, key.String(), iface.DefaultMTU, newNet, nil)
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, iface.DefaultMTU, nil, newNet)
assert.NoError(t, err, "shouldn't return error")
mockRouteManager := &routemanager.MockManager{

View File

@@ -9,14 +9,11 @@ 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
// iOS only
DnsManager dns.IosDnsManager
FileDescriptor int32
DnsManager dns.IosDnsManager
FileDescriptor int32
}

View File

@@ -8,7 +8,6 @@ import (
"testing"
"github.com/pion/transport/v3/stdnet"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/stretchr/testify/require"
@@ -400,12 +399,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", 33100, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun43%d", n), "100.65.65.2/24", iface.DefaultMTU, nil, newNet)
require.NoError(t, err, "should create testing WGIface interface")
defer wgInterface.Close()

View File

@@ -45,7 +45,7 @@ func (n *notifier) onNewRoutes(idMap map[string][]*route.Route) {
}
sort.Strings(newNets)
if !n.hasDiff(n.initialRouteRangers, newNets) {
if !n.hasDiff(n.routeRangers, newNets) {
return
}

View File

@@ -14,7 +14,6 @@ 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"
)
@@ -42,12 +41,11 @@ 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", 33100, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", iface.DefaultMTU, nil, newNet)
require.NoError(t, err, "should create testing WGIface interface")
defer wgInterface.Close()
@@ -177,12 +175,11 @@ 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", 33100, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", iface.DefaultMTU, nil, newNet)
require.NoError(t, err, "should create testing WGIface interface")
defer wgInterface.Close()

View File

@@ -20,7 +20,6 @@
<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"

View File

@@ -94,7 +94,7 @@ func (s *Server) Start() error {
}
// if configuration exists, we just start connections.
config, _ = internal.UpdateOldManagementURL(ctx, config, s.latestConfigInput.ConfigPath)
config, _ = internal.UpdateOldManagementPort(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.UpdateOldManagementURL(ctx, config, s.latestConfigInput.ConfigPath)
config, _ = internal.UpdateOldManagementPort(ctx, config, s.latestConfigInput.ConfigPath)
s.config = config
s.latestConfigInput.ManagementURL = config.ManagementURL.String()
}

View File

@@ -36,8 +36,7 @@ func getOSNameAndVersion() (string, string) {
query := wmi.CreateQuery(&dst, "")
err := wmi.Query(query, &dst)
if err != nil {
log.Error(err)
return "Windows", getBuildVersion()
log.Fatal(err)
}
if len(dst) == 0 {

View File

@@ -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 = internal.DefaultManagementURL
s.adminURL = internal.DefaultAdminURL
s.managementURL = "https://api.wiretrustee.com:33073"
s.adminURL = "https://app.netbird.io"
conn, err := s.getSrvClient(failFastTimeout)
if err != nil {

22
go.mod
View File

@@ -23,7 +23,7 @@ require (
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de
golang.zx2c4.com/wireguard/windows v0.5.3
google.golang.org/grpc v1.56.3
google.golang.org/protobuf v1.30.0
google.golang.org/protobuf v1.31.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0
)
@@ -51,18 +51,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-20231230192609-a9dcce34ff86
github.com/netbirdio/management-integrations/integrations v0.0.0-20231230192609-a9dcce34ff86
github.com/netbirdio/management-integrations/additions v0.0.0-20231205113053-c462587ae695
github.com/netbirdio/management-integrations/integrations v0.0.0-20231205113053-c462587ae695
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/v2 v2.0.0
github.com/pion/transport/v2 v2.2.1
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.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
@@ -73,7 +73,7 @@ require (
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028
golang.org/x/net v0.17.0
golang.org/x/oauth2 v0.8.0
golang.org/x/sync v0.2.0
golang.org/x/sync v0.3.0
golang.org/x/term v0.13.0
google.golang.org/api v0.126.0
gopkg.in/yaml.v3 v3.0.1
@@ -84,7 +84,7 @@ require (
require (
cloud.google.com/go/compute v1.19.3 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/beorn7/perks v1.0.1 // indirect
@@ -93,6 +93,7 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect
@@ -110,7 +111,6 @@ 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
@@ -129,7 +129,6 @@ require (
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/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
@@ -144,10 +143,10 @@ require (
go.opentelemetry.io/otel/sdk v1.11.1 // indirect
go.opentelemetry.io/otel/trace v1.11.1 // indirect
golang.org/x/image v0.10.0 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
golang.org/x/tools v0.6.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.12.0 // indirect
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
@@ -156,7 +155,6 @@ require (
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
)

33
go.sum
View File

@@ -48,8 +48,9 @@ github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6L
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
@@ -161,8 +162,9 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eko/gocache/v3 v3.1.1 h1:r3CBwLnqPkcK56h9Do2CWw1kZ4TeKK0wDE1Oo/YZnhs=
github.com/eko/gocache/v3 v3.1.1/go.mod h1:UpP/LyHAioP/a/dizgl0MpgZ3A3CkS4NbG/mWkGTQ9M=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
@@ -495,10 +497,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-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/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/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=
@@ -665,8 +667,6 @@ github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
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=
@@ -780,8 +780,9 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -872,8 +873,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1004,8 +1005,8 @@ 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=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1060,8 +1061,9 @@ golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1171,8 +1173,9 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -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,12 +19,11 @@ const (
// WGIface represents a interface instance
type WGIface struct {
tun wgTunDevice
userspaceBind bool
tun *tunDevice
configurer wGConfigurer
mu sync.Mutex
configurer wgConfigurer
filter PacketFilter
userspaceBind bool
filter PacketFilter
}
// IsUserspaceBind indicates whether this interfaces is userspace with bind.ICEBind
@@ -32,6 +31,11 @@ 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()
@@ -42,13 +46,13 @@ func (w *WGIface) Address() WGAddress {
return w.tun.WgAddress()
}
// Up configures a Wireguard interface
// Configure configures a Wireguard interface
// The interface must exist before calling this method (e.g. call interface.Create() before)
func (w *WGIface) Up() (*bind.UniversalUDPMuxDefault, error) {
func (w *WGIface) Configure(privateKey string, port int) error {
w.mu.Lock()
defer w.mu.Unlock()
return w.tun.Up()
log.Debugf("configuring Wireguard interface %s", w.tun.DeviceName())
return w.configurer.configureInterface(privateKey, port)
}
// UpdateAddr updates address of the interface
@@ -70,7 +74,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)
}
@@ -113,14 +117,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.WgAddress().Network)
w.filter.SetNetwork(w.tun.address.Network)
w.tun.Wrapper().SetFilter(filter)
w.tun.wrapper.SetFilter(filter)
return nil
}
@@ -137,5 +141,5 @@ func (w *WGIface) GetDevice() *DeviceWrapper {
w.mu.Lock()
defer w.mu.Unlock()
return w.tun.Wrapper()
return w.tun.wrapper
}

View File

@@ -2,39 +2,47 @@ package iface
import (
"fmt"
"sync"
"github.com/pion/transport/v3"
)
// 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
func NewWGIFace(ifaceName string, address string, mtu int, tunAdapter TunAdapter, transportNet transport.Net) (*WGIface, error) {
wgIFace := &WGIface{
mu: sync.Mutex{},
}
wgIFace := &WGIface{
tun: newTunDevice(wgAddress, wgPort, wgPrivKey, mtu, transportNet, args.TunAdapter),
userspaceBind: true,
wgAddress, err := parseWGAddress(address)
if err != nil {
return wgIFace, err
}
tun := newTunDevice(wgAddress, mtu, tunAdapter, transportNet)
wgIFace.tun = tun
wgIFace.configurer = newWGConfigurer(tun)
wgIFace.userspaceBind = !WireGuardModuleIsLoaded()
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(routes []string, dns string, searchDomains []string) error {
func (w *WGIface) CreateOnAndroid(mIFaceArgs MobileIFaceArguments) error {
w.mu.Lock()
defer w.mu.Unlock()
return w.tun.Create(mIFaceArgs)
}
cfgr, err := w.tun.Create(routes, dns, searchDomains)
if err != nil {
return err
}
w.configurer = cfgr
return 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 {
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 this platform")
return fmt.Errorf("this function has not implemented on mobile")
}

View File

@@ -1,20 +0,0 @@
//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
}

View File

@@ -1,38 +0,0 @@
//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")
}

View File

@@ -5,25 +5,47 @@ package iface
import (
"fmt"
"sync"
"github.com/pion/transport/v3"
"github.com/pion/transport/v2"
)
// 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) {
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 nil, err
}
wgIFace := &WGIface{
tun: newTunDevice(iFaceName, wgAddress, wgPort, wgPrivKey, transportNet, args.TunFd),
userspaceBind: true,
return wgIFace, err
}
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([]string, string, []string) error {
return fmt.Errorf("this function has not implemented on this platform")
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")
}

View File

@@ -1,48 +0,0 @@
//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")
}

47
iface/iface_nonandroid.go Normal file
View File

@@ -0,0 +1,47 @@
//go:build !android && !ios
// +build !android,!ios
package iface
import (
"fmt"
"sync"
"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{},
}
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()
}

View File

@@ -34,13 +34,12 @@ 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, wgPort, key, DefaultMTU, newNet, nil)
iface, err := NewWGIFace(ifaceName, addr, DefaultMTU, nil, newNet)
if err != nil {
t.Fatal(err)
}
@@ -53,10 +52,12 @@ func TestWGIface_UpdateAddr(t *testing.T) {
if err != nil {
t.Error(err)
}
}()
_, err = iface.Up()
port, err := getListenPortByName(ifaceName)
if err != nil {
t.Fatal(err)
}
err = iface.Configure(key, port)
if err != nil {
t.Fatal(err)
}
@@ -102,7 +103,7 @@ func Test_CreateInterface(t *testing.T) {
if err != nil {
t.Fatal(err)
}
iface, err := NewWGIFace(ifaceName, wgIP, 33100, key, DefaultMTU, newNet, nil)
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
if err != nil {
t.Fatal(err)
}
@@ -131,13 +132,11 @@ 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, wgPort, key, DefaultMTU, newNet, nil)
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
if err != nil {
t.Fatal(err)
}
@@ -165,12 +164,11 @@ 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, wgPort, key, DefaultMTU, newNet, nil)
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
if err != nil {
t.Fatal(err)
}
@@ -185,7 +183,11 @@ func Test_ConfigureInterface(t *testing.T) {
}
}()
_, err = iface.Up()
port, err := getListenPortByName(ifaceName)
if err != nil {
t.Fatal(err)
}
err = iface.Configure(key, port)
if err != nil {
t.Fatal(err)
}
@@ -217,8 +219,7 @@ func Test_UpdatePeer(t *testing.T) {
if err != nil {
t.Fatal(err)
}
iface, err := NewWGIFace(ifaceName, wgIP, 33100, key, DefaultMTU, newNet, nil)
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
if err != nil {
t.Fatal(err)
}
@@ -232,8 +233,11 @@ func Test_UpdatePeer(t *testing.T) {
t.Error(err)
}
}()
_, err = iface.Up()
port, err := getListenPortByName(ifaceName)
if err != nil {
t.Fatal(err)
}
err = iface.Configure(key, port)
if err != nil {
t.Fatal(err)
}
@@ -247,7 +251,7 @@ func Test_UpdatePeer(t *testing.T) {
if err != nil {
t.Fatal(err)
}
peer, err := getPeer(ifaceName, peerPubKey)
peer, err := iface.configurer.getPeer(ifaceName, peerPubKey)
if err != nil {
t.Fatal(err)
}
@@ -278,8 +282,7 @@ func Test_RemovePeer(t *testing.T) {
if err != nil {
t.Fatal(err)
}
iface, err := NewWGIFace(ifaceName, wgIP, 33100, key, DefaultMTU, newNet, nil)
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
if err != nil {
t.Fatal(err)
}
@@ -293,8 +296,11 @@ func Test_RemovePeer(t *testing.T) {
t.Error(err)
}
}()
_, err = iface.Up()
port, err := getListenPortByName(ifaceName)
if err != nil {
t.Fatal(err)
}
err = iface.Configure(key, port)
if err != nil {
t.Fatal(err)
}
@@ -309,8 +315,7 @@ func Test_RemovePeer(t *testing.T) {
if err != nil {
t.Fatal(err)
}
_, err = getPeer(ifaceName, peerPubKey)
_, err = iface.configurer.getPeer(ifaceName, peerPubKey)
if err.Error() != "peer not found" {
t.Fatal(err)
}
@@ -320,20 +325,17 @@ 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, peer1wgPort, peer1Key.String(), DefaultMTU, newNet, nil)
iface1, err := NewWGIFace(peer1ifaceName, peer1wgIP, DefaultMTU, nil, newNet)
if err != nil {
t.Fatal(err)
}
@@ -341,13 +343,11 @@ func Test_ConnectPeers(t *testing.T) {
if err != nil {
t.Fatal(err)
}
_, err = iface1.Up()
peer1Port, err := getListenPortByName(peer1ifaceName)
if err != nil {
t.Fatal(err)
}
peer1endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer1wgPort))
peer1endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer1Port))
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, peer2wgPort, peer2Key.String(), DefaultMTU, newNet, nil)
iface2, err := NewWGIFace(peer2ifaceName, peer2wgIP, DefaultMTU, nil, newNet)
if err != nil {
t.Fatal(err)
}
@@ -364,13 +364,11 @@ func Test_ConnectPeers(t *testing.T) {
if err != nil {
t.Fatal(err)
}
_, err = iface2.Up()
peer2Port, err := getListenPortByName(peer2ifaceName)
if err != nil {
t.Fatal(err)
}
peer2endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer2wgPort))
peer2endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer2Port))
if err != nil {
t.Fatal(err)
}
@@ -385,6 +383,15 @@ 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)
@@ -396,15 +403,13 @@ 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 := getPeer(peer1ifaceName, peer2Key.PublicKey().String())
peer, gpErr := iface1.configurer.getPeer(peer1ifaceName, peer2Key.PublicKey().String())
if gpErr != nil {
t.Fatal(gpErr)
}
@@ -416,26 +421,17 @@ func Test_ConnectPeers(t *testing.T) {
}
func getPeer(ifaceName, peerPubKey string) (wgtypes.Peer, error) {
func getListenPortByName(name string) (int, error) {
wg, err := wgctrl.New()
if err != nil {
return wgtypes.Peer{}, err
return 0, err
}
defer func() {
err = wg.Close()
if err != nil {
log.Errorf("got error while closing wgctl: %v", err)
}
}()
defer wg.Close()
wgDevice, err := wg.Device(ifaceName)
d, err := wg.Device(name)
if err != nil {
return wgtypes.Peer{}, err
return 0, err
}
for _, peer := range wgDevice.Peers {
if peer.PublicKey.String() == peerPubKey {
return peer, nil
}
}
return wgtypes.Peer{}, fmt.Errorf("peer not found")
return d.ListenPort, nil
}

View File

@@ -1,39 +1,6 @@
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.(*tunDevice).getInterfaceGUIDString()
return w.tun.getInterfaceGUIDString()
}

View File

@@ -0,0 +1,63 @@
//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()
}

View File

@@ -1,32 +0,0 @@
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
}

View File

@@ -1,33 +0,0 @@
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)
}

View File

@@ -1,65 +0,0 @@
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()
}

View File

@@ -1,74 +0,0 @@
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
}

View File

@@ -1,18 +1,12 @@
//go:build !android
// +build !android
package iface
import (
"github.com/netbirdio/netbird/iface/bind"
)
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
type MobileIFaceArguments struct {
Routes []string
Dns string
SearchDomains []string
}
// NetInterface represents a generic network tunnel interface
type NetInterface interface {
Close() error
}

View File

@@ -15,50 +15,42 @@ import (
"github.com/netbirdio/netbird/iface/bind"
)
// ignore the wgTunDevice interface on Android because the creation of the tun device is different on this platform
type wgTunDevice struct {
type tunDevice struct {
address WGAddress
port int
key string
mtu int
iceBind *bind.ICEBind
tunAdapter TunAdapter
iceBind *bind.ICEBind
name string
device *device.Device
wrapper *DeviceWrapper
udpMux *bind.UniversalUDPMuxDefault
configurer wgConfigurer
fd int
name string
device *device.Device
wrapper *DeviceWrapper
}
func newTunDevice(address WGAddress, port int, key string, mtu int, transportNet transport.Net, tunAdapter TunAdapter) wgTunDevice {
return wgTunDevice{
func newTunDevice(address WGAddress, mtu int, tunAdapter TunAdapter, transportNet transport.Net) *tunDevice {
return &tunDevice{
address: address,
port: port,
key: key,
mtu: mtu,
iceBind: bind.NewICEBind(transportNet),
tunAdapter: tunAdapter,
iceBind: bind.NewICEBind(transportNet),
}
}
func (t *wgTunDevice) Create(routes []string, dns string, searchDomains []string) (wgConfigurer, error) {
func (t *tunDevice) Create(mIFaceArgs MobileIFaceArguments) error {
log.Info("create tun interface")
routesString := routesToString(routes)
searchDomainsToString := searchDomainsToString(searchDomains)
fd, err := t.tunAdapter.ConfigureInterface(t.address.String(), t.mtu, dns, searchDomainsToString, routesString)
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)
if err != nil {
log.Errorf("failed to create Android interface: %s", err)
return nil, err
return err
}
tunDevice, name, err := tun.CreateUnmonitoredTUNFromFD(fd)
tunDevice, name, err := tun.CreateUnmonitoredTUNFromFD(t.fd)
if err != nil {
_ = unix.Close(fd)
log.Errorf("failed to create Android interface: %s", err)
return nil, err
unix.Close(t.fd)
return err
}
t.name = name
t.wrapper = newDeviceWrapper(tunDevice)
@@ -69,72 +61,44 @@ func (t *wgTunDevice) Create(routes []string, dns string, searchDomains []string
// this helps with support for the older NetBird clients that had a hardcoded direct mode
// t.device.DisableSomeRoamingForBrokenMobileSemantics()
t.configurer = newWGUSPConfigurer(t.device, t.name)
err = t.configurer.configureInterface(t.key, t.port)
err = t.device.Up()
if err != nil {
t.device.Close()
t.configurer.close()
return nil, err
return err
}
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
log.Debugf("device is ready to use: %s", name)
return nil
}
func (t *wgTunDevice) UpdateAddr(addr WGAddress) error {
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 {
// todo implement
return nil
}
func (t *wgTunDevice) Close() error {
if t.configurer != nil {
t.configurer.close()
}
func (t *tunDevice) Close() (err error) {
if t.device != nil {
t.device.Close()
t.device = nil
}
if t.udpMux != nil {
return t.udpMux.Close()
}
return nil
return
}
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 {
func (t *tunDevice) routesToString(routes []string) string {
return strings.Join(routes, ";")
}
func searchDomainsToString(searchDomains []string) string {
func (t *tunDevice) searchDomainsToString(searchDomains []string) string {
return strings.Join(searchDomains, ";")
}

View File

@@ -1,6 +0,0 @@
package iface
type MobileIFaceArguments struct {
TunAdapter TunAdapter // only for Android
TunFd int // only for iOS
}

View File

@@ -6,129 +6,32 @@ 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"
)
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)
func (c *tunDevice) Create() error {
var err error
c.netInterface, err = c.createWithUserspace()
if err != nil {
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 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
}
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
return c.assignAddr()
}
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided
func (t *tunDevice) assignAddr() error {
cmd := exec.Command("ifconfig", t.name, "inet", t.address.IP.String(), t.address.IP.String())
func (c *tunDevice) assignAddr() error {
cmd := exec.Command("ifconfig", c.name, "inet", c.address.IP.String(), c.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", t.address.Network.String(), "-interface", t.name)
routeCmd := exec.Command("route", "add", "-net", c.address.Network.String(), "-interface", c.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
}

View File

@@ -6,7 +6,7 @@ package iface
import (
"os"
"github.com/pion/transport/v3"
"github.com/pion/transport/v2"
log "github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
"golang.zx2c4.com/wireguard/device"
@@ -16,82 +16,63 @@ import (
)
type tunDevice struct {
name string
address WGAddress
port int
key string
iceBind *bind.ICEBind
tunFd int
address WGAddress
mtu int
tunAdapter TunAdapter
iceBind *bind.ICEBind
device *device.Device
wrapper *DeviceWrapper
udpMux *bind.UniversalUDPMuxDefault
configurer wgConfigurer
fd int
name string
device *device.Device
wrapper *DeviceWrapper
}
func newTunDevice(name string, address WGAddress, port int, key string, transportNet transport.Net, tunFd int) *tunDevice {
func newTunDevice(name string, address WGAddress, mtu int, tunAdapter TunAdapter, transportNet transport.Net) *tunDevice {
return &tunDevice{
name: name,
address: address,
port: port,
key: key,
iceBind: bind.NewICEBind(transportNet),
tunFd: tunFd,
name: name,
address: address,
mtu: mtu,
tunAdapter: tunAdapter,
iceBind: bind.NewICEBind(transportNet),
}
}
func (t *tunDevice) Create() (wgConfigurer, error) {
func (t *tunDevice) Create(tunFd int32) error {
log.Infof("create tun interface")
dupTunFd, err := unix.Dup(t.tunFd)
dupTunFd, err := unix.Dup(int(tunFd))
if err != nil {
log.Errorf("Unable to dup tun fd: %v", err)
return nil, err
return 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 nil, err
unix.Close(dupTunFd)
return err
}
tunDevice, err := tun.CreateTUNFromFile(os.NewFile(uintptr(dupTunFd), "/dev/tun"), 0)
tun, 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 nil, err
unix.Close(dupTunFd)
return err
}
t.wrapper = newDeviceWrapper(tunDevice)
t.wrapper = newDeviceWrapper(tun)
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()
t.configurer = newWGUSPConfigurer(t.device, t.name)
err = t.configurer.configureInterface(t.key, t.port)
err = t.device.Up()
if err != nil {
t.device.Close()
t.configurer.close()
return nil, err
return 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
return nil
}
func (t *tunDevice) Device() *device.Device {
@@ -102,23 +83,6 @@ 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
}
@@ -128,6 +92,10 @@ func (t *tunDevice) UpdateAddr(addr WGAddress) error {
return nil
}
func (t *tunDevice) Wrapper() *DeviceWrapper {
return t.wrapper
func (t *tunDevice) Close() (err error) {
if t.device != nil {
t.device.Close()
}
return
}

View File

@@ -1,209 +0,0 @@
//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
}

View File

@@ -1,33 +0,0 @@
//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)
}

149
iface/tun_linux.go Normal file
View File

@@ -0,0 +1,149 @@
//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)
}

View File

@@ -1,119 +0,0 @@
//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
}

145
iface/tun_unix.go Normal file
View File

@@ -0,0 +1,145 @@
//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)
}

View File

@@ -1,157 +0,0 @@
//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
}

View File

@@ -2,12 +2,14 @@ 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"
@@ -15,131 +17,139 @@ import (
)
type tunDevice 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
name string
address WGAddress
netInterface NetInterface
iceBind *bind.ICEBind
mtu int
uapi net.Listener
wrapper *DeviceWrapper
close chan struct{}
}
func newTunDevice(name string, address WGAddress, port int, key string, mtu int, transportNet transport.Net) wgTunDevice {
func newTunDevice(name string, address WGAddress, mtu int, transportNet transport.Net) *tunDevice {
return &tunDevice{
name: name,
address: address,
port: port,
key: key,
mtu: mtu,
iceBind: bind.NewICEBind(transportNet),
close: make(chan struct{}),
}
}
func (t *tunDevice) Create() (wgConfigurer, error) {
tunDevice, err := tun.CreateTUN(t.name, t.mtu)
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)
if err != nil {
return nil, err
}
t.nativeTunDevice = tunDevice.(*tun.NativeTun)
t.wrapper = newDeviceWrapper(tunDevice)
c.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] "),
)
tunDev := device.NewDevice(c.wrapper, c.iceBind, device.NewLogger(device.LogLevelSilent, "[netbird] "))
err = tunDev.Up()
if err != nil {
_ = tunIface.Close()
return nil, err
}
luid := winipcfg.LUID(t.nativeTunDevice.LUID())
luid := winipcfg.LUID(tunIface.(*tun.NativeTun).LUID())
nbiface, err := luid.IPInterface(windows.AF_INET)
if err != nil {
t.device.Close()
_ = tunIface.Close()
return nil, fmt.Errorf("got error when getting ip interface %s", err)
}
nbiface.NLMTU = uint32(t.mtu)
nbiface.NLMTU = uint32(c.mtu)
err = nbiface.Set()
if err != nil {
t.device.Close()
_ = tunIface.Close()
return nil, fmt.Errorf("got error when getting setting the interface mtu: %s", err)
}
err = t.assignAddr()
c.uapi, err = c.getUAPI(c.name)
if err != nil {
t.device.Close()
_ = tunIface.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
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
}
func (t *tunDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
err := t.device.Up()
if err != nil {
return nil, err
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:
}
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()
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
return err2
}
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 {
func (c *tunDevice) getInterfaceGUIDString() (string, error) {
if c.netInterface == nil {
return "", fmt.Errorf("interface has not been initialized yet")
}
luid := winipcfg.LUID(t.nativeTunDevice.LUID())
windowsDevice := c.netInterface.(*tun.NativeTun)
luid := winipcfg.LUID(windowsDevice.LUID())
guid, err := luid.GUID()
if err != nil {
return "", err
@@ -148,8 +158,14 @@ func (t *tunDevice) getInterfaceGUIDString() (string, error) {
}
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided
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())})
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)
}

View File

@@ -1,26 +0,0 @@
//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
}

View File

@@ -1,11 +0,0 @@
package iface
import (
"net"
"golang.zx2c4.com/wireguard/ipc"
)
func openUAPI(deviceName string) (net.Listener, error) {
return ipc.UAPIListen(deviceName)
}

View File

@@ -1,17 +0,0 @@
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()
}

View File

@@ -0,0 +1,165 @@
//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)
}
}

View File

@@ -1,4 +1,4 @@
//go:build linux && !android
//go:build !android && !ios
package iface
@@ -12,18 +12,17 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
type wgKernelConfigurer struct {
type wGConfigurer struct {
deviceName string
}
func newWGConfigurer(deviceName string) wgConfigurer {
wgc := &wgKernelConfigurer{
func newWGConfigurer(deviceName string) wGConfigurer {
return wGConfigurer{
deviceName: deviceName,
}
return wgc
}
func (c *wgKernelConfigurer) configureInterface(privateKey string, port int) error {
func (c *wGConfigurer) configureInterface(privateKey string, port int) error {
log.Debugf("adding Wireguard private key")
key, err := wgtypes.ParseKey(privateKey)
if err != nil {
@@ -44,7 +43,7 @@ func (c *wgKernelConfigurer) configureInterface(privateKey string, port int) err
return nil
}
func (c *wgKernelConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
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 {
@@ -74,7 +73,7 @@ func (c *wgKernelConfigurer) updatePeer(peerKey string, allowedIps string, keepA
return nil
}
func (c *wgKernelConfigurer) removePeer(peerKey string) error {
func (c *wGConfigurer) removePeer(peerKey string) error {
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
if err != nil {
return err
@@ -95,7 +94,7 @@ func (c *wgKernelConfigurer) removePeer(peerKey string) error {
return nil
}
func (c *wgKernelConfigurer) addAllowedIP(peerKey string, allowedIP string) error {
func (c *wGConfigurer) addAllowedIP(peerKey string, allowedIP string) error {
_, ipNet, err := net.ParseCIDR(allowedIP)
if err != nil {
return err
@@ -122,7 +121,7 @@ func (c *wgKernelConfigurer) addAllowedIP(peerKey string, allowedIP string) erro
return nil
}
func (c *wgKernelConfigurer) removeAllowedIP(peerKey string, allowedIP string) error {
func (c *wGConfigurer) removeAllowedIP(peerKey string, allowedIP string) error {
_, ipNet, err := net.ParseCIDR(allowedIP)
if err != nil {
return err
@@ -164,7 +163,7 @@ func (c *wgKernelConfigurer) removeAllowedIP(peerKey string, allowedIP string) e
return nil
}
func (c *wgKernelConfigurer) getPeer(ifaceName, peerPubKey string) (wgtypes.Peer, error) {
func (c *wGConfigurer) getPeer(ifaceName, peerPubKey string) (wgtypes.Peer, error) {
wg, err := wgctrl.New()
if err != nil {
return wgtypes.Peer{}, err
@@ -188,7 +187,7 @@ func (c *wgKernelConfigurer) getPeer(ifaceName, peerPubKey string) (wgtypes.Peer
return wgtypes.Peer{}, fmt.Errorf("peer not found")
}
func (c *wgKernelConfigurer) configure(config wgtypes.Config) error {
func (c *wGConfigurer) configure(config wgtypes.Config) error {
wg, err := wgctrl.New()
if err != nil {
return err
@@ -204,6 +203,3 @@ func (c *wgKernelConfigurer) configure(config wgtypes.Config) error {
return wg.ConfigureDevice(c.deviceName, config)
}
func (c *wgKernelConfigurer) close() {
}

View File

@@ -1,259 +0,0 @@
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()
}

View File

@@ -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"
INSTANCE_URL="$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT"
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"
echo "You can access the NetBird dashboard at $NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT"
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",
"TokenEndpoint": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN/oauth/v2/token",
"Issuer": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT",
"TokenEndpoint": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT/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/management/v1"
"ManagementEndpoint": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT/management/v1"
}
},
"PKCEAuthorizationFlow": {
@@ -633,12 +633,12 @@ EOF
renderDashboardEnv() {
cat <<EOF
# Endpoints
NETBIRD_MGMT_API_ENDPOINT=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN
NETBIRD_MGMT_GRPC_API_ENDPOINT=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN
NETBIRD_MGMT_API_ENDPOINT=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT
NETBIRD_MGMT_GRPC_API_ENDPOINT=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT
# OIDC
AUTH_AUDIENCE=$NETBIRD_AUTH_CLIENT_ID
AUTH_CLIENT_ID=$NETBIRD_AUTH_CLIENT_ID
AUTH_AUTHORITY=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN
AUTH_AUTHORITY=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT
USE_AUTH0=false
AUTH_SUPPORTED_SCOPES="openid profile email offline_access"
AUTH_REDIRECT_URI=/nb-auth

View File

@@ -7,7 +7,6 @@ import (
"errors"
"flag"
"fmt"
"github.com/netbirdio/management-integrations/integrations"
"io"
"io/fs"
"net"
@@ -32,6 +31,8 @@ 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"
@@ -145,7 +146,7 @@ var (
if disableSingleAccMode {
mgmtSingleAccModeDomain = ""
}
eventStore, key, err := integrations.InitEventStore(config.Datadir, config.DataStoreEncryptionKey)
eventStore, key, err := initEventStore(config.Datadir, config.DataStoreEncryptionKey)
if err != nil {
return fmt.Errorf("failed to initialize database: %s", err)
}
@@ -300,6 +301,20 @@ 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:

View File

@@ -1299,22 +1299,6 @@ 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,

View File

@@ -0,0 +1,68 @@
package checks
import (
"fmt"
"github.com/hashicorp/go-version"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
)
type PostureCheck struct {
// ID of the policy rule
ID string `gorm:"primaryKey"`
// PolicyID is a reference to Policy that this object belongs
PolicyID string `json:"-" gorm:"index"`
NBVersionCheck NBVersionPostureCheck `gorm:"embedded;embeddedPrefix:nb_version_check_"`
OSVersionCheck OSVersionPostureCheck `gorm:"embedded;embeddedPrefix:os_version_check_"`
}
type NBVersionPostureCheck struct {
Enabled bool
MinimumVersionAllowed string
}
func (n *NBVersionPostureCheck) Check(peer nbpeer.Peer) error {
peerNBVersion, err := version.NewVersion(peer.Meta.UIVersion)
if err != nil {
return err
}
constraints, err := version.NewConstraint(">= " + n.MinimumVersionAllowed)
if err != nil {
return err
}
if constraints.Check(peerNBVersion) {
return nil
}
return fmt.Errorf("peer nb version is older than minimum allowed version %s", n.MinimumVersionAllowed)
}
type OSVersionPostureCheck struct {
Enabled bool
MinimumVersionAllowed string
// TODO: add OS context to prevent using the same version on different OS types
}
func (o *OSVersionPostureCheck) Check(peer nbpeer.Peer) error {
peerNBVersion, err := version.NewVersion(peer.Meta.UIVersion)
if err != nil {
return err
}
constraints, err := version.NewConstraint(">= " + o.MinimumVersionAllowed)
if err != nil {
return err
}
if constraints.Check(peerNBVersion) {
return nil
}
return fmt.Errorf("peer OS version is not supported")
}

View File

@@ -784,8 +784,11 @@ components:
type: array
items:
$ref: '#/components/schemas/PolicyRuleUpdate'
postureCheck:
$ref: '#/components/schemas/PostureCheck'
required:
- rules
- PostureCheck
Policy:
allOf:
- $ref: '#/components/schemas/PolicyMinimum'
@@ -796,8 +799,11 @@ components:
type: array
items:
$ref: '#/components/schemas/PolicyRule'
postureCheck:
$ref: '#/components/schemas/PostureCheck'
required:
- rules
- postureCheck
RouteRequest:
type: object
properties:
@@ -1034,6 +1040,35 @@ components:
- initiator_email
- target_id
- meta
PostureCheck:
description: Policy poster check
type: object
properties:
osVersionPostureCheck:
$ref: '#/components/schemas/OSVersionPostureCheck'
nbVersionPostureCheck:
$ref: '#/components/schemas/NBVersionPostureCheck'
required:
- osVersionPostureCheck
- nbVersionPostureCheck
NBVersionPostureCheck:
type: object
properties:
enabled:
type: boolean
example: false
minimumVersionAllowed:
type: string
example: "1.2.3"
OSVersionPostureCheck:
type: object
properties:
enabled:
type: boolean
example: false
minimumVersionAllowed:
type: string
example: "1.2.3"
responses:
not_found:
description: Resource not found

View File

@@ -257,6 +257,12 @@ type GroupRequest struct {
Peers *[]string `json:"peers,omitempty"`
}
// NBVersionPostureCheck defines model for NBVersionPostureCheck.
type NBVersionPostureCheck struct {
Enabled *bool `json:"enabled,omitempty"`
MinimumVersionAllowed *string `json:"minimumVersionAllowed,omitempty"`
}
// Nameserver defines model for Nameserver.
type Nameserver struct {
// Ip Nameserver IP
@@ -329,6 +335,12 @@ type NameserverGroupRequest struct {
SearchDomainsEnabled bool `json:"search_domains_enabled"`
}
// OSVersionPostureCheck defines model for OSVersionPostureCheck.
type OSVersionPostureCheck struct {
Enabled *bool `json:"enabled,omitempty"`
MinimumVersionAllowed *string `json:"minimumVersionAllowed,omitempty"`
}
// Peer defines model for Peer.
type Peer struct {
// AccessiblePeers List of accessible peers
@@ -567,6 +579,9 @@ type Policy struct {
// Name Policy name identifier
Name string `json:"name"`
// PostureCheck Policy poster check
PostureCheck PostureCheck `json:"postureCheck"`
// Query Policy Rego query
Query string `json:"query"`
@@ -717,6 +732,9 @@ type PolicyUpdate struct {
// Name Policy name identifier
Name string `json:"name"`
// PostureCheck Policy poster check
PostureCheck *PostureCheck `json:"postureCheck,omitempty"`
// Query Policy Rego query
Query string `json:"query"`
@@ -724,6 +742,12 @@ type PolicyUpdate struct {
Rules []PolicyRuleUpdate `json:"rules"`
}
// PostureCheck Policy poster check
type PostureCheck struct {
NbVersionPostureCheck NBVersionPostureCheck `json:"nbVersionPostureCheck"`
OsVersionPostureCheck OSVersionPostureCheck `json:"osVersionPostureCheck"`
}
// Route defines model for Route.
type Route struct {
// Description Route description

View File

@@ -9,6 +9,7 @@ import (
"github.com/rs/xid"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/checks"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/netbirdio/netbird/management/server/jwtclaims"
@@ -117,13 +118,8 @@ func (h *Policies) savePolicy(
return
}
if req.Name == "" {
util.WriteError(status.Errorf(status.InvalidArgument, "policy name shouldn't be empty"), w)
return
}
if len(req.Rules) == 0 {
util.WriteError(status.Errorf(status.InvalidArgument, "policy rules shouldn't be empty"), w)
if err := validatePolicyUpdateReq(req); err != nil {
util.WriteError(err, w)
return
}
@@ -206,6 +202,35 @@ func (h *Policies) savePolicy(
policy.Rules = append(policy.Rules, &pr)
}
if req.PostureCheck != nil {
var (
osVersionPostureCheck checks.OSVersionPostureCheck
nbVersionPostureCheck checks.NBVersionPostureCheck
)
osVersionPostureCheckReq := req.PostureCheck.OsVersionPostureCheck
if enabled := osVersionPostureCheckReq.Enabled; enabled != nil {
osVersionPostureCheck.Enabled = *enabled
}
if minAllowedVersionReq := osVersionPostureCheckReq.MinimumVersionAllowed; minAllowedVersionReq != nil {
osVersionPostureCheck.MinimumVersionAllowed = *minAllowedVersionReq
}
nbVersionPostureCheckReq := req.PostureCheck.NbVersionPostureCheck
if enabled := nbVersionPostureCheckReq.Enabled; enabled != nil {
nbVersionPostureCheck.Enabled = *enabled
}
if minAllowedVersionReq := nbVersionPostureCheckReq.MinimumVersionAllowed; minAllowedVersionReq != nil {
nbVersionPostureCheck.MinimumVersionAllowed = *minAllowedVersionReq
}
policy.PostureCheck = checks.PostureCheck{
ID: policyID,
NBVersionCheck: nbVersionPostureCheck,
OSVersionCheck: osVersionPostureCheck,
}
}
if err := h.accountManager.SavePolicy(account.Id, user.Id, &policy); err != nil {
util.WriteError(err, w)
return
@@ -338,6 +363,20 @@ func toPolicyResponse(account *server.Account, policy *server.Policy) *api.Polic
}
ap.Rules = append(ap.Rules, rule)
}
nbVersionCheck := policy.PostureCheck.NBVersionCheck
osVersionCheck := policy.PostureCheck.OSVersionCheck
ap.PostureCheck = api.PostureCheck{
NbVersionPostureCheck: api.NBVersionPostureCheck{
Enabled: &nbVersionCheck.Enabled,
MinimumVersionAllowed: &nbVersionCheck.MinimumVersionAllowed,
},
OsVersionPostureCheck: api.OSVersionPostureCheck{
Enabled: &osVersionCheck.Enabled,
MinimumVersionAllowed: &osVersionCheck.MinimumVersionAllowed,
},
}
return ap
}
@@ -351,3 +390,31 @@ func groupMinimumsToStrings(account *server.Account, gm []string) []string {
}
return result
}
func validatePolicyUpdateReq(req api.PutApiPoliciesPolicyIdJSONRequestBody) error {
if req.Name == "" {
return status.Errorf(status.InvalidArgument, "policy name shouldn't be empty")
}
if len(req.Rules) == 0 {
return status.Errorf(status.InvalidArgument, "policy rules shouldn't be empty")
}
if req.PostureCheck == nil {
return status.Errorf(status.InvalidArgument, "policy posture checks shouldn't be empty")
}
if enabled := req.PostureCheck.NbVersionPostureCheck.Enabled; enabled != nil && *enabled {
if minVersion := req.PostureCheck.NbVersionPostureCheck.MinimumVersionAllowed; minVersion == nil || *minVersion == "" {
return status.Errorf(status.InvalidArgument, "netbird version posture check is enabled, minimum version allowed shouldn't be empty")
}
}
if enabled := req.PostureCheck.OsVersionPostureCheck.Enabled; enabled != nil && *enabled {
if minVersion := req.PostureCheck.OsVersionPostureCheck.MinimumVersionAllowed; minVersion == nil || *minVersion == "" {
return status.Errorf(status.InvalidArgument, "os version posture check is enabled, minimum version allowed shouldn't be empty")
}
}
return nil
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/checks"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/status"
)
@@ -150,16 +151,20 @@ type Policy struct {
// Rules of the policy
Rules []*PolicyRule `gorm:"foreignKey:PolicyID;references:id"`
// PostureCheck of the policy
PostureCheck checks.PostureCheck `gorm:"foreignKey:PolicyID;references:id"`
}
// Copy returns a copy of the policy.
func (p *Policy) Copy() *Policy {
c := &Policy{
ID: p.ID,
Name: p.Name,
Description: p.Description,
Enabled: p.Enabled,
Rules: make([]*PolicyRule, len(p.Rules)),
ID: p.ID,
Name: p.Name,
Description: p.Description,
Enabled: p.Enabled,
Rules: make([]*PolicyRule, len(p.Rules)),
PostureCheck: p.PostureCheck,
}
for i, r := range p.Rules {
c.Rules[i] = r.Copy()
@@ -214,6 +219,21 @@ func (a *Account) getPeerConnectionResources(peerID string) ([]*nbpeer.Peer, []*
continue
}
// TODO: implement posture checks executions based on policy
peer, ok := a.Peers[peerID]
if !ok && peer == nil {
continue
}
if policy.PostureCheck.OSVersionCheck.Enabled {
err := policy.PostureCheck.OSVersionCheck.Check(*peer)
if err != nil {
log.Debugf(err.Error())
continue
}
}
for _, rule := range policy.Rules {
if !rule.Enabled {
continue

View File

@@ -15,6 +15,7 @@ import (
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/checks"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/status"
"github.com/netbirdio/netbird/management/server/telemetry"
@@ -63,7 +64,7 @@ func NewSqliteStore(dataDir string, metrics telemetry.AppMetrics) (*SqliteStore,
err = db.AutoMigrate(
&SetupKey{}, &nbpeer.Peer{}, &User{}, &PersonalAccessToken{}, &Group{}, &Rule{},
&Account{}, &Policy{}, &PolicyRule{}, &route.Route{}, &nbdns.NameServerGroup{},
&installation{}, &account.ExtraSettings{},
&installation{}, &account.ExtraSettings{}, &checks.PostureCheck{},
)
if err != nil {
return nil, err

View File

@@ -1073,10 +1073,11 @@ 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.removeUserFromCache(accountID, targetUserID)
if err != nil {
log.Errorf("remove user from account (%q) cache failed with error: %v", accountID, err)
_, err = am.refreshCache(accountID)
if err != nil {
log.Errorf("refresh account (%q) cache: %v", accountID, err)
}
}
return nil
}