Merge pull request #3025 from netbirdio/feature/add-policy-network-resources

[management] Extends policy with source and destination resources
This commit is contained in:
Bethuel Mmbaga 2024-12-11 17:26:36 +01:00 committed by GitHub
commit 7944b8e843
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 175 additions and 35 deletions

View File

@ -782,15 +782,18 @@ components:
items: items:
type: string type: string
example: "ch8i4ug6lnn4g9hqv797" example: "ch8i4ug6lnn4g9hqv797"
sourceResource:
description: Policy rule source resource that the rule is applied to
$ref: '#/components/schemas/Resource'
destinations: destinations:
description: Policy rule destination group IDs description: Policy rule destination group IDs
type: array type: array
items: items:
type: string type: string
example: "ch8i4ug6lnn4g9h7v7m0" example: "ch8i4ug6lnn4g9h7v7m0"
required: destinationResource:
- sources description: Policy rule destination resource that the rule is applied to
- destinations $ref: '#/components/schemas/Resource'
PolicyRule: PolicyRule:
allOf: allOf:
- $ref: '#/components/schemas/PolicyRuleMinimum' - $ref: '#/components/schemas/PolicyRuleMinimum'
@ -801,14 +804,17 @@ components:
type: array type: array
items: items:
$ref: '#/components/schemas/GroupMinimum' $ref: '#/components/schemas/GroupMinimum'
sourceResource:
description: Policy rule source resource that the rule is applied to
$ref: '#/components/schemas/Resource'
destinations: destinations:
description: Policy rule destination group IDs description: Policy rule destination group IDs
type: array type: array
items: items:
$ref: '#/components/schemas/GroupMinimum' $ref: '#/components/schemas/GroupMinimum'
required: destinationResource:
- sources description: Policy rule destination resource that the rule is applied to
- destinations $ref: '#/components/schemas/Resource'
PolicyMinimum: PolicyMinimum:
type: object type: object
properties: properties:
@ -1176,6 +1182,24 @@ components:
- id - id
- network_type - network_type
- $ref: '#/components/schemas/RouteRequest' - $ref: '#/components/schemas/RouteRequest'
Resource:
type: object
properties:
id:
description: ID of the resource
type: string
example: chacdk86lnnboviihd7g
type:
description: Type of the resource
$ref: '#/components/schemas/ResourceType'
required:
- id
- type
ResourceType:
allOf:
- $ref: '#/components/schemas/NetworkResourceType'
- type: string
example: host
NetworkRequest: NetworkRequest:
type: object type: object
properties: properties:
@ -1227,14 +1251,16 @@ components:
type: string type: string
example: chacdk86lnnboviihd7g example: chacdk86lnnboviihd7g
type: type:
description: Network resource type based of the address $ref: '#/components/schemas/NetworkResourceType'
type: string
enum: [ "host", "subnet", "domain"]
example: host
required: required:
- id - id
- type - type
- $ref: '#/components/schemas/NetworkResourceRequest' - $ref: '#/components/schemas/NetworkResourceRequest'
NetworkResourceType:
description: Network resource type based of the address
type: string
enum: [ "host", "subnet", "domain" ]
example: host
NetworkRouterRequest: NetworkRouterRequest:
type: object type: object
properties: properties:

View File

