[management, client] Add access control support to network routes (#2100)

This commit is contained in:
Bethuel Mmbaga
2024-10-02 14:41:00 +03:00
committed by GitHub
parent a3a479429e
commit ff7863785f
48 changed files with 4683 additions and 2444 deletions

View File

@@ -727,17 +727,39 @@ components:
enum: ["all", "tcp", "udp", "icmp"]
example: "tcp"
ports:
description: Policy rule affected ports or it ranges list
description: Policy rule affected ports
type: array
items:
type: string
example: "80"
port_ranges:
description: Policy rule affected ports ranges list
type: array
items:
$ref: '#/components/schemas/RulePortRange'
required:
- name
- enabled
- bidirectional
- protocol
- action
RulePortRange:
description: Policy rule affected ports range
type: object
properties:
start:
description: The starting port of the range
type: integer
example: 80
end:
description: The ending port of the range
type: integer
example: 320
required:
- start
- end
PolicyRuleUpdate:
allOf:
- $ref: '#/components/schemas/PolicyRuleMinimum'
@@ -1106,6 +1128,12 @@ components:
description: Indicate if the route should be kept after a domain doesn't resolve that IP anymore
type: boolean
example: true
access_control_groups:
description: Access control group identifier associated with route.
type: array
items:
type: string
example: "chacbco6lnnbn6cg5s91"
required:
- id
- description

View File

@@ -780,7 +780,10 @@ type PolicyRule struct {
// Name Policy rule name identifier
Name string `json:"name"`
// Ports Policy rule affected ports or it ranges list
// PortRanges Policy rule affected ports ranges list
PortRanges *[]RulePortRange `json:"port_ranges,omitempty"`
// Ports Policy rule affected ports
Ports *[]string `json:"ports,omitempty"`
// Protocol Policy rule type of the traffic
@@ -816,7 +819,10 @@ type PolicyRuleMinimum struct {
// Name Policy rule name identifier
Name string `json:"name"`
// Ports Policy rule affected ports or it ranges list
// PortRanges Policy rule affected ports ranges list
PortRanges *[]RulePortRange `json:"port_ranges,omitempty"`
// Ports Policy rule affected ports
Ports *[]string `json:"ports,omitempty"`
// Protocol Policy rule type of the traffic
@@ -852,7 +858,10 @@ type PolicyRuleUpdate struct {
// Name Policy rule name identifier
Name string `json:"name"`
// Ports Policy rule affected ports or it ranges list
// PortRanges Policy rule affected ports ranges list
PortRanges *[]RulePortRange `json:"port_ranges,omitempty"`
// Ports Policy rule affected ports
Ports *[]string `json:"ports,omitempty"`
// Protocol Policy rule type of the traffic
@@ -935,6 +944,9 @@ type ProcessCheck struct {
// Route defines model for Route.
type Route struct {
// AccessControlGroups Access control group identifier associated with route.
AccessControlGroups *[]string `json:"access_control_groups,omitempty"`
// Description Route description
Description string `json:"description"`
@@ -977,6 +989,9 @@ type Route struct {
// RouteRequest defines model for RouteRequest.
type RouteRequest struct {
// AccessControlGroups Access control group identifier associated with route.
AccessControlGroups *[]string `json:"access_control_groups,omitempty"`
// Description Route description
Description string `json:"description"`
@@ -1011,6 +1026,15 @@ type RouteRequest struct {
PeerGroups *[]string `json:"peer_groups,omitempty"`
}
// RulePortRange Policy rule affected ports range
type RulePortRange struct {
// End The ending port of the range
End int `json:"end"`
// Start The starting port of the range
Start int `json:"start"`
}
// SetupKey defines model for SetupKey.
type SetupKey struct {
// AutoGroups List of group IDs to auto-assign to peers registered with this key

View File

@@ -172,6 +172,11 @@ func (h *Policies) savePolicy(w http.ResponseWriter, r *http.Request, accountID
return
}
if (rule.Ports != nil && len(*rule.Ports) != 0) && (rule.PortRanges != nil && len(*rule.PortRanges) != 0) {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "specify either individual ports or port ranges, not both"), w)
return
}
if rule.Ports != nil && len(*rule.Ports) != 0 {
for _, v := range *rule.Ports {
if port, err := strconv.Atoi(v); err != nil || port < 1 || port > 65535 {
@@ -182,10 +187,23 @@ func (h *Policies) savePolicy(w http.ResponseWriter, r *http.Request, accountID
}
}
if rule.PortRanges != nil && len(*rule.PortRanges) != 0 {
for _, portRange := range *rule.PortRanges {
if portRange.Start < 1 || portRange.End > 65535 {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "valid port value is in 1..65535 range"), w)
return
}
pr.PortRanges = append(pr.PortRanges, server.RulePortRange{
Start: uint16(portRange.Start),
End: uint16(portRange.End),
})
}
}
// validate policy object
switch pr.Protocol {
case server.PolicyRuleProtocolALL, server.PolicyRuleProtocolICMP:
if len(pr.Ports) != 0 {
if len(pr.Ports) != 0 || len(pr.PortRanges) != 0 {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "for ALL or ICMP protocol ports is not allowed"), w)
return
}
@@ -194,7 +212,7 @@ func (h *Policies) savePolicy(w http.ResponseWriter, r *http.Request, accountID
return
}
case server.PolicyRuleProtocolTCP, server.PolicyRuleProtocolUDP:
if !pr.Bidirectional && len(pr.Ports) == 0 {
if !pr.Bidirectional && (len(pr.Ports) == 0 || len(pr.PortRanges) != 0) {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "for ALL or ICMP protocol type flow can be only bi-directional"), w)
return
}
@@ -320,6 +338,17 @@ func toPolicyResponse(groups []*nbgroup.Group, policy *server.Policy) *api.Polic
rule.Ports = &portsCopy
}
if len(r.PortRanges) != 0 {
portRanges := make([]api.RulePortRange, 0, len(r.PortRanges))
for _, portRange := range r.PortRanges {
portRanges = append(portRanges, api.RulePortRange{
End: int(portRange.End),
Start: int(portRange.Start),
})
}
rule.PortRanges = &portRanges
}
for _, gid := range r.Sources {
_, ok := cache[gid]
if ok {

View File

@@ -117,9 +117,14 @@ func (h *RoutesHandler) CreateRoute(w http.ResponseWriter, r *http.Request) {
peerGroupIds = *req.PeerGroups
}
var accessControlGroupIds []string
if req.AccessControlGroups != nil {
accessControlGroupIds = *req.AccessControlGroups
}
newRoute, err := h.accountManager.CreateRoute(r.Context(), accountID, newPrefix, networkType, domains, peerId, peerGroupIds,
req.Description, route.NetID(req.NetworkId), req.Masquerade, req.Metric, req.Groups, req.Enabled, userID, req.KeepRoute,
)
req.Description, route.NetID(req.NetworkId), req.Masquerade, req.Metric, req.Groups, accessControlGroupIds, req.Enabled, userID, req.KeepRoute)
if err != nil {
util.WriteError(r.Context(), err, w)
return
@@ -233,6 +238,10 @@ func (h *RoutesHandler) UpdateRoute(w http.ResponseWriter, r *http.Request) {
newRoute.PeerGroups = *req.PeerGroups
}
if req.AccessControlGroups != nil {
newRoute.AccessControlGroups = *req.AccessControlGroups
}
err = h.accountManager.SaveRoute(r.Context(), accountID, userID, newRoute)
if err != nil {
util.WriteError(r.Context(), err, w)
@@ -326,6 +335,9 @@ func toRouteResponse(serverRoute *route.Route) (*api.Route, error) {
if len(serverRoute.PeerGroups) > 0 {
route.PeerGroups = &serverRoute.PeerGroups
}
if len(serverRoute.AccessControlGroups) > 0 {
route.AccessControlGroups = &serverRoute.AccessControlGroups
}
return route, nil
}

View File

@@ -105,7 +105,7 @@ func initRoutesTestData() *RoutesHandler {
}
return nil, status.Errorf(status.NotFound, "route with ID %s not found", routeID)
},
CreateRouteFunc: func(_ context.Context, accountID string, prefix netip.Prefix, networkType route.NetworkType, domains domain.List, peerID string, peerGroups []string, description string, netID route.NetID, masquerade bool, metric int, groups []string, enabled bool, _ string, keepRoute bool) (*route.Route, error) {
CreateRouteFunc: func(_ context.Context, accountID string, prefix netip.Prefix, networkType route.NetworkType, domains domain.List, peerID string, peerGroups []string, description string, netID route.NetID, masquerade bool, metric int, groups, accessControlGroups []string, enabled bool, _ string, keepRoute bool) (*route.Route, error) {
if peerID == notFoundPeerID {
return nil, status.Errorf(status.InvalidArgument, "peer with ID %s not found", peerID)
}
@@ -119,18 +119,19 @@ func initRoutesTestData() *RoutesHandler {
}
return &route.Route{
ID: existingRouteID,
NetID: netID,
Peer: peerID,
PeerGroups: peerGroups,
Network: prefix,
Domains: domains,
NetworkType: networkType,
Description: description,
Masquerade: masquerade,
Enabled: enabled,
Groups: groups,
KeepRoute: keepRoute,
ID: existingRouteID,
NetID: netID,
Peer: peerID,
PeerGroups: peerGroups,
Network: prefix,
Domains: domains,
NetworkType: networkType,
Description: description,
Masquerade: masquerade,
Enabled: enabled,
Groups: groups,
KeepRoute: keepRoute,
AccessControlGroups: accessControlGroups,
}, nil
},
SaveRouteFunc: func(_ context.Context, _, _ string, r *route.Route) error {
@@ -268,6 +269,27 @@ func TestRoutesHandlers(t *testing.T) {
Groups: []string{existingGroupID},
},
},
{
name: "POST OK With Access Control Groups",
requestType: http.MethodPost,
requestPath: "/api/routes",
requestBody: bytes.NewBuffer(
[]byte(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\",\"groups\":[\"%s\"],\"access_control_groups\":[\"%s\"]}", existingPeerID, existingGroupID, existingGroupID))),
expectedStatus: http.StatusOK,
expectedBody: true,
expectedRoute: &api.Route{
Id: existingRouteID,
Description: "Post",
NetworkId: "awesomeNet",
Network: toPtr("192.168.0.0/16"),
Peer: &existingPeerID,
NetworkType: route.IPv4NetworkString,
Masquerade: false,
Enabled: false,
Groups: []string{existingGroupID},
AccessControlGroups: &[]string{existingGroupID},
},
},
{
name: "POST Non Linux Peer",
requestType: http.MethodPost,