2024-12-10 15:59:25 +01:00
package policies
2023-03-13 15:14:18 +01:00
import (
"encoding/json"
"net/http"
2023-06-07 08:57:43 +02:00
"strconv"
2023-03-13 15:14:18 +01:00
"github.com/gorilla/mux"
2024-12-10 15:59:25 +01:00
2023-03-13 15:14:18 +01:00
"github.com/netbirdio/netbird/management/server"
2024-12-10 15:59:25 +01:00
"github.com/netbirdio/netbird/management/server/geolocation"
2023-03-13 15:14:18 +01:00
"github.com/netbirdio/netbird/management/server/http/api"
2024-12-10 15:59:25 +01:00
"github.com/netbirdio/netbird/management/server/http/configs"
2023-03-13 15:14:18 +01:00
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/netbirdio/netbird/management/server/status"
2024-12-20 11:30:28 +01:00
"github.com/netbirdio/netbird/management/server/types"
2023-03-13 15:14:18 +01:00
)
2024-12-10 15:59:25 +01:00
// handler is a handler that returns policy of the account
type handler struct {
2023-03-13 15:14:18 +01:00
accountManager server . AccountManager
claimsExtractor * jwtclaims . ClaimsExtractor
}
2024-12-10 15:59:25 +01:00
func AddEndpoints ( accountManager server . AccountManager , locationManager * geolocation . Geolocation , authCfg configs . AuthCfg , router * mux . Router ) {
policiesHandler := newHandler ( accountManager , authCfg )
router . HandleFunc ( "/policies" , policiesHandler . getAllPolicies ) . Methods ( "GET" , "OPTIONS" )
router . HandleFunc ( "/policies" , policiesHandler . createPolicy ) . Methods ( "POST" , "OPTIONS" )
router . HandleFunc ( "/policies/{policyId}" , policiesHandler . updatePolicy ) . Methods ( "PUT" , "OPTIONS" )
router . HandleFunc ( "/policies/{policyId}" , policiesHandler . getPolicy ) . Methods ( "GET" , "OPTIONS" )
router . HandleFunc ( "/policies/{policyId}" , policiesHandler . deletePolicy ) . Methods ( "DELETE" , "OPTIONS" )
addPostureCheckEndpoint ( accountManager , locationManager , authCfg , router )
}
// newHandler creates a new policies handler
func newHandler ( accountManager server . AccountManager , authCfg configs . AuthCfg ) * handler {
return & handler {
2023-03-13 15:14:18 +01:00
accountManager : accountManager ,
claimsExtractor : jwtclaims . NewClaimsExtractor (
jwtclaims . WithAudience ( authCfg . Audience ) ,
jwtclaims . WithUserIDClaim ( authCfg . UserIDClaim ) ,
) ,
}
}
2024-12-10 15:59:25 +01:00
// getAllPolicies list for the account
func ( h * handler ) getAllPolicies ( w http . ResponseWriter , r * http . Request ) {
2023-03-13 15:14:18 +01:00
claims := h . claimsExtractor . FromRequestContext ( r )
2024-09-27 16:10:50 +02:00
accountID , userID , err := h . accountManager . GetAccountIDFromToken ( r . Context ( ) , claims )
2023-03-13 15:14:18 +01:00
if err != nil {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , err , w )
2023-03-13 15:14:18 +01:00
return
}
2024-09-27 16:10:50 +02:00
listPolicies , err := h . accountManager . ListPolicies ( r . Context ( ) , accountID , userID )
2023-03-13 15:14:18 +01:00
if err != nil {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , err , w )
2023-03-13 15:14:18 +01:00
return
}
2024-09-27 16:10:50 +02:00
allGroups , err := h . accountManager . GetAllGroups ( r . Context ( ) , accountID , userID )
if err != nil {
util . WriteError ( r . Context ( ) , err , w )
return
}
policies := make ( [ ] * api . Policy , 0 , len ( listPolicies ) )
for _ , policy := range listPolicies {
resp := toPolicyResponse ( allGroups , policy )
2023-05-29 16:00:18 +02:00
if len ( resp . Rules ) == 0 {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , status . Errorf ( status . Internal , "no rules in the policy" ) , w )
2023-05-29 16:00:18 +02:00
return
}
policies = append ( policies , resp )
}
2024-07-03 11:33:02 +02:00
util . WriteJSONObject ( r . Context ( ) , w , policies )
2023-03-13 15:14:18 +01:00
}
2024-12-10 15:59:25 +01:00
// updatePolicy handles update to a policy identified by a given ID
func ( h * handler ) updatePolicy ( w http . ResponseWriter , r * http . Request ) {
2023-03-13 15:14:18 +01:00
claims := h . claimsExtractor . FromRequestContext ( r )
2024-09-27 16:10:50 +02:00
accountID , userID , err := h . accountManager . GetAccountIDFromToken ( r . Context ( ) , claims )
2023-03-13 15:14:18 +01:00
if err != nil {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , err , w )
2023-03-13 15:14:18 +01:00
return
}
vars := mux . Vars ( r )
2023-05-03 00:15:25 +02:00
policyID := vars [ "policyId" ]
2023-03-13 15:14:18 +01:00
if len ( policyID ) == 0 {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , status . Errorf ( status . InvalidArgument , "invalid policy ID" ) , w )
2023-03-13 15:14:18 +01:00
return
}
2024-09-27 16:10:50 +02:00
_ , err = h . accountManager . GetPolicy ( r . Context ( ) , accountID , policyID , userID )
if err != nil {
util . WriteError ( r . Context ( ) , err , w )
2023-03-13 15:14:18 +01:00
return
}
2024-09-27 16:10:50 +02:00
h . savePolicy ( w , r , accountID , userID , policyID )
2023-03-13 15:14:18 +01:00
}
2024-12-10 15:59:25 +01:00
// createPolicy handles policy creation request
func ( h * handler ) createPolicy ( w http . ResponseWriter , r * http . Request ) {
2023-03-13 15:14:18 +01:00
claims := h . claimsExtractor . FromRequestContext ( r )
2024-09-27 16:10:50 +02:00
accountID , userID , err := h . accountManager . GetAccountIDFromToken ( r . Context ( ) , claims )
2023-03-13 15:14:18 +01:00
if err != nil {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , err , w )
2023-03-13 15:14:18 +01:00
return
}
2024-09-27 16:10:50 +02:00
h . savePolicy ( w , r , accountID , userID , "" )
2023-05-29 16:00:18 +02:00
}
// savePolicy handles policy creation and update
2024-12-10 15:59:25 +01:00
func ( h * handler ) savePolicy ( w http . ResponseWriter , r * http . Request , accountID string , userID string , policyID string ) {
2023-05-29 16:00:18 +02:00
var req api . PutApiPoliciesPolicyIdJSONRequestBody
if err := json . NewDecoder ( r . Body ) . Decode ( & req ) ; err != nil {
2023-03-13 15:14:18 +01:00
util . WriteErrorResponse ( "couldn't parse JSON request" , http . StatusBadRequest , w )
return
}
if req . Name == "" {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , status . Errorf ( status . InvalidArgument , "policy name shouldn't be empty" ) , w )
2023-03-13 15:14:18 +01:00
return
}
2023-05-29 16:00:18 +02:00
if len ( req . Rules ) == 0 {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , status . Errorf ( status . InvalidArgument , "policy rules shouldn't be empty" ) , w )
2023-05-29 16:00:18 +02:00
return
}
2024-12-20 11:30:28 +01:00
policy := & types . Policy {
2023-05-29 16:00:18 +02:00
ID : policyID ,
2024-11-26 10:46:05 +01:00
AccountID : accountID ,
2023-03-13 15:14:18 +01:00
Name : req . Name ,
Enabled : req . Enabled ,
Description : req . Description ,
}
2024-07-03 11:33:02 +02:00
for _ , rule := range req . Rules {
2024-11-26 10:46:05 +01:00
var ruleID string
if rule . Id != nil {
ruleID = * rule . Id
}
2024-12-20 11:30:28 +01:00
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 {
2024-11-26 10:46:05 +01:00
ID : ruleID ,
PolicyID : policyID ,
2024-07-03 11:33:02 +02:00
Name : rule . Name ,
Bidirectional : rule . Bidirectional ,
2023-05-29 16:00:18 +02:00
}
2024-12-20 11:30:28 +01:00
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
}
2024-07-03 11:33:02 +02:00
pr . Enabled = rule . Enabled
if rule . Description != nil {
pr . Description = * rule . Description
2023-05-29 16:00:18 +02:00
}
2024-07-03 11:33:02 +02:00
switch rule . Action {
2023-05-29 16:00:18 +02:00
case api . PolicyRuleUpdateActionAccept :
2024-12-20 11:30:28 +01:00
pr . Action = types . PolicyTrafficActionAccept
2023-05-29 16:00:18 +02:00
case api . PolicyRuleUpdateActionDrop :
2024-12-20 11:30:28 +01:00
pr . Action = types . PolicyTrafficActionDrop
2023-05-29 16:00:18 +02:00
default :
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , status . Errorf ( status . InvalidArgument , "unknown action type" ) , w )
2023-05-29 16:00:18 +02:00
return
}
2023-03-13 15:14:18 +01:00
2024-07-03 11:33:02 +02:00
switch rule . Protocol {
2023-05-29 16:00:18 +02:00
case api . PolicyRuleUpdateProtocolAll :
2024-12-20 11:30:28 +01:00
pr . Protocol = types . PolicyRuleProtocolALL
2023-05-29 16:00:18 +02:00
case api . PolicyRuleUpdateProtocolTcp :
2024-12-20 11:30:28 +01:00
pr . Protocol = types . PolicyRuleProtocolTCP
2023-05-29 16:00:18 +02:00
case api . PolicyRuleUpdateProtocolUdp :
2024-12-20 11:30:28 +01:00
pr . Protocol = types . PolicyRuleProtocolUDP
2023-05-29 16:00:18 +02:00
case api . PolicyRuleUpdateProtocolIcmp :
2024-12-20 11:30:28 +01:00
pr . Protocol = types . PolicyRuleProtocolICMP
2023-05-29 16:00:18 +02:00
default :
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , status . Errorf ( status . InvalidArgument , "unknown protocol type: %v" , rule . Protocol ) , w )
2023-05-29 16:00:18 +02:00
return
}
2024-10-02 13:41:00 +02:00
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
}
2024-07-03 11:33:02 +02:00
if rule . Ports != nil && len ( * rule . Ports ) != 0 {
for _ , v := range * rule . Ports {
2023-06-07 08:57:43 +02:00
if port , err := strconv . Atoi ( v ) ; err != nil || port < 1 || port > 65535 {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , status . Errorf ( status . InvalidArgument , "valid port value is in 1..65535 range" ) , w )
2023-06-07 08:57:43 +02:00
return
}
pr . Ports = append ( pr . Ports , v )
}
2023-05-29 16:00:18 +02:00
}
2024-10-02 13:41:00 +02:00
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
}
2024-12-20 11:30:28 +01:00
pr . PortRanges = append ( pr . PortRanges , types . RulePortRange {
2024-10-02 13:41:00 +02:00
Start : uint16 ( portRange . Start ) ,
End : uint16 ( portRange . End ) ,
} )
}
}
2023-05-29 16:00:18 +02:00
// validate policy object
switch pr . Protocol {
2024-12-20 11:30:28 +01:00
case types . PolicyRuleProtocolALL , types . PolicyRuleProtocolICMP :
2024-10-02 13:41:00 +02:00
if len ( pr . Ports ) != 0 || len ( pr . PortRanges ) != 0 {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , status . Errorf ( status . InvalidArgument , "for ALL or ICMP protocol ports is not allowed" ) , w )
2023-05-29 16:00:18 +02:00
return
2023-03-13 15:14:18 +01:00
}
2023-05-29 16:00:18 +02:00
if ! pr . Bidirectional {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , status . Errorf ( status . InvalidArgument , "for ALL or ICMP protocol type flow can be only bi-directional" ) , w )
2023-05-29 16:00:18 +02:00
return
2023-03-13 15:14:18 +01:00
}
2024-12-20 11:30:28 +01:00
case types . PolicyRuleProtocolTCP , types . PolicyRuleProtocolUDP :
2024-10-02 13:41:00 +02:00
if ! pr . Bidirectional && ( len ( pr . Ports ) == 0 || len ( pr . PortRanges ) != 0 ) {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , status . Errorf ( status . InvalidArgument , "for ALL or ICMP protocol type flow can be only bi-directional" ) , w )
2023-03-13 15:14:18 +01:00
return
}
}
2023-05-29 16:00:18 +02:00
policy . Rules = append ( policy . Rules , & pr )
2023-03-13 15:14:18 +01:00
}
2023-05-29 16:00:18 +02:00
2024-02-20 09:59:56 +01:00
if req . SourcePostureChecks != nil {
2024-09-27 16:10:50 +02:00
policy . SourcePostureChecks = * req . SourcePostureChecks
2024-02-20 09:59:56 +01:00
}
2024-11-26 10:46:05 +01:00
policy , err := h . accountManager . SavePolicy ( r . Context ( ) , accountID , userID , policy )
if err != nil {
2024-09-27 16:10:50 +02:00
util . WriteError ( r . Context ( ) , err , w )
return
}
allGroups , err := h . accountManager . GetAllGroups ( r . Context ( ) , accountID , userID )
if err != nil {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , err , w )
2023-03-13 15:14:18 +01:00
return
}
2024-11-26 10:46:05 +01:00
resp := toPolicyResponse ( allGroups , policy )
2023-05-29 16:00:18 +02:00
if len ( resp . Rules ) == 0 {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , status . Errorf ( status . Internal , "no rules in the policy" ) , w )
2023-03-13 15:14:18 +01:00
return
}
2024-07-03 11:33:02 +02:00
util . WriteJSONObject ( r . Context ( ) , w , resp )
2023-03-13 15:14:18 +01:00
}
2024-12-10 15:59:25 +01:00
// deletePolicy handles policy deletion request
func ( h * handler ) deletePolicy ( w http . ResponseWriter , r * http . Request ) {
2023-03-13 15:14:18 +01:00
claims := h . claimsExtractor . FromRequestContext ( r )
2024-09-27 16:10:50 +02:00
accountID , userID , err := h . accountManager . GetAccountIDFromToken ( r . Context ( ) , claims )
2023-03-13 15:14:18 +01:00
if err != nil {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , err , w )
2023-03-13 15:14:18 +01:00
return
}
vars := mux . Vars ( r )
2023-05-03 00:15:25 +02:00
policyID := vars [ "policyId" ]
2023-03-13 15:14:18 +01:00
if len ( policyID ) == 0 {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , status . Errorf ( status . InvalidArgument , "invalid policy ID" ) , w )
2023-03-13 15:14:18 +01:00
return
}
2024-09-27 16:10:50 +02:00
if err = h . accountManager . DeletePolicy ( r . Context ( ) , accountID , policyID , userID ) ; err != nil {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , err , w )
2023-03-13 15:14:18 +01:00
return
}
2024-12-10 15:59:25 +01:00
util . WriteJSONObject ( r . Context ( ) , w , util . EmptyObject { } )
2023-03-13 15:14:18 +01:00
}
2024-12-10 15:59:25 +01:00
// getPolicy handles a group Get request identified by ID
func ( h * handler ) getPolicy ( w http . ResponseWriter , r * http . Request ) {
2023-03-13 15:14:18 +01:00
claims := h . claimsExtractor . FromRequestContext ( r )
2024-09-27 16:10:50 +02:00
accountID , userID , err := h . accountManager . GetAccountIDFromToken ( r . Context ( ) , claims )
2023-03-13 15:14:18 +01:00
if err != nil {
2024-07-03 11:33:02 +02:00
util . WriteError ( r . Context ( ) , err , w )
2023-03-13 15:14:18 +01:00
return
}
2024-09-27 16:10:50 +02:00
vars := mux . Vars ( r )
policyID := vars [ "policyId" ]
if len ( policyID ) == 0 {
util . WriteError ( r . Context ( ) , status . Errorf ( status . InvalidArgument , "invalid policy ID" ) , w )
return
}
2023-03-13 15:14:18 +01:00
2024-09-27 16:10:50 +02:00
policy , err := h . accountManager . GetPolicy ( r . Context ( ) , accountID , policyID , userID )
if err != nil {
util . WriteError ( r . Context ( ) , err , w )
return
}
2023-03-13 15:14:18 +01:00
2024-09-27 16:10:50 +02:00
allGroups , err := h . accountManager . GetAllGroups ( r . Context ( ) , accountID , userID )
if err != nil {
util . WriteError ( r . Context ( ) , err , w )
return
}
2023-05-29 16:00:18 +02:00
2024-09-27 16:10:50 +02:00
resp := toPolicyResponse ( allGroups , policy )
if len ( resp . Rules ) == 0 {
util . WriteError ( r . Context ( ) , status . Errorf ( status . Internal , "no rules in the policy" ) , w )
return
2023-03-13 15:14:18 +01:00
}
2024-09-27 16:10:50 +02:00
util . WriteJSONObject ( r . Context ( ) , w , resp )
2023-03-13 15:14:18 +01:00
}
2024-12-20 11:30:28 +01:00
func toPolicyResponse ( groups [ ] * types . Group , policy * types . Policy ) * api . Policy {
groupsMap := make ( map [ string ] * types . Group )
2024-09-27 16:10:50 +02:00
for _ , group := range groups {
groupsMap [ group . ID ] = group
}
2023-03-13 15:14:18 +01:00
cache := make ( map [ string ] api . GroupMinimum )
ap := & api . Policy {
2024-02-20 09:59:56 +01:00
Id : & policy . ID ,
Name : policy . Name ,
Description : policy . Description ,
Enabled : policy . Enabled ,
SourcePostureChecks : policy . SourcePostureChecks ,
2023-03-13 15:14:18 +01:00
}
for _ , r := range policy . Rules {
2023-12-04 13:34:06 +01:00
rID := r . ID
rDescription := r . Description
2023-03-13 15:14:18 +01:00
rule := api . PolicyRule {
2024-12-20 11:30:28 +01:00
Id : & rID ,
Name : r . Name ,
Enabled : r . Enabled ,
Description : & rDescription ,
Bidirectional : r . Bidirectional ,
Protocol : api . PolicyRuleProtocol ( r . Protocol ) ,
Action : api . PolicyRuleAction ( r . Action ) ,
SourceResource : r . SourceResource . ToAPIResponse ( ) ,
DestinationResource : r . DestinationResource . ToAPIResponse ( ) ,
2023-05-29 16:00:18 +02:00
}
2024-09-27 16:10:50 +02:00
2023-05-29 16:00:18 +02:00
if len ( r . Ports ) != 0 {
2023-11-27 16:40:02 +01:00
portsCopy := r . Ports
2023-05-29 16:00:18 +02:00
rule . Ports = & portsCopy
2023-03-13 15:14:18 +01:00
}
2024-09-27 16:10:50 +02:00
2024-10-02 13:41:00 +02:00
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
}
2024-12-20 11:30:28 +01:00
var sources [ ] api . GroupMinimum
2023-03-13 15:14:18 +01:00
for _ , gid := range r . Sources {
_ , ok := cache [ gid ]
if ok {
continue
}
2024-12-20 11:30:28 +01:00
2024-09-27 16:10:50 +02:00
if group , ok := groupsMap [ gid ] ; ok {
2023-03-13 15:14:18 +01:00
minimum := api . GroupMinimum {
Id : group . ID ,
Name : group . Name ,
PeersCount : len ( group . Peers ) ,
}
2024-12-20 11:30:28 +01:00
sources = append ( sources , minimum )
2023-03-13 15:14:18 +01:00
cache [ gid ] = minimum
}
}
2024-12-20 11:30:28 +01:00
rule . Sources = & sources
2024-09-27 16:10:50 +02:00
2024-12-20 11:30:28 +01:00
var destinations [ ] api . GroupMinimum
2023-03-13 15:14:18 +01:00
for _ , gid := range r . Destinations {
cachedMinimum , ok := cache [ gid ]
if ok {
2024-12-20 11:30:28 +01:00
destinations = append ( destinations , cachedMinimum )
2023-03-13 15:14:18 +01:00
continue
}
2024-09-27 16:10:50 +02:00
if group , ok := groupsMap [ gid ] ; ok {
2023-03-13 15:14:18 +01:00
minimum := api . GroupMinimum {
Id : group . ID ,
Name : group . Name ,
PeersCount : len ( group . Peers ) ,
}
2024-12-20 11:30:28 +01:00
destinations = append ( destinations , minimum )
2023-03-13 15:14:18 +01:00
cache [ gid ] = minimum
}
}
2024-12-20 11:30:28 +01:00
rule . Destinations = & destinations
2023-03-13 15:14:18 +01:00
ap . Rules = append ( ap . Rules , rule )
}
return ap
}