Skip to content

Commit 9ed3653

Browse files
author
Hedi Nasr
authored
[octavia-ingress-controller] support for whitelist-source-range annotation (kubernetes#1704)
* support for whitelist-source-range annotation * fix whitelist annotation's key * Documentation more clear, and remove useless code * add condition to avoid useless updates of allowed_cidrs
1 parent 6ba2837 commit 9ed3653

3 files changed

Lines changed: 60 additions & 3 deletions

File tree

docs/octavia-ingress-controller/using-octavia-ingress-controller.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
- [Create a backend service](#create-a-backend-service)
1515
- [Create an Ingress resource](#create-an-ingress-resource)
1616
- [Enable TLS encryption](#enable-tls-encryption)
17+
- [Allow CIDRs](#allow-cidrs)
1718

1819
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
1920

@@ -449,3 +450,34 @@ Ingress and enable the more secure HTTPS protocol.
449450
> NOTE: octavia-ingress-controller currently doesn't support to integrate with
450451
> `cert-manager` to create the non-existing secret dynamically. Could be improved
451452
> in the future.
453+
454+
## Allow CIDRs
455+
456+
By using the annotation `octavia.ingress.kubernetes.io/whitelist-source-range`,
457+
you can restrict access to certain IP addresses.
458+
The value should be a comma-separated list of CIDRs.
459+
460+
Example:
461+
462+
```yaml
463+
apiVersion: networking.k8s.io/v1
464+
kind: Ingress
465+
metadata:
466+
name: test-octavia-ingress
467+
annotations:
468+
kubernetes.io/ingress.class: "openstack"
469+
octavia.ingress.kubernetes.io/internal: "false"
470+
octavia.ingress.kubernetes.io/whitelist-source-range: 192.168.1.0/23
471+
spec:
472+
rules:
473+
- host: foo.bar.com
474+
http:
475+
paths:
476+
- path: /ping
477+
pathType: Exact
478+
backend:
479+
service:
480+
name: webserver
481+
port:
482+
number: 8080
483+
```

pkg/ingress/controller/controller.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ const (
9595
// Default to true.
9696
IngressAnnotationInternal = "octavia.ingress.kubernetes.io/internal"
9797

98+
// IngressAnnotationSourceRangesKey is the key of the annotation on an ingress to set allowed IP ranges on their LoadBalancers.
99+
// It should be a comma-separated list of CIDRs.
100+
IngressAnnotationSourceRangesKey = "octavia.ingress.kubernetes.io/whitelist-source-range"
101+
98102
// IngressControllerTag is added to the related resources.
99103
IngressControllerTag = "octavia.ingress.kubernetes.io"
100104

@@ -312,6 +316,10 @@ func NewController(conf config.Config) *Controller {
312316
// Two different versions of the same Ingress will always have different RVs.
313317
return
314318
}
319+
newAnnotations := newIng.ObjectMeta.Annotations
320+
oldAnnotations := oldIng.ObjectMeta.Annotations
321+
delete(newAnnotations, "kubectl.kubernetes.io/last-applied-configuration")
322+
delete(oldAnnotations, "kubectl.kubernetes.io/last-applied-configuration")
315323

316324
key := fmt.Sprintf("%s/%s", newIng.Namespace, newIng.Name)
317325
validOld := IsValid(oldIng)
@@ -322,7 +330,7 @@ func NewController(conf config.Config) *Controller {
322330
} else if validOld && !validCur {
323331
recorder.Event(newIng, apiv1.EventTypeNormal, "Deleting", fmt.Sprintf("Ingress %s", key))
324332
controller.queue.AddRateLimited(Event{Obj: newIng, Type: DeleteEvent})
325-
} else if validCur && !reflect.DeepEqual(newIng.Spec, oldIng.Spec) {
333+
} else if validCur && (!reflect.DeepEqual(newIng.Spec, oldIng.Spec) || !reflect.DeepEqual(newAnnotations, oldAnnotations)) {
326334
recorder.Event(newIng, apiv1.EventTypeNormal, "Updating", fmt.Sprintf("Ingress %s", key))
327335
controller.queue.AddRateLimited(Event{Obj: newIng, Type: UpdateEvent})
328336
} else {
@@ -703,7 +711,9 @@ func (c *Controller) ensureIngress(ing *nwv1.Ingress) error {
703711
}
704712

705713
// Create listener
706-
listener, err := c.osClient.EnsureListener(resName, lb.ID, secretRefs)
714+
sourceRanges := getStringFromIngressAnnotation(ing, IngressAnnotationSourceRangesKey, "0.0.0.0/0")
715+
listenerAllowedCIDRs := strings.Split(sourceRanges, ",")
716+
listener, err := c.osClient.EnsureListener(resName, lb.ID, secretRefs, listenerAllowedCIDRs)
707717
if err != nil {
708718
return err
709719
}

pkg/ingress/controller/openstack/octavia.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package openstack
1919
import (
2020
"errors"
2121
"fmt"
22+
"reflect"
2223
"strings"
2324
"time"
2425

@@ -335,7 +336,7 @@ func (os *OpenStack) UpdateLoadBalancerDescription(lbID string, newDescription s
335336
}
336337

337338
// EnsureListener creates a loadbalancer listener in octavia if it does not exist, wait for the loadbalancer to be ACTIVE.
338-
func (os *OpenStack) EnsureListener(name string, lbID string, secretRefs []string) (*listeners.Listener, error) {
339+
func (os *OpenStack) EnsureListener(name string, lbID string, secretRefs []string, listenerAllowedCIDRs []string) (*listeners.Listener, error) {
339340
listener, err := openstackutil.GetListenerByName(os.Octavia, name, lbID)
340341
if err != nil {
341342
if err != openstackutil.ErrNotFound {
@@ -356,12 +357,26 @@ func (os *OpenStack) EnsureListener(name string, lbID string, secretRefs []strin
356357
opts.ProtocolPort = 443
357358
opts.Protocol = "TERMINATED_HTTPS"
358359
}
360+
if len(listenerAllowedCIDRs) > 0 {
361+
opts.AllowedCIDRs = listenerAllowedCIDRs
362+
}
359363
listener, err = listeners.Create(os.Octavia, opts).Extract()
360364
if err != nil {
361365
return nil, fmt.Errorf("error creating listener: %v", err)
362366
}
363367

364368
log.WithFields(log.Fields{"lbID": lbID, "listenerName": name}).Info("listener created")
369+
} else {
370+
if len(listenerAllowedCIDRs) > 0 && !reflect.DeepEqual(listener.AllowedCIDRs, listenerAllowedCIDRs) {
371+
_, err := listeners.Update(os.Octavia, listener.ID, listeners.UpdateOpts{
372+
AllowedCIDRs: &listenerAllowedCIDRs,
373+
}).Extract()
374+
if err != nil {
375+
return nil, fmt.Errorf("failed to update listener allowed CIDRs: %v", err)
376+
}
377+
378+
log.WithFields(log.Fields{"listenerID": listener.ID}).Debug("listener allowed CIDRs updated")
379+
}
365380
}
366381

367382
_, err = os.waitLoadbalancerActiveProvisioningStatus(lbID)

0 commit comments

Comments
 (0)