mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-18 08:16:39 +00:00
[management, client] Add logout feature (#4268)
This commit is contained in:
@@ -22,4 +22,5 @@ type Client interface {
|
||||
GetNetworkMap(sysInfo *system.Info) (*proto.NetworkMap, error)
|
||||
IsHealthy() bool
|
||||
SyncMeta(sysInfo *system.Info) error
|
||||
Logout() error
|
||||
}
|
||||
|
||||
@@ -497,6 +497,32 @@ func (c *GrpcClient) notifyConnected() {
|
||||
c.connStateCallback.MarkManagementConnected()
|
||||
}
|
||||
|
||||
func (c *GrpcClient) Logout() error {
|
||||
serverKey, err := c.GetServerPublicKey()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get server public key: %w", err)
|
||||
}
|
||||
|
||||
mgmCtx, cancel := context.WithTimeout(c.ctx, time.Second*5)
|
||||
defer cancel()
|
||||
|
||||
message := &proto.Empty{}
|
||||
encryptedMSG, err := encryption.EncryptMessage(*serverKey, c.key, message)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encrypt logout message: %w", err)
|
||||
}
|
||||
|
||||
_, err = c.realClient.Logout(mgmCtx, &proto.EncryptedMessage{
|
||||
WgPubKey: c.key.PublicKey().String(),
|
||||
Body: encryptedMSG,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("logout: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func infoToMetaData(info *system.Info) *proto.PeerSystemMeta {
|
||||
if info == nil {
|
||||
return nil
|
||||
|
||||
@@ -19,6 +19,7 @@ type MockClient struct {
|
||||
GetDeviceAuthorizationFlowFunc func(serverKey wgtypes.Key) (*proto.DeviceAuthorizationFlow, error)
|
||||
GetPKCEAuthorizationFlowFunc func(serverKey wgtypes.Key) (*proto.PKCEAuthorizationFlow, error)
|
||||
SyncMetaFunc func(sysInfo *system.Info) error
|
||||
LogoutFunc func() error
|
||||
}
|
||||
|
||||
func (m *MockClient) IsHealthy() bool {
|
||||
@@ -85,3 +86,10 @@ func (m *MockClient) SyncMeta(sysInfo *system.Info) error {
|
||||
}
|
||||
return m.SyncMetaFunc(sysInfo)
|
||||
}
|
||||
|
||||
func (m *MockClient) Logout() error {
|
||||
if m.LogoutFunc == nil {
|
||||
return nil
|
||||
}
|
||||
return m.LogoutFunc()
|
||||
}
|
||||
|
||||
@@ -3825,7 +3825,7 @@ var file_management_proto_rawDesc = []byte{
|
||||
0x6e, 0x12, 0x06, 0x0a, 0x02, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x55, 0x54,
|
||||
0x10, 0x01, 0x2a, 0x22, 0x0a, 0x0a, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x12, 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04,
|
||||
0x44, 0x52, 0x4f, 0x50, 0x10, 0x01, 0x32, 0x90, 0x04, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67,
|
||||
0x44, 0x52, 0x4f, 0x50, 0x10, 0x01, 0x32, 0xcd, 0x04, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67,
|
||||
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05,
|
||||
0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
||||
0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73,
|
||||
@@ -3858,8 +3858,12 @@ var file_management_proto_rawDesc = []byte{
|
||||
0x6e, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
||||
0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
|
||||
0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x06, 0x4c, 0x6f, 0x67,
|
||||
0x6f, 0x75, 0x74, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
||||
0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
|
||||
0x65, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45,
|
||||
0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -3986,15 +3990,17 @@ var file_management_proto_depIdxs = []int32{
|
||||
5, // 57: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage
|
||||
5, // 58: management.ManagementService.GetPKCEAuthorizationFlow:input_type -> management.EncryptedMessage
|
||||
5, // 59: management.ManagementService.SyncMeta:input_type -> management.EncryptedMessage
|
||||
5, // 60: management.ManagementService.Login:output_type -> management.EncryptedMessage
|
||||
5, // 61: management.ManagementService.Sync:output_type -> management.EncryptedMessage
|
||||
16, // 62: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse
|
||||
17, // 63: management.ManagementService.isHealthy:output_type -> management.Empty
|
||||
5, // 64: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage
|
||||
5, // 65: management.ManagementService.GetPKCEAuthorizationFlow:output_type -> management.EncryptedMessage
|
||||
17, // 66: management.ManagementService.SyncMeta:output_type -> management.Empty
|
||||
60, // [60:67] is the sub-list for method output_type
|
||||
53, // [53:60] is the sub-list for method input_type
|
||||
5, // 60: management.ManagementService.Logout:input_type -> management.EncryptedMessage
|
||||
5, // 61: management.ManagementService.Login:output_type -> management.EncryptedMessage
|
||||
5, // 62: management.ManagementService.Sync:output_type -> management.EncryptedMessage
|
||||
16, // 63: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse
|
||||
17, // 64: management.ManagementService.isHealthy:output_type -> management.Empty
|
||||
5, // 65: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage
|
||||
5, // 66: management.ManagementService.GetPKCEAuthorizationFlow:output_type -> management.EncryptedMessage
|
||||
17, // 67: management.ManagementService.SyncMeta:output_type -> management.Empty
|
||||
17, // 68: management.ManagementService.Logout:output_type -> management.Empty
|
||||
61, // [61:69] is the sub-list for method output_type
|
||||
53, // [53:61] is the sub-list for method input_type
|
||||
53, // [53:53] is the sub-list for extension type_name
|
||||
53, // [53:53] is the sub-list for extension extendee
|
||||
0, // [0:53] is the sub-list for field type_name
|
||||
|
||||
@@ -45,6 +45,9 @@ service ManagementService {
|
||||
// sync meta will evaluate the checks and update the peer meta with the result.
|
||||
// EncryptedMessage of the request has a body of Empty.
|
||||
rpc SyncMeta(EncryptedMessage) returns (Empty) {}
|
||||
|
||||
// Logout logs out the peer and removes it from the management server
|
||||
rpc Logout(EncryptedMessage) returns (Empty) {}
|
||||
}
|
||||
|
||||
message EncryptedMessage {
|
||||
|
||||
@@ -48,6 +48,8 @@ type ManagementServiceClient interface {
|
||||
// sync meta will evaluate the checks and update the peer meta with the result.
|
||||
// EncryptedMessage of the request has a body of Empty.
|
||||
SyncMeta(ctx context.Context, in *EncryptedMessage, opts ...grpc.CallOption) (*Empty, error)
|
||||
// Logout logs out the peer and removes it from the management server
|
||||
Logout(ctx context.Context, in *EncryptedMessage, opts ...grpc.CallOption) (*Empty, error)
|
||||
}
|
||||
|
||||
type managementServiceClient struct {
|
||||
@@ -144,6 +146,15 @@ func (c *managementServiceClient) SyncMeta(ctx context.Context, in *EncryptedMes
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *managementServiceClient) Logout(ctx context.Context, in *EncryptedMessage, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/management.ManagementService/Logout", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ManagementServiceServer is the server API for ManagementService service.
|
||||
// All implementations must embed UnimplementedManagementServiceServer
|
||||
// for forward compatibility
|
||||
@@ -178,6 +189,8 @@ type ManagementServiceServer interface {
|
||||
// sync meta will evaluate the checks and update the peer meta with the result.
|
||||
// EncryptedMessage of the request has a body of Empty.
|
||||
SyncMeta(context.Context, *EncryptedMessage) (*Empty, error)
|
||||
// Logout logs out the peer and removes it from the management server
|
||||
Logout(context.Context, *EncryptedMessage) (*Empty, error)
|
||||
mustEmbedUnimplementedManagementServiceServer()
|
||||
}
|
||||
|
||||
@@ -206,6 +219,9 @@ func (UnimplementedManagementServiceServer) GetPKCEAuthorizationFlow(context.Con
|
||||
func (UnimplementedManagementServiceServer) SyncMeta(context.Context, *EncryptedMessage) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SyncMeta not implemented")
|
||||
}
|
||||
func (UnimplementedManagementServiceServer) Logout(context.Context, *EncryptedMessage) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Logout not implemented")
|
||||
}
|
||||
func (UnimplementedManagementServiceServer) mustEmbedUnimplementedManagementServiceServer() {}
|
||||
|
||||
// UnsafeManagementServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
@@ -348,6 +364,24 @@ func _ManagementService_SyncMeta_Handler(srv interface{}, ctx context.Context, d
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ManagementService_Logout_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(EncryptedMessage)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ManagementServiceServer).Logout(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/management.ManagementService/Logout",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ManagementServiceServer).Logout(ctx, req.(*EncryptedMessage))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// ManagementService_ServiceDesc is the grpc.ServiceDesc for ManagementService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@@ -379,6 +413,10 @@ var ManagementService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "SyncMeta",
|
||||
Handler: _ManagementService_SyncMeta_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Logout",
|
||||
Handler: _ManagementService_Logout_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
|
||||
@@ -19,7 +19,9 @@ import (
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
integrationsConfig "github.com/netbirdio/management-integrations/integrations/config"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/integrations/integrated_validator"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
|
||||
"github.com/netbirdio/netbird/encryption"
|
||||
"github.com/netbirdio/netbird/management/proto"
|
||||
@@ -909,6 +911,44 @@ func (s *GRPCServer) SyncMeta(ctx context.Context, req *proto.EncryptedMessage)
|
||||
return &proto.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *GRPCServer) Logout(ctx context.Context, req *proto.EncryptedMessage) (*proto.Empty, error) {
|
||||
log.WithContext(ctx).Debugf("Logout request from peer [%s]", req.WgPubKey)
|
||||
|
||||
empty := &proto.Empty{}
|
||||
peerKey, err := s.parseRequest(ctx, req, empty)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
peer, err := s.accountManager.GetStore().GetPeerByPeerPubKey(ctx, store.LockingStrengthShare, peerKey.String())
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Debugf("peer %s is not registered for logout", peerKey.String())
|
||||
// TODO: consider idempotency
|
||||
return nil, mapError(ctx, err)
|
||||
}
|
||||
|
||||
// nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, nbContext.PeerIDKey, peer.ID)
|
||||
// nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, nbContext.AccountIDKey, peer.AccountID)
|
||||
|
||||
userID := peer.UserID
|
||||
if userID == "" {
|
||||
userID = activity.SystemInitiator
|
||||
}
|
||||
|
||||
if err = s.accountManager.DeletePeer(ctx, peer.AccountID, peer.ID, userID); err != nil {
|
||||
log.WithContext(ctx).Errorf("failed to logout peer %s: %v", peerKey.String(), err)
|
||||
return nil, mapError(ctx, err)
|
||||
}
|
||||
|
||||
s.accountManager.BufferUpdateAccountPeers(ctx, peer.AccountID)
|
||||
|
||||
log.WithContext(ctx).Infof("peer %s logged out successfully", peerKey.String())
|
||||
|
||||
return &proto.Empty{}, nil
|
||||
}
|
||||
|
||||
// toProtocolChecks converts posture checks to protocol checks.
|
||||
func toProtocolChecks(ctx context.Context, postureChecks []*posture.Checks) []*proto.Checks {
|
||||
protoChecks := make([]*proto.Checks, 0, len(postureChecks))
|
||||
|
||||
Reference in New Issue
Block a user