@@ -181,10 +181,17 @@ func (cs *CSCloud) EnsureLoadBalancer(ctx context.Context, clusterName string, s
181181 return nil , err
182182 }
183183
184- if lbRule != nil && isFirewallSupported (network .Service ) {
185- klog .V (4 ).Infof ("Creating firewall rules for load balancer rule: %v (%v:%v:%v)" , lbRuleName , protocol , lbRule .Publicip , port .Port )
186- if _ , err := lb .updateFirewallRule (lbRule .Publicipid , int (port .Port ), protocol , service .Spec .LoadBalancerSourceRanges ); err != nil {
187- return nil , err
184+ if lbRule != nil {
185+ if isFirewallSupported (network .Service ) {
186+ klog .V (4 ).Infof ("Creating firewall rules for load balancer rule: %v (%v:%v:%v)" , lbRuleName , protocol , lbRule .Publicip , port .Port )
187+ if _ , err := lb .updateFirewallRule (lbRule .Publicipid , int (port .Port ), protocol , service .Spec .LoadBalancerSourceRanges ); err != nil {
188+ return nil , err
189+ }
190+ } else if isNetworkACLSupported (network .Service ) {
191+ klog .V (4 ).Infof ("Creating ACL rules for load balancer rule: %v (%v:%v:%v)" , lbRuleName , protocol , lbRule .Publicip , port .Port )
192+ if _ , err := lb .updateNetworkACL (int (port .Port ), protocol , network .Id ); err != nil {
193+ return nil , err
194+ }
188195 }
189196 }
190197 }
@@ -205,6 +212,11 @@ func (cs *CSCloud) EnsureLoadBalancer(ctx context.Context, clusterName string, s
205212 return nil , err
206213 }
207214
215+ klog .V (4 ).Infof ("Deleting Network ACL rules associated with load balancer rule: %v (%v:%v)" , lbRule .Name , protocol , port )
216+ if _ , err := lb .deleteNetworkACLRule (int (port ), protocol , lb .networkID ); err != nil {
217+ return nil , err
218+ }
219+
208220 klog .V (4 ).Infof ("Deleting obsolete load balancer rule: %v" , lbRule .Name )
209221 if err := lb .deleteLoadBalancerRule (lbRule ); err != nil {
210222 return nil , err
@@ -278,6 +290,15 @@ func isFirewallSupported(services []cloudstack.NetworkServiceInternal) bool {
278290 return false
279291}
280292
293+ func isNetworkACLSupported (services []cloudstack.NetworkServiceInternal ) bool {
294+ for _ , svc := range services {
295+ if svc .Name == "NetworkACL" {
296+ return true
297+ }
298+ }
299+ return false
300+ }
301+
281302// EnsureLoadBalancerDeleted deletes the specified load balancer if it exists, returning
282303// nil if the load balancer specified either didn't exist or was successfully deleted.
283304func (cs * CSCloud ) EnsureLoadBalancerDeleted (ctx context.Context , clusterName string , service * corev1.Service ) error {
@@ -290,7 +311,7 @@ func (cs *CSCloud) EnsureLoadBalancerDeleted(ctx context.Context, clusterName st
290311 }
291312
292313 for _ , lbRule := range lb .rules {
293- klog .V (4 ).Infof ("Deleting firewall rules for load balancer: %v" , lbRule .Name )
314+ klog .V (4 ).Infof ("Deleting firewall rules / Network ACLs for load balancer: %v" , lbRule .Name )
294315 protocol := ProtocolFromLoadBalancer (lbRule .Protocol )
295316 if protocol == LoadBalancerProtocolInvalid {
296317 klog .Errorf ("Error parsing protocol: %v" , lbRule .Protocol )
@@ -299,9 +320,29 @@ func (cs *CSCloud) EnsureLoadBalancerDeleted(ctx context.Context, clusterName st
299320 if err != nil {
300321 klog .Errorf ("Error parsing port: %v" , err )
301322 } else {
302- _ , err = lb . deleteFirewallRule ( lbRule . Publicipid , int ( port ), protocol )
323+ networkId , err := cs . getNetworkIDFromIPAddress ( lb . ipAddrID )
303324 if err != nil {
304- klog .Errorf ("Error deleting firewall rule: %v" , err )
325+ return err
326+ }
327+ network , count , err := lb .Network .GetNetworkByID (networkId , cloudstack .WithProject (lb .projectID ))
328+ if err != nil {
329+ if count == 0 {
330+ klog .Errorf ("No network found with ID: %v" , networkId )
331+ return err
332+ }
333+ return err
334+ }
335+ if network .Vpcid == "" {
336+ _ , err = lb .deleteFirewallRule (lbRule .Publicipid , int (port ), protocol )
337+ if err != nil {
338+ klog .Errorf ("Error deleting firewall rule: %v" , err )
339+ }
340+ } else {
341+ klog .V (4 ).Infof ("Deleting network ACLs for %v - %v" , int (port ), protocol )
342+ _ , err = lb .deleteNetworkACLRule (int (port ), protocol , networkId )
343+ if err != nil {
344+ klog .Errorf ("Error deleting Network ACL rule: %v" , err )
345+ }
305346 }
306347 }
307348
@@ -365,6 +406,27 @@ func (cs *CSCloud) getLoadBalancer(service *corev1.Service) (*loadBalancer, erro
365406 return lb , nil
366407}
367408
409+ // Get network ID from Public IP Address
410+ func (cs * CSCloud ) getNetworkIDFromIPAddress (publicIpId string ) (string , error ) {
411+ ip , count , err := cs .client .Address .GetPublicIpAddressByID (publicIpId )
412+ if err != nil {
413+ klog .Errorf ("Failed to fetch the public IP for id: %v" , publicIpId )
414+ return "" , err
415+ }
416+ if count == 0 {
417+ return "" , err
418+ }
419+ if ip .Networkid != "" {
420+ network , _ , netErr := cs .client .Network .GetNetworkByID (ip .Associatednetworkid )
421+ if netErr != nil {
422+ klog .Errorf ("Failed to fetch the network for id: %v" , ip .Associatednetworkid )
423+ return "" , err
424+ }
425+ return network .Id , nil
426+ }
427+ return "" , nil
428+ }
429+
368430// verifyHosts verifies if all hosts belong to the same network, and returns the host ID's and network ID.
369431func (cs * CSCloud ) verifyHosts (nodes []* corev1.Node ) ([]string , string , error ) {
370432 hostNames := map [string ]bool {}
@@ -790,6 +852,67 @@ func (lb *loadBalancer) updateFirewallRule(publicIpId string, publicPort int, pr
790852 return true , err
791853}
792854
855+ func (lb * loadBalancer ) updateNetworkACL (publicPort int , protocol LoadBalancerProtocol , networkId string ) (bool , error ) {
856+ network , _ , err := lb .Network .GetNetworkByID (networkId )
857+ if err != nil {
858+ return false , fmt .Errorf ("error fetching Network with ID: %v, due to: %s" , networkId , err )
859+ }
860+
861+ networkAclList , count , err := lb .NetworkACL .GetNetworkACLListByID (network .Aclid )
862+ if err != nil {
863+ return false , fmt .Errorf ("error fetching Network ACL List with ID: %v, due to: %s" , network .Aclid , err )
864+ }
865+
866+ if count == 0 {
867+ return false , fmt .Errorf ("failed to find network ACL List with id: %v" , network .Aclid )
868+ }
869+
870+ if networkAclList .Name == "default_allow" || networkAclList .Name == "default_deny" {
871+ klog .Infof ("Network is using a default network ACL. Cannot add ACL rules to default ACLs" )
872+ return true , err
873+ }
874+
875+ networkAclParams := lb .NetworkACL .NewListNetworkACLsParams ()
876+ networkAclParams .SetAclid (network .Aclid )
877+ networkAclParams .SetNetworkid (networkId )
878+
879+ networkAclResponse , err := lb .NetworkACL .ListNetworkACLs (networkAclParams )
880+
881+ if err != nil {
882+ return false , fmt .Errorf ("error fetching Network ACL with ID: %v for network with id: %v, due to: %s" , network .Aclid , networkId , err )
883+ }
884+
885+ // find all network ACL rules that have a matching proto+port
886+ // a map may or may not be faster, but is a bit easier to understand
887+ filtered := make (map [* cloudstack.NetworkACL ]bool )
888+ for _ , netAclRule := range networkAclResponse .NetworkACLs {
889+ if netAclRule .Protocol == protocol .IPProtocol () && netAclRule .Startport == strconv .Itoa (publicPort ) && netAclRule .Endport == strconv .Itoa (publicPort ) {
890+ filtered [netAclRule ] = true
891+ }
892+ }
893+
894+ if len (filtered ) > 0 {
895+ klog .V (4 ).Infof ("Network ACL rule for port %v and protocol %v already exists. No need to added a duplicate rule" , publicPort , protocol )
896+ return true , err
897+ }
898+
899+ // create ACL rule
900+ acl := lb .NetworkACL .NewCreateNetworkACLParams (protocol .CSProtocol ())
901+ acl .SetAclid (network .Aclid )
902+ acl .SetAction ("Allow" )
903+ acl .SetCidrlist ([]string {"0.0.0.0/0" })
904+ acl .SetStartport (publicPort )
905+ acl .SetEndport (publicPort )
906+ acl .SetNetworkid (networkId )
907+ acl .SetTraffictype ("Ingress" )
908+
909+ _ , err = lb .NetworkACL .CreateNetworkACL (acl )
910+ if err != nil {
911+ return false , fmt .Errorf ("error creating Network ACL for port: %v, due to: %s" , publicPort , err )
912+ }
913+ return true , err
914+ }
915+
793916// deleteFirewallRule deletes the firewall rule associated with the ip:port:protocol combo
794917//
795918// returns true when corresponding rules were deleted
@@ -828,6 +951,46 @@ func (lb *loadBalancer) deleteFirewallRule(publicIpId string, publicPort int, pr
828951 return deleted , err
829952}
830953
954+ // Delete Network ACLs deletes the Network ACL rule associated with the ip:port:protocol combo
955+ func (lb * loadBalancer ) deleteNetworkACLRule (publicPort int , protocol LoadBalancerProtocol , networkID string ) (bool , error ) {
956+ p := lb .NetworkACL .NewListNetworkACLsParams ()
957+ p .SetListall (true )
958+ p .SetNetworkid (networkID )
959+ if lb .projectID != "" {
960+ p .SetProjectid (lb .projectID )
961+ }
962+
963+ r , err := lb .NetworkACL .ListNetworkACLs (p )
964+ if err != nil {
965+ return false , fmt .Errorf ("error fetching Network ACL rules Network ID %v: %v" , networkID , err )
966+ }
967+
968+ // filter by proto:port
969+ filtered := make ([]* cloudstack.NetworkACL , 0 , 1 )
970+ for _ , rule := range r .NetworkACLs {
971+ if rule .Protocol == protocol .IPProtocol () && rule .Startport == strconv .Itoa (publicPort ) && rule .Endport == strconv .Itoa (publicPort ) {
972+ filtered = append (filtered , rule )
973+ }
974+ }
975+
976+ // delete first filtered rules
977+ if len (filtered ) == 0 {
978+ klog .V (4 ).Infof ("No ACL rules found matching protocol: %v and port: %v" , protocol , publicPort )
979+ return true , nil
980+ }
981+ deleted := false
982+ ruleToBeDeleted := filtered [0 ]
983+ deleteAclParams := lb .NetworkACL .NewDeleteNetworkACLParams (ruleToBeDeleted .Id )
984+ _ , err = lb .NetworkACL .DeleteNetworkACL (deleteAclParams )
985+ if err != nil {
986+ klog .Errorf ("Error deleting old Network ACL rule %v: %v" , ruleToBeDeleted .Id , err )
987+ } else {
988+ deleted = true
989+ }
990+
991+ return deleted , err
992+ }
993+
831994// getStringFromServiceAnnotation searches a given v1.Service for a specific annotationKey and either returns the annotation's value or a specified defaultSetting
832995func getStringFromServiceAnnotation (service * corev1.Service , annotationKey string , defaultSetting string ) string {
833996 klog .V (4 ).Infof ("getStringFromServiceAnnotation(%s/%s, %v, %v)" , service .Namespace , service .Name , annotationKey , defaultSetting )
0 commit comments