@@ -181,10 +181,17 @@ func (cs *CSCloud) EnsureLoadBalancer(ctx context.Context, clusterName string, s
181
181
return nil , err
182
182
}
183
183
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
+ }
188
195
}
189
196
}
190
197
}
@@ -205,6 +212,11 @@ func (cs *CSCloud) EnsureLoadBalancer(ctx context.Context, clusterName string, s
205
212
return nil , err
206
213
}
207
214
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
+
208
220
klog .V (4 ).Infof ("Deleting obsolete load balancer rule: %v" , lbRule .Name )
209
221
if err := lb .deleteLoadBalancerRule (lbRule ); err != nil {
210
222
return nil , err
@@ -278,6 +290,15 @@ func isFirewallSupported(services []cloudstack.NetworkServiceInternal) bool {
278
290
return false
279
291
}
280
292
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
+
281
302
// EnsureLoadBalancerDeleted deletes the specified load balancer if it exists, returning
282
303
// nil if the load balancer specified either didn't exist or was successfully deleted.
283
304
func (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
290
311
}
291
312
292
313
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 )
294
315
protocol := ProtocolFromLoadBalancer (lbRule .Protocol )
295
316
if protocol == LoadBalancerProtocolInvalid {
296
317
klog .Errorf ("Error parsing protocol: %v" , lbRule .Protocol )
@@ -299,9 +320,29 @@ func (cs *CSCloud) EnsureLoadBalancerDeleted(ctx context.Context, clusterName st
299
320
if err != nil {
300
321
klog .Errorf ("Error parsing port: %v" , err )
301
322
} else {
302
- _ , err = lb . deleteFirewallRule ( lbRule . Publicipid , int ( port ), protocol )
323
+ networkId , err := cs . getNetworkIDFromIPAddress ( lb . ipAddrID )
303
324
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
+ }
305
346
}
306
347
}
307
348
@@ -365,6 +406,27 @@ func (cs *CSCloud) getLoadBalancer(service *corev1.Service) (*loadBalancer, erro
365
406
return lb , nil
366
407
}
367
408
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
+
368
430
// verifyHosts verifies if all hosts belong to the same network, and returns the host ID's and network ID.
369
431
func (cs * CSCloud ) verifyHosts (nodes []* corev1.Node ) ([]string , string , error ) {
370
432
hostNames := map [string ]bool {}
@@ -790,6 +852,67 @@ func (lb *loadBalancer) updateFirewallRule(publicIpId string, publicPort int, pr
790
852
return true , err
791
853
}
792
854
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
+
793
916
// deleteFirewallRule deletes the firewall rule associated with the ip:port:protocol combo
794
917
//
795
918
// returns true when corresponding rules were deleted
@@ -828,6 +951,46 @@ func (lb *loadBalancer) deleteFirewallRule(publicIpId string, publicPort int, pr
828
951
return deleted , err
829
952
}
830
953
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
+
831
994
// getStringFromServiceAnnotation searches a given v1.Service for a specific annotationKey and either returns the annotation's value or a specified defaultSetting
832
995
func getStringFromServiceAnnotation (service * corev1.Service , annotationKey string , defaultSetting string ) string {
833
996
klog .V (4 ).Infof ("getStringFromServiceAnnotation(%s/%s, %v, %v)" , service .Namespace , service .Name , annotationKey , defaultSetting )
0 commit comments