diff --git a/.github/workflows/golang-test-linux.yml b/.github/workflows/golang-test-linux.yml index da1db5c03..5f7d7b4a3 100644 --- a/.github/workflows/golang-test-linux.yml +++ b/.github/workflows/golang-test-linux.yml @@ -13,7 +13,7 @@ concurrency: jobs: build-cache: runs-on: ubuntu-22.04 - steps: + steps: - name: Checkout code uses: actions/checkout@v4 @@ -183,7 +183,7 @@ jobs: run: git --no-pager diff --exit-code - name: Login to Docker hub - if: matrix.store == 'mysql' + if: matrix.store == 'mysql' && (github.repository == github.head.repo.full_name || !github.head_ref) uses: docker/login-action@v1 with: username: ${{ secrets.DOCKER_USER }} @@ -243,7 +243,7 @@ jobs: run: git --no-pager diff --exit-code - name: Login to Docker hub - if: matrix.store == 'mysql' + if: matrix.store == 'mysql' && (github.repository == github.head.repo.full_name || !github.head_ref) uses: docker/login-action@v1 with: username: ${{ secrets.DOCKER_USER }} @@ -303,7 +303,7 @@ jobs: run: git --no-pager diff --exit-code - name: Login to Docker hub - if: matrix.store == 'mysql' + if: matrix.store == 'mysql' && (github.repository == github.head.repo.full_name || !github.head_ref) uses: docker/login-action@v1 with: username: ${{ secrets.DOCKER_USER }} diff --git a/client/configs/configs.go b/client/configs/configs.go new file mode 100644 index 000000000..8f9c3ba28 --- /dev/null +++ b/client/configs/configs.go @@ -0,0 +1,24 @@ +package configs + +import ( + "os" + "path/filepath" + "runtime" +) + +var StateDir string + +func init() { + StateDir = os.Getenv("NB_STATE_DIR") + if StateDir != "" { + return + } + switch runtime.GOOS { + case "windows": + StateDir = filepath.Join(os.Getenv("PROGRAMDATA"), "Netbird") + case "darwin", "linux": + StateDir = "/var/lib/netbird" + case "freebsd", "openbsd", "netbsd", "dragonfly": + StateDir = "/var/db/netbird" + } +} diff --git a/client/internal/dns/consts.go b/client/internal/dns/consts.go new file mode 100644 index 000000000..b333d0808 --- /dev/null +++ b/client/internal/dns/consts.go @@ -0,0 +1,18 @@ +//go:build !android + +package dns + +import ( + "github.com/netbirdio/netbird/client/configs" + "os" + "path/filepath" +) + +var fileUncleanShutdownResolvConfLocation string + +func init() { + fileUncleanShutdownResolvConfLocation = os.Getenv("NB_UNCLEAN_SHUTDOWN_RESOLV_FILE") + if fileUncleanShutdownResolvConfLocation == "" { + fileUncleanShutdownResolvConfLocation = filepath.Join(configs.StateDir, "resolv.conf") + } +} diff --git a/client/internal/dns/consts_freebsd.go b/client/internal/dns/consts_freebsd.go deleted file mode 100644 index 64c8fe5eb..000000000 --- a/client/internal/dns/consts_freebsd.go +++ /dev/null @@ -1,5 +0,0 @@ -package dns - -const ( - fileUncleanShutdownResolvConfLocation = "/var/db/netbird/resolv.conf" -) diff --git a/client/internal/dns/consts_linux.go b/client/internal/dns/consts_linux.go deleted file mode 100644 index 15614b0c5..000000000 --- a/client/internal/dns/consts_linux.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build !android - -package dns - -const ( - fileUncleanShutdownResolvConfLocation = "/var/lib/netbird/resolv.conf" -) diff --git a/client/internal/engine.go b/client/internal/engine.go index 7b6f269df..b50532b7d 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -763,12 +763,13 @@ func (e *Engine) updateConfig(conf *mgmProto.PeerConfig) error { } } - e.statusRecorder.UpdateLocalPeerState(peer.LocalPeerState{ - IP: e.config.WgAddr, - PubKey: e.config.WgPrivateKey.PublicKey().String(), - KernelInterface: device.WireGuardModuleIsLoaded(), - FQDN: conf.GetFqdn(), - }) + state := e.statusRecorder.GetLocalPeerState() + state.IP = e.config.WgAddr + state.PubKey = e.config.WgPrivateKey.PublicKey().String() + state.KernelInterface = device.WireGuardModuleIsLoaded() + state.FQDN = conf.GetFqdn() + + e.statusRecorder.UpdateLocalPeerState(state) return nil } diff --git a/client/internal/peer/status.go b/client/internal/peer/status.go index dc461257a..0df2a2e81 100644 --- a/client/internal/peer/status.go +++ b/client/internal/peer/status.go @@ -84,6 +84,12 @@ type LocalPeerState struct { Routes map[string]struct{} } +// Clone returns a copy of the LocalPeerState +func (l LocalPeerState) Clone() LocalPeerState { + l.Routes = maps.Clone(l.Routes) + return l +} + // SignalState contains the latest state of a signal connection type SignalState struct { URL string @@ -501,7 +507,7 @@ func (d *Status) GetPeerStateChangeNotifier(peer string) <-chan struct{} { func (d *Status) GetLocalPeerState() LocalPeerState { d.mux.Lock() defer d.mux.Unlock() - return d.localPeer + return d.localPeer.Clone() } // UpdateLocalPeerState updates local peer status diff --git a/client/internal/routemanager/systemops/systemops_linux.go b/client/internal/routemanager/systemops/systemops_linux.go index 9041cbf2d..1da92cc80 100644 --- a/client/internal/routemanager/systemops/systemops_linux.go +++ b/client/internal/routemanager/systemops/systemops_linux.go @@ -266,7 +266,7 @@ func addRoute(prefix netip.Prefix, nexthop Nexthop, tableID int) error { return fmt.Errorf("add gateway and device: %w", err) } - if err := netlink.RouteAdd(route); err != nil && !errors.Is(err, syscall.EEXIST) && !errors.Is(err, syscall.EAFNOSUPPORT) { + if err := netlink.RouteAdd(route); err != nil && !errors.Is(err, syscall.EEXIST) && !isOpErr(err) { return fmt.Errorf("netlink add route: %w", err) } @@ -289,7 +289,7 @@ func addUnreachableRoute(prefix netip.Prefix, tableID int) error { Dst: ipNet, } - if err := netlink.RouteAdd(route); err != nil && !errors.Is(err, syscall.EEXIST) && !errors.Is(err, syscall.EAFNOSUPPORT) { + if err := netlink.RouteAdd(route); err != nil && !errors.Is(err, syscall.EEXIST) && !isOpErr(err) { return fmt.Errorf("netlink add unreachable route: %w", err) } @@ -312,7 +312,7 @@ func removeUnreachableRoute(prefix netip.Prefix, tableID int) error { if err := netlink.RouteDel(route); err != nil && !errors.Is(err, syscall.ESRCH) && !errors.Is(err, syscall.ENOENT) && - !errors.Is(err, syscall.EAFNOSUPPORT) { + !isOpErr(err) { return fmt.Errorf("netlink remove unreachable route: %w", err) } @@ -338,7 +338,7 @@ func removeRoute(prefix netip.Prefix, nexthop Nexthop, tableID int) error { return fmt.Errorf("add gateway and device: %w", err) } - if err := netlink.RouteDel(route); err != nil && !errors.Is(err, syscall.ESRCH) && !errors.Is(err, syscall.EAFNOSUPPORT) { + if err := netlink.RouteDel(route); err != nil && !errors.Is(err, syscall.ESRCH) && !isOpErr(err) { return fmt.Errorf("netlink remove route: %w", err) } @@ -362,7 +362,7 @@ func flushRoutes(tableID, family int) error { routes[i].Dst = &net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)} } } - if err := netlink.RouteDel(&routes[i]); err != nil && !errors.Is(err, syscall.EAFNOSUPPORT) { + if err := netlink.RouteDel(&routes[i]); err != nil && !isOpErr(err) { result = multierror.Append(result, fmt.Errorf("failed to delete route %v from table %d: %w", routes[i], tableID, err)) } } @@ -450,7 +450,7 @@ func addRule(params ruleParams) error { rule.Invert = params.invert rule.SuppressPrefixlen = params.suppressPrefix - if err := netlink.RuleAdd(rule); err != nil && !errors.Is(err, syscall.EEXIST) && !errors.Is(err, syscall.EAFNOSUPPORT) { + if err := netlink.RuleAdd(rule); err != nil && !errors.Is(err, syscall.EEXIST) && !isOpErr(err) { return fmt.Errorf("add routing rule: %w", err) } @@ -467,7 +467,7 @@ func removeRule(params ruleParams) error { rule.Priority = params.priority rule.SuppressPrefixlen = params.suppressPrefix - if err := netlink.RuleDel(rule); err != nil && !errors.Is(err, syscall.ENOENT) && !errors.Is(err, syscall.EAFNOSUPPORT) { + if err := netlink.RuleDel(rule); err != nil && !errors.Is(err, syscall.ENOENT) && !isOpErr(err) { return fmt.Errorf("remove routing rule: %w", err) } @@ -509,3 +509,13 @@ func hasSeparateRouting() ([]netip.Prefix, error) { } return nil, ErrRoutingIsSeparate } + +func isOpErr(err error) bool { + // EAFTNOSUPPORT when ipv6 is disabled via sysctl, EOPNOTSUPP when disabled in boot options or otherwise not supported + if errors.Is(err, syscall.EAFNOSUPPORT) || errors.Is(err, syscall.EOPNOTSUPP) { + log.Debugf("route operation not supported: %v", err) + return true + } + + return false +} diff --git a/client/internal/statemanager/path.go b/client/internal/statemanager/path.go index 6cfd79a12..d232e5f0c 100644 --- a/client/internal/statemanager/path.go +++ b/client/internal/statemanager/path.go @@ -1,23 +1,16 @@ package statemanager import ( + "github.com/netbirdio/netbird/client/configs" "os" "path/filepath" - "runtime" ) // GetDefaultStatePath returns the path to the state file based on the operating system // It returns an empty string if the path cannot be determined. func GetDefaultStatePath() string { - switch runtime.GOOS { - case "windows": - return filepath.Join(os.Getenv("PROGRAMDATA"), "Netbird", "state.json") - case "darwin", "linux": - return "/var/lib/netbird/state.json" - case "freebsd", "openbsd", "netbsd", "dragonfly": - return "/var/db/netbird/state.json" + if path := os.Getenv("NB_DNS_STATE_FILE"); path != "" { + return path } - - return "" - + return filepath.Join(configs.StateDir, "state.json") } diff --git a/go.mod b/go.mod index 9d7cb1fe4..0c6d6be99 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( ) require ( - fyne.io/fyne/v2 v2.5.0 + fyne.io/fyne/v2 v2.5.3 fyne.io/systray v1.11.0 github.com/TheJumpCloud/jcapi-go v3.0.0+incompatible github.com/c-robinson/iplib v1.0.3 @@ -146,7 +146,7 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fredbi/uri v1.1.0 // indirect github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // indirect - github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a // indirect + github.com/fyne-io/glfw-js v0.0.0-20241126112943-313d8a0fe1d0 // indirect github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // indirect github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect @@ -155,8 +155,8 @@ require ( github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect - github.com/go-text/render v0.1.0 // indirect - github.com/go-text/typesetting v0.1.0 // indirect + github.com/go-text/render v0.2.0 // indirect + github.com/go-text/typesetting v0.2.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/btree v1.1.2 // indirect @@ -206,7 +206,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.53.0 // indirect github.com/prometheus/procfs v0.15.0 // indirect - github.com/rymdport/portal v0.2.2 // indirect + github.com/rymdport/portal v0.3.0 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect diff --git a/go.sum b/go.sum index 8383475a4..f8b6c208b 100644 --- a/go.sum +++ b/go.sum @@ -50,8 +50,8 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -fyne.io/fyne/v2 v2.5.0 h1:lEjEIso0Vi4sJXYngIMoXOM6aUjqnPjK7pBpxRxG9aI= -fyne.io/fyne/v2 v2.5.0/go.mod h1:9D4oT3NWeG+MLi/lP7ItZZyujHC/qqMJpoGTAYX5Uqc= +fyne.io/fyne/v2 v2.5.3 h1:k6LjZx6EzRZhClsuzy6vucLZBstdH2USDGHSGWq8ly8= +fyne.io/fyne/v2 v2.5.3/go.mod h1:0GOXKqyvNwk3DLmsFu9v0oYM0ZcD1ysGnlHCerKoAmo= fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg= fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= @@ -204,8 +204,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe h1:A/wiwvQ0CAjPkuJytaD+SsXkPU0asQ+guQEIg1BJGX4= github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe/go.mod h1:d4clgH0/GrRwWjRzJJQXxT/h1TyuNSfF/X64zb/3Ggg= -github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a h1:ybgRdYvAHTn93HW79bLiBiJwVL4jVeyGQRZMgImoeWs= -github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a/go.mod h1:gsGA2dotD4v0SR6PmPCYvS9JuOeMwAtmfvDE7mbYXMY= +github.com/fyne-io/glfw-js v0.0.0-20241126112943-313d8a0fe1d0 h1:/1YRWFv9bAWkoo3SuxpFfzpXH0D/bQnTjNXyF4ih7Os= +github.com/fyne-io/glfw-js v0.0.0-20241126112943-313d8a0fe1d0/go.mod h1:gsGA2dotD4v0SR6PmPCYvS9JuOeMwAtmfvDE7mbYXMY= github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 h1:hnLq+55b7Zh7/2IRzWCpiTcAvjv/P8ERF+N7+xXbZhk= github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2/go.mod h1:eO7W361vmlPOrykIg+Rsh1SZ3tQBaOsfzZhsIOb/Lm0= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -246,12 +246,12 @@ github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqw github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/go-text/render v0.1.0 h1:osrmVDZNHuP1RSu3pNG7Z77Sd2xSbcb/xWytAj9kyVs= -github.com/go-text/render v0.1.0/go.mod h1:jqEuNMenrmj6QRnkdpeaP0oKGFLDNhDkVKwGjsWWYU4= -github.com/go-text/typesetting v0.1.0 h1:vioSaLPYcHwPEPLT7gsjCGDCoYSbljxoHJzMnKwVvHw= -github.com/go-text/typesetting v0.1.0/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZAofLhAzpSzI= -github.com/go-text/typesetting-utils v0.0.0-20240329101916-eee87fb235a3 h1:levTnuLLUmpavLGbJYLJA7fQnKeS7P1eCdAlM+vReXk= -github.com/go-text/typesetting-utils v0.0.0-20240329101916-eee87fb235a3/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= +github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc= +github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU= +github.com/go-text/typesetting v0.2.0 h1:fbzsgbmk04KiWtE+c3ZD4W2nmCRzBqrqQOvYlwAOdho= +github.com/go-text/typesetting v0.2.0/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I= +github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY= +github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -621,8 +621,8 @@ github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/rymdport/portal v0.2.2 h1:P2Q/4k673zxdFAsbD8EESZ7psfuO6/4jNu6EDrDICkM= -github.com/rymdport/portal v0.2.2/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4= +github.com/rymdport/portal v0.3.0 h1:QRHcwKwx3kY5JTQcsVhmhC3TGqGQb9LFghVNUy8AdB8= +github.com/rymdport/portal v0.3.0/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU= github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= diff --git a/infrastructure_files/docker-compose.yml.tmpl.traefik b/infrastructure_files/docker-compose.yml.tmpl.traefik index 7d51c4ffb..71471c3ef 100644 --- a/infrastructure_files/docker-compose.yml.tmpl.traefik +++ b/infrastructure_files/docker-compose.yml.tmpl.traefik @@ -50,6 +50,24 @@ services: - traefik.http.services.netbird-signal.loadbalancer.server.port=80 - traefik.http.services.netbird-signal.loadbalancer.server.scheme=h2c + # Relay + relay: + image: netbirdio/relay:$NETBIRD_RELAY_TAG + restart: unless-stopped + environment: + - NB_LOG_LEVEL=info + - NB_LISTEN_ADDRESS=:$NETBIRD_RELAY_PORT + - NB_EXPOSED_ADDRESS=$NETBIRD_RELAY_DOMAIN:$NETBIRD_RELAY_PORT + # todo: change to a secure secret + - NB_AUTH_SECRET=$NETBIRD_RELAY_AUTH_SECRET + ports: + - $NETBIRD_RELAY_PORT:$NETBIRD_RELAY_PORT + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" + # Management management: image: netbirdio/management:$NETBIRD_MANAGEMENT_TAG diff --git a/management/server/account_test.go b/management/server/account_test.go index da8c418a1..ce87987f3 100644 --- a/management/server/account_test.go +++ b/management/server/account_test.go @@ -16,6 +16,7 @@ import ( "time" "github.com/golang-jwt/jwt" + "github.com/netbirdio/netbird/management/server/util" resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types" @@ -3014,12 +3015,12 @@ func BenchmarkSyncAndMarkPeer(b *testing.B) { minMsPerOpCICD float64 maxMsPerOpCICD float64 }{ - {"Small", 50, 5, 1, 3, 3, 14}, - {"Medium", 500, 100, 7, 13, 10, 80}, - {"Large", 5000, 200, 65, 80, 60, 220}, - {"Small single", 50, 10, 1, 3, 3, 70}, - {"Medium single", 500, 10, 7, 13, 10, 32}, - {"Large 5", 5000, 15, 65, 80, 60, 200}, + {"Small", 50, 5, 1, 3, 3, 19}, + {"Medium", 500, 100, 7, 13, 10, 90}, + {"Large", 5000, 200, 65, 80, 60, 240}, + {"Small single", 50, 10, 1, 3, 3, 80}, + {"Medium single", 500, 10, 7, 13, 10, 37}, + {"Large 5", 5000, 15, 65, 80, 60, 220}, } log.SetOutput(io.Discard) @@ -3081,12 +3082,12 @@ func BenchmarkLoginPeer_ExistingPeer(b *testing.B) { minMsPerOpCICD float64 maxMsPerOpCICD float64 }{ - {"Small", 50, 5, 102, 110, 102, 120}, - {"Medium", 500, 100, 105, 140, 105, 170}, - {"Large", 5000, 200, 160, 200, 160, 300}, - {"Small single", 50, 10, 102, 110, 102, 120}, - {"Medium single", 500, 10, 105, 140, 105, 170}, - {"Large 5", 5000, 15, 160, 200, 160, 270}, + {"Small", 50, 5, 102, 110, 102, 130}, + {"Medium", 500, 100, 105, 140, 105, 190}, + {"Large", 5000, 200, 160, 200, 160, 320}, + {"Small single", 50, 10, 102, 110, 102, 130}, + {"Medium single", 500, 10, 105, 140, 105, 190}, + {"Large 5", 5000, 15, 160, 200, 160, 290}, } log.SetOutput(io.Discard) diff --git a/management/server/http/handlers/networks/resources_handler.go b/management/server/http/handlers/networks/resources_handler.go index a0dc9a10d..6499bd652 100644 --- a/management/server/http/handlers/networks/resources_handler.go +++ b/management/server/http/handlers/networks/resources_handler.go @@ -123,6 +123,7 @@ func (h *resourceHandler) createResource(w http.ResponseWriter, r *http.Request) resource.NetworkID = mux.Vars(r)["networkId"] resource.AccountID = accountID + resource.Enabled = true resource, err = h.resourceManager.CreateResource(r.Context(), userID, resource) if err != nil { util.WriteError(r.Context(), err, w) diff --git a/management/server/http/handlers/networks/routers_handler.go b/management/server/http/handlers/networks/routers_handler.go index 2cf39a132..7ca95d902 100644 --- a/management/server/http/handlers/networks/routers_handler.go +++ b/management/server/http/handlers/networks/routers_handler.go @@ -85,7 +85,7 @@ func (h *routersHandler) createRouter(w http.ResponseWriter, r *http.Request) { router.NetworkID = networkID router.AccountID = accountID - + router.Enabled = true router, err = h.routersManager.CreateRouter(r.Context(), userID, router) if err != nil { util.WriteError(r.Context(), err, w) diff --git a/management/server/peer.go b/management/server/peer.go index 9a61b25ce..280f96a8f 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -323,6 +323,8 @@ func (am *DefaultAccountManager) UpdatePeer(ctx context.Context, accountID, user if peerLabelChanged || requiresPeerUpdates { am.UpdateAccountPeers(ctx, accountID) + } else if sshChanged { + am.UpdateAccountPeer(ctx, account, peer) } return peer, nil @@ -1136,6 +1138,36 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account wg.Wait() } +// UpdateAccountPeer updates a single peer that belongs to an account. +// Should be called when changes need to be synced to a specific peer only. +func (am *DefaultAccountManager) UpdateAccountPeer(ctx context.Context, account *types.Account, peer *nbpeer.Peer) { + if !am.peersUpdateManager.HasChannel(peer.ID) { + log.WithContext(ctx).Tracef("peer %s doesn't have a channel, skipping network map update", peer.ID) + return + } + + approvedPeersMap, err := am.GetValidatedPeers(account) + if err != nil { + log.WithContext(ctx).Errorf("failed to send update to peer %s, failed to validate peers: %v", peer.ID, err) + return + } + + dnsCache := &DNSConfigCache{} + customZone := account.GetPeersCustomZone(ctx, am.dnsDomain) + resourcePolicies := account.GetResourcePoliciesMap() + routers := account.GetResourceRoutersMap() + + postureChecks, err := am.getPeerPostureChecks(account, peer.ID) + if err != nil { + log.WithContext(ctx).Errorf("failed to send update to peer %s, failed to get posture checks: %v", peer.ID, err) + return + } + + remotePeerNetworkMap := account.GetPeerNetworkMap(ctx, peer.ID, customZone, approvedPeersMap, resourcePolicies, routers, am.metrics.AccountManagerMetrics()) + update := toSyncResponse(ctx, nil, peer, nil, nil, remotePeerNetworkMap, am.GetDNSDomain(), postureChecks, dnsCache, account.Settings.RoutingPeerDNSResolutionEnabled) + am.peersUpdateManager.SendUpdate(ctx, peer.ID, &UpdateMessage{Update: update, NetworkMap: remotePeerNetworkMap}) +} + // getNextPeerExpiration returns the minimum duration in which the next peer of the account will expire if it was found. // If there is no peer that expires this function returns false and a duration of 0. // This function only considers peers that haven't been expired yet and that are connected. diff --git a/management/server/peer_test.go b/management/server/peer_test.go index 5f500c226..2f5d0e047 100644 --- a/management/server/peer_test.go +++ b/management/server/peer_test.go @@ -13,13 +13,14 @@ import ( "testing" "time" - "github.com/netbirdio/netbird/management/server/util" "github.com/rs/xid" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" + "github.com/netbirdio/netbird/management/server/util" + resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types" routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types" networkTypes "github.com/netbirdio/netbird/management/server/networks/types" @@ -937,7 +938,7 @@ func BenchmarkUpdateAccountPeers(b *testing.B) { {"Small single", 50, 10, 90, 120, 90, 120}, {"Medium single", 500, 10, 110, 170, 120, 200}, {"Large 5", 5000, 15, 1300, 2100, 4900, 7000}, - {"Extra Large", 2000, 2000, 1300, 2400, 4000, 6400}, + {"Extra Large", 2000, 2000, 1300, 2400, 3800, 6400}, } log.SetOutput(io.Discard) diff --git a/management/server/token_mgr.go b/management/server/token_mgr.go index ef8276b59..fd67fa3e3 100644 --- a/management/server/token_mgr.go +++ b/management/server/token_mgr.go @@ -158,7 +158,7 @@ func (m *TimeBasedAuthSecretsManager) refreshTURNTokens(ctx context.Context, pee log.WithContext(ctx).Debugf("stopping TURN refresh for %s", peerID) return case <-ticker.C: - m.pushNewTURNTokens(ctx, peerID) + m.pushNewTURNAndRelayTokens(ctx, peerID) } } } @@ -178,7 +178,7 @@ func (m *TimeBasedAuthSecretsManager) refreshRelayTokens(ctx context.Context, pe } } -func (m *TimeBasedAuthSecretsManager) pushNewTURNTokens(ctx context.Context, peerID string) { +func (m *TimeBasedAuthSecretsManager) pushNewTURNAndRelayTokens(ctx context.Context, peerID string) { turnToken, err := m.turnHmacToken.GenerateToken(sha1.New) if err != nil { log.Errorf("failed to generate token for peer '%s': %s", peerID, err) @@ -201,10 +201,21 @@ func (m *TimeBasedAuthSecretsManager) pushNewTURNTokens(ctx context.Context, pee update := &proto.SyncResponse{ WiretrusteeConfig: &proto.WiretrusteeConfig{ Turns: turns, - // omit Relay to avoid updates there }, } + // workaround for the case when client is unable to handle turn and relay updates at different time + if m.relayCfg != nil { + token, err := m.GenerateRelayToken() + if err == nil { + update.WiretrusteeConfig.Relay = &proto.RelayConfig{ + Urls: m.relayCfg.Addresses, + TokenPayload: token.Payload, + TokenSignature: token.Signature, + } + } + } + log.WithContext(ctx).Debugf("sending new TURN credentials to peer %s", peerID) m.updateManager.SendUpdate(ctx, peerID, &UpdateMessage{Update: update}) } diff --git a/management/server/token_mgr_test.go b/management/server/token_mgr_test.go index 3e63346c2..2aafb9f68 100644 --- a/management/server/token_mgr_test.go +++ b/management/server/token_mgr_test.go @@ -133,11 +133,14 @@ loop: } } if relay := update.Update.GetWiretrusteeConfig().GetRelay(); relay != nil { - relayUpdates++ - if relayUpdates == 1 { - firstRelayUpdate = relay - } else { - secondRelayUpdate = relay + // avoid updating on turn updates since they also send relay credentials + if update.Update.GetWiretrusteeConfig().GetTurns() == nil { + relayUpdates++ + if relayUpdates == 1 { + firstRelayUpdate = relay + } else { + secondRelayUpdate = relay + } } } }