@ -143,6 +143,13 @@ const (
PolicyRuleUpdateProtocolUdp PolicyRuleUpdateProtocol = "udp" PolicyRuleUpdateProtocolUdp PolicyRuleUpdateProtocol = "udp"
) )
// Defines values for ResourceType.
const (
ResourceTypeDomain ResourceType = "domain"
ResourceTypeHost ResourceType = "host"
ResourceTypeSubnet ResourceType = "subnet"
)
// Defines values for UserStatus. // Defines values for UserStatus.
const ( const (
UserStatusActive UserStatus = "active" UserStatusActive UserStatus = "active"
@ -540,9 +547,6 @@ type NetworkResource struct {
Type NetworkResourceType `json:"type"` Type NetworkResourceType `json:"type"`
} }
// NetworkResourceType Network resource type based of the address
type NetworkResourceType string
// NetworkResourceRequest defines model for NetworkResourceRequest. // NetworkResourceRequest defines model for NetworkResourceRequest.
type NetworkResourceRequest struct { type NetworkResourceRequest struct {
// Address Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or a domain like example.com) // Address Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or a domain like example.com)
@ -555,6 +559,9 @@ type NetworkResourceRequest struct {
Name string `json:"name"` Name string `json:"name"`
} }
// NetworkResourceType Network resource type based of the address
type NetworkResourceType string
// NetworkRouter defines model for NetworkRouter. // NetworkRouter defines model for NetworkRouter.
type NetworkRouter struct { type NetworkRouter struct {
// Id Network Router Id // Id Network Router Id
@ -873,10 +880,11 @@ type PolicyRule struct {
Bidirectional bool `json:"bidirectional"` Bidirectional bool `json:"bidirectional"`
// Description Policy rule friendly description // Description Policy rule friendly description
Description *string `json:"description,omitempty"` Description *string `json:"description,omitempty"`
DestinationResource *Resource `json:"destinationResource,omitempty"`
// Destinations Policy rule destination group IDs // Destinations Policy rule destination group IDs
Destinations []GroupMinimum `json:"destinations"` Destinations *[]GroupMinimum `json:"destinations,omitempty"`
// Enabled Policy rule status // Enabled Policy rule status
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
@ -894,10 +902,11 @@ type PolicyRule struct {
Ports *[]string `json:"ports,omitempty"` Ports *[]string `json:"ports,omitempty"`
// Protocol Policy rule type of the traffic // Protocol Policy rule type of the traffic
Protocol PolicyRuleProtocol `json:"protocol"` Protocol PolicyRuleProtocol `json:"protocol"`
SourceResource *Resource `json:"sourceResource,omitempty"`
// Sources Policy rule source group IDs // Sources Policy rule source group IDs
Sources []GroupMinimum `json:"sources"` Sources *[]GroupMinimum `json:"sources,omitempty"`
} }
// PolicyRuleAction Policy rule accept or drops packets // PolicyRuleAction Policy rule accept or drops packets
@ -951,10 +960,11 @@ type PolicyRuleUpdate struct {
Bidirectional bool `json:"bidirectional"` Bidirectional bool `json:"bidirectional"`
// Description Policy rule friendly description // Description Policy rule friendly description
Description *string `json:"description,omitempty"` Description *string `json:"description,omitempty"`
DestinationResource *Resource `json:"destinationResource,omitempty"`
// Destinations Policy rule destination group IDs // Destinations Policy rule destination group IDs
Destinations []string `json:"destinations"` Destinations *[]string `json:"destinations,omitempty"`
// Enabled Policy rule status // Enabled Policy rule status
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
@ -972,10 +982,11 @@ type PolicyRuleUpdate struct {
Ports *[]string `json:"ports,omitempty"` Ports *[]string `json:"ports,omitempty"`
// Protocol Policy rule type of the traffic // Protocol Policy rule type of the traffic
Protocol PolicyRuleUpdateProtocol `json:"protocol"` Protocol PolicyRuleUpdateProtocol `json:"protocol"`
SourceResource *Resource `json:"sourceResource,omitempty"`
// Sources Policy rule source group IDs // Sources Policy rule source group IDs
Sources []string `json:"sources"` Sources *[]string `json:"sources,omitempty"`
} }
// PolicyRuleUpdateAction Policy rule accept or drops packets // PolicyRuleUpdateAction Policy rule accept or drops packets
@ -1049,6 +1060,16 @@ type ProcessCheck struct {
Processes []Process `json:"processes"` Processes []Process `json:"processes"`
} }
// Resource defines model for Resource.
type Resource struct {
// Id Resource ID
Id string `json:"id"`
Type ResourceType `json:"type"`
}
// ResourceType defines model for ResourceType.
type ResourceType string
// Route defines model for Route. // Route defines model for Route.
type Route struct { type Route struct {
// AccessControlGroups Access control group identifier associated with route. // AccessControlGroups Access control group identifier associated with route.

View File

@ -147,15 +147,56 @@ func (h *handler) savePolicy(w http.ResponseWriter, r *http.Request, accountID s
ruleID = *rule.Id ruleID = *rule.Id
} }
hasSources := rule.Sources != nil
hasSourceResource := rule.SourceResource != nil
hasDestinations := rule.Destinations != nil
hasDestinationResource := rule.DestinationResource != nil
if hasSources && hasSourceResource {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "specify either sources or source resources, not both"), w)
return
}
if hasDestinations && hasDestinationResource {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "specify either destinations or destination resources, not both"), w)
return
}
if !(hasSources || hasSourceResource) || !(hasDestinations || hasDestinationResource) {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "specify either sources or source resources and destinations or destination resources"), w)
return
}
pr := types.PolicyRule{ pr := types.PolicyRule{
ID: ruleID, ID: ruleID,
PolicyID: policyID, PolicyID: policyID,
Name: rule.Name, Name: rule.Name,
Destinations: rule.Destinations,
Sources: rule.Sources,
Bidirectional: rule.Bidirectional, Bidirectional: rule.Bidirectional,
} }
if hasSources {
pr.Sources = *rule.Sources
}
if hasSourceResource {
// TODO: validate the resource id and type
sourceResource := &types.Resource{}
sourceResource.FromAPIRequest(rule.SourceResource)
pr.SourceResource = *sourceResource
}
if hasDestinations {
pr.Destinations = *rule.Destinations
}
if hasDestinationResource {
// TODO: validate the resource id and type
destinationResource := &types.Resource{}
destinationResource.FromAPIRequest(rule.DestinationResource)
pr.DestinationResource = *destinationResource
}
pr.Enabled = rule.Enabled pr.Enabled = rule.Enabled
if rule.Description != nil { if rule.Description != nil {
pr.Description = *rule.Description pr.Description = *rule.Description
@ -338,13 +379,15 @@ func toPolicyResponse(groups []*nbgroup.Group, policy *types.Policy) *api.Policy
rID := r.ID rID := r.ID
rDescription := r.Description rDescription := r.Description
rule := api.PolicyRule{ rule := api.PolicyRule{
Id: &rID, Id: &rID,
Name: r.Name, Name: r.Name,
Enabled: r.Enabled, Enabled: r.Enabled,
Description: &rDescription, Description: &rDescription,
Bidirectional: r.Bidirectional, Bidirectional: r.Bidirectional,
Protocol: api.PolicyRuleProtocol(r.Protocol), Protocol: api.PolicyRuleProtocol(r.Protocol),
Action: api.PolicyRuleAction(r.Action), Action: api.PolicyRuleAction(r.Action),
SourceResource: r.SourceResource.ToAPIResponse(),
DestinationResource: r.DestinationResource.ToAPIResponse(),
} }
if len(r.Ports) != 0 { if len(r.Ports) != 0 {
@ -363,26 +406,30 @@ func toPolicyResponse(groups []*nbgroup.Group, policy *types.Policy) *api.Policy
rule.PortRanges = &portRanges rule.PortRanges = &portRanges
} }
var sources []api.GroupMinimum
for _, gid := range r.Sources { for _, gid := range r.Sources {
_, ok := cache[gid] _, ok := cache[gid]
if ok { if ok {
continue continue
} }
if group, ok := groupsMap[gid]; ok { if group, ok := groupsMap[gid]; ok {
minimum := api.GroupMinimum{ minimum := api.GroupMinimum{
Id: group.ID, Id: group.ID,
Name: group.Name, Name: group.Name,
PeersCount: len(group.Peers), PeersCount: len(group.Peers),
} }
rule.Sources = append(rule.Sources, minimum) sources = append(sources, minimum)
cache[gid] = minimum cache[gid] = minimum
} }
} }
rule.Sources = &sources
var destinations []api.GroupMinimum
for _, gid := range r.Destinations { for _, gid := range r.Destinations {
cachedMinimum, ok := cache[gid] cachedMinimum, ok := cache[gid]
if ok { if ok {
rule.Destinations = append(rule.Destinations, cachedMinimum) destinations = append(destinations, cachedMinimum)
continue continue
} }
if group, ok := groupsMap[gid]; ok { if group, ok := groupsMap[gid]; ok {
@ -391,10 +438,12 @@ func toPolicyResponse(groups []*nbgroup.Group, policy *types.Policy) *api.Policy
Name: group.Name, Name: group.Name,
PeersCount: len(group.Peers), PeersCount: len(group.Peers),
} }
rule.Destinations = append(rule.Destinations, minimum) destinations = append(destinations, minimum)
cache[gid] = minimum cache[gid] = minimum
} }
} }
rule.Destinations = &destinations
ap.Rules = append(ap.Rules, rule) ap.Rules = append(ap.Rules, rule)
} }
return ap return ap

