Merge branch 'main' into feat/network-map-serial

This commit is contained in:
Zoltan Papp
2025-12-18 15:59:53 +01:00
522 changed files with 44940 additions and 8063 deletions
+18 -6
View File
@@ -18,6 +18,13 @@ import (
"google.golang.org/grpc/status"
"github.com/netbirdio/management-integrations/integrations"
"github.com/netbirdio/netbird/management/internals/controllers/network_map/controller"
"github.com/netbirdio/netbird/management/internals/controllers/network_map/update_channel"
"github.com/netbirdio/netbird/management/internals/modules/peers"
"github.com/netbirdio/netbird/management/internals/modules/peers/ephemeral/manager"
nbgrpc "github.com/netbirdio/netbird/management/internals/shared/grpc"
"github.com/netbirdio/netbird/client/system"
"github.com/netbirdio/netbird/encryption"
"github.com/netbirdio/netbird/management/internals/server/config"
@@ -26,7 +33,6 @@ import (
"github.com/netbirdio/netbird/management/server/groups"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/mock_server"
"github.com/netbirdio/netbird/management/server/peers"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/store"
@@ -66,7 +72,6 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
}
t.Cleanup(cleanUp)
peersUpdateManager := mgmt.NewPeersUpdateManager(nil)
eventStore := &activity.InMemoryEventStore{}
ctrl := gomock.NewController(t)
@@ -109,15 +114,22 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
Return(&types.ExtraSettings{}, nil).
AnyTimes()
accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false)
ctx := context.Background()
updateManager := update_channel.NewPeersUpdateManager(metrics)
requestBuffer := mgmt.NewAccountRequestBuffer(ctx, store)
networkMapController := controller.NewController(ctx, store, metrics, updateManager, requestBuffer, mgmt.MockIntegratedValidator{}, settingsMockManager, "netbird.selfhosted", port_forwarding.NewControllerMock(), manager.NewEphemeralManager(store, peersManger), config)
accountManager, err := mgmt.BuildManager(context.Background(), config, store, networkMapController, nil, "", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false)
if err != nil {
t.Fatal(err)
}
groupsManager := groups.NewManagerMock()
secretsManager := mgmt.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay, settingsMockManager, groupsManager)
mgmtServer, err := mgmt.NewServer(context.Background(), config, accountManager, settingsMockManager, peersUpdateManager, secretsManager, nil, nil, nil, mgmt.MockIntegratedValidator{})
secretsManager, err := nbgrpc.NewTimeBasedAuthSecretsManager(updateManager, config.TURNConfig, config.Relay, settingsMockManager, groupsManager)
if err != nil {
t.Fatal(err)
}
mgmtServer, err := nbgrpc.NewServer(config, accountManager, settingsMockManager, secretsManager, nil, nil, mgmt.MockIntegratedValidator{}, networkMapController)
if err != nil {
t.Fatal(err)
}
@@ -301,7 +313,7 @@ func TestClient_Sync(t *testing.T) {
defer cancel()
go func() {
err = client.Sync(ctx, info, func(msg *mgmtProto.SyncResponse) error {
err = client.Sync(ctx, info, 0, func(msg *mgmtProto.SyncResponse) error {
ch <- msg
return nil
})
+12 -11
View File
@@ -1,19 +1,20 @@
package common
// LoginFlag introduces additional login flags to the PKCE authorization request
// LoginFlag introduces additional login flags to the PKCE authorization request.
//
// # Config Values
//
// | Value | Flag | OAuth Parameters |
// |-------|----------------------|-----------------------------------------|
// | 0 | LoginFlagPromptLogin | prompt=login |
// | 1 | LoginFlagMaxAge0 | max_age=0 |
type LoginFlag uint8
const (
// LoginFlagPrompt adds prompt=login to the authorization request
LoginFlagPrompt LoginFlag = iota
// LoginFlagPromptLogin adds prompt=login to the authorization request
LoginFlagPromptLogin LoginFlag = iota
// LoginFlagMaxAge0 adds max_age=0 to the authorization request
LoginFlagMaxAge0
// LoginFlagNone disables all login flags
LoginFlagNone
)
func (l LoginFlag) IsPromptLogin() bool {
return l == LoginFlagPrompt
}
func (l LoginFlag) IsMaxAge0Login() bool {
return l == LoginFlagMaxAge0
}
+3 -3
View File
@@ -22,6 +22,7 @@ import (
"github.com/netbirdio/netbird/encryption"
"github.com/netbirdio/netbird/shared/management/domain"
"github.com/netbirdio/netbird/shared/management/proto"
"github.com/netbirdio/netbird/util/wsproxy"
)
const ConnectTimeout = 10 * time.Second
@@ -52,10 +53,9 @@ func NewClient(ctx context.Context, addr string, ourPrivateKey wgtypes.Key, tlsE
operation := func() error {
var err error
conn, err = nbgrpc.CreateConnection(ctx, addr, tlsEnabled)
conn, err = nbgrpc.CreateConnection(ctx, addr, tlsEnabled, wsproxy.ManagementComponent)
if err != nil {
log.Printf("createConnection error: %v", err)
return err
return fmt.Errorf("create connection: %w", err)
}
return nil
}
-26
View File
@@ -1,26 +0,0 @@
package client
import (
"testing"
)
func TestGrpcClient_LastNetworkMapSerial_SetGet(t *testing.T) {
c := &GrpcClient{}
if got := c.getLastNetworkMapSerial(); got != 0 {
t.Fatalf("initial serial should be 0, got %d", got)
}
c.setLastNetworkMapSerial(123)
if got := c.getLastNetworkMapSerial(); got != 123 {
t.Fatalf("serial after set should be 123, got %d", got)
}
// overwrite should work
c.setLastNetworkMapSerial(5)
if got := c.getLastNetworkMapSerial(); got != 5 {
t.Fatalf("serial after overwrite should be 5, got %d", got)
}
}
+25
View File
@@ -4,10 +4,14 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"github.com/netbirdio/netbird/shared/management/http/api"
)
// ErrGroupNotFound is returned when a group is not found
var ErrGroupNotFound = errors.New("group not found")
// GroupsAPI APIs for Groups, do not use directly
type GroupsAPI struct {
c *Client
@@ -27,6 +31,27 @@ func (a *GroupsAPI) List(ctx context.Context) ([]api.Group, error) {
return ret, err
}
// GetByName get group by name
// See more: https://docs.netbird.io/api/resources/groups#list-all-groups
func (a *GroupsAPI) GetByName(ctx context.Context, groupName string) (*api.Group, error) {
params := map[string]string{"name": groupName}
resp, err := a.c.NewRequest(ctx, "GET", "/api/groups", nil, params)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.Group](resp)
if err != nil {
return nil, err
}
if len(ret) == 0 {
return nil, ErrGroupNotFound
}
return &ret[0], nil
}
// Get get group info
// See more: https://docs.netbird.io/api/resources/groups#retrieve-a-group
func (a *GroupsAPI) Get(ctx context.Context, groupID string) (*api.Group, error) {
+93 -1
View File
@@ -463,6 +463,9 @@ components:
description: (Cloud only) Indicates whether peer needs approval
type: boolean
example: true
disapproval_reason:
description: (Cloud only) Reason why the peer requires approval
type: string
country_code:
$ref: '#/components/schemas/CountryCode'
city_name:
@@ -507,6 +510,48 @@ components:
- serial_number
- extra_dns_labels
- ephemeral
PeerTemporaryAccessRequest:
type: object
properties:
name:
description: Peer's hostname
type: string
example: temp-host-1
wg_pub_key:
description: Peer's WireGuard public key
type: string
example: "n0r3pL4c3h0ld3rK3y=="
rules:
description: List of temporary access rules
type: array
items:
type: string
example: "tcp/80"
required:
- name
- wg_pub_key
- rules
PeerTemporaryAccessResponse:
type: object
properties:
name:
description: Peer's hostname
type: string
example: temp-host-1
id:
description: Peer ID
type: string
example: chacbco6lnnbn6cg5s90
rules:
description: List of temporary access rules
type: array
items:
type: string
example: "tcp/80"
required:
- name
- id
- rules
AccessiblePeer:
allOf:
- $ref: '#/components/schemas/PeerMinimum'
@@ -1404,7 +1449,8 @@ components:
allOf:
- $ref: '#/components/schemas/NetworkResourceType'
- type: string
example: host
enum: ["peer"]
example: peer
NetworkRequest:
type: object
properties:
@@ -2793,6 +2839,42 @@ paths:
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/peers/{peerId}/temporary-access:
post:
summary: Create a Temporary Access Peer
description: Creates a temporary access peer that can be used to access this peer and this peer only. The temporary access peer and its access policies will be automatically deleted after it disconnects.
tags: [ Peers ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: peerId
required: true
schema:
type: string
description: The unique identifier of a peer
requestBody:
description: Temporary Access Peer create request
content:
'application/json':
schema:
$ref: '#/components/schemas/PeerTemporaryAccessRequest'
responses:
'200':
description: Temporary Access Peer response
content:
application/json:
schema:
$ref: '#/components/schemas/PeerTemporaryAccessResponse'
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/peers/{peerId}/ingress/ports:
get:
x-cloud-only: true
@@ -3280,6 +3362,14 @@ paths:
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: query
name: name
required: false
schema:
type: string
description: Filter groups by name (exact match)
example: "devs"
responses:
'200':
description: A JSON Array of Groups
@@ -3293,6 +3383,8 @@ paths:
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'404':
"$ref": "#/components/responses/not_found"
'403':
"$ref": "#/components/responses/forbidden"
'500':
+40
View File
@@ -168,6 +168,7 @@ const (
const (
ResourceTypeDomain ResourceType = "domain"
ResourceTypeHost ResourceType = "host"
ResourceTypePeer ResourceType = "peer"
ResourceTypeSubnet ResourceType = "subnet"
)
@@ -1036,6 +1037,9 @@ type Peer struct {
// CreatedAt Peer creation date (UTC)
CreatedAt time.Time `json:"created_at"`
// DisapprovalReason (Cloud only) Reason why the peer requires approval
DisapprovalReason *string `json:"disapproval_reason,omitempty"`
// DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud
DnsLabel string `json:"dns_label"`
@@ -1123,6 +1127,9 @@ type PeerBatch struct {
// CreatedAt Peer creation date (UTC)
CreatedAt time.Time `json:"created_at"`
// DisapprovalReason (Cloud only) Reason why the peer requires approval
DisapprovalReason *string `json:"disapproval_reason,omitempty"`
// DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud
DnsLabel string `json:"dns_label"`
@@ -1221,6 +1228,30 @@ type PeerRequest struct {
SshEnabled bool `json:"ssh_enabled"`
}
// PeerTemporaryAccessRequest defines model for PeerTemporaryAccessRequest.
type PeerTemporaryAccessRequest struct {
// Name Peer's hostname
Name string `json:"name"`
// Rules List of temporary access rules
Rules []string `json:"rules"`
// WgPubKey Peer's WireGuard public key
WgPubKey string `json:"wg_pub_key"`
}
// PeerTemporaryAccessResponse defines model for PeerTemporaryAccessResponse.
type PeerTemporaryAccessResponse struct {
// Id Peer ID
Id string `json:"id"`
// Name Peer's hostname
Name string `json:"name"`
// Rules List of temporary access rules
Rules []string `json:"rules"`
}
// PersonalAccessToken defines model for PersonalAccessToken.
type PersonalAccessToken struct {
// CreatedAt Date the token was created
@@ -1877,6 +1908,12 @@ type GetApiEventsNetworkTrafficParamsConnectionType string
// GetApiEventsNetworkTrafficParamsDirection defines parameters for GetApiEventsNetworkTraffic.
type GetApiEventsNetworkTrafficParamsDirection string
// GetApiGroupsParams defines parameters for GetApiGroups.
type GetApiGroupsParams struct {
// Name Filter groups by name (exact match)
Name *string `form:"name,omitempty" json:"name,omitempty"`
}
// GetApiPeersParams defines parameters for GetApiPeers.
type GetApiPeersParams struct {
// Name Filter peers by name
@@ -1949,6 +1986,9 @@ type PostApiPeersPeerIdIngressPortsJSONRequestBody = IngressPortAllocationReques
// PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody defines body for PutApiPeersPeerIdIngressPortsAllocationId for application/json ContentType.
type PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody = IngressPortAllocationRequest
// PostApiPeersPeerIdTemporaryAccessJSONRequestBody defines body for PostApiPeersPeerIdTemporaryAccess for application/json ContentType.
type PostApiPeersPeerIdTemporaryAccessJSONRequestBody = PeerTemporaryAccessRequest
// PostApiPoliciesJSONRequestBody defines body for PostApiPolicies for application/json ContentType.
type PostApiPoliciesJSONRequestBody = PolicyUpdate
+2
View File
@@ -106,6 +106,8 @@ func WriteError(ctx context.Context, err error, w http.ResponseWriter) {
httpStatus = http.StatusUnauthorized
case status.BadRequest:
httpStatus = http.StatusBadRequest
case status.TooManyRequests:
httpStatus = http.StatusTooManyRequests
default:
}
msg = strings.ToLower(err.Error())
+1 -1
View File
@@ -1,4 +1,4 @@
package operations
// Operation represents a permission operation type
type Operation string
type Operation string
File diff suppressed because it is too large Load Diff
+19
View File
@@ -151,6 +151,12 @@ message Flags {
bool blockInbound = 9;
bool lazyConnectionEnabled = 10;
bool enableSSHRoot = 11;
bool enableSSHSFTP = 12;
bool enableSSHLocalPortForwarding = 13;
bool enableSSHRemotePortForwarding = 14;
bool disableSSHAuth = 15;
}
// PeerSystemMeta is machine meta data like OS and version.
@@ -245,6 +251,14 @@ message FlowConfig {
bool dnsCollection = 8;
}
// JWTConfig represents JWT authentication configuration
message JWTConfig {
string issuer = 1;
string audience = 2;
string keysLocation = 3;
int64 maxTokenAge = 4;
}
// ProtectedHostConfig is similar to HostConfig but has additional user and password
// Mostly used for TURN servers
message ProtectedHostConfig {
@@ -340,6 +354,8 @@ message SSHConfig {
// sshPubKey is a SSH public key of a peer to be added to authorized_hosts.
// This property should be ignore if SSHConfig comes from PeerConfig.
bytes sshPubKey = 2;
JWTConfig jwtConfig = 3;
}
// DeviceAuthorizationFlowRequest empty struct for future expansion
@@ -415,12 +431,15 @@ message DNSConfig {
bool ServiceEnable = 1;
repeated NameServerGroup NameServerGroups = 2;
repeated CustomZone CustomZones = 3;
int64 ForwarderPort = 4 [deprecated = true];
}
// CustomZone represents a dns.CustomZone
message CustomZone {
string Domain = 1;
repeated SimpleRecord Records = 2;
bool SearchDomainDisabled = 3;
bool SkipPTRProcess = 4;
}
// SimpleRecord represents a dns.SimpleRecord
+3
View File
@@ -37,6 +37,9 @@ const (
// Unauthenticated indicates that user is not authenticated due to absence of valid credentials
Unauthenticated Type = 10
// TooManyRequests indicates that the user has sent too many requests in a given amount of time (rate limiting)
TooManyRequests Type = 11
)
// Type is a type of the Error