From 181dd93695f62fbe748aab40c7582e8173fd0af7 Mon Sep 17 00:00:00 2001 From: Viktor Liu <17948409+lixmal@users.noreply.github.com> Date: Wed, 14 Aug 2024 12:15:02 +0200 Subject: [PATCH 1/5] [client] Update png systray disconnected icon (#2428) --- client/ui/netbird-systemtray-disconnected.png | Bin 9258 -> 9816 bytes ...netbird-systemtray-update-disconnected.png | Bin 11929 -> 12437 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/client/ui/netbird-systemtray-disconnected.png b/client/ui/netbird-systemtray-disconnected.png index 0e1b7275f3f048770befda090aba02ee1deb4de8..3aae73231cf22b82a85962695cc5af29885ec802 100644 GIT binary patch delta 5938 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%0DA$VN=0^HnDeg1j6mi)lA z8r95Sz1HBV_tl^s&86hf<+lChY)&#Yv}j693MD#GEdo^a z2kF0$JwX2Y6+;`cN)O2wkbjXg_M9@d(C`9I&kyDS(pYOy+Nq2`jZsntzy_1B_XNDy~o6k|20lW z^n2TXWKR9ZIn*~#E?=IB>s45iCSCrfyYpVujXZ|y5G7a1q1TdJMk-)amp07Wr}gn+ z()~P?Tvhs<8G|=+VF27db9p-zu9g7ro;vW-g@&Gf4ZS9c^c^lxPoeMw)1U7T+VJdc zZpIYol^t#mhQ+Q>WC&7AMaMX5zT~{OOWsLY2wxf8^*k(qtX{a%9iiU4-Cr_c*gl;R zGsqzZD38y+uXdcg2jWX#A9~){Byg-a1B=J9j{a%V zHUcM#wufR80@IT~kM5$4>*N+_LuctFuJfSNEc((o*;KHUYIgiE2?#M&_BuZEDe%_( zdpr>B(jQ|^gZ$IYmg0RTqa921t*yL1GhB#R=@hm$xRJGzk z+q5Vnd+(Y*Nsu+SER1f}BLpQOu^S?;18&-Z^#f~nnnVv$?bqs!vAzdFjWVhPvE zJsdB{I=%+>T)`BOGNF5rP91OrwNp-*HRR7JA`Ood{PZ`O zX_juEfS)C%pF$~Tp;kfaKJ>B%ZS0Y0vw5ilCjhwP!_WQv_Vwc{$&XEpXvMGZFxe_r z7pydD**ho0&1hEp?O#w&C&n6{^ff%5%{#jf7WaEN?Z&~W;qM`$d7hT_7jP!&4Xqq+ zY}U6$!Kf8N#SZ(IGZVv8^G9NdwB}Rhmyi*_^KZh`97E58-;*PUT)L>yccz|_tY#^V zN%D3R(dPs1Qp@3KNIp74)Sqj7R*HGmpW3bRf0LmZJxES9sUYA_oN`xzf0F81R=dog zC)`m!N!`odt@A|~TbLl^gkPSWLcdQ*KBo=|Pcs3R2-AYI)M^{~v_-W$;?(HV0=-lv z^5!Q6S1`uusYC=Zj4Lrc>FEVYpBtht6Pve>ekS~v|I`;*%JP#WPXBE@&HqAA8Cd4> zlB3d;s@G2`T;Y-Zt_L#LJVZO>V+J#|Im1&(-}b)T)mb+@nc(_<8l``KJUeTeY$|pf iU(NTw^E1wNizB{L)T06V%{pIzdg*JMXuZ|INB;*}Gaxts delta 5569 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 wR0$f*f~UWGa&b1qT;2aK9?tN;K2 diff --git a/client/ui/netbird-systemtray-update-disconnected.png b/client/ui/netbird-systemtray-update-disconnected.png index 44a30dc9a773cdbf9ba60b3dd2cc0b849aeb2d0f..3fbe88953aac6d630a053a45c5d8a51ea2a0e4e3 100644 GIT binary patch delta 8580 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 G)c*hvnsxI4 delta 8235 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 From 6016d2f7ceb468547876416b835abf5edd3bbc53 Mon Sep 17 00:00:00 2001 From: Bethuel Mmbaga Date: Wed, 14 Aug 2024 13:30:10 +0300 Subject: [PATCH 2/5] Fix lint (#2427) --- client/internal/auth/device_flow.go | 3 ++- client/internal/engine.go | 6 +++--- client/internal/routemanager/sysctl/sysctl_linux.go | 3 ++- client/ssh/server.go | 6 +++--- management/client/grpc.go | 7 ++++--- management/server/grpcserver.go | 12 ++++++------ .../server/http/posture_checks_handler_test.go | 2 +- management/server/idp/auth0_test.go | 5 +++-- management/server/jwtclaims/jwtValidator.go | 4 ++-- management/server/posture_checks.go | 2 +- sharedsock/sock_nolinux.go | 2 +- 11 files changed, 28 insertions(+), 24 deletions(-) diff --git a/client/internal/auth/device_flow.go b/client/internal/auth/device_flow.go index 3c51fe4f5..87d00de5e 100644 --- a/client/internal/auth/device_flow.go +++ b/client/internal/auth/device_flow.go @@ -3,6 +3,7 @@ package auth import ( "context" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -180,7 +181,7 @@ func (d *DeviceAuthorizationFlow) WaitToken(ctx context.Context, info AuthFlowIn continue } - return TokenInfo{}, fmt.Errorf(tokenResponse.ErrorDescription) + return TokenInfo{}, errors.New(tokenResponse.ErrorDescription) } tokenInfo := TokenInfo{ diff --git a/client/internal/engine.go b/client/internal/engine.go index 9e275c007..d65322d6a 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -960,9 +960,9 @@ func (e *Engine) connWorker(conn *peer.Conn, peerKey string) { for { // randomize starting time a bit - min := 500 - max := 2000 - duration := time.Duration(rand.Intn(max-min)+min) * time.Millisecond + minValue := 500 + maxValue := 2000 + duration := time.Duration(rand.Intn(maxValue-minValue)+minValue) * time.Millisecond select { case <-e.ctx.Done(): return diff --git a/client/internal/routemanager/sysctl/sysctl_linux.go b/client/internal/routemanager/sysctl/sysctl_linux.go index 3f2937c89..43394a823 100644 --- a/client/internal/routemanager/sysctl/sysctl_linux.go +++ b/client/internal/routemanager/sysctl/sysctl_linux.go @@ -1,4 +1,5 @@ -// go:build !android +//go:build !android + package sysctl import ( diff --git a/client/ssh/server.go b/client/ssh/server.go index ae5c65c4a..a390302b7 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -118,9 +118,9 @@ func (srv *DefaultServer) publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) b func prepareUserEnv(user *user.User, shell string) []string { return []string{ - fmt.Sprintf("SHELL=" + shell), - fmt.Sprintf("USER=" + user.Username), - fmt.Sprintf("HOME=" + user.HomeDir), + fmt.Sprint("SHELL=" + shell), + fmt.Sprint("USER=" + user.Username), + fmt.Sprint("HOME=" + user.HomeDir), } } diff --git a/management/client/grpc.go b/management/client/grpc.go index eaadcd317..74e808c32 100644 --- a/management/client/grpc.go +++ b/management/client/grpc.go @@ -2,6 +2,7 @@ package client import ( "context" + "errors" "fmt" "io" "sync" @@ -267,7 +268,7 @@ func (c *GrpcClient) receiveEvents(stream proto.ManagementService_SyncClient, se // GetServerPublicKey returns server's WireGuard public key (used later for encrypting messages sent to the server) func (c *GrpcClient) GetServerPublicKey() (*wgtypes.Key, error) { if !c.ready() { - return nil, fmt.Errorf(errMsgNoMgmtConnection) + return nil, errors.New(errMsgNoMgmtConnection) } mgmCtx, cancel := context.WithTimeout(c.ctx, 5*time.Second) @@ -314,7 +315,7 @@ func (c *GrpcClient) IsHealthy() bool { func (c *GrpcClient) login(serverKey wgtypes.Key, req *proto.LoginRequest) (*proto.LoginResponse, error) { if !c.ready() { - return nil, fmt.Errorf(errMsgNoMgmtConnection) + return nil, errors.New(errMsgNoMgmtConnection) } loginReq, err := encryption.EncryptMessage(serverKey, c.key, req) @@ -452,7 +453,7 @@ func (c *GrpcClient) GetPKCEAuthorizationFlow(serverKey wgtypes.Key) (*proto.PKC // It should be used if there is changes on peer posture check after initial sync. func (c *GrpcClient) SyncMeta(sysInfo *system.Info) error { if !c.ready() { - return fmt.Errorf(errMsgNoMgmtConnection) + return errors.New(errMsgNoMgmtConnection) } serverPubKey, err := c.GetServerPublicKey() diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index 7738abe5e..ff7a71cfd 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -257,7 +257,7 @@ func (s *GRPCServer) validateToken(ctx context.Context, jwtToken string) (string } if err := s.accountManager.CheckUserAccessByJWTGroups(ctx, claims); err != nil { - return "", status.Errorf(codes.PermissionDenied, err.Error()) + return "", status.Error(codes.PermissionDenied, err.Error()) } return claims.UserId, nil @@ -268,15 +268,15 @@ func mapError(ctx context.Context, err error) error { if e, ok := internalStatus.FromError(err); ok { switch e.Type() { case internalStatus.PermissionDenied: - return status.Errorf(codes.PermissionDenied, e.Message) + return status.Error(codes.PermissionDenied, e.Message) case internalStatus.Unauthorized: - return status.Errorf(codes.PermissionDenied, e.Message) + return status.Error(codes.PermissionDenied, e.Message) case internalStatus.Unauthenticated: - return status.Errorf(codes.PermissionDenied, e.Message) + return status.Error(codes.PermissionDenied, e.Message) case internalStatus.PreconditionFailed: - return status.Errorf(codes.FailedPrecondition, e.Message) + return status.Error(codes.FailedPrecondition, e.Message) case internalStatus.NotFound: - return status.Errorf(codes.NotFound, e.Message) + return status.Error(codes.NotFound, e.Message) default: } } diff --git a/management/server/http/posture_checks_handler_test.go b/management/server/http/posture_checks_handler_test.go index dcb6e4924..974edafde 100644 --- a/management/server/http/posture_checks_handler_test.go +++ b/management/server/http/posture_checks_handler_test.go @@ -46,7 +46,7 @@ func initPostureChecksTestData(postureChecks ...*posture.Checks) *PostureChecksH testPostureChecks[postureChecks.ID] = postureChecks if err := postureChecks.Validate(); err != nil { - return status.Errorf(status.InvalidArgument, err.Error()) + return status.Errorf(status.InvalidArgument, err.Error()) //nolint } return nil diff --git a/management/server/idp/auth0_test.go b/management/server/idp/auth0_test.go index de42ced99..f8a0e1210 100644 --- a/management/server/idp/auth0_test.go +++ b/management/server/idp/auth0_test.go @@ -3,6 +3,7 @@ package idp import ( "context" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -44,14 +45,14 @@ type mockJsonParser struct { func (m *mockJsonParser) Marshal(v interface{}) ([]byte, error) { if m.marshalErrorString != "" { - return nil, fmt.Errorf(m.marshalErrorString) + return nil, errors.New(m.marshalErrorString) } return m.jsonParser.Marshal(v) } func (m *mockJsonParser) Unmarshal(data []byte, v interface{}) error { if m.unmarshalErrorString != "" { - return fmt.Errorf(m.unmarshalErrorString) + return errors.New(m.unmarshalErrorString) } return m.jsonParser.Unmarshal(data, v) } diff --git a/management/server/jwtclaims/jwtValidator.go b/management/server/jwtclaims/jwtValidator.go index c3417a769..39676982e 100644 --- a/management/server/jwtclaims/jwtValidator.go +++ b/management/server/jwtclaims/jwtValidator.go @@ -150,7 +150,7 @@ func (m *JWTValidator) ValidateAndParse(ctx context.Context, token string) (*jwt // If we get here, the required token is missing errorMsg := "required authorization token not found" log.WithContext(ctx).Debugf(" Error: No credentials found (CredentialsOptional=false)") - return nil, fmt.Errorf(errorMsg) + return nil, errors.New(errorMsg) } // Now parse the token @@ -173,7 +173,7 @@ func (m *JWTValidator) ValidateAndParse(ctx context.Context, token string) (*jwt // Check if the parsed token is valid... if !parsedToken.Valid { errorMsg := "token is invalid" - log.WithContext(ctx).Debugf(errorMsg) + log.WithContext(ctx).Debug(errorMsg) return nil, errors.New(errorMsg) } diff --git a/management/server/posture_checks.go b/management/server/posture_checks.go index 4a7c9755d..4180550e6 100644 --- a/management/server/posture_checks.go +++ b/management/server/posture_checks.go @@ -60,7 +60,7 @@ func (am *DefaultAccountManager) SavePostureChecks(ctx context.Context, accountI } if err := postureChecks.Validate(); err != nil { - return status.Errorf(status.InvalidArgument, err.Error()) + return status.Errorf(status.InvalidArgument, err.Error()) //nolint } exists, uniqName := am.savePostureChecks(account, postureChecks) diff --git a/sharedsock/sock_nolinux.go b/sharedsock/sock_nolinux.go index 93ac6b96f..a36ef67c6 100644 --- a/sharedsock/sock_nolinux.go +++ b/sharedsock/sock_nolinux.go @@ -10,5 +10,5 @@ import ( // Listen is not supported on other platforms then Linux func Listen(port int, filter BPFFilter) (net.PacketConn, error) { - return nil, fmt.Errorf(fmt.Sprintf("Not supported OS %s. SharedSocket is only supported on Linux", runtime.GOOS)) + return nil, fmt.Errorf("not supported OS %s. SharedSocket is only supported on Linux", runtime.GOOS) } From a6c59601f92c09a4b40a0c016c2fed7dcb8f2465 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sun, 18 Aug 2024 14:19:31 +0200 Subject: [PATCH 3/5] Update Slack invite link (#2445) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 370445412..1c5e76627 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@
- +

From 049b5fb7ede553da0d812590d083b6c77e5ca4a2 Mon Sep 17 00:00:00 2001 From: pascal-fischer <32096965+pascal-fischer@users.noreply.github.com> Date: Mon, 19 Aug 2024 12:50:11 +0200 Subject: [PATCH 4/5] Split DB calls in peer login (#2439) --- management/server/account.go | 22 ++++++ management/server/file_store.go | 29 ++++++++ management/server/peer.go | 116 +++++++++++++++++--------------- management/server/sql_store.go | 28 ++++++++ management/server/store.go | 2 + 5 files changed, 144 insertions(+), 53 deletions(-) diff --git a/management/server/account.go b/management/server/account.go index 972272746..4c150fd7e 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -2072,6 +2072,28 @@ func (am *DefaultAccountManager) GetAccountIDForPeerKey(ctx context.Context, pee return am.Store.GetAccountIDByPeerPubKey(ctx, peerKey) } +func (am *DefaultAccountManager) handleUserPeer(ctx context.Context, peer *nbpeer.Peer, settings *Settings) (bool, error) { + user, err := am.Store.GetUserByUserID(ctx, peer.UserID) + if err != nil { + return false, err + } + + err = checkIfPeerOwnerIsBlocked(peer, user) + if err != nil { + return false, err + } + + if peerLoginExpired(ctx, peer, settings) { + err = am.handleExpiredPeer(ctx, user, peer) + if err != nil { + return false, err + } + return true, nil + } + + return false, nil +} + // addAllGroup to account object if it doesn't exist func addAllGroup(account *Account) error { if len(account.Groups) == 0 { diff --git a/management/server/file_store.go b/management/server/file_store.go index 6e3536bcd..1927568ef 100644 --- a/management/server/file_store.go +++ b/management/server/file_store.go @@ -469,6 +469,35 @@ func (s *FileStore) GetUserByTokenID(_ context.Context, tokenID string) (*User, return account.Users[userID].Copy(), nil } +func (s *FileStore) GetUserByUserID(_ context.Context, userID string) (*User, error) { + accountID, ok := s.UserID2AccountID[userID] + if !ok { + return nil, status.Errorf(status.NotFound, "accountID not found: provided userID doesn't exists") + } + + account, err := s.getAccount(accountID) + if err != nil { + return nil, err + } + + return account.Users[userID].Copy(), nil +} + +func (s *FileStore) GetAccountGroups(ctx context.Context, accountID string) ([]*nbgroup.Group, error) { + account, err := s.getAccount(accountID) + if err != nil { + return nil, err + } + + groupsSlice := make([]*nbgroup.Group, 0, len(account.Groups)) + + for _, group := range account.Groups { + groupsSlice = append(groupsSlice, group) + } + + return groupsSlice, nil +} + // GetAllAccounts returns all accounts func (s *FileStore) GetAllAccounts(_ context.Context) (all []*Account) { s.mux.Lock() diff --git a/management/server/peer.go b/management/server/peer.go index 7afe6ee0d..93234d9de 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -549,16 +549,25 @@ func (am *DefaultAccountManager) SyncPeer(ctx context.Context, sync PeerSync, ac return nil, nil, nil, status.NewPeerNotRegisteredError() } - err = checkIfPeerOwnerIsBlocked(peer, account) - if err != nil { - return nil, nil, nil, err + if peer.UserID != "" { + log.Infof("Peer has no userID") + + user, err := account.FindUser(peer.UserID) + if err != nil { + return nil, nil, nil, err + } + + err = checkIfPeerOwnerIsBlocked(peer, user) + if err != nil { + return nil, nil, nil, err + } } if peerLoginExpired(ctx, peer, account.Settings) { return nil, nil, nil, status.NewPeerLoginExpiredError() } - peer, updated := updatePeerMeta(peer, sync.Meta, account) + updated := peer.UpdateMetaIfNew(sync.Meta) if updated { err = am.Store.SavePeer(ctx, account.Id, peer) if err != nil { @@ -624,31 +633,28 @@ func (am *DefaultAccountManager) LoginPeer(ctx context.Context, login PeerLogin) // it means that the client has already checked if it needs login and had been through the SSO flow // so, we can skip this check and directly proceed with the login if login.UserID == "" { + log.Info("Peer needs login") err = am.checkIFPeerNeedsLoginWithoutLock(ctx, accountID, login) if err != nil { return nil, nil, nil, err } } - unlock := am.Store.AcquireWriteLockByUID(ctx, accountID) + unlockAccount := am.Store.AcquireReadLockByUID(ctx, accountID) + defer unlockAccount() + unlockPeer := am.Store.AcquireWriteLockByUID(ctx, login.WireGuardPubKey) defer func() { - if unlock != nil { - unlock() + if unlockPeer != nil { + unlockPeer() } }() - // fetch the account from the store once more after acquiring lock to avoid concurrent updates inconsistencies - account, err := am.Store.GetAccount(ctx, accountID) + peer, err := am.Store.GetPeerByPeerPubKey(ctx, login.WireGuardPubKey) if err != nil { return nil, nil, nil, err } - peer, err := account.FindPeerByPubKey(login.WireGuardPubKey) - if err != nil { - return nil, nil, nil, status.NewPeerNotRegisteredError() - } - - err = checkIfPeerOwnerIsBlocked(peer, account) + settings, err := am.Store.GetAccountSettings(ctx, accountID) if err != nil { return nil, nil, nil, err } @@ -656,21 +662,39 @@ func (am *DefaultAccountManager) LoginPeer(ctx context.Context, login PeerLogin) // this flag prevents unnecessary calls to the persistent store. shouldStorePeer := false updateRemotePeers := false - if peerLoginExpired(ctx, peer, account.Settings) { - err = am.handleExpiredPeer(ctx, login, account, peer) + + if login.UserID != "" { + changed, err := am.handleUserPeer(ctx, peer, settings) if err != nil { return nil, nil, nil, err } - updateRemotePeers = true - shouldStorePeer = true + if changed { + shouldStorePeer = true + updateRemotePeers = true + } } - isRequiresApproval, isStatusChanged, err := am.integratedPeerValidator.IsNotValidPeer(ctx, account.Id, peer, account.GetPeerGroupsList(peer.ID), account.Settings.Extra) + groups, err := am.Store.GetAccountGroups(ctx, accountID) if err != nil { return nil, nil, nil, err } - peer, updated := updatePeerMeta(peer, login.Meta, account) + var grps []string + for _, group := range groups { + for _, id := range group.Peers { + if id == peer.ID { + grps = append(grps, group.ID) + break + } + } + } + + isRequiresApproval, isStatusChanged, err := am.integratedPeerValidator.IsNotValidPeer(ctx, accountID, peer, grps, settings.Extra) + if err != nil { + return nil, nil, nil, err + } + + updated := peer.UpdateMetaIfNew(login.Meta) if updated { shouldStorePeer = true } @@ -687,8 +711,13 @@ func (am *DefaultAccountManager) LoginPeer(ctx context.Context, login PeerLogin) } } - unlock() - unlock = nil + unlockPeer() + unlockPeer = nil + + account, err := am.Store.GetAccount(ctx, accountID) + if err != nil { + return nil, nil, nil, err + } if updateRemotePeers || isStatusChanged { am.updateAccountPeers(ctx, account) @@ -746,36 +775,30 @@ func (am *DefaultAccountManager) getValidatedPeerWithMap(ctx context.Context, is return peer, account.GetPeerNetworkMap(ctx, peer.ID, customZone, approvedPeersMap, am.metrics.AccountManagerMetrics()), postureChecks, nil } -func (am *DefaultAccountManager) handleExpiredPeer(ctx context.Context, login PeerLogin, account *Account, peer *nbpeer.Peer) error { - err := checkAuth(ctx, login.UserID, peer) +func (am *DefaultAccountManager) handleExpiredPeer(ctx context.Context, user *User, peer *nbpeer.Peer) error { + err := checkAuth(ctx, user.Id, peer) if err != nil { return err } // If peer was expired before and if it reached this point, it is re-authenticated. // UserID is present, meaning that JWT validation passed successfully in the API layer. - updatePeerLastLogin(peer, account) - - // sync user last login with peer last login - user, err := account.FindUser(login.UserID) - if err != nil { - return status.Errorf(status.Internal, "couldn't find user") - } - - err = am.Store.SaveUserLastLogin(account.Id, user.Id, peer.LastLogin) + peer = peer.UpdateLastLogin() + err = am.Store.SavePeer(ctx, peer.AccountID, peer) if err != nil { return err } - am.StoreEvent(ctx, login.UserID, peer.ID, account.Id, activity.UserLoggedInPeer, peer.EventMeta(am.GetDNSDomain())) + err = am.Store.SaveUserLastLogin(user.AccountID, user.Id, peer.LastLogin) + if err != nil { + return err + } + + am.StoreEvent(ctx, user.Id, peer.ID, user.AccountID, activity.UserLoggedInPeer, peer.EventMeta(am.GetDNSDomain())) return nil } -func checkIfPeerOwnerIsBlocked(peer *nbpeer.Peer, account *Account) error { +func checkIfPeerOwnerIsBlocked(peer *nbpeer.Peer, user *User) error { if peer.AddedWithSSOLogin() { - user, err := account.FindUser(peer.UserID) - if err != nil { - return status.Errorf(status.PermissionDenied, "user doesn't exist") - } if user.IsBlocked() { return status.Errorf(status.PermissionDenied, "user is blocked") } @@ -805,11 +828,6 @@ func peerLoginExpired(ctx context.Context, peer *nbpeer.Peer, settings *Settings return false } -func updatePeerLastLogin(peer *nbpeer.Peer, account *Account) { - peer.UpdateLastLogin() - account.UpdatePeer(peer) -} - // UpdatePeerSSHKey updates peer's public SSH key func (am *DefaultAccountManager) UpdatePeerSSHKey(ctx context.Context, peerID string, sshKey string) error { if sshKey == "" { @@ -908,14 +926,6 @@ func (am *DefaultAccountManager) GetPeer(ctx context.Context, accountID, peerID, return nil, status.Errorf(status.Internal, "user %s has no access to peer %s under account %s", userID, peerID, accountID) } -func updatePeerMeta(peer *nbpeer.Peer, meta nbpeer.PeerSystemMeta, account *Account) (*nbpeer.Peer, bool) { - if peer.UpdateMetaIfNew(meta) { - account.UpdatePeer(peer) - return peer, true - } - return peer, false -} - // updateAccountPeers updates all peers that belong to an account. // Should be called when changes have to be synced to peers. func (am *DefaultAccountManager) updateAccountPeers(ctx context.Context, account *Account) { diff --git a/management/server/sql_store.go b/management/server/sql_store.go index c44ab7f09..912e31410 100644 --- a/management/server/sql_store.go +++ b/management/server/sql_store.go @@ -468,6 +468,34 @@ func (s *SqlStore) GetUserByTokenID(ctx context.Context, tokenID string) (*User, return &user, nil } +func (s *SqlStore) GetUserByUserID(ctx context.Context, userID string) (*User, error) { + var user User + result := s.db.First(&user, idQueryCondition, userID) + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return nil, status.Errorf(status.NotFound, "user not found: index lookup failed") + } + log.WithContext(ctx).Errorf("error when getting user from the store: %s", result.Error) + return nil, status.Errorf(status.Internal, "issue getting user from store") + } + + return &user, nil +} + +func (s *SqlStore) GetAccountGroups(ctx context.Context, accountID string) ([]*nbgroup.Group, error) { + var groups []*nbgroup.Group + result := s.db.Find(&groups, idQueryCondition, accountID) + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return nil, status.Errorf(status.NotFound, "accountID not found: index lookup failed") + } + log.WithContext(ctx).Errorf("error when getting groups from the store: %s", result.Error) + return nil, status.Errorf(status.Internal, "issue getting groups from store") + } + + return groups, nil +} + func (s *SqlStore) GetAllAccounts(ctx context.Context) (all []*Account) { var accounts []Account result := s.db.Find(&accounts) diff --git a/management/server/store.go b/management/server/store.go index 864871c8e..a2b489391 100644 --- a/management/server/store.go +++ b/management/server/store.go @@ -41,6 +41,8 @@ type Store interface { GetAccountByPrivateDomain(ctx context.Context, domain string) (*Account, error) GetTokenIDByHashedToken(ctx context.Context, secret string) (string, error) GetUserByTokenID(ctx context.Context, tokenID string) (*User, error) + GetUserByUserID(ctx context.Context, userID string) (*User, error) + GetAccountGroups(ctx context.Context, accountID string) ([]*nbgroup.Group, error) GetPostureCheckByChecksDefinition(accountID string, checks *posture.ChecksDefinition) (*posture.Checks, error) SaveAccount(ctx context.Context, account *Account) error SaveUsers(accountID string, users map[string]*User) error From d2b04922e9a46e41581f9df5fbd194f1db265c4a Mon Sep 17 00:00:00 2001 From: pascal-fischer <32096965+pascal-fischer@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:46:58 +0200 Subject: [PATCH 5/5] Add script for loading tun module for synology (#2423) --- release_files/install.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/release_files/install.sh b/release_files/install.sh index 198d74428..d9d436ba5 100755 --- a/release_files/install.sh +++ b/release_files/install.sh @@ -151,6 +151,22 @@ add_aur_repo() { ${SUDO} pacman -Rs "$REMOVE_PKGS" --noconfirm } +prepare_tun_module() { + # Create the necessary file structure for /dev/net/tun + if [ ! -c /dev/net/tun ]; then + if [ ! -d /dev/net ]; then + mkdir -m 755 /dev/net + fi + mknod /dev/net/tun c 10 200 + chmod 0755 /dev/net/tun + fi + + # Load the tun module if not already loaded + if ! lsmod | grep -q "^tun\s"; then + insmod /lib/modules/tun.ko + fi +} + install_native_binaries() { # Checks for supported architecture case "$ARCH" in @@ -268,6 +284,10 @@ install_netbird() { ;; esac + if [ "$OS_NAME" = "synology" ]; then + prepare_tun_module + fi + # Add package manager to config ${SUDO} mkdir -p "$CONFIG_FOLDER" echo "package_manager=$PACKAGE_MANAGER" | ${SUDO} tee "$CONFIG_FILE" > /dev/null