View File

@ -177,7 +177,9 @@ func TestPoliciesWritePolicy(t *testing.T) {
"Description": "Description", "Description": "Description",
"Protocol": "tcp", "Protocol": "tcp",
"Action": "accept", "Action": "accept",
"Bidirectional":true "Bidirectional":true,
"Sources": ["F"],
"Destinations": ["G"]
} }
]}`)), ]}`)),
expectedStatus: http.StatusOK, expectedStatus: http.StatusOK,
@ -193,6 +195,8 @@ func TestPoliciesWritePolicy(t *testing.T) {
Protocol: "tcp", Protocol: "tcp",
Action: "accept", Action: "accept",
Bidirectional: true, Bidirectional: true,
Sources: &[]api.GroupMinimum{{Id: "F"}},
Destinations: &[]api.GroupMinimum{{Id: "G"}},
}, },
}, },
}, },
@ -221,7 +225,9 @@ func TestPoliciesWritePolicy(t *testing.T) {
"Description": "Description", "Description": "Description",
"Protocol": "tcp", "Protocol": "tcp",
"Action": "accept", "Action": "accept",
"Bidirectional":true "Bidirectional":true,
"Sources": ["F"],
"Destinations": ["F"]
} }
]}`)), ]}`)),
expectedStatus: http.StatusOK, expectedStatus: http.StatusOK,
@ -237,6 +243,8 @@ func TestPoliciesWritePolicy(t *testing.T) {
Protocol: "tcp", Protocol: "tcp",
Action: "accept", Action: "accept",
Bidirectional: true, Bidirectional: true,
Sources: &[]api.GroupMinimum{{Id: "F"}},
Destinations: &[]api.GroupMinimum{{Id: "F"}},
}, },
}, },
}, },

