From 63f6514be50f30ae849df02f99fd8939dfc596e0 Mon Sep 17 00:00:00 2001 From: Yury Gargay Date: Tue, 21 Nov 2023 17:38:33 +0100 Subject: [PATCH 01/13] Add tenv linter (#1322) Tenv is analyzer that detects using `os.Setenv` instead of `t.Setenv` since Go 1.17. --- .golangci.yaml | 7 +++++++ client/internal/dns/server_test.go | 8 ++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index b637c7ed1bd..d0926fffc36 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -19,6 +19,12 @@ linters-settings: enable: - nilness + tenv: + # The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures. + # Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked. + # Default: false + all: true + linters: disable-all: true enable: @@ -28,6 +34,7 @@ linters: - govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string - ineffassign # detects when assignments to existing variables are not used - staticcheck # is a go vet on steroids, applying a ton of static analysis checks + - tenv # Tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17. - typecheck # like the front-end of a Go compiler, parses and type-checks Go code - unused # checks for unused constants, variables, functions and types ## disable by default but the have interesting results so lets add them diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go index 62b4e186718..875a1a46f95 100644 --- a/client/internal/dns/server_test.go +++ b/client/internal/dns/server_test.go @@ -322,9 +322,9 @@ func TestUpdateDNSServer(t *testing.T) { func TestDNSFakeResolverHandleUpdates(t *testing.T) { ov := os.Getenv("NB_WG_KERNEL_DISABLED") - defer os.Setenv("NB_WG_KERNEL_DISABLED", ov) + defer t.Setenv("NB_WG_KERNEL_DISABLED", ov) - _ = os.Setenv("NB_WG_KERNEL_DISABLED", "true") + t.Setenv("NB_WG_KERNEL_DISABLED", "true") newNet, err := stdnet.NewNet(nil) if err != nil { t.Errorf("create stdnet: %v", err) @@ -773,9 +773,9 @@ func TestDNSPermanent_matchOnly(t *testing.T) { func createWgInterfaceWithBind(t *testing.T) (*iface.WGIface, error) { t.Helper() ov := os.Getenv("NB_WG_KERNEL_DISABLED") - defer os.Setenv("NB_WG_KERNEL_DISABLED", ov) + defer t.Setenv("NB_WG_KERNEL_DISABLED", ov) - _ = os.Setenv("NB_WG_KERNEL_DISABLED", "true") + t.Setenv("NB_WG_KERNEL_DISABLED", "true") newNet, err := stdnet.NewNet(nil) if err != nil { t.Fatalf("create stdnet: %v", err) From 96cdcf8e49c15c159bd874c1ae5ebc8de2bf84f8 Mon Sep 17 00:00:00 2001 From: Bethuel Mmbaga Date: Tue, 21 Nov 2023 20:02:16 +0300 Subject: [PATCH 02/13] Add client UI shortcut links for standard users in Windows (#1323) * Change SetShellVarContext scope to create program links for standard users * Include guidelines for building the Windows Netbird installer during development * Add Wireguard driver requirement to Windows build instructions --- CONTRIBUTING.md | 36 ++++++++++++++++++++++++++++++++++++ client/installer.nsis | 9 +++------ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 80be72fa92b..29a12402e2d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -183,6 +183,42 @@ To start NetBird the management service: ./management management --log-level debug --log-file console --config ./management.json ``` +#### Windows Netbird Installer +Create dist directory +```shell +mkdir -p dist/netbird_windows_amd64 +``` + +UI client +```shell +CC=x86_64-w64-mingw32-gcc CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -o netbird-ui.exe -ldflags "-s -w -H windowsgui" ./client/ui +mv netbird-ui.exe ./dist/netbird_windows_amd64/ +``` + +Client +```shell +CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o netbird.exe ./client/ +mv netbird.exe ./dist/netbird_windows_amd64/ +``` +> 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 `./dist/netbird_windows_amd64/`. + +NSIS compiler +- [Windows-nsis]( https://nsis.sourceforge.io/Download) +- [MacOS-makensis](https://formulae.brew.sh/formula/makensis#default) +- [Linux-makensis](https://manpages.ubuntu.com/manpages/trusty/man1/makensis.1.html) + +NSIS Plugins. Download and move them to the NSIS plugins folder. +- [EnVar](https://nsis.sourceforge.io/mediawiki/images/7/7f/EnVar_plugin.zip) +- [ShellExecAsUser](https://nsis.sourceforge.io/mediawiki/images/6/68/ShellExecAsUser_amd64-Unicode.7z) + +Windows Installer +```shell +export APPVER=0.0.0.1 +makensis -V4 client/installer.nsis +``` + +The installer `netbird-installer.exe` will be created in root directory. + ### Test suite The tests can be started via: diff --git a/client/installer.nsis b/client/installer.nsis index e2e3ac11817..fbffa326d88 100644 --- a/client/installer.nsis +++ b/client/installer.nsis @@ -166,10 +166,9 @@ WriteRegStr ${REG_ROOT} "${UI_REG_APP_PATH}" "" "$INSTDIR\${UI_APP_EXE}" EnVar::SetHKLM EnVar::AddValueEx "path" "$INSTDIR" -SetShellVarContext current +SetShellVarContext all CreateShortCut "$SMPROGRAMS\${APP_NAME}.lnk" "$INSTDIR\${UI_APP_EXE}" CreateShortCut "$DESKTOP\${APP_NAME}.lnk" "$INSTDIR\${UI_APP_EXE}" -SetShellVarContext all SectionEnd Section -Post @@ -196,10 +195,9 @@ Delete "$INSTDIR\${MAIN_APP_EXE}" Delete "$INSTDIR\wintun.dll" RmDir /r "$INSTDIR" -SetShellVarContext current +SetShellVarContext all Delete "$DESKTOP\${APP_NAME}.lnk" Delete "$SMPROGRAMS\${APP_NAME}.lnk" -SetShellVarContext all DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}" DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}" @@ -209,8 +207,7 @@ SectionEnd Function LaunchLink -SetShellVarContext current +SetShellVarContext all SetOutPath $INSTDIR ShellExecAsUser::ShellExecAsUser "" "$DESKTOP\${APP_NAME}.lnk" -SetShellVarContext all FunctionEnd From ab895be4a39bfdde1453d14fcb1022ff20465006 Mon Sep 17 00:00:00 2001 From: Pascal Fischer Date: Wed, 22 Nov 2023 16:23:19 +0100 Subject: [PATCH 03/13] fix get os info for windows to report correct versions --- client/system/info_windows.go | 41 ++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/client/system/info_windows.go b/client/system/info_windows.go index c8c3276c989..ede689fa1b9 100644 --- a/client/system/info_windows.go +++ b/client/system/info_windows.go @@ -5,17 +5,24 @@ import ( "fmt" "os" "runtime" + "strings" log "github.com/sirupsen/logrus" + "github.com/yusufpapurcu/wmi" "golang.org/x/sys/windows/registry" "github.com/netbirdio/netbird/version" ) +type Win32_OperatingSystem struct { + Caption string +} + // GetInfo retrieves and parses the system information func GetInfo(ctx context.Context) *Info { - ver := getOSVersion() - gio := &Info{Kernel: "windows", OSVersion: ver, Core: ver, Platform: "unknown", OS: "windows", GoOS: runtime.GOOS, CPUs: runtime.NumCPU()} + osName, osVersion := getOSNameAndVersion() + buildVersion := getBuildVersion() + gio := &Info{Kernel: "windows", OSVersion: buildVersion, Core: osVersion, Platform: "unknown", OS: osName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU()} systemHostname, _ := os.Hostname() gio.Hostname = extractDeviceName(ctx, systemHostname) gio.WiretrusteeVersion = version.NetbirdVersion() @@ -24,7 +31,35 @@ func GetInfo(ctx context.Context) *Info { return gio } -func getOSVersion() string { +func getOSNameAndVersion() (string, string) { + var dst []Win32_OperatingSystem + query := wmi.CreateQuery(&dst, "") + err := wmi.Query(query, &dst) + if err != nil { + log.Fatal(err) + } + + if len(dst) == 0 { + return "Windows", getBuildVersion() + } + + split := strings.Split(dst[0].Caption, " ") + + if len(split) < 3 { + return "Windows", getBuildVersion() + } + + name := split[1] + version := split[2] + if split[2] == "Server" { + name = fmt.Sprintf("%s %s", split[1], split[2]) + version = split[3] + } + + return name, version +} + +func getBuildVersion() string { k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) if err != nil { log.Error(err) From 5a3ee4f9c49c10b79d5f5de300108afde3b4b614 Mon Sep 17 00:00:00 2001 From: hg <36822348+hg@users.noreply.github.com> Date: Fri, 24 Nov 2023 01:15:07 +0600 Subject: [PATCH 04/13] Add systemd .service files (#1316) (#1318) Add systemd .service files --- release_files/systemd/env | 3 ++ .../systemd/netbird-management.service | 41 +++++++++++++++++++ release_files/systemd/netbird-signal.service | 41 +++++++++++++++++++ release_files/systemd/netbird@.service | 41 +++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 release_files/systemd/env create mode 100644 release_files/systemd/netbird-management.service create mode 100644 release_files/systemd/netbird-signal.service create mode 100644 release_files/systemd/netbird@.service diff --git a/release_files/systemd/env b/release_files/systemd/env new file mode 100644 index 00000000000..9e7f2e138b6 --- /dev/null +++ b/release_files/systemd/env @@ -0,0 +1,3 @@ +# Extra flags you might want to pass to the daemon +FLAGS="" + diff --git a/release_files/systemd/netbird-management.service b/release_files/systemd/netbird-management.service new file mode 100644 index 00000000000..7fc0aa9ed41 --- /dev/null +++ b/release_files/systemd/netbird-management.service @@ -0,0 +1,41 @@ +[Unit] +Description=Netbird Management +Documentation=https://netbird.io/docs +After=network-online.target syslog.target +Wants=network-online.target + +[Service] +Type=simple +EnvironmentFile=-/etc/default/netbird-management +ExecStart=/usr/bin/netbird-mgmt management $FLAGS +Restart=on-failure +RestartSec=5 +TimeoutStopSec=10 +CacheDirectory=netbird +ConfigurationDirectory=netbird +LogDirectory=netbird +RuntimeDirectory=netbird +StateDirectory=netbird + +# sandboxing +LockPersonality=yes +MemoryDenyWriteExecute=yes +NoNewPrivileges=yes +PrivateMounts=yes +PrivateTmp=yes +ProtectClock=yes +ProtectControlGroups=yes +ProtectHome=yes +ProtectHostname=yes +ProtectKernelLogs=yes +ProtectKernelModules=yes +ProtectKernelTunables=yes +ProtectSystem=yes +RemoveIPC=yes +RestrictNamespaces=yes +RestrictRealtime=yes +RestrictSUIDSGID=yes + +[Install] +WantedBy=multi-user.target + diff --git a/release_files/systemd/netbird-signal.service b/release_files/systemd/netbird-signal.service new file mode 100644 index 00000000000..c7e775f4960 --- /dev/null +++ b/release_files/systemd/netbird-signal.service @@ -0,0 +1,41 @@ +[Unit] +Description=Netbird Signal +Documentation=https://netbird.io/docs +After=network-online.target syslog.target +Wants=network-online.target + +[Service] +Type=simple +EnvironmentFile=-/etc/default/netbird-signal +ExecStart=/usr/bin/netbird-signal run $FLAGS +Restart=on-failure +RestartSec=5 +TimeoutStopSec=10 +CacheDirectory=netbird +ConfigurationDirectory=netbird +LogDirectory=netbird +RuntimeDirectory=netbird +StateDirectory=netbird + +# sandboxing +LockPersonality=yes +MemoryDenyWriteExecute=yes +NoNewPrivileges=yes +PrivateMounts=yes +PrivateTmp=yes +ProtectClock=yes +ProtectControlGroups=yes +ProtectHome=yes +ProtectHostname=yes +ProtectKernelLogs=yes +ProtectKernelModules=yes +ProtectKernelTunables=yes +ProtectSystem=yes +RemoveIPC=yes +RestrictNamespaces=yes +RestrictRealtime=yes +RestrictSUIDSGID=yes + +[Install] +WantedBy=multi-user.target + diff --git a/release_files/systemd/netbird@.service b/release_files/systemd/netbird@.service new file mode 100644 index 00000000000..39e3b6b2355 --- /dev/null +++ b/release_files/systemd/netbird@.service @@ -0,0 +1,41 @@ +[Unit] +Description=Netbird Client (%i) +Documentation=https://netbird.io/docs +After=network-online.target syslog.target NetworkManager.service +Wants=network-online.target + +[Service] +Type=simple +EnvironmentFile=-/etc/default/netbird +ExecStart=/usr/bin/netbird service run --log-file /var/log/netbird/client-%i.log --config /etc/netbird/%i.json --daemon-addr unix:///var/run/netbird/%i.sock $FLAGS +Restart=on-failure +RestartSec=5 +TimeoutStopSec=10 +CacheDirectory=netbird +ConfigurationDirectory=netbird +LogDirectory=netbird +RuntimeDirectory=netbird +StateDirectory=netbird + +# sandboxing +LockPersonality=yes +MemoryDenyWriteExecute=yes +NoNewPrivileges=yes +PrivateMounts=yes +PrivateTmp=yes +ProtectClock=yes +ProtectControlGroups=yes +ProtectHome=yes +ProtectHostname=yes +ProtectKernelLogs=yes +ProtectKernelModules=no # needed to load wg module for kernel-mode WireGuard +ProtectKernelTunables=no +ProtectSystem=yes +RemoveIPC=yes +RestrictNamespaces=yes +RestrictRealtime=yes +RestrictSUIDSGID=yes + +[Install] +WantedBy=multi-user.target + From fdd23d4644168c5d014732b2d1466f4a6b023dc3 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Fri, 24 Nov 2023 11:31:22 +0100 Subject: [PATCH 05/13] Remove the gateway check for routes (#1317) Most operating systems add a /32 route for the default gateway address to its routing table This will allow routes to be configured into the system even when the incoming range contains the default gateway. In case a range is a sub-range of an existing route and this range happens to contain the default gateway it attempts to create a default gateway route to prevent loop issues --- client/internal/routemanager/client.go | 2 + client/internal/routemanager/manager.go | 2 +- client/internal/routemanager/systemops_bsd.go | 55 ++++++----- .../internal/routemanager/systemops_linux.go | 34 +++++-- .../routemanager/systemops_nonandroid.go | 81 +++++++++++++--- .../routemanager/systemops_nonandroid_test.go | 96 ++++++++++++++++--- .../routemanager/systemops_nonlinux.go | 8 +- .../routemanager/systemops_windows.go | 25 +++-- 8 files changed, 231 insertions(+), 72 deletions(-) diff --git a/client/internal/routemanager/client.go b/client/internal/routemanager/client.go index fda7b012f34..ee98d503de5 100644 --- a/client/internal/routemanager/client.go +++ b/client/internal/routemanager/client.go @@ -12,6 +12,8 @@ import ( "github.com/netbirdio/netbird/route" ) +const minRangeBits = 7 + type routerPeerStatus struct { connected bool relayed bool diff --git a/client/internal/routemanager/manager.go b/client/internal/routemanager/manager.go index 1f812983c4e..479ac873f48 100644 --- a/client/internal/routemanager/manager.go +++ b/client/internal/routemanager/manager.go @@ -155,7 +155,7 @@ func (m *DefaultManager) classifiesRoutes(newRoutes []*route.Route) (map[string] if !ownNetworkIDs[networkID] { // if prefix is too small, lets assume is a possible default route which is not yet supported // we skip this route management - if newRoute.Network.Bits() < 7 { + if newRoute.Network.Bits() < minRangeBits { log.Errorf("this agent version: %s, doesn't support default routes, received %s, skipping this route", version.NetbirdVersion(), newRoute.Network) continue diff --git a/client/internal/routemanager/systemops_bsd.go b/client/internal/routemanager/systemops_bsd.go index e777ec8ecbd..b2da8075cfa 100644 --- a/client/internal/routemanager/systemops_bsd.go +++ b/client/internal/routemanager/systemops_bsd.go @@ -27,24 +27,24 @@ const ( RTF_MULTICAST = 0x800000 ) -func existsInRouteTable(prefix netip.Prefix) (bool, error) { +func getRoutesFromTable() ([]netip.Prefix, error) { tab, err := route.FetchRIB(syscall.AF_UNSPEC, route.RIBTypeRoute, 0) if err != nil { - return false, err + return nil, err } msgs, err := route.ParseRIB(route.RIBTypeRoute, tab) if err != nil { - return false, err + return nil, err } - + var prefixList []netip.Prefix for _, msg := range msgs { m := msg.(*route.RouteMessage) if m.Version < 3 || m.Version > 5 { - return false, fmt.Errorf("unexpected RIB message version: %d", m.Version) + return nil, fmt.Errorf("unexpected RIB message version: %d", m.Version) } if m.Type != 4 /* RTM_GET */ { - return true, fmt.Errorf("unexpected RIB message type: %d", m.Type) + return nil, fmt.Errorf("unexpected RIB message type: %d", m.Type) } if m.Flags&RTF_UP == 0 || @@ -52,31 +52,42 @@ func existsInRouteTable(prefix netip.Prefix) (bool, error) { continue } - dst, err := toIPAddr(m.Addrs[0]) - if err != nil { - return true, fmt.Errorf("unexpected RIB destination: %v", err) + addr, ok := toNetIPAddr(m.Addrs[0]) + if !ok { + continue + } + + mask, ok := toNetIPMASK(m.Addrs[2]) + if !ok { + continue } + cidr, _ := mask.Size() - mask, _ := toIPAddr(m.Addrs[2]) - cidr, _ := net.IPMask(mask.To4()).Size() - if dst.String() == prefix.Addr().String() && cidr == prefix.Bits() { - return true, nil + routePrefix := netip.PrefixFrom(addr, cidr) + if routePrefix.IsValid() { + prefixList = append(prefixList, routePrefix) } } - - return false, nil + return prefixList, nil } -func toIPAddr(a route.Addr) (net.IP, error) { +func toNetIPAddr(a route.Addr) (netip.Addr, bool) { switch t := a.(type) { case *route.Inet4Addr: ip := net.IPv4(t.IP[0], t.IP[1], t.IP[2], t.IP[3]) - return ip, nil - case *route.Inet6Addr: - ip := make(net.IP, net.IPv6len) - copy(ip, t.IP[:]) - return ip, nil + addr := netip.MustParseAddr(ip.String()) + return addr, true + default: + return netip.Addr{}, false + } +} + +func toNetIPMASK(a route.Addr) (net.IPMask, bool) { + switch t := a.(type) { + case *route.Inet4Addr: + mask := net.IPv4Mask(t.IP[0], t.IP[1], t.IP[2], t.IP[3]) + return mask, true default: - return net.IP{}, fmt.Errorf("unknown family: %v", t) + return nil, false } } diff --git a/client/internal/routemanager/systemops_linux.go b/client/internal/routemanager/systemops_linux.go index fb2938d55e6..b5b4f569647 100644 --- a/client/internal/routemanager/systemops_linux.go +++ b/client/internal/routemanager/systemops_linux.go @@ -60,15 +60,26 @@ func addToRouteTable(prefix netip.Prefix, addr string) error { return nil } -func removeFromRouteTable(prefix netip.Prefix) error { +func removeFromRouteTable(prefix netip.Prefix, addr string) error { _, ipNet, err := net.ParseCIDR(prefix.String()) if err != nil { return err } + addrMask := "/32" + if prefix.Addr().Unmap().Is6() { + addrMask = "/128" + } + + ip, _, err := net.ParseCIDR(addr + addrMask) + if err != nil { + return err + } + route := &netlink.Route{ Scope: netlink.SCOPE_UNIVERSE, Dst: ipNet, + Gw: ip, } err = netlink.RouteDel(route) @@ -79,15 +90,16 @@ func removeFromRouteTable(prefix netip.Prefix) error { return nil } -func existsInRouteTable(prefix netip.Prefix) (bool, error) { +func getRoutesFromTable() ([]netip.Prefix, error) { tab, err := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_UNSPEC) if err != nil { - return true, err + return nil, err } msgs, err := syscall.ParseNetlinkMessage(tab) if err != nil { - return true, err + return nil, err } + var prefixList []netip.Prefix loop: for _, m := range msgs { switch m.Header.Type { @@ -97,7 +109,7 @@ loop: rt := (*routeInfoInMemory)(unsafe.Pointer(&m.Data[0])) attrs, err := syscall.ParseNetlinkRouteAttr(&m) if err != nil { - return true, err + return nil, err } if rt.Family != syscall.AF_INET { continue loop @@ -105,17 +117,21 @@ loop: for _, attr := range attrs { if attr.Attr.Type == syscall.RTA_DST { - ip := net.IP(attr.Value) + addr, ok := netip.AddrFromSlice(attr.Value) + if !ok { + continue + } mask := net.CIDRMask(int(rt.DstLen), len(attr.Value)*8) cidr, _ := mask.Size() - if ip.String() == prefix.Addr().String() && cidr == prefix.Bits() { - return true, nil + routePrefix := netip.PrefixFrom(addr, cidr) + if routePrefix.IsValid() && routePrefix.Addr().Is4() { + prefixList = append(prefixList, routePrefix) } } } } } - return false, nil + return prefixList, nil } func enableIPForwarding() error { diff --git a/client/internal/routemanager/systemops_nonandroid.go b/client/internal/routemanager/systemops_nonandroid.go index 3ddf72686de..b229a580f45 100644 --- a/client/internal/routemanager/systemops_nonandroid.go +++ b/client/internal/routemanager/systemops_nonandroid.go @@ -14,40 +14,91 @@ import ( var errRouteNotFound = fmt.Errorf("route not found") func addToRouteTableIfNoExists(prefix netip.Prefix, addr string) error { + ok, err := existsInRouteTable(prefix) + if err != nil { + return err + } + if ok { + log.Warnf("skipping adding a new route for network %s because it already exists", prefix) + return nil + } + + ok, err = isSubRange(prefix) + if err != nil { + return err + } + + if ok { + err := addRouteForCurrentDefaultGateway(prefix) + if err != nil { + log.Warnf("unable to add route for current default gateway route. Will proceed without it. error: %s", err) + } + } + + return addToRouteTable(prefix, addr) +} + +func addRouteForCurrentDefaultGateway(prefix netip.Prefix) error { defaultGateway, err := getExistingRIBRouteGateway(netip.MustParsePrefix("0.0.0.0/0")) if err != nil && err != errRouteNotFound { return err } - gatewayIP := netip.MustParseAddr(defaultGateway.String()) - if prefix.Contains(gatewayIP) { - log.Warnf("skipping adding a new route for network %s because it overlaps with the default gateway: %s", prefix, gatewayIP) + addr := netip.MustParseAddr(defaultGateway.String()) + + if !prefix.Contains(addr) { + log.Debugf("skipping adding a new route for gateway %s because it is not in the network %s", addr, prefix) return nil } - ok, err := existsInRouteTable(prefix) + gatewayPrefix := netip.PrefixFrom(addr, 32) + + ok, err := existsInRouteTable(gatewayPrefix) if err != nil { - return err + return fmt.Errorf("unable to check if there is an existing route for gateway %s. error: %s", gatewayPrefix, err) } + if ok { - log.Warnf("skipping adding a new route for network %s because it already exists", prefix) + log.Debugf("skipping adding a new route for gateway %s because it already exists", gatewayPrefix) return nil } - return addToRouteTable(prefix, addr) + gatewayHop, err := getExistingRIBRouteGateway(gatewayPrefix) + if err != nil && err != errRouteNotFound { + return fmt.Errorf("unable to get the next hop for the default gateway address. error: %s", err) + } + log.Debugf("adding a new route for gateway %s with next hop %s", gatewayPrefix, gatewayHop) + return addToRouteTable(gatewayPrefix, gatewayHop.String()) } -func removeFromRouteTableIfNonSystem(prefix netip.Prefix, addr string) error { - addrIP := net.ParseIP(addr) - prefixGateway, err := getExistingRIBRouteGateway(prefix) +func existsInRouteTable(prefix netip.Prefix) (bool, error) { + routes, err := getRoutesFromTable() if err != nil { - return err + return false, err } - if prefixGateway != nil && !prefixGateway.Equal(addrIP) { - log.Warnf("route for network %s is pointing to a different gateway: %s, should be pointing to: %s, not removing", prefix, prefixGateway, addrIP) - return nil + for _, tableRoute := range routes { + if tableRoute == prefix { + return true, nil + } + } + return false, nil +} + +func isSubRange(prefix netip.Prefix) (bool, error) { + routes, err := getRoutesFromTable() + if err != nil { + return false, err } - return removeFromRouteTable(prefix) + for _, tableRoute := range routes { + if tableRoute.Bits() > minRangeBits && tableRoute.Contains(prefix.Addr()) && tableRoute.Bits() < prefix.Bits() { + return true, nil + } + } + return false, nil +} + +func removeFromRouteTableIfNonSystem(prefix netip.Prefix, addr string) error { + return removeFromRouteTable(prefix, addr) } func getExistingRIBRouteGateway(prefix netip.Prefix) (net.IP, error) { diff --git a/client/internal/routemanager/systemops_nonandroid_test.go b/client/internal/routemanager/systemops_nonandroid_test.go index bb31834d1e6..3646dc3da08 100644 --- a/client/internal/routemanager/systemops_nonandroid_test.go +++ b/client/internal/routemanager/systemops_nonandroid_test.go @@ -24,13 +24,13 @@ func TestAddRemoveRoutes(t *testing.T) { shouldBeRemoved bool }{ { - name: "Should Add And Remove Route", + name: "Should Add And Remove Route 100.66.120.0/24", prefix: netip.MustParsePrefix("100.66.120.0/24"), shouldRouteToWireguard: true, shouldBeRemoved: true, }, { - name: "Should Not Add Or Remove Route", + name: "Should Not Add Or Remove Route 127.0.0.1/32", prefix: netip.MustParsePrefix("127.0.0.1/32"), shouldRouteToWireguard: false, shouldBeRemoved: false, @@ -51,29 +51,32 @@ func TestAddRemoveRoutes(t *testing.T) { require.NoError(t, err, "should create testing wireguard interface") err = addToRouteTableIfNoExists(testCase.prefix, wgInterface.Address().IP.String()) - require.NoError(t, err, "should not return err") + require.NoError(t, err, "addToRouteTableIfNoExists should not return err") prefixGateway, err := getExistingRIBRouteGateway(testCase.prefix) - require.NoError(t, err, "should not return err") + require.NoError(t, err, "getExistingRIBRouteGateway should not return err") if testCase.shouldRouteToWireguard { require.Equal(t, wgInterface.Address().IP.String(), prefixGateway.String(), "route should point to wireguard interface IP") } else { require.NotEqual(t, wgInterface.Address().IP.String(), prefixGateway.String(), "route should point to a different interface") } + exists, err := existsInRouteTable(testCase.prefix) + require.NoError(t, err, "existsInRouteTable should not return err") + if exists && testCase.shouldRouteToWireguard { + err = removeFromRouteTableIfNonSystem(testCase.prefix, wgInterface.Address().IP.String()) + require.NoError(t, err, "removeFromRouteTableIfNonSystem should not return err") - err = removeFromRouteTableIfNonSystem(testCase.prefix, wgInterface.Address().IP.String()) - require.NoError(t, err, "should not return err") - - prefixGateway, err = getExistingRIBRouteGateway(testCase.prefix) - require.NoError(t, err, "should not return err") + prefixGateway, err = getExistingRIBRouteGateway(testCase.prefix) + require.NoError(t, err, "getExistingRIBRouteGateway should not return err") - internetGateway, err := getExistingRIBRouteGateway(netip.MustParsePrefix("0.0.0.0/0")) - require.NoError(t, err) + internetGateway, err := getExistingRIBRouteGateway(netip.MustParsePrefix("0.0.0.0/0")) + require.NoError(t, err) - if testCase.shouldBeRemoved { - require.Equal(t, internetGateway, prefixGateway, "route should be pointing to default internet gateway") - } else { - require.NotEqual(t, internetGateway, prefixGateway, "route should be pointing to a different gateway than the internet gateway") + if testCase.shouldBeRemoved { + require.Equal(t, internetGateway, prefixGateway, "route should be pointing to default internet gateway") + } else { + require.NotEqual(t, internetGateway, prefixGateway, "route should be pointing to a different gateway than the internet gateway") + } } }) } @@ -215,3 +218,66 @@ func TestAddExistAndRemoveRouteNonAndroid(t *testing.T) { }) } } + +func TestExistsInRouteTable(t *testing.T) { + addresses, err := net.InterfaceAddrs() + if err != nil { + t.Fatal("shouldn't return error when fetching interface addresses: ", err) + } + + var addressPrefixes []netip.Prefix + for _, address := range addresses { + p := netip.MustParsePrefix(address.String()) + if p.Addr().Is4() { + addressPrefixes = append(addressPrefixes, p.Masked()) + } + } + + for _, prefix := range addressPrefixes { + exists, err := existsInRouteTable(prefix) + if err != nil { + t.Fatal("shouldn't return error when checking if address exists in route table: ", err) + } + if !exists { + t.Fatalf("address %s should exist in route table", prefix) + } + } +} + +func TestIsSubRange(t *testing.T) { + addresses, err := net.InterfaceAddrs() + if err != nil { + t.Fatal("shouldn't return error when fetching interface addresses: ", err) + } + + var subRangeAddressPrefixes []netip.Prefix + var nonSubRangeAddressPrefixes []netip.Prefix + for _, address := range addresses { + p := netip.MustParsePrefix(address.String()) + if !p.Addr().IsLoopback() && p.Addr().Is4() && p.Bits() < 32 { + p2 := netip.PrefixFrom(p.Masked().Addr(), p.Bits()+1) + subRangeAddressPrefixes = append(subRangeAddressPrefixes, p2) + nonSubRangeAddressPrefixes = append(nonSubRangeAddressPrefixes, p.Masked()) + } + } + + for _, prefix := range subRangeAddressPrefixes { + isSubRangePrefix, err := isSubRange(prefix) + if err != nil { + t.Fatal("shouldn't return error when checking if address is sub-range: ", err) + } + if !isSubRangePrefix { + t.Fatalf("address %s should be sub-range of an existing route in the table", prefix) + } + } + + for _, prefix := range nonSubRangeAddressPrefixes { + isSubRangePrefix, err := isSubRange(prefix) + if err != nil { + t.Fatal("shouldn't return error when checking if address is sub-range: ", err) + } + if isSubRangePrefix { + t.Fatalf("address %s should not be sub-range of an existing route in the table", prefix) + } + } +} diff --git a/client/internal/routemanager/systemops_nonlinux.go b/client/internal/routemanager/systemops_nonlinux.go index 537042099f2..47bd60eb02b 100644 --- a/client/internal/routemanager/systemops_nonlinux.go +++ b/client/internal/routemanager/systemops_nonlinux.go @@ -21,8 +21,12 @@ func addToRouteTable(prefix netip.Prefix, addr string) error { return nil } -func removeFromRouteTable(prefix netip.Prefix) error { - cmd := exec.Command("route", "delete", prefix.String()) +func removeFromRouteTable(prefix netip.Prefix, addr string) error { + args := []string{"delete", prefix.String()} + if runtime.GOOS == "darwin" { + args = append(args, addr) + } + cmd := exec.Command("route", args...) out, err := cmd.Output() if err != nil { return err diff --git a/client/internal/routemanager/systemops_windows.go b/client/internal/routemanager/systemops_windows.go index 2233748bfd4..309c184b9ca 100644 --- a/client/internal/routemanager/systemops_windows.go +++ b/client/internal/routemanager/systemops_windows.go @@ -15,23 +15,32 @@ type Win32_IP4RouteTable struct { Mask string } -func existsInRouteTable(prefix netip.Prefix) (bool, error) { +func getRoutesFromTable() ([]netip.Prefix, error) { var routes []Win32_IP4RouteTable query := "SELECT Destination, Mask FROM Win32_IP4RouteTable" err := wmi.Query(query, &routes) if err != nil { - return true, err + return nil, err } + var prefixList []netip.Prefix for _, route := range routes { - ip := net.ParseIP(route.Mask) - ip = ip.To4() - mask := net.IPv4Mask(ip[0], ip[1], ip[2], ip[3]) + addr, err := netip.ParseAddr(route.Destination) + if err != nil { + continue + } + maskSlice := net.ParseIP(route.Mask).To4() + if maskSlice == nil { + continue + } + mask := net.IPv4Mask(maskSlice[0], maskSlice[1], maskSlice[2], maskSlice[3]) cidr, _ := mask.Size() - if route.Destination == prefix.Addr().String() && cidr == prefix.Bits() { - return true, nil + + routePrefix := netip.PrefixFrom(addr, cidr) + if routePrefix.IsValid() && routePrefix.Addr().Is4() { + prefixList = append(prefixList, routePrefix) } } - return false, nil + return prefixList, nil } From cf9e447bf0b3b2579f4d34459dc8e4a4759dd413 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Fri, 24 Nov 2023 14:27:40 +0100 Subject: [PATCH 06/13] Update signing pipelines to version 0.0.10 (#1329) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5833638c509..c3d43a65b1a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ on: - 'client/ui/**' env: - SIGN_PIPE_VER: "v0.0.9" + SIGN_PIPE_VER: "v0.0.10" GORELEASER_VER: "v1.14.1" concurrency: From 0ca06b566ae168b42562d6b44073cfbda69ba9c1 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Fri, 24 Nov 2023 17:49:39 +0100 Subject: [PATCH 07/13] Add Windows version to correct system info field (#1330) --- client/system/info_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/system/info_windows.go b/client/system/info_windows.go index ede689fa1b9..69b4ad0083c 100644 --- a/client/system/info_windows.go +++ b/client/system/info_windows.go @@ -22,7 +22,7 @@ type Win32_OperatingSystem struct { func GetInfo(ctx context.Context) *Info { osName, osVersion := getOSNameAndVersion() buildVersion := getBuildVersion() - gio := &Info{Kernel: "windows", OSVersion: buildVersion, Core: osVersion, Platform: "unknown", OS: osName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU()} + gio := &Info{Kernel: "windows", OSVersion: osVersion, Core: buildVersion, Platform: "unknown", OS: osName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU()} systemHostname, _ := os.Hostname() gio.Hostname = extractDeviceName(ctx, systemHostname) gio.WiretrusteeVersion = version.NetbirdVersion() From 63d211c698d319a7fe673399fcaa770b957b67e8 Mon Sep 17 00:00:00 2001 From: Yury Gargay Date: Mon, 27 Nov 2023 13:01:00 +0100 Subject: [PATCH 08/13] Prepare regexps on compile time (#1327) --- client/internal/dns/network_manager_linux.go | 9 ++------- dns/dns.go | 4 ++-- management/server/account.go | 5 +++-- management/server/http/middleware/access_control.go | 10 +++------- management/server/metrics/selfhosted.go | 8 +------- management/server/nameserver.go | 3 ++- version/version.go | 11 +++++++++++ 7 files changed, 24 insertions(+), 26 deletions(-) diff --git a/client/internal/dns/network_manager_linux.go b/client/internal/dns/network_manager_linux.go index 0b7ae7d4c2e..8bc3ef297c2 100644 --- a/client/internal/dns/network_manager_linux.go +++ b/client/internal/dns/network_manager_linux.go @@ -7,12 +7,12 @@ import ( "encoding/binary" "fmt" "net/netip" - "regexp" "time" "github.com/godbus/dbus/v5" "github.com/hashicorp/go-version" "github.com/miekg/dns" + nbversion "github.com/netbirdio/netbird/version" log "github.com/sirupsen/logrus" ) @@ -289,12 +289,7 @@ func isNetworkManagerSupportedVersion() bool { } func parseVersion(inputVersion string) (*version.Version, error) { - reg, err := regexp.Compile(version.SemverRegexpRaw) - if err != nil { - return nil, err - } - - if inputVersion == "" || !reg.MatchString(inputVersion) { + if inputVersion == "" || !nbversion.SemverRegexp.MatchString(inputVersion) { return nil, fmt.Errorf("couldn't parse the provided version: Not SemVer") } diff --git a/dns/dns.go b/dns/dns.go index b58b79b5344..18528c74328 100644 --- a/dns/dns.go +++ b/dns/dns.go @@ -86,6 +86,8 @@ func (s SimpleRecord) Len() uint16 { } } +var invalidHostMatcher = regexp.MustCompile(invalidHostLabel) + // GetParsedDomainLabel returns a domain label with max 59 characters, // parsed for old Hosts.txt requirements, and converted to ASCII and lowercase func GetParsedDomainLabel(name string) (string, error) { @@ -99,8 +101,6 @@ func GetParsedDomainLabel(name string) (string, error) { return "", fmt.Errorf("unable to convert host label to ASCII, error: %v", err) } - invalidHostMatcher := regexp.MustCompile(invalidHostLabel) - validHost := strings.ToLower(invalidHostMatcher.ReplaceAllString(ascii, "-")) if len(validHost) > 58 { validHost = validHost[:59] diff --git a/management/server/account.go b/management/server/account.go index 8f9b1e151f9..fb5198190d1 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -1596,9 +1596,10 @@ func (am *DefaultAccountManager) GetAllConnectedPeers() (map[string]struct{}, er return am.peersUpdateManager.GetAllConnectedPeers(), nil } +var invalidDomainRegexp = regexp.MustCompile(`^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$`) + func isDomainValid(domain string) bool { - re := regexp.MustCompile(`^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$`) - return re.MatchString(domain) + return invalidDomainRegexp.MatchString(domain) } // GetDNSDomain returns the configured dnsDomain diff --git a/management/server/http/middleware/access_control.go b/management/server/http/middleware/access_control.go index 434f2f6441e..31b5a2a9d7b 100644 --- a/management/server/http/middleware/access_control.go +++ b/management/server/http/middleware/access_control.go @@ -33,6 +33,8 @@ func NewAccessControl(audience, userIDClaim string, getUser GetUser) *AccessCont } } +var tokenPathRegexp = regexp.MustCompile(`^.*/api/users/.*/tokens.*$`) + // Handler method of the middleware which forbids all modify requests for non admin users // It also adds func (a *AccessControl) Handler(h http.Handler) http.Handler { @@ -55,13 +57,7 @@ func (a *AccessControl) Handler(h http.Handler) http.Handler { switch r.Method { case http.MethodDelete, http.MethodPost, http.MethodPatch, http.MethodPut: - ok, err := regexp.MatchString(`^.*/api/users/.*/tokens.*$`, r.URL.Path) - if err != nil { - log.Debugf("regex failed") - util.WriteError(status.Errorf(status.Internal, ""), w) - return - } - if ok { + if tokenPathRegexp.MatchString(r.URL.Path) { log.Debugf("valid Path") h.ServeHTTP(w, r) return diff --git a/management/server/metrics/selfhosted.go b/management/server/metrics/selfhosted.go index cf6b2e44041..e5ef6893eb7 100644 --- a/management/server/metrics/selfhosted.go +++ b/management/server/metrics/selfhosted.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "net/http" - "regexp" "sort" "strings" "time" @@ -381,15 +380,10 @@ func createPostRequest(ctx context.Context, endpoint string, payloadStr string) } func getMinMaxVersion(inputList []string) (string, string) { - reg, err := regexp.Compile(version.SemverRegexpRaw) - if err != nil { - return "", "" - } - versions := make([]*version.Version, 0) for _, raw := range inputList { - if raw != "" && reg.MatchString(raw) { + if raw != "" && nbversion.SemverRegexp.MatchString(raw) { v, err := version.NewVersion(raw) if err == nil { versions = append(versions, v) diff --git a/management/server/nameserver.go b/management/server/nameserver.go index 807adf28a53..1b8d59e2944 100644 --- a/management/server/nameserver.go +++ b/management/server/nameserver.go @@ -267,8 +267,9 @@ func validateGroups(list []string, groups map[string]*Group) error { return nil } +var domainMatcher = regexp.MustCompile(domainPattern) + func validateDomain(domain string) error { - domainMatcher := regexp.MustCompile(domainPattern) if !domainMatcher.MatchString(domain) { return errors.New("domain should consists of only letters, numbers, and hyphens with no leading, trailing hyphens, or spaces") } diff --git a/version/version.go b/version/version.go index d9c119f9005..d70a5effaa7 100644 --- a/version/version.go +++ b/version/version.go @@ -1,8 +1,19 @@ package version +import ( + "regexp" + + v "github.com/hashicorp/go-version" +) + // will be replaced with the release version when using goreleaser var version = "development" +var ( + VersionRegexp = regexp.MustCompile("^" + v.VersionRegexpRaw + "$") + SemverRegexp = regexp.MustCompile("^" + v.SemverRegexpRaw + "$") +) + // NetbirdVersion returns the Netbird version func NetbirdVersion() string { return version From d1a323fa9dc36e34f0af5703ff162a71c197f6e8 Mon Sep 17 00:00:00 2001 From: Yury Gargay Date: Mon, 27 Nov 2023 16:40:02 +0100 Subject: [PATCH 09/13] Add gocritic linter (#1324) * Add gocritic linter `gocritic` provides diagnostics that check for bugs, performance, and style issues We disable the following checks: - commentFormatting - captLocal - deprecatedComment This PR contains many `//nolint:gocritic` to disable `appendAssign`. --- .golangci.yaml | 7 ++++++ client/cmd/status.go | 4 ++-- client/firewall/iptables/manager_linux.go | 12 ++++++---- client/firewall/nftables/manager_linux.go | 2 +- client/internal/acl/manager_test.go | 18 +++++++------- client/internal/auth/device_flow.go | 5 ++-- client/internal/auth/oauth.go | 8 +++---- client/internal/config.go | 4 ++-- client/internal/dns/network_manager_linux.go | 2 +- client/internal/dns/server.go | 2 +- client/internal/ebpf/ebpf/manager_linux.go | 2 +- client/internal/engine.go | 24 ++++++++----------- client/internal/engine_test.go | 2 +- .../internal/routemanager/iptables_linux.go | 8 +++---- .../internal/routemanager/nftables_linux.go | 8 +++---- .../routemanager/nftables_linux_test.go | 20 ++++++++-------- client/ssh/client.go | 8 +++---- client/system/info_linux.go | 4 ++-- iface/wg_configurer_nonandroid.go | 2 +- management/client/client_test.go | 14 +++++------ management/server/account.go | 7 +++--- management/server/http/policies_handler.go | 2 +- management/server/http/setupkeys_handler.go | 9 +++---- management/server/idp/zitadel.go | 8 +++---- management/server/metrics/selfhosted.go | 10 ++++---- management/server/network.go | 2 +- management/server/policy.go | 2 +- management/server/setupkey.go | 2 +- sharedsock/sock_linux.go | 2 +- signal/client/grpc.go | 9 +++---- util/retry.go | 2 +- 31 files changed, 110 insertions(+), 101 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index d0926fffc36..d847e63c3fe 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -12,6 +12,12 @@ linters-settings: # Default: false check-type-assertions: false + gocritic: + disabled-checks: + - commentFormatting + - captLocal + - deprecatedComment + govet: # Enable all analyzers. # Default: false @@ -42,6 +48,7 @@ linters: - dupword # dupword checks for duplicate words in the source code - durationcheck # durationcheck checks for two durations multiplied together - forbidigo # forbidigo forbids identifiers + - gocritic # provides diagnostics that check for bugs, performance and style issues - mirror # mirror reports wrong mirror patterns of bytes/strings usage - misspell # misspess finds commonly misspelled English words in comments - nilerr # finds the code that returns nil even if it checks that the error is not nil diff --git a/client/cmd/status.go b/client/cmd/status.go index 9dfd042f83c..74d2061ffac 100644 --- a/client/cmd/status.go +++ b/client/cmd/status.go @@ -234,7 +234,7 @@ func mapPeers(peers []*proto.PeerState) peersStateOutput { continue } if isPeerConnected { - peersConnected = peersConnected + 1 + peersConnected++ localICE = pbPeerState.GetLocalIceCandidateType() remoteICE = pbPeerState.GetRemoteIceCandidateType() @@ -407,7 +407,7 @@ func parsePeers(peers peersStateOutput) string { peerState.LastStatusUpdate.Format("2006-01-02 15:04:05"), ) - peersString = peersString + peerString + peersString += peerString } return peersString } diff --git a/client/firewall/iptables/manager_linux.go b/client/firewall/iptables/manager_linux.go index 4ce904df6c5..b9243f4ca2b 100644 --- a/client/firewall/iptables/manager_linux.go +++ b/client/firewall/iptables/manager_linux.go @@ -463,14 +463,16 @@ func (m *Manager) actionToStr(action fw.Action) string { } func (m *Manager) transformIPsetName(ipsetName string, sPort, dPort string) string { - if ipsetName == "" { + switch { + case ipsetName == "": return "" - } else if sPort != "" && dPort != "" { + case sPort != "" && dPort != "": return ipsetName + "-sport-dport" - } else if sPort != "" { + case sPort != "": return ipsetName + "-sport" - } else if dPort != "" { + case dPort != "": return ipsetName + "-dport" + default: + return ipsetName } - return ipsetName } diff --git a/client/firewall/nftables/manager_linux.go b/client/firewall/nftables/manager_linux.go index 6c46048b4b4..93379bad85b 100644 --- a/client/firewall/nftables/manager_linux.go +++ b/client/firewall/nftables/manager_linux.go @@ -791,7 +791,7 @@ func (m *Manager) flushWithBackoff() (err error) { return err } time.Sleep(backoffTime) - backoffTime = backoffTime * 2 + backoffTime *= 2 continue } break diff --git a/client/internal/acl/manager_test.go b/client/internal/acl/manager_test.go index 25de2a57fd5..d55a1cad64c 100644 --- a/client/internal/acl/manager_test.go +++ b/client/internal/acl/manager_test.go @@ -189,31 +189,33 @@ func TestDefaultManagerSquashRules(t *testing.T) { } r := rules[0] - if r.PeerIP != "0.0.0.0" { + switch { + case r.PeerIP != "0.0.0.0": t.Errorf("IP should be 0.0.0.0, got: %v", r.PeerIP) return - } else if r.Direction != mgmProto.FirewallRule_IN { + case r.Direction != mgmProto.FirewallRule_IN: t.Errorf("direction should be IN, got: %v", r.Direction) return - } else if r.Protocol != mgmProto.FirewallRule_ALL { + case r.Protocol != mgmProto.FirewallRule_ALL: t.Errorf("protocol should be ALL, got: %v", r.Protocol) return - } else if r.Action != mgmProto.FirewallRule_ACCEPT { + case r.Action != mgmProto.FirewallRule_ACCEPT: t.Errorf("action should be ACCEPT, got: %v", r.Action) return } r = rules[1] - if r.PeerIP != "0.0.0.0" { + switch { + case r.PeerIP != "0.0.0.0": t.Errorf("IP should be 0.0.0.0, got: %v", r.PeerIP) return - } else if r.Direction != mgmProto.FirewallRule_OUT { + case r.Direction != mgmProto.FirewallRule_OUT: t.Errorf("direction should be OUT, got: %v", r.Direction) return - } else if r.Protocol != mgmProto.FirewallRule_ALL { + case r.Protocol != mgmProto.FirewallRule_ALL: t.Errorf("protocol should be ALL, got: %v", r.Protocol) return - } else if r.Action != mgmProto.FirewallRule_ACCEPT { + case r.Action != mgmProto.FirewallRule_ACCEPT: t.Errorf("action should be ACCEPT, got: %v", r.Action) return } diff --git a/client/internal/auth/device_flow.go b/client/internal/auth/device_flow.go index c28e4277287..3c51fe4f54c 100644 --- a/client/internal/auth/device_flow.go +++ b/client/internal/auth/device_flow.go @@ -4,12 +4,13 @@ import ( "context" "encoding/json" "fmt" - "github.com/netbirdio/netbird/client/internal" "io" "net/http" "net/url" "strings" "time" + + "github.com/netbirdio/netbird/client/internal" ) // HostedGrantType grant type for device flow on Hosted @@ -174,7 +175,7 @@ func (d *DeviceAuthorizationFlow) WaitToken(ctx context.Context, info AuthFlowIn if tokenResponse.Error == "authorization_pending" { continue } else if tokenResponse.Error == "slow_down" { - interval = interval + (3 * time.Second) + interval += (3 * time.Second) ticker.Reset(interval) continue } diff --git a/client/internal/auth/oauth.go b/client/internal/auth/oauth.go index 82adf91b9ec..23bde2be2c3 100644 --- a/client/internal/auth/oauth.go +++ b/client/internal/auth/oauth.go @@ -92,15 +92,15 @@ func authenticateWithPKCEFlow(ctx context.Context, config *internal.Config) (OAu func authenticateWithDeviceCodeFlow(ctx context.Context, config *internal.Config) (OAuthFlow, error) { deviceFlowInfo, err := internal.GetDeviceAuthorizationFlowInfo(ctx, config.PrivateKey, config.ManagementURL) if err != nil { - s, ok := gstatus.FromError(err) - if ok && s.Code() == codes.NotFound { + switch s, ok := gstatus.FromError(err); { + case ok && s.Code() == codes.NotFound: return nil, fmt.Errorf("no SSO provider returned from management. " + "Please proceed with setting up this device using setup keys " + "https://docs.netbird.io/how-to/register-machines-using-setup-keys") - } else if ok && s.Code() == codes.Unimplemented { + case ok && s.Code() == codes.Unimplemented: return nil, fmt.Errorf("the management server, %s, does not support SSO providers, "+ "please update your server or use Setup Keys to login", config.ManagementURL) - } else { + default: return nil, fmt.Errorf("getting device authorization flow info failed with error: %v", err) } } diff --git a/client/internal/config.go b/client/internal/config.go index cd665016b67..646848a2f66 100644 --- a/client/internal/config.go +++ b/client/internal/config.go @@ -273,9 +273,9 @@ func parseURL(serviceName, serviceURL string) (*url.URL, error) { if parsedMgmtURL.Port() == "" { switch parsedMgmtURL.Scheme { case "https": - parsedMgmtURL.Host = parsedMgmtURL.Host + ":443" + parsedMgmtURL.Host += ":443" case "http": - parsedMgmtURL.Host = parsedMgmtURL.Host + ":80" + parsedMgmtURL.Host += ":80" default: log.Infof("unable to determine a default port for schema %s in URL %s", parsedMgmtURL.Scheme, serviceURL) } diff --git a/client/internal/dns/network_manager_linux.go b/client/internal/dns/network_manager_linux.go index 8bc3ef297c2..d5c2f60b260 100644 --- a/client/internal/dns/network_manager_linux.go +++ b/client/internal/dns/network_manager_linux.go @@ -122,7 +122,7 @@ func (n *networkManagerDbusConfigurator) applyDNSConfig(config hostDNSConfig) er searchDomains = append(searchDomains, dns.Fqdn(dConf.domain)) } - newDomainList := append(searchDomains, matchDomains...) + newDomainList := append(searchDomains, matchDomains...) //nolint:gocritic priority := networkManagerDbusSearchDomainOnlyPriority switch { diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index 6655a6e4e1d..9bb9a76a96f 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -252,7 +252,7 @@ func (s *DefaultServer) applyConfiguration(update nbdns.Config) error { if err != nil { return fmt.Errorf("not applying dns update, error: %v", err) } - muxUpdates := append(localMuxUpdates, upstreamMuxUpdates...) + muxUpdates := append(localMuxUpdates, upstreamMuxUpdates...) //nolint:gocritic s.updateMux(muxUpdates) s.updateLocalResolver(localRecords) diff --git a/client/internal/ebpf/ebpf/manager_linux.go b/client/internal/ebpf/ebpf/manager_linux.go index 9dfdc0ad149..7520a6387a9 100644 --- a/client/internal/ebpf/ebpf/manager_linux.go +++ b/client/internal/ebpf/ebpf/manager_linux.go @@ -50,7 +50,7 @@ func GetEbpfManagerInstance() manager.Manager { } func (tf *GeneralManager) setFeatureFlag(feature uint16) { - tf.featureFlags = tf.featureFlags | feature + tf.featureFlags |= feature } func (tf *GeneralManager) loadXdp() error { diff --git a/client/internal/engine.go b/client/internal/engine.go index 35fd822f2d5..4d461b74637 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -204,14 +204,12 @@ func (e *Engine) Start() error { e.dnsServer = dns.NewDefaultServerPermanentUpstream(e.ctx, e.wgInterface, e.mobileDep.HostDNSAddresses, *dnsConfig, e.mobileDep.NetworkChangeListener) go e.mobileDep.DnsReadyListener.OnReady() } - } else { + } else if e.dnsServer == nil { // todo fix custom address - 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, err = dns.NewDefaultServer(e.ctx, e.wgInterface, e.config.CustomDNSAddress) + if err != nil { + e.close() + return err } } @@ -490,15 +488,13 @@ func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error { } else { log.Debugf("SSH server is already running") } - } else { + } else if !isNil(e.sshServer) { // Disable SSH server request, so stop it if it was running - if !isNil(e.sshServer) { - err := e.sshServer.Stop() - if err != nil { - log.Warnf("failed to stop SSH server %v", err) - } - e.sshServer = nil + err := e.sshServer.Stop() + if err != nil { + log.Warnf("failed to stop SSH server %v", err) } + e.sshServer = nil } return nil } diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index a855cf051df..08cd29da84c 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -869,7 +869,7 @@ loop: case <-ticker.C: totalConnected := 0 for _, engine := range engines { - totalConnected = totalConnected + getConnectedPeers(engine) + totalConnected += getConnectedPeers(engine) } if totalConnected == expectedConnected { log.Infof("total connected=%d", totalConnected) diff --git a/client/internal/routemanager/iptables_linux.go b/client/internal/routemanager/iptables_linux.go index 9f60193059b..e9fbb7d3c9e 100644 --- a/client/internal/routemanager/iptables_linux.go +++ b/client/internal/routemanager/iptables_linux.go @@ -173,7 +173,7 @@ func (i *iptablesManager) addJumpRules() error { return err } if i.ipv4Client != nil { - rule := append(iptablesDefaultForwardingRule, ipv4Forwarding) + rule := append(iptablesDefaultForwardingRule, ipv4Forwarding) //nolint:gocritic err = i.ipv4Client.Insert(iptablesFilterTable, iptablesForwardChain, 1, rule...) if err != nil { @@ -181,7 +181,7 @@ func (i *iptablesManager) addJumpRules() error { } i.rules[ipv4][ipv4Forwarding] = rule - rule = append(iptablesDefaultNatRule, ipv4Nat) + rule = append(iptablesDefaultNatRule, ipv4Nat) //nolint:gocritic err = i.ipv4Client.Insert(iptablesNatTable, iptablesPostRoutingChain, 1, rule...) if err != nil { return err @@ -190,14 +190,14 @@ func (i *iptablesManager) addJumpRules() error { } if i.ipv6Client != nil { - rule := append(iptablesDefaultForwardingRule, ipv6Forwarding) + rule := append(iptablesDefaultForwardingRule, ipv6Forwarding) //nolint:gocritic err = i.ipv6Client.Insert(iptablesFilterTable, iptablesForwardChain, 1, rule...) if err != nil { return err } i.rules[ipv6][ipv6Forwarding] = rule - rule = append(iptablesDefaultNatRule, ipv6Nat) + rule = append(iptablesDefaultNatRule, ipv6Nat) //nolint:gocritic err = i.ipv6Client.Insert(iptablesNatTable, iptablesPostRoutingChain, 1, rule...) if err != nil { return err diff --git a/client/internal/routemanager/nftables_linux.go b/client/internal/routemanager/nftables_linux.go index e62b1a404f6..3ecfa9630f9 100644 --- a/client/internal/routemanager/nftables_linux.go +++ b/client/internal/routemanager/nftables_linux.go @@ -300,7 +300,7 @@ func (n *nftablesManager) acceptForwardRule(sourceNetwork string) error { dst := generateCIDRMatcherExpressions("destination", "0.0.0.0/0") var exprs []expr.Any - exprs = append(src, append(dst, &expr.Verdict{ + exprs = append(src, append(dst, &expr.Verdict{ //nolint:gocritic Kind: expr.VerdictAccept, })...) @@ -322,7 +322,7 @@ func (n *nftablesManager) acceptForwardRule(sourceNetwork string) error { src = generateCIDRMatcherExpressions("source", "0.0.0.0/0") dst = generateCIDRMatcherExpressions("destination", sourceNetwork) - exprs = append(src, append(dst, &expr.Verdict{ + exprs = append(src, append(dst, &expr.Verdict{ //nolint:gocritic Kind: expr.VerdictAccept, })...) @@ -421,9 +421,9 @@ func (n *nftablesManager) insertRoutingRule(format, chain string, pair routerPai var expression []expr.Any if isNat { - expression = append(sourceExp, append(destExp, &expr.Counter{}, &expr.Masq{})...) + expression = append(sourceExp, append(destExp, &expr.Counter{}, &expr.Masq{})...) //nolint:gocritic } else { - expression = append(sourceExp, append(destExp, exprCounterAccept...)...) + expression = append(sourceExp, append(destExp, exprCounterAccept...)...) //nolint:gocritic } ruleKey := genKey(format, pair.ID) diff --git a/client/internal/routemanager/nftables_linux_test.go b/client/internal/routemanager/nftables_linux_test.go index dec80015661..d60d53e50d9 100644 --- a/client/internal/routemanager/nftables_linux_test.go +++ b/client/internal/routemanager/nftables_linux_test.go @@ -44,7 +44,7 @@ func TestNftablesManager_RestoreOrCreateContainers(t *testing.T) { sourceExp := generateCIDRMatcherExpressions("source", pair.source) destExp := generateCIDRMatcherExpressions("destination", pair.destination) - forward4Exp := append(sourceExp, append(destExp, exprCounterAccept...)...) + forward4Exp := append(sourceExp, append(destExp, exprCounterAccept...)...) //nolint:gocritic forward4RuleKey := genKey(forwardingFormat, pair.ID) inserted4Forwarding := nftablesTestingClient.InsertRule(&nftables.Rule{ Table: manager.tableIPv4, @@ -53,7 +53,7 @@ func TestNftablesManager_RestoreOrCreateContainers(t *testing.T) { UserData: []byte(forward4RuleKey), }) - nat4Exp := append(sourceExp, append(destExp, &expr.Counter{}, &expr.Masq{})...) + nat4Exp := append(sourceExp, append(destExp, &expr.Counter{}, &expr.Masq{})...) //nolint:gocritic nat4RuleKey := genKey(natFormat, pair.ID) inserted4Nat := nftablesTestingClient.InsertRule(&nftables.Rule{ @@ -76,7 +76,7 @@ func TestNftablesManager_RestoreOrCreateContainers(t *testing.T) { sourceExp = generateCIDRMatcherExpressions("source", pair.source) destExp = generateCIDRMatcherExpressions("destination", pair.destination) - forward6Exp := append(sourceExp, append(destExp, exprCounterAccept...)...) + forward6Exp := append(sourceExp, append(destExp, exprCounterAccept...)...) //nolint:gocritic forward6RuleKey := genKey(forwardingFormat, pair.ID) inserted6Forwarding := nftablesTestingClient.InsertRule(&nftables.Rule{ Table: manager.tableIPv6, @@ -85,7 +85,7 @@ func TestNftablesManager_RestoreOrCreateContainers(t *testing.T) { UserData: []byte(forward6RuleKey), }) - nat6Exp := append(sourceExp, append(destExp, &expr.Counter{}, &expr.Masq{})...) + nat6Exp := append(sourceExp, append(destExp, &expr.Counter{}, &expr.Masq{})...) //nolint:gocritic nat6RuleKey := genKey(natFormat, pair.ID) inserted6Nat := nftablesTestingClient.InsertRule(&nftables.Rule{ @@ -149,7 +149,7 @@ func TestNftablesManager_InsertRoutingRules(t *testing.T) { sourceExp := generateCIDRMatcherExpressions("source", testCase.inputPair.source) destExp := generateCIDRMatcherExpressions("destination", testCase.inputPair.destination) - testingExpression := append(sourceExp, destExp...) + testingExpression := append(sourceExp, destExp...) //nolint:gocritic fwdRuleKey := genKey(forwardingFormat, testCase.inputPair.ID) found := 0 @@ -188,7 +188,7 @@ func TestNftablesManager_InsertRoutingRules(t *testing.T) { sourceExp = generateCIDRMatcherExpressions("source", getInPair(testCase.inputPair).source) destExp = generateCIDRMatcherExpressions("destination", getInPair(testCase.inputPair).destination) - testingExpression = append(sourceExp, destExp...) + testingExpression = append(sourceExp, destExp...) //nolint:gocritic inFwdRuleKey := genKey(inForwardingFormat, testCase.inputPair.ID) found = 0 @@ -252,7 +252,7 @@ func TestNftablesManager_RemoveRoutingRules(t *testing.T) { sourceExp := generateCIDRMatcherExpressions("source", testCase.inputPair.source) destExp := generateCIDRMatcherExpressions("destination", testCase.inputPair.destination) - forwardExp := append(sourceExp, append(destExp, exprCounterAccept...)...) + forwardExp := append(sourceExp, append(destExp, exprCounterAccept...)...) //nolint:gocritic forwardRuleKey := genKey(forwardingFormat, testCase.inputPair.ID) insertedForwarding := nftablesTestingClient.InsertRule(&nftables.Rule{ Table: table, @@ -261,7 +261,7 @@ func TestNftablesManager_RemoveRoutingRules(t *testing.T) { UserData: []byte(forwardRuleKey), }) - natExp := append(sourceExp, append(destExp, &expr.Counter{}, &expr.Masq{})...) + natExp := append(sourceExp, append(destExp, &expr.Counter{}, &expr.Masq{})...) //nolint:gocritic natRuleKey := genKey(natFormat, testCase.inputPair.ID) insertedNat := nftablesTestingClient.InsertRule(&nftables.Rule{ @@ -274,7 +274,7 @@ func TestNftablesManager_RemoveRoutingRules(t *testing.T) { sourceExp = generateCIDRMatcherExpressions("source", getInPair(testCase.inputPair).source) destExp = generateCIDRMatcherExpressions("destination", getInPair(testCase.inputPair).destination) - forwardExp = append(sourceExp, append(destExp, exprCounterAccept...)...) + forwardExp = append(sourceExp, append(destExp, exprCounterAccept...)...) //nolint:gocritic inForwardRuleKey := genKey(inForwardingFormat, testCase.inputPair.ID) insertedInForwarding := nftablesTestingClient.InsertRule(&nftables.Rule{ Table: table, @@ -283,7 +283,7 @@ func TestNftablesManager_RemoveRoutingRules(t *testing.T) { UserData: []byte(inForwardRuleKey), }) - natExp = append(sourceExp, append(destExp, &expr.Counter{}, &expr.Masq{})...) + natExp = append(sourceExp, append(destExp, &expr.Counter{}, &expr.Masq{})...) //nolint:gocritic inNatRuleKey := genKey(inNatFormat, testCase.inputPair.ID) insertedInNat := nftablesTestingClient.InsertRule(&nftables.Rule{ diff --git a/client/ssh/client.go b/client/ssh/client.go index 29ebb2481e8..2dc70e8fc1d 100644 --- a/client/ssh/client.go +++ b/client/ssh/client.go @@ -2,11 +2,12 @@ package ssh import ( "fmt" - "golang.org/x/crypto/ssh" - "golang.org/x/term" "net" "os" "time" + + "golang.org/x/crypto/ssh" + "golang.org/x/term" ) // Client wraps crypto/ssh Client to simplify usage @@ -73,8 +74,7 @@ func (c *Client) OpenTerminal() error { if err := session.Wait(); err != nil { if e, ok := err.(*ssh.ExitError); ok { - switch e.ExitStatus() { - case 130: + if e.ExitStatus() == 130 { return nil } } diff --git a/client/system/info_linux.go b/client/system/info_linux.go index a4ab9f9312f..21a4d482a64 100644 --- a/client/system/info_linux.go +++ b/client/system/info_linux.go @@ -44,8 +44,8 @@ func GetInfo(ctx context.Context) *Info { } } - osStr := strings.Replace(info, "\n", "", -1) - osStr = strings.Replace(osStr, "\r\n", "", -1) + osStr := strings.ReplaceAll(info, "\n", "") + osStr = strings.ReplaceAll(osStr, "\r\n", "") osInfo := strings.Split(osStr, " ") if osName == "" { osName = osInfo[3] diff --git a/iface/wg_configurer_nonandroid.go b/iface/wg_configurer_nonandroid.go index 6749c0966ab..3d9aff7a96c 100644 --- a/iface/wg_configurer_nonandroid.go +++ b/iface/wg_configurer_nonandroid.go @@ -141,7 +141,7 @@ func (c *wGConfigurer) removeAllowedIP(peerKey string, allowedIP string) error { for i, existingAllowedIP := range existingPeer.AllowedIPs { if existingAllowedIP.String() == ipNet.String() { - newAllowedIPs = append(existingPeer.AllowedIPs[:i], existingPeer.AllowedIPs[i+1:]...) + newAllowedIPs = append(existingPeer.AllowedIPs[:i], existingPeer.AllowedIPs[i+1:]...) //nolint:gocritic break } } diff --git a/management/client/client_test.go b/management/client/client_test.go index c5e5b814067..9ebb58420c1 100644 --- a/management/client/client_test.go +++ b/management/client/client_test.go @@ -285,7 +285,7 @@ func Test_SystemMetaDataFromClient(t *testing.T) { testKey, err := wgtypes.GenerateKey() if err != nil { - log.Fatal(err) + t.Fatal(err) } serverAddr := lis.Addr().String() @@ -293,12 +293,12 @@ func Test_SystemMetaDataFromClient(t *testing.T) { testClient, err := NewClient(ctx, serverAddr, testKey, false) if err != nil { - log.Fatalf("error while creating testClient: %v", err) + t.Fatalf("error while creating testClient: %v", err) } key, err := testClient.GetServerPublicKey() if err != nil { - log.Fatalf("error while getting server public key from testclient, %v", err) + t.Fatalf("error while getting server public key from testclient, %v", err) } var actualMeta *mgmtProto.PeerSystemMeta @@ -364,7 +364,7 @@ func Test_GetDeviceAuthorizationFlow(t *testing.T) { testKey, err := wgtypes.GenerateKey() if err != nil { - log.Fatal(err) + t.Fatal(err) } serverAddr := lis.Addr().String() @@ -372,7 +372,7 @@ func Test_GetDeviceAuthorizationFlow(t *testing.T) { client, err := NewClient(ctx, serverAddr, testKey, false) if err != nil { - log.Fatalf("error while creating testClient: %v", err) + t.Fatalf("error while creating testClient: %v", err) } expectedFlowInfo := &mgmtProto.DeviceAuthorizationFlow{ @@ -408,7 +408,7 @@ func Test_GetPKCEAuthorizationFlow(t *testing.T) { testKey, err := wgtypes.GenerateKey() if err != nil { - log.Fatal(err) + t.Fatal(err) } serverAddr := lis.Addr().String() @@ -416,7 +416,7 @@ func Test_GetPKCEAuthorizationFlow(t *testing.T) { client, err := NewClient(ctx, serverAddr, testKey, false) if err != nil { - log.Fatalf("error while creating testClient: %v", err) + t.Fatalf("error while creating testClient: %v", err) } expectedFlowInfo := &mgmtProto.PKCEAuthorizationFlow{ diff --git a/management/server/account.go b/management/server/account.go index fb5198190d1..09071d103b4 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -950,14 +950,15 @@ func (am *DefaultAccountManager) newAccount(userID, domain string) (*Account, er _, err := am.Store.GetAccount(accountId) statusErr, _ := status.FromError(err) - if err == nil { + switch { + case err == nil: log.Warnf("an account with ID already exists, retrying...") continue - } else if statusErr.Type() == status.NotFound { + case statusErr.Type() == status.NotFound: newAccount := newAccountWithId(accountId, userID, domain) am.StoreEvent(userID, newAccount.Id, accountId, activity.AccountCreated, nil) return newAccount, nil - } else { + default: return nil, err } } diff --git a/management/server/http/policies_handler.go b/management/server/http/policies_handler.go index c8f58f8a42b..f8c876a4135 100644 --- a/management/server/http/policies_handler.go +++ b/management/server/http/policies_handler.go @@ -300,7 +300,7 @@ func toPolicyResponse(account *server.Account, policy *server.Policy) *api.Polic Action: api.PolicyRuleAction(r.Action), } if len(r.Ports) != 0 { - portsCopy := r.Ports[:] + portsCopy := r.Ports rule.Ports = &portsCopy } for _, gid := range r.Sources { diff --git a/management/server/http/setupkeys_handler.go b/management/server/http/setupkeys_handler.go index cddae672caf..4adf3fdd055 100644 --- a/management/server/http/setupkeys_handler.go +++ b/management/server/http/setupkeys_handler.go @@ -192,13 +192,14 @@ func writeSuccess(w http.ResponseWriter, key *server.SetupKey) { func toResponseBody(key *server.SetupKey) *api.SetupKey { var state string - if key.IsExpired() { + switch { + case key.IsExpired(): state = "expired" - } else if key.IsRevoked() { + case key.IsRevoked(): state = "revoked" - } else if key.IsOverUsed() { + case key.IsOverUsed(): state = "overused" - } else { + default: state = "valid" } diff --git a/management/server/idp/zitadel.go b/management/server/idp/zitadel.go index 5325e51bebe..926f078b208 100644 --- a/management/server/idp/zitadel.go +++ b/management/server/idp/zitadel.go @@ -463,11 +463,9 @@ func (zp zitadelProfile) userData() *UserData { if zp.Human != nil { email = zp.Human.Email.Email name = zp.Human.Profile.DisplayName - } else { - if len(zp.LoginNames) > 0 { - email = zp.LoginNames[0] - name = zp.LoginNames[0] - } + } else if len(zp.LoginNames) > 0 { + email = zp.LoginNames[0] + name = zp.LoginNames[0] } return &UserData{ diff --git a/management/server/metrics/selfhosted.go b/management/server/metrics/selfhosted.go index e5ef6893eb7..90d69b47b77 100644 --- a/management/server/metrics/selfhosted.go +++ b/management/server/metrics/selfhosted.go @@ -200,14 +200,14 @@ func (w *Worker) generateProperties() properties { expirationEnabled++ } - groups = groups + len(account.Groups) - routes = routes + len(account.Routes) + groups += len(account.Groups) + routes += len(account.Routes) for _, route := range account.Routes { if len(route.PeerGroups) > 0 { routesWithRGGroups++ } } - nameservers = nameservers + len(account.NameServerGroups) + nameservers += len(account.NameServerGroups) for _, policy := range account.Policies { for _, rule := range policy.Rules { @@ -231,10 +231,10 @@ func (w *Worker) generateProperties() properties { } for _, key := range account.SetupKeys { - setupKeysUsage = setupKeysUsage + key.UsedTimes + setupKeysUsage += key.UsedTimes if key.Ephemeral { ephemeralPeersSKs++ - ephemeralPeersSKUsage = ephemeralPeersSKUsage + key.UsedTimes + ephemeralPeersSKUsage += key.UsedTimes } } diff --git a/management/server/network.go b/management/server/network.go index c5b165caeda..daa67f2dcc7 100644 --- a/management/server/network.go +++ b/management/server/network.go @@ -66,7 +66,7 @@ func NewNetwork() *Network { func (n *Network) IncSerial() { n.mu.Lock() defer n.mu.Unlock() - n.Serial = n.Serial + 1 + n.Serial++ } // CurrentSerial returns the Network.Serial of the network (latest state id) diff --git a/management/server/policy.go b/management/server/policy.go index 24188f93c01..fc222cb4087 100644 --- a/management/server/policy.go +++ b/management/server/policy.go @@ -406,7 +406,7 @@ func (am *DefaultAccountManager) ListPolicies(accountID, userID string) ([]*Poli return nil, status.Errorf(status.PermissionDenied, "Only Administrators can view policies") } - return account.Policies[:], nil + return account.Policies, nil } func (am *DefaultAccountManager) deletePolicy(account *Account, policyID string) (*Policy, error) { diff --git a/management/server/setupkey.go b/management/server/setupkey.go index 3bd14b61e06..d347fb18137 100644 --- a/management/server/setupkey.go +++ b/management/server/setupkey.go @@ -137,7 +137,7 @@ func (key *SetupKey) HiddenCopy(length int) *SetupKey { // IncrementUsage makes a copy of a key, increments the UsedTimes by 1 and sets LastUsed to now func (key *SetupKey) IncrementUsage() *SetupKey { c := key.Copy() - c.UsedTimes = c.UsedTimes + 1 + c.UsedTimes++ c.LastUsed = time.Now().UTC() return c } diff --git a/sharedsock/sock_linux.go b/sharedsock/sock_linux.go index b823bb5081c..656fdc8ca24 100644 --- a/sharedsock/sock_linux.go +++ b/sharedsock/sock_linux.go @@ -248,7 +248,7 @@ func (s *SharedSocket) ReadFrom(b []byte) (n int, addr net.Addr, err error) { decodedLayers := make([]gopacket.LayerType, 0, 3) - err = parser.DecodeLayers(pkt.buf[:], &decodedLayers) + err = parser.DecodeLayers(pkt.buf, &decodedLayers) if err != nil { return 0, nil, err } diff --git a/signal/client/grpc.go b/signal/client/grpc.go index 9f70234e92f..7aa9f9ce98c 100644 --- a/signal/client/grpc.go +++ b/signal/client/grpc.go @@ -354,16 +354,17 @@ func (c *GrpcClient) receive(stream proto.SignalExchange_ConnectStreamClient, for { msg, err := stream.Recv() - if s, ok := status.FromError(err); ok && s.Code() == codes.Canceled { + switch s, ok := status.FromError(err); { + case ok && s.Code() == codes.Canceled: log.Debugf("stream canceled (usually indicates shutdown)") return err - } else if s.Code() == codes.Unavailable { + case s.Code() == codes.Unavailable: log.Debugf("Signal Service is unavailable") return err - } else if err == io.EOF { + case err == io.EOF: log.Debugf("Signal Service stream closed by server") return err - } else if err != nil { + case err != nil: return err } log.Tracef("received a new message from Peer [fingerprint: %s]", msg.Key) diff --git a/util/retry.go b/util/retry.go index 3bffcf288d8..2d5fbf6cfdc 100644 --- a/util/retry.go +++ b/util/retry.go @@ -15,7 +15,7 @@ func Retry(attempts int, sleep time.Duration, toExec func() error, onError func( if attempts--; attempts > 0 { jitter := time.Duration(rand.Int63n(int64(sleep))) - sleep = sleep + jitter/2 + sleep += jitter / 2 onError(err) time.Sleep(sleep) From dc05102b8f8ee8c1f3c17c51211ccb27f2685f8c Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 28 Nov 2023 13:09:33 +0100 Subject: [PATCH 10/13] Fix panic on empty username for invites (#1334) Validate email and user are not empty --- management/server/http/users_handler.go | 7 ++++++- management/server/user.go | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/management/server/http/users_handler.go b/management/server/http/users_handler.go index e2bf77de65b..e474ac19ab8 100644 --- a/management/server/http/users_handler.go +++ b/management/server/http/users_handler.go @@ -155,9 +155,14 @@ func (h *UsersHandler) CreateUser(w http.ResponseWriter, r *http.Request) { email = *req.Email } + name := "" + if req.Name != nil { + name = *req.Name + } + newUser, err := h.accountManager.CreateUser(account.Id, user.Id, &server.UserInfo{ Email: email, - Name: *req.Name, + Name: name, Role: req.Role, AutoGroups: req.AutoGroups, IsServiceUser: req.IsServiceUser, diff --git a/management/server/user.go b/management/server/user.go index b96bf743cda..b759bba4259 100644 --- a/management/server/user.go +++ b/management/server/user.go @@ -258,6 +258,14 @@ func (am *DefaultAccountManager) inviteNewUser(accountID, userID string, invite return nil, fmt.Errorf("provided user update is nil") } + switch { + case invite.Name == "": + return nil, status.Errorf(status.InvalidArgument, "name can't be empty") + case invite.Email == "": + return nil, status.Errorf(status.InvalidArgument, "email can't be empty") + default: + } + account, err := am.Store.GetAccount(accountID) if err != nil { return nil, status.Errorf(status.NotFound, "account %s doesn't exist", accountID) From c2eaf8a1c0984d2a8beed5b322e6b6be2bc9eaf9 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 28 Nov 2023 14:23:38 +0100 Subject: [PATCH 11/13] Add account deletion endpoint (#1331) Adding support to account owners to delete an account This will remove all users from local, and if --user-delete-from-idp is set it will remove from the remote IDP --- management/server/account.go | 52 +++++++++++++ management/server/account_test.go | 25 ++++++ management/server/file_store.go | 35 +++++++++ management/server/file_store_test.go | 54 +++++++++++++ management/server/http/accounts_handler.go | 24 ++++++ management/server/http/api/openapi.yml | 26 +++++++ management/server/http/handler.go | 2 + management/server/http/users_handler.go | 2 +- management/server/mock_server/account_mock.go | 9 +++ management/server/sqlite_store.go | 42 ++++++++-- management/server/sqlite_store_test.go | 77 ++++++++++++++++++- management/server/store.go | 1 + 12 files changed, 342 insertions(+), 7 deletions(-) diff --git a/management/server/account.go b/management/server/account.go index 09071d103b4..cbdb247db14 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -64,6 +64,7 @@ type AccountManager interface { GetAccountByUserOrAccountID(userID, accountID, domain string) (*Account, error) GetAccountFromToken(claims jwtclaims.AuthorizationClaims) (*Account, *User, error) GetAccountFromPAT(pat string) (*Account, *User, *PersonalAccessToken, error) + DeleteAccount(accountID, userID string) error MarkPATUsed(tokenID string) error GetUser(claims jwtclaims.AuthorizationClaims) (*User, error) ListUsers(accountID string) ([]*User, error) @@ -1004,6 +1005,57 @@ func (am *DefaultAccountManager) warmupIDPCache() error { return nil } +// DeleteAccount deletes an account and all its users from local store and from the remote IDP if the requester is an admin and account owner +func (am *DefaultAccountManager) DeleteAccount(accountID, userID string) error { + unlock := am.Store.AcquireAccountLock(accountID) + defer unlock() + account, err := am.Store.GetAccount(accountID) + if err != nil { + return err + } + + user, err := account.FindUser(userID) + if err != nil { + return err + } + + if !user.IsAdmin() { + return status.Errorf(status.PermissionDenied, "user is not allowed to delete account") + } + + if user.Id != account.CreatedBy { + return status.Errorf(status.PermissionDenied, "user is not allowed to delete account. Only account owner can delete account") + } + for _, otherUser := range account.Users { + if otherUser.IsServiceUser { + continue + } + + if otherUser.Id == userID { + continue + } + + deleteUserErr := am.deleteRegularUser(account, userID, otherUser.Id) + if deleteUserErr != nil { + return deleteUserErr + } + } + + err = am.deleteRegularUser(account, userID, userID) + if err != nil { + log.Errorf("failed deleting user %s. error: %s", userID, err) + return err + } + + err = am.Store.DeleteAccount(account) + if err != nil { + log.Errorf("failed deleting account %s. error: %s", accountID, err) + return err + } + log.Debugf("account %s deleted", accountID) + return nil +} + // GetAccountByUserOrAccountID looks for an account by user or accountID, if no account is provided and // userID doesn't have an account associated with it, one account is created func (am *DefaultAccountManager) GetAccountByUserOrAccountID(userID, accountID, domain string) (*Account, error) { diff --git a/management/server/account_test.go b/management/server/account_test.go index ad0ccfbcea7..3343acbde77 100644 --- a/management/server/account_test.go +++ b/management/server/account_test.go @@ -746,6 +746,31 @@ func TestAccountManager_GetAccount(t *testing.T) { } } +func TestAccountManager_DeleteAccount(t *testing.T) { + manager, err := createManager(t) + if err != nil { + t.Fatal(err) + return + } + + expectedId := "test_account" + userId := "account_creator" + account, err := createAccount(manager, expectedId, userId, "") + if err != nil { + t.Fatal(err) + } + + err = manager.DeleteAccount(account.Id, userId) + if err != nil { + t.Fatal(err) + } + + getAccount, err := manager.Store.GetAccount(account.Id) + if err == nil { + t.Fatal(fmt.Errorf("expected to get an error when trying to get deleted account, got %v", getAccount)) + } +} + func TestAccountManager_AddPeer(t *testing.T) { manager, err := createManager(t) if err != nil { diff --git a/management/server/file_store.go b/management/server/file_store.go index 73c52927e81..97fdc9a92e0 100644 --- a/management/server/file_store.go +++ b/management/server/file_store.go @@ -351,6 +351,41 @@ func (s *FileStore) SaveAccount(account *Account) error { return s.persist(s.storeFile) } +func (s *FileStore) DeleteAccount(account *Account) error { + s.mux.Lock() + defer s.mux.Unlock() + + if account.Id == "" { + return status.Errorf(status.InvalidArgument, "account id should not be empty") + } + + for keyID := range account.SetupKeys { + delete(s.SetupKeyID2AccountID, strings.ToUpper(keyID)) + } + + // enforce peer to account index and delete peer to route indexes for rebuild + for _, peer := range account.Peers { + delete(s.PeerKeyID2AccountID, peer.Key) + delete(s.PeerID2AccountID, peer.ID) + } + + for _, user := range account.Users { + for _, pat := range user.PATs { + delete(s.TokenID2UserID, pat.ID) + delete(s.HashedPAT2TokenID, pat.HashedToken) + } + delete(s.UserID2AccountID, user.Id) + } + + if account.DomainCategory == PrivateCategory && account.IsDomainPrimaryAccount { + delete(s.PrivateDomain2AccountID, account.Domain) + } + + delete(s.Accounts, account.Id) + + return s.persist(s.storeFile) +} + // DeleteHashedPAT2TokenIDIndex removes an entry from the indexing map HashedPAT2TokenID func (s *FileStore) DeleteHashedPAT2TokenIDIndex(hashedToken string) error { s.mux.Lock() diff --git a/management/server/file_store_test.go b/management/server/file_store_test.go index 206873d2022..f5baf985852 100644 --- a/management/server/file_store_test.go +++ b/management/server/file_store_test.go @@ -121,6 +121,60 @@ func TestSaveAccount(t *testing.T) { } } +func TestDeleteAccount(t *testing.T) { + storeDir := t.TempDir() + storeFile := filepath.Join(storeDir, "store.json") + err := util.CopyFileContents("testdata/store.json", storeFile) + if err != nil { + t.Fatal(err) + } + + store, err := NewFileStore(storeDir, nil) + if err != nil { + t.Fatal(err) + } + var account *Account + for _, a := range store.Accounts { + account = a + break + } + + require.NotNil(t, account, "failed to restore a FileStore file and get at least one account") + + err = store.DeleteAccount(account) + require.NoError(t, err, "failed to delete account, error: %v", err) + + _, ok := store.Accounts[account.Id] + require.False(t, ok, "failed to delete account") + + for id := range account.Users { + _, ok := store.UserID2AccountID[id] + assert.False(t, ok, "failed to delete UserID2AccountID index") + for _, pat := range account.Users[id].PATs { + _, ok := store.HashedPAT2TokenID[pat.HashedToken] + assert.False(t, ok, "failed to delete HashedPAT2TokenID index") + _, ok = store.TokenID2UserID[pat.ID] + assert.False(t, ok, "failed to delete TokenID2UserID index") + } + } + + for _, p := range account.Peers { + _, ok := store.PeerKeyID2AccountID[p.Key] + assert.False(t, ok, "failed to delete PeerKeyID2AccountID index") + _, ok = store.PeerID2AccountID[p.ID] + assert.False(t, ok, "failed to delete PeerID2AccountID index") + } + + for id := range account.SetupKeys { + _, ok := store.SetupKeyID2AccountID[id] + assert.False(t, ok, "failed to delete SetupKeyID2AccountID index") + } + + _, ok = store.PrivateDomain2AccountID[account.Domain] + assert.False(t, ok, "failed to delete PrivateDomain2AccountID index") + +} + func TestStore(t *testing.T) { store := newStore(t) diff --git a/management/server/http/accounts_handler.go b/management/server/http/accounts_handler.go index a5d7a9501c4..e5e0dff8d85 100644 --- a/management/server/http/accounts_handler.go +++ b/management/server/http/accounts_handler.go @@ -98,6 +98,30 @@ func (h *AccountsHandler) UpdateAccount(w http.ResponseWriter, r *http.Request) util.WriteJSONObject(w, &resp) } +// DeleteAccount is a HTTP DELETE handler to delete an account +func (h *AccountsHandler) DeleteAccount(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodDelete { + util.WriteErrorResponse("wrong HTTP method", http.StatusMethodNotAllowed, w) + return + } + + claims := h.claimsExtractor.FromRequestContext(r) + vars := mux.Vars(r) + targetAccountID := vars["accountId"] + if len(targetAccountID) == 0 { + util.WriteError(status.Errorf(status.InvalidArgument, "invalid account ID"), w) + return + } + + err := h.accountManager.DeleteAccount(targetAccountID, claims.UserId) + if err != nil { + util.WriteError(err, w) + return + } + + util.WriteJSONObject(w, emptyObject{}) +} + func toAccountResponse(account *server.Account) *api.Account { return &api.Account{ Id: account.Id, diff --git a/management/server/http/api/openapi.yml b/management/server/http/api/openapi.yml index 64e97426a61..0ed8261480f 100644 --- a/management/server/http/api/openapi.yml +++ b/management/server/http/api/openapi.yml @@ -1074,6 +1074,32 @@ paths: '500': "$ref": "#/components/responses/internal_error" /api/accounts/{accountId}: + delete: + summary: Delete an Account + description: Deletes an account and all its resources. Only administrators and account owners can delete accounts. + tags: [ Accounts ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: accountId + required: true + schema: + type: string + description: The unique identifier of an account + responses: + '200': + description: Delete account status code + content: { } + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" put: summary: Update an Account description: Update information about an account diff --git a/management/server/http/handler.go b/management/server/http/handler.go index c589512e5d4..8c77d27dc4a 100644 --- a/management/server/http/handler.go +++ b/management/server/http/handler.go @@ -7,6 +7,7 @@ import ( "github.com/rs/cors" "github.com/netbirdio/management-integrations/integrations" + s "github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server/http/middleware" "github.com/netbirdio/netbird/management/server/jwtclaims" @@ -105,6 +106,7 @@ func APIHandler(accountManager s.AccountManager, jwtValidator jwtclaims.JWTValid func (apiHandler *apiHandler) addAccountsEndpoint() { accountsHandler := NewAccountsHandler(apiHandler.AccountManager, apiHandler.AuthCfg) apiHandler.Router.HandleFunc("/accounts/{accountId}", accountsHandler.UpdateAccount).Methods("PUT", "OPTIONS") + apiHandler.Router.HandleFunc("/accounts/{accountId}", accountsHandler.DeleteAccount).Methods("DELETE", "OPTIONS") apiHandler.Router.HandleFunc("/accounts", accountsHandler.GetAllAccounts).Methods("GET", "OPTIONS") } diff --git a/management/server/http/users_handler.go b/management/server/http/users_handler.go index e474ac19ab8..5d92b65e5d8 100644 --- a/management/server/http/users_handler.go +++ b/management/server/http/users_handler.go @@ -94,7 +94,7 @@ func (h *UsersHandler) UpdateUser(w http.ResponseWriter, r *http.Request) { util.WriteJSONObject(w, toUserResponse(newUser, claims.UserId)) } -// DeleteUser is a DELETE request to delete a user (only works for service users right now) +// DeleteUser is a DELETE request to delete a user func (h *UsersHandler) DeleteUser(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodDelete { util.WriteErrorResponse("wrong HTTP method", http.StatusMethodNotAllowed, w) diff --git a/management/server/mock_server/account_mock.go b/management/server/mock_server/account_mock.go index f6b2e1641d2..84b23a4f257 100644 --- a/management/server/mock_server/account_mock.go +++ b/management/server/mock_server/account_mock.go @@ -68,6 +68,7 @@ type MockAccountManager struct { ListNameServerGroupsFunc func(accountID string) ([]*nbdns.NameServerGroup, error) CreateUserFunc func(accountID, userID string, key *server.UserInfo) (*server.UserInfo, error) GetAccountFromTokenFunc func(claims jwtclaims.AuthorizationClaims) (*server.Account, *server.User, error) + DeleteAccountFunc func(accountID, userID string) error GetDNSDomainFunc func() string StoreEventFunc func(initiatorID, targetID, accountID string, activityID activity.Activity, meta map[string]any) GetEventsFunc func(accountID, userID string) ([]*activity.Event, error) @@ -157,6 +158,14 @@ func (am *MockAccountManager) GetAccountFromPAT(pat string) (*server.Account, *s return nil, nil, nil, status.Errorf(codes.Unimplemented, "method GetAccountFromPAT is not implemented") } +// DeleteAccount mock implementation of DeleteAccount from server.AccountManager interface +func (am *MockAccountManager) DeleteAccount(accountID, userID string) error { + if am.DeleteAccountFunc != nil { + return am.DeleteAccountFunc(accountID, userID) + } + return status.Errorf(codes.Unimplemented, "method DeleteAccount is not implemented") +} + // MarkPATUsed mock implementation of MarkPATUsed from server.AccountManager interface func (am *MockAccountManager) MarkPATUsed(pat string) error { if am.MarkPATUsedFunc != nil { diff --git a/management/server/sqlite_store.go b/management/server/sqlite_store.go index bbb13f8c6db..0cd0abe4a47 100644 --- a/management/server/sqlite_store.go +++ b/management/server/sqlite_store.go @@ -7,15 +7,16 @@ import ( "sync" "time" - nbdns "github.com/netbirdio/netbird/dns" - "github.com/netbirdio/netbird/management/server/status" - "github.com/netbirdio/netbird/management/server/telemetry" - "github.com/netbirdio/netbird/route" log "github.com/sirupsen/logrus" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/clause" "gorm.io/gorm/logger" + + nbdns "github.com/netbirdio/netbird/dns" + "github.com/netbirdio/netbird/management/server/status" + "github.com/netbirdio/netbird/management/server/telemetry" + "github.com/netbirdio/netbird/route" ) // SqliteStore represents an account storage backed by a Sqlite DB persisted to disk @@ -202,6 +203,37 @@ func (s *SqliteStore) SaveAccount(account *Account) error { return err } +func (s *SqliteStore) DeleteAccount(account *Account) error { + start := time.Now() + + err := s.db.Transaction(func(tx *gorm.DB) error { + result := tx.Select(clause.Associations).Delete(account.Policies, "account_id = ?", account.Id) + if result.Error != nil { + return result.Error + } + + result = tx.Select(clause.Associations).Delete(account.UsersG, "account_id = ?", account.Id) + if result.Error != nil { + return result.Error + } + + result = tx.Select(clause.Associations).Delete(account) + if result.Error != nil { + return result.Error + } + + return nil + }) + + took := time.Since(start) + if s.metrics != nil { + s.metrics.StoreMetrics().CountPersistenceDuration(took) + } + log.Debugf("took %d ms to delete an account to the SQLite", took.Milliseconds()) + + return err +} + func (s *SqliteStore) SaveInstallationID(ID string) error { installation := installation{InstallationIDValue: ID} installation.ID = uint(s.installationPK) @@ -336,7 +368,7 @@ func (s *SqliteStore) GetAccount(accountID string) (*Account, error) { var rules []*PolicyRule err := s.db.Model(&PolicyRule{}).Find(&rules, "policy_id = ?", policy.ID).Error if err != nil { - return nil, status.Errorf(status.NotFound, "account not found") + return nil, status.Errorf(status.NotFound, "rule not found") } account.Policies[i].Rules = rules } diff --git a/management/server/sqlite_store_test.go b/management/server/sqlite_store_test.go index 4a16e25255d..eef469f40d7 100644 --- a/management/server/sqlite_store_test.go +++ b/management/server/sqlite_store_test.go @@ -9,9 +9,10 @@ import ( "time" "github.com/google/uuid" - "github.com/netbirdio/netbird/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/netbirdio/netbird/util" ) func TestSqlite_NewStore(t *testing.T) { @@ -98,6 +99,80 @@ func TestSqlite_SaveAccount(t *testing.T) { } } +func TestSqlite_DeleteAccount(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("The SQLite store is not properly supported by Windows yet") + } + + store := newSqliteStore(t) + + testUserID := "testuser" + user := NewAdminUser(testUserID) + user.PATs = map[string]*PersonalAccessToken{"testtoken": { + ID: "testtoken", + Name: "test token", + }} + + account := newAccountWithId("account_id", testUserID, "") + setupKey := GenerateDefaultSetupKey() + account.SetupKeys[setupKey.Key] = setupKey + account.Peers["testpeer"] = &Peer{ + Key: "peerkey", + SetupKey: "peerkeysetupkey", + IP: net.IP{127, 0, 0, 1}, + Meta: PeerSystemMeta{}, + Name: "peer name", + Status: &PeerStatus{Connected: true, LastSeen: time.Now().UTC()}, + } + account.Users[testUserID] = user + + err := store.SaveAccount(account) + require.NoError(t, err) + + if len(store.GetAllAccounts()) != 1 { + t.Errorf("expecting 1 Accounts to be stored after SaveAccount()") + } + + err = store.DeleteAccount(account) + require.NoError(t, err) + + if len(store.GetAllAccounts()) != 0 { + t.Errorf("expecting 0 Accounts to be stored after DeleteAccount()") + } + + _, err = store.GetAccountByPeerPubKey("peerkey") + require.Error(t, err, "expecting error after removing DeleteAccount when getting account by peer public key") + + _, err = store.GetAccountByUser("testuser") + require.Error(t, err, "expecting error after removing DeleteAccount when getting account by user") + + _, err = store.GetAccountByPeerID("testpeer") + require.Error(t, err, "expecting error after removing DeleteAccount when getting account by peer id") + + _, err = store.GetAccountBySetupKey(setupKey.Key) + require.Error(t, err, "expecting error after removing DeleteAccount when getting account by setup key") + + _, err = store.GetAccount(account.Id) + require.Error(t, err, "expecting error after removing DeleteAccount when getting account by id") + + for _, policy := range account.Policies { + var rules []*PolicyRule + err = store.db.Model(&PolicyRule{}).Find(&rules, "policy_id = ?", policy.ID).Error + require.NoError(t, err, "expecting no error after removing DeleteAccount when searching for policy rules") + require.Len(t, rules, 0, "expecting no policy rules to be found after removing DeleteAccount") + + } + + for _, accountUser := range account.Users { + var pats []*PersonalAccessToken + err = store.db.Model(&PersonalAccessToken{}).Find(&pats, "user_id = ?", accountUser.Id).Error + require.NoError(t, err, "expecting no error after removing DeleteAccount when searching for personal access token") + require.Len(t, pats, 0, "expecting no personal access token to be found after removing DeleteAccount") + + } + +} + func TestSqlite_SavePeerStatus(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("The SQLite store is not properly supported by Windows yet") diff --git a/management/server/store.go b/management/server/store.go index 66b239f9614..25511539a28 100644 --- a/management/server/store.go +++ b/management/server/store.go @@ -14,6 +14,7 @@ import ( type Store interface { GetAllAccounts() []*Account GetAccount(accountID string) (*Account, error) + DeleteAccount(account *Account) error GetAccountByUser(userID string) (*Account, error) GetAccountByPeerPubKey(peerKey string) (*Account, error) GetAccountByPeerID(peerID string) (*Account, error) From b6211ad0202ac998b7add702bd89147492791001 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Wed, 29 Nov 2023 09:33:05 +0100 Subject: [PATCH 12/13] Fix group membership for peers API response (#1337) --- management/server/http/peers_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/management/server/http/peers_handler.go b/management/server/http/peers_handler.go index 3d0d735ecea..527e18f9d93 100644 --- a/management/server/http/peers_handler.go +++ b/management/server/http/peers_handler.go @@ -215,7 +215,7 @@ func toGroupsInfo(groups map[string]*server.Group, peerID string) []api.GroupMin } groupsChecked[group.ID] = struct{}{} for _, pk := range group.Peers { - if pk != peerID { + if pk == peerID { info := api.GroupMinimum{ Id: group.ID, Name: group.Name, From 7a46a63a14f0d65e17d990a985d2171fbae718ce Mon Sep 17 00:00:00 2001 From: Bethuel Mmbaga Date: Wed, 29 Nov 2023 17:01:27 +0300 Subject: [PATCH 13/13] Fix the inability to set hostname with the flag in daemon mode (#1339) Pass the hostname set in the flag into the protocol message when running in daemon mode. --- client/cmd/login.go | 3 +- client/cmd/up.go | 3 +- client/proto/daemon.pb.go | 264 ++++++++++++++++++++------------------ client/proto/daemon.proto | 3 + client/server/server.go | 11 ++ 5 files changed, 160 insertions(+), 124 deletions(-) diff --git a/client/cmd/login.go b/client/cmd/login.go index b643556494a..ac79199e2d6 100644 --- a/client/cmd/login.go +++ b/client/cmd/login.go @@ -85,6 +85,7 @@ var loginCmd = &cobra.Command{ PreSharedKey: preSharedKey, ManagementUrl: managementURL, IsLinuxDesktopClient: isLinuxRunningDesktop(), + Hostname: hostName, } var loginErr error @@ -114,7 +115,7 @@ var loginCmd = &cobra.Command{ if loginResp.NeedsSSOLogin { openURL(cmd, loginResp.VerificationURIComplete, loginResp.UserCode) - _, err = client.WaitSSOLogin(ctx, &proto.WaitSSOLoginRequest{UserCode: loginResp.UserCode}) + _, err = client.WaitSSOLogin(ctx, &proto.WaitSSOLoginRequest{UserCode: loginResp.UserCode, Hostname: hostName}) if err != nil { return fmt.Errorf("waiting sso login failed with: %v", err) } diff --git a/client/cmd/up.go b/client/cmd/up.go index 80ed04b5761..dd4c7290ee2 100644 --- a/client/cmd/up.go +++ b/client/cmd/up.go @@ -149,6 +149,7 @@ func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error { CleanNATExternalIPs: natExternalIPs != nil && len(natExternalIPs) == 0, CustomDNSAddress: customDNSAddressConverted, IsLinuxDesktopClient: isLinuxRunningDesktop(), + Hostname: hostName, } var loginErr error @@ -179,7 +180,7 @@ func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error { openURL(cmd, loginResp.VerificationURIComplete, loginResp.UserCode) - _, err = client.WaitSSOLogin(ctx, &proto.WaitSSOLoginRequest{UserCode: loginResp.UserCode}) + _, err = client.WaitSSOLogin(ctx, &proto.WaitSSOLoginRequest{UserCode: loginResp.UserCode, Hostname: hostName}) if err != nil { return fmt.Errorf("waiting sso login failed with: %v", err) } diff --git a/client/proto/daemon.pb.go b/client/proto/daemon.pb.go index 4dc98942019..03eb3c49b77 100644 --- a/client/proto/daemon.pb.go +++ b/client/proto/daemon.pb.go @@ -43,6 +43,7 @@ type LoginRequest struct { CleanNATExternalIPs bool `protobuf:"varint,6,opt,name=cleanNATExternalIPs,proto3" json:"cleanNATExternalIPs,omitempty"` CustomDNSAddress []byte `protobuf:"bytes,7,opt,name=customDNSAddress,proto3" json:"customDNSAddress,omitempty"` IsLinuxDesktopClient bool `protobuf:"varint,8,opt,name=isLinuxDesktopClient,proto3" json:"isLinuxDesktopClient,omitempty"` + Hostname string `protobuf:"bytes,9,opt,name=hostname,proto3" json:"hostname,omitempty"` } func (x *LoginRequest) Reset() { @@ -133,6 +134,13 @@ func (x *LoginRequest) GetIsLinuxDesktopClient() bool { return false } +func (x *LoginRequest) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + type LoginResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -210,6 +218,7 @@ type WaitSSOLoginRequest struct { unknownFields protoimpl.UnknownFields UserCode string `protobuf:"bytes,1,opt,name=userCode,proto3" json:"userCode,omitempty"` + Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"` } func (x *WaitSSOLoginRequest) Reset() { @@ -251,6 +260,13 @@ func (x *WaitSSOLoginRequest) GetUserCode() string { return "" } +func (x *WaitSSOLoginRequest) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + type WaitSSOLoginResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1051,7 +1067,7 @@ var file_daemon_proto_rawDesc = []byte{ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xca, 0x02, 0x0a, 0x0c, 0x4c, 0x6f, + 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe6, 0x02, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x53, 0x68, 0x61, @@ -1072,128 +1088,132 @@ var file_daemon_proto_rawDesc = []byte{ 0x73, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x69, 0x73, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x69, 0x73, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, - 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x22, 0xb5, 0x01, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x65, 0x65, 0x64, - 0x73, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0d, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1a, - 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x76, 0x65, - 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x49, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x55, 0x52, 0x49, 0x12, 0x38, 0x0a, 0x17, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x49, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x49, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x22, 0x31, - 0x0a, 0x13, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x64, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x64, - 0x65, 0x22, 0x16, 0x0a, 0x14, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0b, 0x0a, 0x09, 0x55, 0x70, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0c, 0x0a, 0x0a, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, - 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x11, 0x67, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, - 0x0a, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x75, 0x6c, 0x6c, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x65, 0x6d, 0x6f, - 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x0d, 0x0a, 0x0b, 0x44, 0x6f, 0x77, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0e, 0x0a, 0x0c, 0x44, 0x6f, 0x77, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb3, 0x01, 0x0a, 0x11, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x55, - 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x46, 0x69, - 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, - 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, - 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x53, 0x68, 0x61, 0x72, - 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x55, 0x52, - 0x4c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x55, 0x52, - 0x4c, 0x22, 0xcf, 0x02, 0x0a, 0x09, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, - 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, - 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x72, - 0x65, 0x63, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x12, 0x34, 0x0a, 0x15, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, - 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x15, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, - 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x74, - 0x65, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, - 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, + 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0xb5, 0x01, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x53, 0x53, 0x4f, + 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x65, 0x65, + 0x64, 0x73, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, + 0x65, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, + 0x65, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x49, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x49, + 0x12, 0x38, 0x0a, 0x17, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x55, 0x52, 0x49, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x17, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, + 0x52, 0x49, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x22, 0x4d, 0x0a, 0x13, 0x57, 0x61, + 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x57, 0x61, 0x69, + 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x0b, 0x0a, 0x09, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0c, + 0x0a, 0x0a, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x0a, 0x0d, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, + 0x11, 0x67, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x67, 0x65, 0x74, 0x46, 0x75, 0x6c, + 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x0e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x65, + 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0a, + 0x66, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x22, 0x0d, 0x0a, 0x0b, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x0e, 0x0a, 0x0c, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x12, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x22, 0xb3, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x72, 0x6c, 0x12, + 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x72, 0x65, + 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x70, 0x72, 0x65, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, + 0x08, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x55, 0x52, 0x4c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x55, 0x52, 0x4c, 0x22, 0xcf, 0x02, 0x0a, 0x09, 0x50, 0x65, + 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, + 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6c, 0x61, 0x79, + 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, + 0x64, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x06, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x12, 0x34, 0x0a, 0x15, 0x6c, 0x6f, 0x63, + 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, - 0x71, 0x64, 0x6e, 0x22, 0x76, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x28, 0x0a, - 0x0f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x22, 0x3d, 0x0a, 0x0b, 0x53, - 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, - 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x1c, 0x0a, 0x09, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0x41, 0x0a, 0x0f, 0x4d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, - 0x03, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, - 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0xef, 0x01, - 0x0a, 0x0a, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x41, 0x0a, 0x0f, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x35, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x69, - 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, - 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, - 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, - 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, - 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, - 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x32, - 0xf7, 0x02, 0x0a, 0x0d, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x12, 0x36, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x2e, 0x64, 0x61, 0x65, - 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x57, 0x61, 0x69, - 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, - 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, - 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x02, 0x55, 0x70, 0x12, 0x11, 0x2e, 0x64, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x33, 0x0a, 0x04, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, - 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, - 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x18, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x64, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x36, 0x0a, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x22, 0x76, 0x0a, 0x0e, 0x4c, + 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, + 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, + 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, + 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, + 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, + 0x71, 0x64, 0x6e, 0x22, 0x3d, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x55, 0x52, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x22, 0x41, 0x0a, 0x0f, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0xef, 0x01, 0x0a, 0x0a, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x41, 0x0a, 0x0f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x35, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, + 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3e, + 0x0a, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, + 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, + 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x32, 0xf7, 0x02, 0x0a, 0x0d, 0x44, 0x61, 0x65, 0x6d, + 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x4c, 0x6f, 0x67, + 0x69, 0x6e, 0x12, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, + 0x6e, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, + 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, + 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, + 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2d, + 0x0a, 0x02, 0x55, 0x70, 0x12, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, + 0x2e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, + 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, + 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x04, 0x44, 0x6f, 0x77, 0x6e, + 0x12, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, + 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, + 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x64, 0x61, 0x65, + 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, + 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/client/proto/daemon.proto b/client/proto/daemon.proto index 8bed1ec9dc3..c2983c94379 100644 --- a/client/proto/daemon.proto +++ b/client/proto/daemon.proto @@ -52,6 +52,8 @@ message LoginRequest { bytes customDNSAddress = 7; bool isLinuxDesktopClient = 8; + + string hostname = 9; } message LoginResponse { @@ -63,6 +65,7 @@ message LoginResponse { message WaitSSOLoginRequest { string userCode = 1; + string hostname = 2; } message WaitSSOLoginResponse {} diff --git a/client/server/server.go b/client/server/server.go index faac2227355..b9c7b0a5e04 100644 --- a/client/server/server.go +++ b/client/server/server.go @@ -7,6 +7,7 @@ import ( "time" "github.com/netbirdio/netbird/client/internal/auth" + "github.com/netbirdio/netbird/client/system" log "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" @@ -181,6 +182,11 @@ func (s *Server) Login(callerCtx context.Context, msg *proto.LoginRequest) (*pro s.latestConfigInput.CustomDNSAddress = []byte{} } + if msg.Hostname != "" { + // nolint + ctx = context.WithValue(ctx, system.DeviceNameCtxKey, msg.Hostname) + } + s.mutex.Unlock() inputConfig.PreSharedKey = &msg.PreSharedKey @@ -275,6 +281,11 @@ func (s *Server) WaitSSOLogin(callerCtx context.Context, msg *proto.WaitSSOLogin ctx = metadata.NewOutgoingContext(ctx, md) } + if msg.Hostname != "" { + // nolint + ctx = context.WithValue(ctx, system.DeviceNameCtxKey, msg.Hostname) + } + s.actCancel = cancel s.mutex.Unlock()