View File

@ -41,9 +41,15 @@ type PolicyRule struct {
// Destinations policy destination groups // Destinations policy destination groups
Destinations []string `gorm:"serializer:json"` Destinations []string `gorm:"serializer:json"`
// DestinationResource policy destination resource that the rule is applied to
DestinationResource Resource `gorm:"serializer:json"`
// Sources policy source groups // Sources policy source groups
Sources []string `gorm:"serializer:json"` Sources []string `gorm:"serializer:json"`
// SourceResource policy source resource that the rule is applied to
SourceResource Resource `gorm:"serializer:json"`
// Bidirectional define if the rule is applicable in both directions, sources, and destinations // Bidirectional define if the rule is applicable in both directions, sources, and destinations
Bidirectional bool Bidirectional bool

View File

@ -0,0 +1,30 @@
package types
import (
"github.com/netbirdio/netbird/management/server/http/api"
)
type Resource struct {
ID string
Type string
}
func (r *Resource) ToAPIResponse() *api.Resource {
if r.ID == "" && r.Type == "" {
return nil
}
return &api.Resource{
Id: r.ID,
Type: api.ResourceType(r.Type),
}
}
func (r *Resource) FromAPIRequest(req *api.Resource) {
if req == nil {
return
}
r.ID = req.Id
r.Type = string(req.Type)
}