Repository: kubernetes-sigs/aws-alb-ingress-controller
Pull request #1473 (2020-10-08 06:26:10)

Updated nlb-ip with more details around proxy protocol
added specific annotation required for proxy protocol.
reference links to annotation from K8s doc.
reference links to NLB Proxy Protocol V2 from AWS NLB doc.

k8s-ci-robot: Welcome @rohanmangal! <br><br>It looks like this is your first PR to <a href='https://github.com/kubernetes-sigs/aws-alb-ingress-controller'>kubernetes-sigs/aws-alb-ingress-controller</a> 🎉. Please refer to our [pull request process documentation](https://git.k8s.io/community/contributors/guide/pull-requests.md) to help your PR have a smooth ride to approval. <br><br>You will be prompted by a bot to use commands during the review process. Do not be afraid to follow the prompts! It is okay to experiment. [Here is the bot commands documentation](https://go.k8s.io/bot-commands). <br><br>You can also check if kubernetes-sigs/aws-alb-ingress-controller has [its own contribution guidelines](https://github.com/kubernetes-sigs/aws-alb-ingress-controller/tree/master/CONTRIBUTING.md). <br><br>You may want to refer to our [testing guide](https://git.k8s.io/community/contributors/devel/sig-testing/testing.md) if you run into trouble with your tests not passing. <br><br>If you are having difficulty getting your pull request seen, please follow the [recommended escalation practices](https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#why-is-my-pull-request-not-getting-reviewed). Also, for tips and tricks in the contribution process you may want to read the [Kubernetes contributor cheat sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet/README.md). We want to make sure your contribution gets all the attention it needs! <br><br>Thank you, and welcome to Kubernetes. :smiley:
k8s-ci-robot: Hi @rohanmangal. Thanks for your PR.

I'm waiting for a [kubernetes-sigs](https://github.com/orgs/kubernetes-sigs/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://git.k8s.io/community/community-membership.md#member) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Faws-alb-ingress-controller).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

k8s-ci-robot: Thanks for your pull request. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

:memo: **Please follow instructions at <https://git.k8s.io/community/CLA.md#the-contributor-license-agreement> to sign the CLA.**

It may take a couple minutes for the CLA signature to be fully registered; after that, please reply here with a new comment and we'll verify.  Thanks.

---

- If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address.  Check your existing CLA data and verify that your [email is set on your git commits](https://help.github.com/articles/setting-your-email-in-git/).
- If you signed the CLA as a corporation, please sign in with your organization's credentials at <https://identity.linuxfoundation.org/projects/cncf> to be authorized.
- If you have done the above and are still having issues with the CLA being reported as unsigned, please log a ticket with the Linux Foundation Helpdesk: <https://support.linuxfoundation.org/>
- Should you encounter any issues with the Linux Foundation Helpdesk, send a message to the backup e-mail support address at: login-issues@jira.linuxfoundation.org

<!-- need_sender_cla -->

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
	
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/aws-alb-ingress-controller/pull/1473#" title="Author self-approved">rohanmangal</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign  after the PR has been reviewed.
You can assign the PR to them by writing `/assign ` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Faws-alb-ingress-controller).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/aws-alb-ingress-controller/blob/v2_ga/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
rohanmangal: I signed it

Patch:
diff --git a/Dockerfile b/Dockerfile
index 3320d9023..3aef80a17 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -15,6 +15,8 @@ ARG TARGETARCH
 RUN --mount=type=bind,target=. \
     --mount=type=cache,target=/root/.cache/go-build \
     GOOS=${TARGETOS} GOARCH=${TARGETARCH} GO111MODULE=on \
+    CGO_CPPFLAGS="-D_FORTIFY_SOURCE=2" \
+    CGO_LDFLAGS="-Wl,-z,relro,-z,now" \
     go build -ldflags="-s -w" -buildmode=pie -a -o /out/controller main.go
 
 FROM amazonlinux:2 as bin-unix
@@ -25,4 +27,4 @@ ENTRYPOINT ["/controller"]
 FROM bin-unix AS bin-linux
 FROM bin-unix AS bin-darwin
 
-FROM bin-${TARGETOS} as bin
\ No newline at end of file
+FROM bin-${TARGETOS} as bin
diff --git a/config/controller/kustomization.yaml b/config/controller/kustomization.yaml
index 3a7ff14d1..344bbc004 100644
--- a/config/controller/kustomization.yaml
+++ b/config/controller/kustomization.yaml
@@ -2,6 +2,7 @@ resources:
 - controller.yaml
 patchesStrategicMerge:
 - iam_for_sa_patch.yaml
+- security_context_patch.yaml
 
 apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
diff --git a/config/controller/security_context_patch.yaml b/config/controller/security_context_patch.yaml
new file mode 100644
index 000000000..4017a6ed8
--- /dev/null
+++ b/config/controller/security_context_patch.yaml
@@ -0,0 +1,13 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: controller
+spec:
+  template:
+    spec:
+      containers:
+        - name: controller
+          securityContext:
+            readOnlyRootFilesystem: true
+            allowPrivilegeEscalation: false
+            runAsNonRoot: true
diff --git a/controllers/elbv2/eventhandlers/endpoints.go b/controllers/elbv2/eventhandlers/endpoints.go
index 8698be1ec..9809f3373 100644
--- a/controllers/elbv2/eventhandlers/endpoints.go
+++ b/controllers/elbv2/eventhandlers/endpoints.go
@@ -15,6 +15,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 )
 
+// NewEnqueueRequestsForEndpointsEvent constructs new enqueueRequestsForEndpointsEvent.
 func NewEnqueueRequestsForEndpointsEvent(k8sClient client.Client, logger logr.Logger) handler.EventHandler {
 	return &enqueueRequestsForEndpointsEvent{
 		k8sClient: k8sClient,
diff --git a/controllers/elbv2/eventhandlers/node.go b/controllers/elbv2/eventhandlers/node.go
index 45bfc1dd0..e68cba363 100644
--- a/controllers/elbv2/eventhandlers/node.go
+++ b/controllers/elbv2/eventhandlers/node.go
@@ -17,6 +17,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 )
 
+// NewEnqueueRequestsForNodeEvent constructs new enqueueRequestsForNodeEvent.
 func NewEnqueueRequestsForNodeEvent(k8sClient client.Client, logger logr.Logger) handler.EventHandler {
 	return &enqueueRequestsForNodeEvent{
 		k8sClient: k8sClient,
diff --git a/controllers/elbv2/targetgroupbinding_controller.go b/controllers/elbv2/targetgroupbinding_controller.go
index 2fe6ac405..376ae7607 100644
--- a/controllers/elbv2/targetgroupbinding_controller.go
+++ b/controllers/elbv2/targetgroupbinding_controller.go
@@ -38,23 +38,21 @@ const (
 )
 
 // NewTargetGroupBindingReconciler constructs new targetGroupBindingReconciler
-func NewTargetGroupBindingReconciler(k8sClient client.Client, k8sFieldIndexer client.FieldIndexer, finalizerManager k8s.FinalizerManager, tgbResourceManager targetgroupbinding.ResourceManager, log logr.Logger) *targetGroupBindingReconciler {
+func NewTargetGroupBindingReconciler(k8sClient client.Client, finalizerManager k8s.FinalizerManager, tgbResourceManager targetgroupbinding.ResourceManager, logger logr.Logger) *targetGroupBindingReconciler {
 	return &targetGroupBindingReconciler{
 		k8sClient:          k8sClient,
-		k8sFieldIndexer:    k8sFieldIndexer,
 		finalizerManager:   finalizerManager,
 		tgbResourceManager: tgbResourceManager,
-		log:                log,
+		logger:             logger,
 	}
 }
 
 // targetGroupBindingReconciler reconciles a TargetGroupBinding object
 type targetGroupBindingReconciler struct {
 	k8sClient          client.Client
-	k8sFieldIndexer    client.FieldIndexer
 	finalizerManager   k8s.FinalizerManager
 	tgbResourceManager targetgroupbinding.ResourceManager
-	log                logr.Logger
+	logger             logr.Logger
 }
 
 // +kubebuilder:rbac:groups=elbv2.k8s.aws,resources=targetgroupbindings,verbs=get;list;watch;update;patch;create;delete
@@ -68,25 +66,7 @@ type targetGroupBindingReconciler struct {
 // +kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;update;patch;create;delete
 
 func (r *targetGroupBindingReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
-	return runtime.HandleReconcileError(r.reconcile(req), r.log)
-}
-
-func (r *targetGroupBindingReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
-	r.k8sFieldIndexer.IndexField(ctx, &elbv2api.TargetGroupBinding{},
-		targetgroupbinding.IndexKeyServiceRefName, targetgroupbinding.IndexFuncServiceRefName)
-	r.k8sFieldIndexer.IndexField(ctx, &elbv2api.TargetGroupBinding{},
-		targetgroupbinding.IndexKeyTargetType, targetgroupbinding.IndexFuncTargetType)
-
-	epEventsHandler := eventhandlers.NewEnqueueRequestsForEndpointsEvent(r.k8sClient,
-		r.log.WithName("eventHandlers").WithName("endpoints"))
-	nodeEventsHandler := eventhandlers.NewEnqueueRequestsForNodeEvent(r.k8sClient,
-		r.log.WithName("eventHandlers").WithName("node"))
-	return ctrl.NewControllerManagedBy(mgr).
-		For(&elbv2api.TargetGroupBinding{}).
-		Watches(&source.Kind{Type: &corev1.Endpoints{}}, epEventsHandler).
-		Watches(&source.Kind{Type: &corev1.Node{}}, nodeEventsHandler).
-		WithOptions(controller.Options{MaxConcurrentReconciles: 3}).
-		Complete(r)
+	return runtime.HandleReconcileError(r.reconcile(req), r.logger)
 }
 
 func (r *targetGroupBindingReconciler) reconcile(req ctrl.Request) error {
@@ -123,3 +103,32 @@ func (r *targetGroupBindingReconciler) cleanupTargetGroupBinding(ctx context.Con
 	}
 	return nil
 }
+
+func (r *targetGroupBindingReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
+	if err := r.setupIndexes(ctx, mgr.GetFieldIndexer()); err != nil {
+		return err
+	}
+
+	epEventsHandler := eventhandlers.NewEnqueueRequestsForEndpointsEvent(r.k8sClient,
+		r.logger.WithName("eventHandlers").WithName("endpoints"))
+	nodeEventsHandler := eventhandlers.NewEnqueueRequestsForNodeEvent(r.k8sClient,
+		r.logger.WithName("eventHandlers").WithName("node"))
+	return ctrl.NewControllerManagedBy(mgr).
+		For(&elbv2api.TargetGroupBinding{}).
+		Watches(&source.Kind{Type: &corev1.Endpoints{}}, epEventsHandler).
+		Watches(&source.Kind{Type: &corev1.Node{}}, nodeEventsHandler).
+		WithOptions(controller.Options{MaxConcurrentReconciles: 3}).
+		Complete(r)
+}
+
+func (r *targetGroupBindingReconciler) setupIndexes(ctx context.Context, fieldIndexer client.FieldIndexer) error {
+	if err := fieldIndexer.IndexField(ctx, &elbv2api.TargetGroupBinding{},
+		targetgroupbinding.IndexKeyServiceRefName, targetgroupbinding.IndexFuncServiceRefName); err != nil {
+		return err
+	}
+	if err := fieldIndexer.IndexField(ctx, &elbv2api.TargetGroupBinding{},
+		targetgroupbinding.IndexKeyTargetType, targetgroupbinding.IndexFuncTargetType); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/controllers/ingress/eventhandlers/ingress_events.go b/controllers/ingress/eventhandlers/ingress_events.go
index cd86e83d4..76b0cf902 100644
--- a/controllers/ingress/eventhandlers/ingress_events.go
+++ b/controllers/ingress/eventhandlers/ingress_events.go
@@ -2,6 +2,7 @@ package eventhandlers
 
 import (
 	"context"
+	"github.com/go-logr/logr"
 	networking "k8s.io/api/networking/v1beta1"
 	"k8s.io/apimachinery/pkg/api/equality"
 	"k8s.io/client-go/tools/record"
@@ -9,13 +10,14 @@ import (
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/ingress"
 	"sigs.k8s.io/controller-runtime/pkg/event"
 	"sigs.k8s.io/controller-runtime/pkg/handler"
-	"sigs.k8s.io/controller-runtime/pkg/log"
 )
 
-var logger = log.Log.WithName("eventhandlers").WithName("ingress")
-
-func NewEnqueueRequestsForIngressEvent(groupLoader ingress.GroupLoader, eventRecorder record.EventRecorder) *enqueueRequestsForIngressEvent {
-	return &enqueueRequestsForIngressEvent{groupLoader: groupLoader, eventRecorder: eventRecorder}
+func NewEnqueueRequestsForIngressEvent(groupLoader ingress.GroupLoader, eventRecorder record.EventRecorder, logger logr.Logger) *enqueueRequestsForIngressEvent {
+	return &enqueueRequestsForIngressEvent{
+		groupLoader:   groupLoader,
+		eventRecorder: eventRecorder,
+		logger:        logger,
+	}
 }
 
 var _ handler.EventHandler = (*enqueueRequestsForIngressEvent)(nil)
@@ -23,6 +25,7 @@ var _ handler.EventHandler = (*enqueueRequestsForIngressEvent)(nil)
 type enqueueRequestsForIngressEvent struct {
 	groupLoader   ingress.GroupLoader
 	eventRecorder record.EventRecorder
+	logger        logr.Logger
 }
 
 func (h *enqueueRequestsForIngressEvent) Create(e event.CreateEvent, queue workqueue.RateLimitingInterface) {
@@ -33,11 +36,14 @@ func (h *enqueueRequestsForIngressEvent) Update(e event.UpdateEvent, queue workq
 	ingOld := e.ObjectOld.(*networking.Ingress)
 	ingNew := e.ObjectNew.(*networking.Ingress)
 
-	// we only care three update event: 1. Ingress annotation updates 2. Ingress spec updates 3. Ingress deletion
+	// we only care below update event:
+	//	1. Ingress annotation updates
+	//	2. Ingress spec updates
+	//	3. Ingress deletion
 	if equality.Semantic.DeepEqual(ingOld.Annotations, ingNew.Annotations) &&
 		equality.Semantic.DeepEqual(ingOld.Spec, ingNew.Spec) &&
 		equality.Semantic.DeepEqual(ingOld.DeletionTimestamp.IsZero(), ingNew.DeletionTimestamp.IsZero()) {
-		logger.V(1).Info("ignoring unchanged Ingress Update event", "event", e)
+		h.logger.V(1).Info("ignoring unchanged Ingress Update event", "event", e)
 		return
 	}
 
@@ -52,6 +58,7 @@ func (h *enqueueRequestsForIngressEvent) Delete(e event.DeleteEvent, queue workq
 }
 
 func (h *enqueueRequestsForIngressEvent) Generic(e event.GenericEvent, queue workqueue.RateLimitingInterface) {
+	h.enqueueIfBelongsToGroup(queue, e.Object.(*networking.Ingress))
 }
 
 func (h *enqueueRequestsForIngressEvent) enqueueIfBelongsToGroup(queue workqueue.RateLimitingInterface, ingList ...*networking.Ingress) {
@@ -64,7 +71,7 @@ func (h *enqueueRequestsForIngressEvent) enqueueIfBelongsToGroup(queue workqueue
 			continue
 		}
 		if groupID == nil {
-			logger.V(1).Info("ignoring Ingress", "Ingress", ing)
+			h.logger.V(1).Info("ignoring Ingress", "Ingress", ing)
 			continue
 		}
 		groupIDs[*groupID] = struct{}{}
diff --git a/controllers/ingress/eventhandlers/secret_events.go b/controllers/ingress/eventhandlers/secret_events.go
new file mode 100644
index 000000000..2464fc93c
--- /dev/null
+++ b/controllers/ingress/eventhandlers/secret_events.go
@@ -0,0 +1,101 @@
+package eventhandlers
+
+import (
+	"context"
+	"github.com/go-logr/logr"
+	corev1 "k8s.io/api/core/v1"
+	networking "k8s.io/api/networking/v1beta1"
+	"k8s.io/apimachinery/pkg/api/equality"
+	"k8s.io/apimachinery/pkg/api/meta"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/client-go/tools/record"
+	"k8s.io/client-go/util/workqueue"
+	"sigs.k8s.io/aws-load-balancer-controller/pkg/ingress"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/event"
+	"sigs.k8s.io/controller-runtime/pkg/handler"
+)
+
+// NewEnqueueRequestsForSecretEvent constructs new enqueueRequestsForSecretEvent.
+func NewEnqueueRequestsForSecretEvent(ingEventChan chan<- event.GenericEvent, svcEventChan chan<- event.GenericEvent,
+	k8sClient client.Client, eventRecorder record.EventRecorder, logger logr.Logger) *enqueueRequestsForSecretEvent {
+	return &enqueueRequestsForSecretEvent{
+		ingEventChan:  ingEventChan,
+		svcEventChan:  svcEventChan,
+		k8sClient:     k8sClient,
+		eventRecorder: eventRecorder,
+		logger:        logger,
+	}
+}
+
+var _ handler.EventHandler = (*enqueueRequestsForSecretEvent)(nil)
+
+type enqueueRequestsForSecretEvent struct {
+	ingEventChan  chan<- event.GenericEvent
+	svcEventChan  chan<- event.GenericEvent
+	k8sClient     client.Client
+	eventRecorder record.EventRecorder
+	logger        logr.Logger
+}
+
+func (h *enqueueRequestsForSecretEvent) Create(e event.CreateEvent, _ workqueue.RateLimitingInterface) {
+	h.enqueueImpactedObjects(e.Meta)
+}
+
+func (h *enqueueRequestsForSecretEvent) Update(e event.UpdateEvent, _ workqueue.RateLimitingInterface) {
+	secretOld := e.ObjectOld.(*corev1.Secret)
+	secretNew := e.ObjectNew.(*corev1.Secret)
+
+	// we only care below update event:
+	//	1. Secret data updates
+	//	2. Secret deletions
+	if equality.Semantic.DeepEqual(secretOld.Data, secretNew.Data) &&
+		equality.Semantic.DeepEqual(secretOld.DeletionTimestamp.IsZero(), secretNew.DeletionTimestamp.IsZero()) {
+		h.logger.V(1).Info("ignoring unchanged Secret Update event")
+		return
+	}
+
+	h.enqueueImpactedObjects(e.MetaNew)
+}
+
+func (h *enqueueRequestsForSecretEvent) Delete(e event.DeleteEvent, _ workqueue.RateLimitingInterface) {
+	h.enqueueImpactedObjects(e.Meta)
+}
+
+func (h *enqueueRequestsForSecretEvent) Generic(e event.GenericEvent, _ workqueue.RateLimitingInterface) {
+	// we don't have any generic event for secrets.
+}
+
+func (h *enqueueRequestsForSecretEvent) enqueueImpactedObjects(secret metav1.Object) {
+	ingList := &networking.IngressList{}
+	if err := h.k8sClient.List(context.Background(), ingList,
+		client.InNamespace(secret.GetNamespace()),
+		client.MatchingFields{ingress.IndexKeySecretRefName: secret.GetName()}); err != nil {
+		h.logger.Error(err, "failed to fetch ingresses")
+		return
+	}
+	for index := range ingList.Items {
+		ing := &ingList.Items[index]
+		meta, _ := meta.Accessor(ing)
+		h.ingEventChan <- event.GenericEvent{
+			Meta:   meta,
+			Object: ing,
+		}
+	}
+
+	svcList := &corev1.ServiceList{}
+	if err := h.k8sClient.List(context.Background(), svcList,
+		client.InNamespace(secret.GetNamespace()),
+		client.MatchingFields{ingress.IndexKeySecretRefName: secret.GetName()}); err != nil {
+		h.logger.Error(err, "failed to fetch services")
+		return
+	}
+	for index := range svcList.Items {
+		svc := &svcList.Items[index]
+		meta, _ := meta.Accessor(svc)
+		h.svcEventChan <- event.GenericEvent{
+			Meta:   meta,
+			Object: svc,
+		}
+	}
+}
diff --git a/controllers/ingress/eventhandlers/service_events.go b/controllers/ingress/eventhandlers/service_events.go
new file mode 100644
index 000000000..f905b3ef4
--- /dev/null
+++ b/controllers/ingress/eventhandlers/service_events.go
@@ -0,0 +1,86 @@
+package eventhandlers
+
+import (
+	"context"
+	"github.com/go-logr/logr"
+	corev1 "k8s.io/api/core/v1"
+	networking "k8s.io/api/networking/v1beta1"
+	"k8s.io/apimachinery/pkg/api/equality"
+	"k8s.io/apimachinery/pkg/api/meta"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/client-go/tools/record"
+	"k8s.io/client-go/util/workqueue"
+	"sigs.k8s.io/aws-load-balancer-controller/pkg/ingress"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/event"
+	"sigs.k8s.io/controller-runtime/pkg/handler"
+)
+
+// NewEnqueueRequestsForServiceEvent constructs new enqueueRequestsForServiceEvent.
+func NewEnqueueRequestsForServiceEvent(ingEventChan chan<- event.GenericEvent,
+	k8sClient client.Client, eventRecorder record.EventRecorder, logger logr.Logger) *enqueueRequestsForServiceEvent {
+	return &enqueueRequestsForServiceEvent{
+		ingEventChan:  ingEventChan,
+		k8sClient:     k8sClient,
+		eventRecorder: eventRecorder,
+		logger:        logger,
+	}
+}
+
+var _ handler.EventHandler = (*enqueueRequestsForServiceEvent)(nil)
+
+type enqueueRequestsForServiceEvent struct {
+	ingEventChan  chan<- event.GenericEvent
+	k8sClient     client.Client
+	eventRecorder record.EventRecorder
+	logger        logr.Logger
+}
+
+func (h *enqueueRequestsForServiceEvent) Create(e event.CreateEvent, _ workqueue.RateLimitingInterface) {
+	h.enqueueImpactedIngresses(e.Meta)
+}
+
+func (h *enqueueRequestsForServiceEvent) Update(e event.UpdateEvent, _ workqueue.RateLimitingInterface) {
+	svcOld := e.ObjectOld.(*corev1.Service)
+	svcNew := e.ObjectNew.(*corev1.Service)
+
+	// we only care below update event:
+	//	1. Service annotation updates
+	//	2. Service spec updates
+	//	3. Service deletions
+	if equality.Semantic.DeepEqual(svcOld.Annotations, svcNew.Annotations) &&
+		equality.Semantic.DeepEqual(svcOld.Spec, svcNew.Spec) &&
+		equality.Semantic.DeepEqual(svcOld.DeletionTimestamp.IsZero(), svcNew.DeletionTimestamp.IsZero()) {
+		h.logger.V(1).Info("ignoring unchanged Service Update event", "event", e)
+		return
+	}
+
+	h.enqueueImpactedIngresses(e.MetaNew)
+}
+
+func (h *enqueueRequestsForServiceEvent) Delete(e event.DeleteEvent, _ workqueue.RateLimitingInterface) {
+	h.enqueueImpactedIngresses(e.Meta)
+}
+
+func (h *enqueueRequestsForServiceEvent) Generic(e event.GenericEvent, _ workqueue.RateLimitingInterface) {
+	h.enqueueImpactedIngresses(e.Meta)
+}
+
+func (h *enqueueRequestsForServiceEvent) enqueueImpactedIngresses(svc metav1.Object) {
+	ingList := &networking.IngressList{}
+	if err := h.k8sClient.List(context.Background(), ingList,
+		client.InNamespace(svc.GetNamespace()),
+		client.MatchingFields{ingress.IndexKeyServiceRefName: svc.GetName()}); err != nil {
+		h.logger.Error(err, "failed to fetch ingresses")
+		return
+	}
+
+	for index := range ingList.Items {
+		ing := &ingList.Items[index]
+		meta, _ := meta.Accessor(ing)
+		h.ingEventChan <- event.GenericEvent{
+			Meta:   meta,
+			Object: ing,
+		}
+	}
+}
diff --git a/controllers/ingress/group_controller.go b/controllers/ingress/group_controller.go
index c74b0acd3..3fc7b58b0 100644
--- a/controllers/ingress/group_controller.go
+++ b/controllers/ingress/group_controller.go
@@ -6,6 +6,7 @@ import (
 	"github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
 	networking "k8s.io/api/networking/v1beta1"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/client-go/tools/record"
 	"sigs.k8s.io/aws-load-balancer-controller/controllers/ingress/eventhandlers"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
@@ -17,50 +18,60 @@ import (
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/controller"
+	"sigs.k8s.io/controller-runtime/pkg/event"
 	"sigs.k8s.io/controller-runtime/pkg/source"
 )
 
-const controllerName = "ingress"
+const (
+	ingressTagPrefix        = "ingress.k8s.aws"
+	ingressAnnotationPrefix = "alb.ingress.kubernetes.io"
+	controllerName          = "ingress"
+)
 
 // NewGroupReconciler constructs new GroupReconciler
 func NewGroupReconciler(cloud aws.Cloud, k8sClient client.Client, eventRecorder record.EventRecorder,
 	networkingSGManager networkingpkg.SecurityGroupManager, networkingSGReconciler networkingpkg.SecurityGroupReconciler, clusterName string,
 	subnetsResolver networkingpkg.SubnetsResolver, logger logr.Logger) *groupReconciler {
-	annotationParser := annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io")
+	annotationParser := annotations.NewSuffixAnnotationParser(ingressAnnotationPrefix)
 	authConfigBuilder := ingress.NewDefaultAuthConfigBuilder(annotationParser)
 	enhancedBackendBuilder := ingress.NewDefaultEnhancedBackendBuilder(annotationParser)
+	referenceIndexer := ingress.NewDefaultReferenceIndexer(enhancedBackendBuilder, authConfigBuilder, logger)
 	modelBuilder := ingress.NewDefaultModelBuilder(k8sClient, eventRecorder,
 		cloud.EC2(), cloud.ACM(),
 		annotationParser, subnetsResolver,
 		authConfigBuilder, enhancedBackendBuilder,
 		cloud.VpcID(), clusterName, logger)
 	stackMarshaller := deploy.NewDefaultStackMarshaller()
-	stackDeployer := deploy.NewDefaultStackDeployer(cloud, k8sClient, networkingSGManager, networkingSGReconciler, clusterName, "ingress.k8s.aws", logger)
+	stackDeployer := deploy.NewDefaultStackDeployer(cloud, k8sClient, networkingSGManager, networkingSGReconciler, clusterName, ingressTagPrefix, logger)
 	groupLoader := ingress.NewDefaultGroupLoader(k8sClient, annotationParser, "alb")
 	k8sFinalizerManager := k8s.NewDefaultFinalizerManager(k8sClient, logger)
 	finalizerManager := ingress.NewDefaultFinalizerManager(k8sFinalizerManager)
 
 	return &groupReconciler{
 		k8sClient:        k8sClient,
+		eventRecorder:    eventRecorder,
 		groupLoader:      groupLoader,
 		finalizerManager: finalizerManager,
+		referenceIndexer: referenceIndexer,
 		modelBuilder:     modelBuilder,
 		stackMarshaller:  stackMarshaller,
 		stackDeployer:    stackDeployer,
-		log:              logger,
+		logger:           logger,
 	}
 }
 
 // GroupReconciler reconciles a ingress group
 type groupReconciler struct {
-	k8sClient       client.Client
-	modelBuilder    ingress.ModelBuilder
-	stackMarshaller deploy.StackMarshaller
-	stackDeployer   deploy.StackDeployer
+	k8sClient        client.Client
+	eventRecorder    record.EventRecorder
+	referenceIndexer ingress.ReferenceIndexer
+	modelBuilder     ingress.ModelBuilder
+	stackMarshaller  deploy.StackMarshaller
+	stackDeployer    deploy.StackDeployer
 
 	groupLoader      ingress.GroupLoader
 	finalizerManager ingress.FinalizerManager
-	log              logr.Logger
+	logger           logr.Logger
 }
 
 // +kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;update;patch
@@ -74,7 +85,7 @@ type groupReconciler struct {
 func (r *groupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 	ctx := context.Background()
 	ingGroupID := ingress.DecodeGroupIDFromReconcileRequest(req)
-	_ = r.log.WithValues("groupID", ingGroupID)
+	_ = r.logger.WithValues("groupID", ingGroupID)
 	ingGroup, err := r.groupLoader.Load(ctx, ingGroupID)
 	if err != nil {
 		return ctrl.Result{}, err
@@ -91,11 +102,11 @@ func (r *groupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 	if err != nil {
 		return ctrl.Result{}, err
 	}
-	r.log.Info("successfully built model", "model", stackJSON)
+	r.logger.Info("successfully built model", "model", stackJSON)
 	if err := r.stackDeployer.Deploy(ctx, stack); err != nil {
 		return ctrl.Result{}, err
 	}
-	r.log.Info("successfully deployed model")
+	r.logger.Info("successfully deployed model")
 
 	if len(ingGroup.Members) > 0 {
 		lbDNS, err := lb.DNSName().Resolve(ctx)
@@ -139,21 +150,72 @@ func (r *groupReconciler) updateIngressStatus(ctx context.Context, ing *networki
 	return nil
 }
 
-func (r *groupReconciler) SetupWithManager(mgr ctrl.Manager) error {
+func (r *groupReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
 	c, err := controller.New(controllerName, mgr, controller.Options{
-		MaxConcurrentReconciles: 1,
+		MaxConcurrentReconciles: 3,
 		Reconciler:              r,
 	})
 	if err != nil {
 		return err
 	}
-	return r.setupWatches(mgr, c, r.groupLoader)
+	if err := r.setupIndexes(ctx, mgr.GetFieldIndexer()); err != nil {
+		return err
+	}
+	if err := r.setupWatches(ctx, c); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (r *groupReconciler) setupIndexes(ctx context.Context, fieldIndexer client.FieldIndexer) error {
+	if err := fieldIndexer.IndexField(ctx, &networking.Ingress{}, ingress.IndexKeyServiceRefName,
+		func(obj runtime.Object) []string {
+			return r.referenceIndexer.BuildServiceRefIndexes(context.Background(), obj.(*networking.Ingress))
+		},
+	); err != nil {
+		return err
+	}
+	if err := fieldIndexer.IndexField(ctx, &networking.Ingress{}, ingress.IndexKeySecretRefName,
+		func(obj runtime.Object) []string {
+			return r.referenceIndexer.BuildSecretRefIndexes(context.Background(), obj.(*networking.Ingress))
+		},
+	); err != nil {
+		return err
+	}
+	if err := fieldIndexer.IndexField(ctx, &corev1.Service{}, ingress.IndexKeySecretRefName,
+		func(obj runtime.Object) []string {
+			return r.referenceIndexer.BuildSecretRefIndexes(context.Background(), obj.(*corev1.Service))
+		},
+	); err != nil {
+		return err
+	}
+	return nil
 }
 
-func (r *groupReconciler) setupWatches(mgr ctrl.Manager, c controller.Controller, groupLoader ingress.GroupLoader) error {
-	ingEventHandler := eventhandlers.NewEnqueueRequestsForIngressEvent(groupLoader, mgr.GetEventRecorderFor(controllerName))
+func (r *groupReconciler) setupWatches(_ context.Context, c controller.Controller) error {
+	ingEventChan := make(chan event.GenericEvent)
+	svcEventChan := make(chan event.GenericEvent)
+	ingEventHandler := eventhandlers.NewEnqueueRequestsForIngressEvent(r.groupLoader, r.eventRecorder,
+		r.logger.WithName("eventHandlers").WithName("ingress"))
+	svcEventHandler := eventhandlers.NewEnqueueRequestsForServiceEvent(ingEventChan, r.k8sClient, r.eventRecorder,
+		r.logger.WithName("eventHandlers").WithName("service"))
+	secretEventHandler := eventhandlers.NewEnqueueRequestsForSecretEvent(ingEventChan, svcEventChan, r.k8sClient, r.eventRecorder,
+		r.logger.WithName("eventHandlers").WithName("secret"))
+
+	if err := c.Watch(&source.Channel{Source: ingEventChan}, ingEventHandler); err != nil {
+		return err
+	}
+	if err := c.Watch(&source.Channel{Source: svcEventChan}, svcEventHandler); err != nil {
+		return err
+	}
 	if err := c.Watch(&source.Kind{Type: &networking.Ingress{}}, ingEventHandler); err != nil {
 		return err
 	}
+	if err := c.Watch(&source.Kind{Type: &corev1.Service{}}, svcEventHandler); err != nil {
+		return err
+	}
+	if err := c.Watch(&source.Kind{Type: &corev1.Secret{}}, secretEventHandler); err != nil {
+		return err
+	}
 	return nil
 }
diff --git a/controllers/service/eventhandlers/service_events.go b/controllers/service/eventhandlers/service_events.go
index d404c0b06..cd471f8b1 100644
--- a/controllers/service/eventhandlers/service_events.go
+++ b/controllers/service/eventhandlers/service_events.go
@@ -1,6 +1,7 @@
 package eventhandlers
 
 import (
+	"github.com/go-logr/logr"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/equality"
 	"k8s.io/apimachinery/pkg/types"
@@ -9,16 +10,15 @@ import (
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
 	"sigs.k8s.io/controller-runtime/pkg/event"
 	"sigs.k8s.io/controller-runtime/pkg/handler"
-	"sigs.k8s.io/controller-runtime/pkg/log"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 )
 
-var logger = log.Log.WithName("eventhandlers").WithName("service")
-
-func NewEnqueueRequestForServiceEvent(eventRecorder record.EventRecorder, annotationParser annotations.Parser) *enqueueRequestsForServiceEvent {
+// NewEnqueueRequestForServiceEvent constructs new enqueueRequestsForServiceEvent.
+func NewEnqueueRequestForServiceEvent(eventRecorder record.EventRecorder, annotationParser annotations.Parser, logger logr.Logger) *enqueueRequestsForServiceEvent {
 	return &enqueueRequestsForServiceEvent{
 		eventRecorder:    eventRecorder,
 		annotationParser: annotationParser,
+		logger:           logger,
 	}
 }
 
@@ -27,6 +27,7 @@ var _ handler.EventHandler = (*enqueueRequestsForServiceEvent)(nil)
 type enqueueRequestsForServiceEvent struct {
 	eventRecorder    record.EventRecorder
 	annotationParser annotations.Parser
+	logger           logr.Logger
 }
 
 func (h *enqueueRequestsForServiceEvent) Create(e event.CreateEvent, queue workqueue.RateLimitingInterface) {
@@ -40,7 +41,7 @@ func (h *enqueueRequestsForServiceEvent) Update(e event.UpdateEvent, queue workq
 	if equality.Semantic.DeepEqual(oldSvc.Annotations, newSvc.Annotations) &&
 		equality.Semantic.DeepEqual(oldSvc.Spec, newSvc.Spec) &&
 		equality.Semantic.DeepEqual(oldSvc.DeletionTimestamp.IsZero(), newSvc.DeletionTimestamp.IsZero()) {
-		logger.V(1).Info("Ignoring unchanged Service update event", "event", e)
+		h.logger.V(1).Info("Ignoring unchanged Service update event", "event", e)
 		return
 	}
 
diff --git a/controllers/service/service_controller.go b/controllers/service/service_controller.go
index bc8128aaa..5bd22af89 100644
--- a/controllers/service/service_controller.go
+++ b/controllers/service/service_controller.go
@@ -5,6 +5,7 @@ import (
 	"github.com/go-logr/logr"
 	"github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
+	"k8s.io/client-go/tools/record"
 	"sigs.k8s.io/aws-load-balancer-controller/controllers/service/eventhandlers"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/aws"
@@ -28,30 +29,34 @@ const (
 	controllerName          = "service"
 )
 
-func NewServiceReconciler(cloud aws.Cloud, k8sClient client.Client,
+func NewServiceReconciler(cloud aws.Cloud, k8sClient client.Client, eventRecorder record.EventRecorder,
 	sgManager networking.SecurityGroupManager, sgReconciler networking.SecurityGroupReconciler,
 	clusterName string, resolver networking.SubnetsResolver, logger logr.Logger) *serviceReconciler {
 	annotationParser := annotations.NewSuffixAnnotationParser(serviceAnnotationPrefix)
 	modelBuilder := nlb.NewDefaultModelBuilder(clusterName, resolver, annotationParser)
 	return &serviceReconciler{
 		k8sClient:        k8sClient,
-		logger:           logger,
+		eventRecorder:    eventRecorder,
 		annotationParser: annotationParser,
 		finalizerManager: k8s.NewDefaultFinalizerManager(k8sClient, logger),
 		modelBuilder:     modelBuilder,
 		stackMarshaller:  deploy.NewDefaultStackMarshaller(),
 		stackDeployer:    deploy.NewDefaultStackDeployer(cloud, k8sClient, sgManager, sgReconciler, clusterName, serviceTagPrefix, logger),
+		logger:           logger,
 	}
 }
 
 type serviceReconciler struct {
-	k8sClient        client.Client
-	logger           logr.Logger
+	k8sClient     client.Client
+	eventRecorder record.EventRecorder
+
 	annotationParser annotations.Parser
 	finalizerManager k8s.FinalizerManager
 	modelBuilder     nlb.ModelBuilder
 	stackMarshaller  deploy.StackMarshaller
 	stackDeployer    deploy.StackDeployer
+
+	logger logr.Logger
 }
 
 // +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;update;patch
@@ -138,19 +143,23 @@ func (r *serviceReconciler) updateServiceStatus(ctx context.Context, svc *corev1
 	return nil
 }
 
-func (r *serviceReconciler) SetupWithManager(mgr ctrl.Manager) error {
+func (r *serviceReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
 	c, err := controller.New(controllerName, mgr, controller.Options{
-		MaxConcurrentReconciles: 1,
+		MaxConcurrentReconciles: 3,
 		Reconciler:              r,
 	})
 	if err != nil {
 		return err
 	}
-	return r.setupWatches(mgr, c)
+	if err := r.setupWatches(ctx, c); err != nil {
+		return err
+	}
+	return nil
 }
 
-func (r *serviceReconciler) setupWatches(mgr ctrl.Manager, c controller.Controller) error {
-	svcEventHandler := eventhandlers.NewEnqueueRequestForServiceEvent(mgr.GetEventRecorderFor(controllerName), r.annotationParser)
+func (r *serviceReconciler) setupWatches(_ context.Context, c controller.Controller) error {
+	svcEventHandler := eventhandlers.NewEnqueueRequestForServiceEvent(r.eventRecorder, r.annotationParser,
+		r.logger.WithName("eventHandlers").WithName("service"))
 	if err := c.Watch(&source.Kind{Type: &corev1.Service{}}, svcEventHandler); err != nil {
 		return err
 	}
diff --git a/docs/guide/nlb-ip.md b/docs/guide/nlb-ip.md
index 95f5b72d7..cfed90b12 100644
--- a/docs/guide/nlb-ip.md
+++ b/docs/guide/nlb-ip.md
@@ -39,7 +39,12 @@ For internal load balancer, you must tag the private subnets as follows:
 |  `kubernetes.io/role/internal-elb`  |  `1`  |
 
 ## Protocols
-Support is available for both TCP and UDP protocols. In case of TCP, NLB in IP mode does not pass the client source IP address to the pods. You can configure protocol v2 via annotation if you need the client source IP address.
+Support is available for both TCP and UDP protocols. In case of TCP, NLB in IP mode does not pass the client source IP address to the pods. You can configure [NLB proxy protocol v2](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#proxy-protocol) via [annotation](https://kubernetes.io/docs/concepts/services-networking/service/#proxy-protocol-support-on-aws) if you need the client source IP address.
+
+to enable proxy protocol v2, apply the following annotation to your service:
+```yaml
+service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
+```
 
 ## Security group
 NLB does not currently support a managed security group. For ingress access, the controller will resolve the security group for the ENI corresponding tho the endpoint pod. If the ENI has a single security group, it gets used. In case of multiple security gropus, the controller expects to find only one security group tagged with the Kubernetes cluster id. Controller will update the ingress rules on the security groups as per the service spec.
diff --git a/main.go b/main.go
index b409f91c5..11893ad11 100644
--- a/main.go
+++ b/main.go
@@ -121,27 +121,25 @@ func main() {
 
 	subnetResolver := networking.NewSubnetsResolver(cloud.EC2(), cloud.VpcID(), k8sClusterName, ctrl.Log.WithName("subnets-resolver"))
 	ingGroupReconciler := ingress.NewGroupReconciler(cloud, mgr.GetClient(), mgr.GetEventRecorderFor("ingress"),
-		sgManager, sgReconciler, k8sClusterName, subnetResolver, ctrl.Log)
-	tgbReconciler := elbv2controller.NewTargetGroupBindingReconciler(mgr.GetClient(), mgr.GetFieldIndexer(), finalizerManager, tgbResManager,
-		ctrl.Log.WithName("controllers").WithName("TargetGroupBinding"))
-	svcReconciler := service.NewServiceReconciler(
-		cloud,
-		mgr.GetClient(),
-		sgManager,
-		sgReconciler,
-		k8sClusterName,
-		subnetResolver,
+		sgManager, sgReconciler, k8sClusterName, subnetResolver,
+		ctrl.Log.WithName("controllers").WithName("Ingress"))
+	svcReconciler := service.NewServiceReconciler(cloud, mgr.GetClient(), mgr.GetEventRecorderFor("service"),
+		sgManager, sgReconciler, k8sClusterName, subnetResolver,
 		ctrl.Log.WithName("controllers").WithName("Service"))
-	if err = ingGroupReconciler.SetupWithManager(mgr); err != nil {
+	tgbReconciler := elbv2controller.NewTargetGroupBindingReconciler(mgr.GetClient(), finalizerManager, tgbResManager,
+		ctrl.Log.WithName("controllers").WithName("TargetGroupBinding"))
+
+	ctx := context.Background()
+	if err = ingGroupReconciler.SetupWithManager(ctx, mgr); err != nil {
 		setupLog.Error(err, "unable to create controller", "controller", "Ingress")
 		os.Exit(1)
 	}
-	if err := tgbReconciler.SetupWithManager(context.Background(), mgr); err != nil {
-		setupLog.Error(err, "unable to create controller", "controller", "TargetGroupBinding")
+	if err = svcReconciler.SetupWithManager(ctx, mgr); err != nil {
+		setupLog.Error(err, "Unable to create controller", "controller", "Service")
 		os.Exit(1)
 	}
-	if err = svcReconciler.SetupWithManager(mgr); err != nil {
-		setupLog.Error(err, "Unable to create controller", "controller", "Service")
+	if err := tgbReconciler.SetupWithManager(ctx, mgr); err != nil {
+		setupLog.Error(err, "unable to create controller", "controller", "TargetGroupBinding")
 		os.Exit(1)
 	}
 
diff --git a/pkg/deploy/ec2/security_group_manager.go b/pkg/deploy/ec2/security_group_manager.go
index 689ea24e2..910897954 100644
--- a/pkg/deploy/ec2/security_group_manager.go
+++ b/pkg/deploy/ec2/security_group_manager.go
@@ -2,11 +2,11 @@ package ec2
 
 import (
 	"context"
-	"errors"
 	awssdk "github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws/awserr"
 	ec2sdk "github.com/aws/aws-sdk-go/service/ec2"
 	"github.com/go-logr/logr"
+	"github.com/pkg/errors"
 	"k8s.io/apimachinery/pkg/util/wait"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/deploy/tracking"
@@ -131,7 +131,7 @@ func (m *defaultSecurityGroupManager) Delete(ctx context.Context, sdkSG networki
 		}
 		return true, nil
 	}, ctx.Done()); err != nil {
-		return err
+		return errors.Wrap(err, "failed to delete securityGroup")
 	}
 	m.logger.Info("deleted securityGroup",
 		"securityGroupID", sdkSG.SecurityGroupID)
diff --git a/pkg/deploy/elbv2/listener_rule_manager.go b/pkg/deploy/elbv2/listener_rule_manager.go
index c69c51761..47f259e86 100644
--- a/pkg/deploy/elbv2/listener_rule_manager.go
+++ b/pkg/deploy/elbv2/listener_rule_manager.go
@@ -3,12 +3,21 @@ package elbv2
 import (
 	"context"
 	awssdk "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/awserr"
 	elbv2sdk "github.com/aws/aws-sdk-go/service/elbv2"
 	"github.com/go-logr/logr"
 	"github.com/google/go-cmp/cmp"
+	"github.com/pkg/errors"
+	"k8s.io/apimachinery/pkg/util/wait"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
 	elbv2equality "sigs.k8s.io/aws-load-balancer-controller/pkg/equality/elbv2"
 	elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2"
+	"time"
+)
+
+const (
+	defaultWaitLSExistencePollInterval = 2 * time.Second
+	defaultWaitLSExistenceTimeout      = 20 * time.Second
 )
 
 // ListenerRuleManager is responsible for create/update/delete ListenerRule resources.
@@ -23,8 +32,10 @@ type ListenerRuleManager interface {
 // NewDefaultListenerRuleManager constructs new defaultListenerRuleManager.
 func NewDefaultListenerRuleManager(elbv2Client services.ELBV2, logger logr.Logger) *defaultListenerRuleManager {
 	return &defaultListenerRuleManager{
-		elbv2Client: elbv2Client,
-		logger:      logger,
+		elbv2Client:                 elbv2Client,
+		logger:                      logger,
+		waitLSExistencePollInterval: defaultWaitLSExistencePollInterval,
+		waitLSExistenceTimeout:      defaultWaitLSExistenceTimeout,
 	}
 }
 
@@ -32,6 +43,9 @@ func NewDefaultListenerRuleManager(elbv2Client services.ELBV2, logger logr.Logge
 type defaultListenerRuleManager struct {
 	elbv2Client services.ELBV2
 	logger      logr.Logger
+
+	waitLSExistencePollInterval time.Duration
+	waitLSExistenceTimeout      time.Duration
 }
 
 func (m *defaultListenerRuleManager) Create(ctx context.Context, resLR *elbv2model.ListenerRule) (elbv2model.ListenerRuleStatus, error) {
@@ -40,14 +54,25 @@ func (m *defaultListenerRuleManager) Create(ctx context.Context, resLR *elbv2mod
 		return elbv2model.ListenerRuleStatus{}, err
 	}
 
+	ctx, cancel := context.WithTimeout(ctx, m.waitLSExistenceTimeout)
+	defer cancel()
 	m.logger.Info("creating listener rule",
 		"stackID", resLR.Stack().StackID(),
 		"resourceID", resLR.ID())
-	resp, err := m.elbv2Client.CreateRuleWithContext(ctx, req)
-	if err != nil {
-		return elbv2model.ListenerRuleStatus{}, err
+	var sdkLR *elbv2sdk.Rule
+	if err := wait.PollImmediateUntil(m.waitLSExistencePollInterval, func() (done bool, err error) {
+		resp, err := m.elbv2Client.CreateRuleWithContext(ctx, req)
+		if err != nil {
+			if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "ListenerNotFound" {
+				return false, nil
+			}
+			return false, err
+		}
+		sdkLR = resp.Rules[0]
+		return true, nil
+	}, ctx.Done()); err != nil {
+		return elbv2model.ListenerRuleStatus{}, errors.Wrap(err, "failed to create listener rule")
 	}
-	sdkLR := resp.Rules[0]
 	m.logger.Info("created listener rule",
 		"stackID", resLR.Stack().StackID(),
 		"resourceID", resLR.ID(),
diff --git a/pkg/deploy/elbv2/target_group_binding_manager.go b/pkg/deploy/elbv2/target_group_binding_manager.go
index 9d4105227..011382391 100644
--- a/pkg/deploy/elbv2/target_group_binding_manager.go
+++ b/pkg/deploy/elbv2/target_group_binding_manager.go
@@ -119,7 +119,7 @@ func (m *defaultTargetGroupBindingManager) Delete(ctx context.Context, tgb *elbv
 		return err
 	}
 	if err := m.waitUntilTargetGroupBindingDeleted(ctx, tgb); err != nil {
-		return err
+		return errors.Wrap(err, "failed to wait targetGroupBinding deletion")
 	}
 	m.logger.Info("deleted targetGroupBinding",
 		"targetGroupBinding", k8s.NamespacedName(tgb))
diff --git a/pkg/deploy/elbv2/target_group_manager.go b/pkg/deploy/elbv2/target_group_manager.go
index 290af5f6b..3495989f7 100644
--- a/pkg/deploy/elbv2/target_group_manager.go
+++ b/pkg/deploy/elbv2/target_group_manager.go
@@ -6,6 +6,7 @@ import (
 	"github.com/aws/aws-sdk-go/aws/awserr"
 	elbv2sdk "github.com/aws/aws-sdk-go/service/elbv2"
 	"github.com/go-logr/logr"
+	"github.com/pkg/errors"
 	"k8s.io/apimachinery/pkg/util/wait"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/aws/services"
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/deploy/tracking"
@@ -119,7 +120,7 @@ func (m *defaultTargetGroupManager) Delete(ctx context.Context, sdkTG TargetGrou
 		}
 		return true, nil
 	}, ctx.Done()); err != nil {
-		return err
+		return errors.Wrap(err, "failed to delete targetGroup")
 	}
 	m.logger.Info("deleted targetGroup",
 		"arn", awssdk.StringValue(req.TargetGroupArn))
diff --git a/pkg/ingress/auth_config_builder.go b/pkg/ingress/auth_config_builder.go
index 5b98409ff..1f4fc1359 100644
--- a/pkg/ingress/auth_config_builder.go
+++ b/pkg/ingress/auth_config_builder.go
@@ -6,6 +6,14 @@ import (
 	"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
 )
 
+const (
+	defaultAuthType                     = AuthTypeNone
+	defaultAuthScope                    = "openid"
+	defaultAuthSessionCookieName        = "AWSELBAuthSessionCookie"
+	defaultAuthSessionTimeout           = 604800
+	defaultAuthOnUnauthenticatedRequest = "authenticate"
+)
+
 // Auth config for Service / Ingresses
 type AuthConfig struct {
 	Type                     AuthType
@@ -41,41 +49,36 @@ func (b *defaultAuthConfigBuilder) Build(ctx context.Context, svcAndIngAnnotatio
 	if err != nil {
 		return AuthConfig{}, err
 	}
-	authConfig := AuthConfig{
-		Type: authType,
+	authOnUnauthenticatedRequest := b.buildAuthOnUnauthenticatedRequest(ctx, svcAndIngAnnotations)
+	authScope := b.buildAuthScope(ctx, svcAndIngAnnotations)
+	authSessionCookieName := b.buildAuthSessionCookieName(ctx, svcAndIngAnnotations)
+	authSessionTimeout, err := b.buildAuthSessionTimeout(ctx, svcAndIngAnnotations)
+	if err != nil {
+		return AuthConfig{}, err
+	}
+	authIDPCognito, err := b.buildAuthIDPConfigCognito(ctx, svcAndIngAnnotations)
+	if err != nil {
+		return AuthConfig{}, err
 	}
-	if authType != AuthTypeNone {
-		authOnUnauthenticatedRequest := b.buildAuthOnUnauthenticatedRequest(ctx, svcAndIngAnnotations)
-		authScope := b.buildAuthScope(ctx, svcAndIngAnnotations)
-		authSessionCookieName := b.buildAuthSessionCookieName(ctx, svcAndIngAnnotations)
-		authSessionTimeout, err := b.buildAuthSessionTimeout(ctx, svcAndIngAnnotations)
-		if err != nil {
-			return AuthConfig{}, err
-		}
-		authConfig.OnUnauthenticatedRequest = authOnUnauthenticatedRequest
-		authConfig.Scope = authScope
-		authConfig.SessionCookieName = authSessionCookieName
-		authConfig.SessionTimeout = authSessionTimeout
+	authIDPOIDC, err := b.buildAuthIDPConfigOIDC(ctx, svcAndIngAnnotations)
+	if err != nil {
+		return AuthConfig{}, err
 	}
 
-	switch authType {
-	case AuthTypeCognito:
-		authIDP, err := b.buildAuthIDPConfigCognito(ctx, svcAndIngAnnotations)
-		if err != nil {
-			return AuthConfig{}, err
-		}
-		authConfig.IDPConfigCognito = &authIDP
-	case AuthTypeOIDC:
-		authIDP, err := b.buildAuthIDPConfigOIDC(ctx, svcAndIngAnnotations)
-		if err != nil {
-			return AuthConfig{}, err
-		}
-		authConfig.IDPConfigOIDC = &authIDP
+	authConfig := AuthConfig{
+		Type:                     authType,
+		OnUnauthenticatedRequest: authOnUnauthenticatedRequest,
+		Scope:                    authScope,
+		SessionCookieName:        authSessionCookieName,
+		SessionTimeout:           authSessionTimeout,
+		IDPConfigOIDC:            authIDPOIDC,
+		IDPConfigCognito:         authIDPCognito,
 	}
+
 	return authConfig, nil
 }
 
-func (b *defaultAuthConfigBuilder) buildAuthType(ctx context.Context, svcAndIngAnnotations map[string]string) (AuthType, error) {
+func (b *defaultAuthConfigBuilder) buildAuthType(_ context.Context, svcAndIngAnnotations map[string]string) (AuthType, error) {
 	rawAuthType := string(defaultAuthType)
 	_ = b.annotationParser.ParseStringAnnotation(annotations.IngressSuffixAuthType, &rawAuthType, svcAndIngAnnotations)
 	switch rawAuthType {
@@ -90,49 +93,49 @@ func (b *defaultAuthConfigBuilder) buildAuthType(ctx context.Context, svcAndIngA
 	}
 }
 
-func (b *defaultAuthConfigBuilder) buildAuthIDPConfigCognito(ctx context.Context, svcAndIngAnnotations map[string]string) (AuthIDPConfigCognito, error) {
+func (b *defaultAuthConfigBuilder) buildAuthIDPConfigCognito(_ context.Context, svcAndIngAnnotations map[string]string) (*AuthIDPConfigCognito, error) {
 	authIDP := AuthIDPConfigCognito{}
 	exists, err := b.annotationParser.ParseJSONAnnotation(annotations.IngressSuffixAuthIDPCognito, &authIDP, svcAndIngAnnotations)
 	if err != nil {
-		return AuthIDPConfigCognito{}, err
+		return nil, err
 	}
 	if !exists {
-		return AuthIDPConfigCognito{}, errors.Errorf("missing %v configuration", annotations.IngressSuffixAuthIDPCognito)
+		return nil, nil
 	}
-	return authIDP, nil
+	return &authIDP, nil
 }
 
-func (b *defaultAuthConfigBuilder) buildAuthIDPConfigOIDC(ctx context.Context, svcAndIngAnnotations map[string]string) (AuthIDPConfigOIDC, error) {
+func (b *defaultAuthConfigBuilder) buildAuthIDPConfigOIDC(_ context.Context, svcAndIngAnnotations map[string]string) (*AuthIDPConfigOIDC, error) {
 	authIDP := AuthIDPConfigOIDC{}
 	exists, err := b.annotationParser.ParseJSONAnnotation(annotations.IngressSuffixAuthIDPOIDC, &authIDP, svcAndIngAnnotations)
 	if err != nil {
-		return AuthIDPConfigOIDC{}, err
+		return nil, err
 	}
 	if !exists {
-		return AuthIDPConfigOIDC{}, errors.Errorf("missing %v configuration", annotations.IngressSuffixAuthIDPOIDC)
+		return nil, nil
 	}
-	return authIDP, nil
+	return &authIDP, nil
 }
 
-func (b *defaultAuthConfigBuilder) buildAuthOnUnauthenticatedRequest(ctx context.Context, svcAndIngAnnotations map[string]string) string {
+func (b *defaultAuthConfigBuilder) buildAuthOnUnauthenticatedRequest(_ context.Context, svcAndIngAnnotations map[string]string) string {
 	rawOnUnauthenticatedRequest := defaultAuthOnUnauthenticatedRequest
 	_ = b.annotationParser.ParseStringAnnotation(annotations.IngressSuffixAuthOnUnauthenticatedRequest, &rawOnUnauthenticatedRequest, svcAndIngAnnotations)
 	return rawOnUnauthenticatedRequest
 }
 
-func (b *defaultAuthConfigBuilder) buildAuthScope(ctx context.Context, svcAndIngAnnotations map[string]string) string {
+func (b *defaultAuthConfigBuilder) buildAuthScope(_ context.Context, svcAndIngAnnotations map[string]string) string {
 	rawAuthScope := defaultAuthScope
 	_ = b.annotationParser.ParseStringAnnotation(annotations.IngressSuffixAuthScope, &rawAuthScope, svcAndIngAnnotations)
 	return rawAuthScope
 }
 
-func (b *defaultAuthConfigBuilder) buildAuthSessionCookieName(ctx context.Context, svcAndIngAnnotations map[string]string) string {
+func (b *defaultAuthConfigBuilder) buildAuthSessionCookieName(_ context.Context, svcAndIngAnnotations map[string]string) string {
 	rawAuthSessionCookieName := defaultAuthSessionCookieName
 	_ = b.annotationParser.ParseStringAnnotation(annotations.IngressSuffixAuthSessionCookie, &rawAuthSessionCookieName, svcAndIngAnnotations)
 	return rawAuthSessionCookieName
 }
 
-func (b *defaultAuthConfigBuilder) buildAuthSessionTimeout(ctx context.Context, svcAndIngAnnotations map[string]string) (int64, error) {
+func (b *defaultAuthConfigBuilder) buildAuthSessionTimeout(_ context.Context, svcAndIngAnnotations map[string]string) (int64, error) {
 	rawAuthSessionTimeout := int64(defaultAuthSessionTimeout)
 	if _, err := b.annotationParser.ParseInt64Annotation(annotations.IngressSuffixAuthSessionTimeout, &rawAuthSessionTimeout, svcAndIngAnnotations); err != nil {
 		return 0, err
diff --git a/pkg/ingress/enhanced_backend_builder.go b/pkg/ingress/enhanced_backend_builder.go
index a26a58099..3fba0a295 100644
--- a/pkg/ingress/enhanced_backend_builder.go
+++ b/pkg/ingress/enhanced_backend_builder.go
@@ -10,12 +10,6 @@ import (
 )
 
 const (
-	defaultAuthType                     = AuthTypeNone
-	defaultAuthScope                    = "openid"
-	defaultAuthSessionCookieName        = "AWSELBAuthSessionCookie"
-	defaultAuthSessionTimeout           = 604800
-	defaultAuthOnUnauthenticatedRequest = "authenticate"
-
 	magicServicePortUseAnnotation = "use-annotation"
 )
 
diff --git a/pkg/ingress/reference_indexer.go b/pkg/ingress/reference_indexer.go
new file mode 100644
index 000000000..9d47829da
--- /dev/null
+++ b/pkg/ingress/reference_indexer.go
@@ -0,0 +1,106 @@
+package ingress
+
+import (
+	"context"
+	"fmt"
+	"github.com/go-logr/logr"
+	networking "k8s.io/api/networking/v1beta1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/sets"
+)
+
+const (
+	// IndexKey for services referenced by Ingress.
+	IndexKeyServiceRefName = "ingress.serviceRef.name"
+	// IndexKey for secrets referenced by Ingress or Service.
+	IndexKeySecretRefName = "ingress.secretRef.name"
+)
+
+// ReferenceIndexer has the ability to index Ingresses with referenced objects.
+type ReferenceIndexer interface {
+	BuildServiceRefIndexes(ctx context.Context, ing *networking.Ingress) []string
+	BuildSecretRefIndexes(ctx context.Context, ingOrSvc metav1.Object) []string
+}
+
+// NewDefaultReferenceIndexer constructs new defaultReferenceIndexer.
+func NewDefaultReferenceIndexer(enhancedBackendBuilder EnhancedBackendBuilder, authConfigBuilder AuthConfigBuilder, logger logr.Logger) *defaultReferenceIndexer {
+	return &defaultReferenceIndexer{
+		enhancedBackendBuilder: enhancedBackendBuilder,
+		authConfigBuilder:      authConfigBuilder,
+		logger:                 logger,
+	}
+}
+
+var _ ReferenceIndexer = &defaultReferenceIndexer{}
+
+// default implementation for ReferenceIndexer
+type defaultReferenceIndexer struct {
+	enhancedBackendBuilder EnhancedBackendBuilder
+	authConfigBuilder      AuthConfigBuilder
+	logger                 logr.Logger
+}
+
+func (i *defaultReferenceIndexer) BuildServiceRefIndexes(ctx context.Context, ing *networking.Ingress) []string {
+	var backends []networking.IngressBackend
+	if ing.Spec.Backend != nil {
+		backends = append(backends, *ing.Spec.Backend)
+	}
+	for _, rule := range ing.Spec.Rules {
+		if rule.HTTP == nil {
+			continue
+		}
+		for _, path := range rule.HTTP.Paths {
+			backends = append(backends, path.Backend)
+		}
+	}
+
+	serviceNames := sets.NewString()
+	for _, backend := range backends {
+		enhancedBackend, err := i.enhancedBackendBuilder.Build(ctx, ing, backend)
+		if err != nil {
+			i.logger.Error(err, "failed to build Ingress indexes",
+				"indexKey", IndexKeyServiceRefName)
+			fmt.Println(err)
+			return nil
+		}
+		serviceNamesFromBackend := extractServiceNamesFromAction(enhancedBackend.Action)
+		serviceNames.Insert(serviceNamesFromBackend...)
+	}
+	return serviceNames.List()
+}
+
+func (i *defaultReferenceIndexer) BuildSecretRefIndexes(ctx context.Context, ingOrSvc metav1.Object) []string {
+	authCfg, err := i.authConfigBuilder.Build(ctx, ingOrSvc.GetAnnotations())
+	if err != nil {
+		i.logger.Error(err, "failed to build Ingress indexes",
+			"indexKey", IndexKeySecretRefName)
+		return nil
+	}
+	return extractSecretNamesFromAuthConfig(authCfg)
+}
+
+func extractServiceNamesFromAction(action Action) []string {
+	if action.Type != ActionTypeForward || action.ForwardConfig == nil {
+		return nil
+	}
+	serviceNames := sets.NewString()
+	for _, tgt := range action.ForwardConfig.TargetGroups {
+		serviceNamesFromTGT := extractServiceNamesFromTargetGroupTuple(tgt)
+		serviceNames.Insert(serviceNamesFromTGT...)
+	}
+	return serviceNames.List()
+}
+
+func extractServiceNamesFromTargetGroupTuple(tgt TargetGroupTuple) []string {
+	if tgt.ServiceName == nil {
+		return nil
+	}
+	return []string{*tgt.ServiceName}
+}
+
+func extractSecretNamesFromAuthConfig(authCfg AuthConfig) []string {
+	if authCfg.IDPConfigOIDC == nil {
+		return nil
+	}
+	return []string{authCfg.IDPConfigOIDC.SecretName}
+}


Test patch:
diff --git a/controllers/elbv2/suite_test.go b/controllers/elbv2/suite_test.go
deleted file mode 100644
index f0ca2210e..000000000
--- a/controllers/elbv2/suite_test.go
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package controllers
-
-import (
-	"path/filepath"
-	"testing"
-
-	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-	"k8s.io/client-go/kubernetes/scheme"
-	"k8s.io/client-go/rest"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/envtest"
-	"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
-	logf "sigs.k8s.io/controller-runtime/pkg/log"
-	"sigs.k8s.io/controller-runtime/pkg/log/zap"
-
-	elbv2v1alpha1 "sigs.k8s.io/aws-load-balancer-controller/apis/elbv2/v1alpha1"
-	// +kubebuilder:scaffold:imports
-)
-
-// These tests use Ginkgo (BDD-style Go testing framework). Refer to
-// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
-
-var cfg *rest.Config
-var k8sClient client.Client
-var testEnv *envtest.Environment
-
-func TestAPIs(t *testing.T) {
-	RegisterFailHandler(Fail)
-
-	RunSpecsWithDefaultAndCustomReporters(t,
-		"Controller Suite",
-		[]Reporter{printer.NewlineReporter{}})
-}
-
-var _ = BeforeSuite(func(done Done) {
-	logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
-
-	By("bootstrapping test environment")
-	testEnv = &envtest.Environment{
-		CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
-	}
-
-	var err error
-	cfg, err = testEnv.Start()
-	Expect(err).ToNot(HaveOccurred())
-	Expect(cfg).ToNot(BeNil())
-
-	err = elbv2v1alpha1.AddToScheme(scheme.Scheme)
-	Expect(err).NotTo(HaveOccurred())
-
-	// +kubebuilder:scaffold:scheme
-
-	k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
-	Expect(err).ToNot(HaveOccurred())
-	Expect(k8sClient).ToNot(BeNil())
-
-	close(done)
-}, 60)
-
-var _ = AfterSuite(func() {
-	By("tearing down the test environment")
-	err := testEnv.Stop()
-	Expect(err).ToNot(HaveOccurred())
-})
diff --git a/pkg/ingress/auth_config_builder_test.go b/pkg/ingress/auth_config_builder_test.go
new file mode 100644
index 000000000..6cf3b0426
--- /dev/null
+++ b/pkg/ingress/auth_config_builder_test.go
@@ -0,0 +1,198 @@
+package ingress
+
+import (
+	"context"
+	"github.com/stretchr/testify/assert"
+	"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
+	"testing"
+)
+
+func Test_defaultAuthConfigBuilder_Build(t *testing.T) {
+	type args struct {
+		svcAndIngAnnotations map[string]string
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    AuthConfig
+		wantErr error
+	}{
+		{
+			name: "cognito auth annotation",
+			args: args{
+				svcAndIngAnnotations: map[string]string{
+					"alb.ingress.kubernetes.io/auth-type":                       "cognito",
+					"alb.ingress.kubernetes.io/auth-idp-cognito":                `{"userPoolARN":"arn:aws:cognito-idp:us-west-2:xxx:userpool/xxx","userPoolClientID":"my-clientID","userPoolDomain":"my-domain","authenticationRequestExtraParams":{"key":"value"}}`,
+					"alb.ingress.kubernetes.io/auth-on-unauthenticated-request": "deny",
+					"alb.ingress.kubernetes.io/auth-scope":                      "email",
+					"alb.ingress.kubernetes.io/auth-session-cookie":             "my-cookie",
+					"alb.ingress.kubernetes.io/auth-session-timeout":            "86400",
+				},
+			},
+			want: AuthConfig{
+				Type: AuthTypeCognito,
+				IDPConfigCognito: &AuthIDPConfigCognito{
+					UserPoolARN:      "arn:aws:cognito-idp:us-west-2:xxx:userpool/xxx",
+					UserPoolClientID: "my-clientID",
+					UserPoolDomain:   "my-domain",
+					AuthenticationRequestExtraParams: map[string]string{
+						"key": "value",
+					},
+				},
+				OnUnauthenticatedRequest: "deny",
+				Scope:                    "email",
+				SessionCookieName:        "my-cookie",
+				SessionTimeout:           86400,
+			},
+		},
+		{
+			name: "cognito auth annotation - old camelcase case json key",
+			args: args{
+				svcAndIngAnnotations: map[string]string{
+					"alb.ingress.kubernetes.io/auth-type":                       "cognito",
+					"alb.ingress.kubernetes.io/auth-idp-cognito":                `{"UserPoolArn":"arn:aws:cognito-idp:us-west-2:xxx:userpool/xxx","UserPoolClientId":"my-clientID","UserPoolDomain":"my-domain","AuthenticationRequestExtraParams":{"key":"value"}}`,
+					"alb.ingress.kubernetes.io/auth-on-unauthenticated-request": "deny",
+					"alb.ingress.kubernetes.io/auth-scope":                      "email",
+					"alb.ingress.kubernetes.io/auth-session-cookie":             "my-cookie",
+					"alb.ingress.kubernetes.io/auth-session-timeout":            "86400",
+				},
+			},
+			want: AuthConfig{
+				Type: AuthTypeCognito,
+				IDPConfigCognito: &AuthIDPConfigCognito{
+					UserPoolARN:      "arn:aws:cognito-idp:us-west-2:xxx:userpool/xxx",
+					UserPoolClientID: "my-clientID",
+					UserPoolDomain:   "my-domain",
+					AuthenticationRequestExtraParams: map[string]string{
+						"key": "value",
+					},
+				},
+				OnUnauthenticatedRequest: "deny",
+				Scope:                    "email",
+				SessionCookieName:        "my-cookie",
+				SessionTimeout:           86400,
+			},
+		},
+		{
+			name: "oidc auth annotation - old camelcase case JSON key",
+			args: args{
+				svcAndIngAnnotations: map[string]string{
+					"alb.ingress.kubernetes.io/auth-type":                       "oidc",
+					"alb.ingress.kubernetes.io/auth-idp-oidc":                   `{"issuer":"https://example.com","authorizationEndpoint":"https://authorization.example.com","tokenEndpoint":"https://token.example.com","userInfoEndpoint":"https://userinfo.example.com","secretName":"my-k8s-secret","authenticationRequestExtraParams":{"key":"value"}}`,
+					"alb.ingress.kubernetes.io/auth-on-unauthenticated-request": "deny",
+					"alb.ingress.kubernetes.io/auth-scope":                      "email",
+					"alb.ingress.kubernetes.io/auth-session-cookie":             "my-cookie",
+					"alb.ingress.kubernetes.io/auth-session-timeout":            "86400",
+				},
+			},
+			want: AuthConfig{
+				Type: AuthTypeOIDC,
+				IDPConfigOIDC: &AuthIDPConfigOIDC{
+					Issuer:                "https://example.com",
+					AuthorizationEndpoint: "https://authorization.example.com",
+					TokenEndpoint:         "https://token.example.com",
+					UserInfoEndpoint:      "https://userinfo.example.com",
+					SecretName:            "my-k8s-secret",
+					AuthenticationRequestExtraParams: map[string]string{
+						"key": "value",
+					},
+				},
+				OnUnauthenticatedRequest: "deny",
+				Scope:                    "email",
+				SessionCookieName:        "my-cookie",
+				SessionTimeout:           86400,
+			},
+		},
+		{
+			name: "oidc auth annotation - old camelcase case JSON key",
+			args: args{
+				svcAndIngAnnotations: map[string]string{
+					"alb.ingress.kubernetes.io/auth-type":                       "oidc",
+					"alb.ingress.kubernetes.io/auth-idp-oidc":                   `{"Issuer":"https://example.com","AuthorizationEndpoint":"https://authorization.example.com","TokenEndpoint":"https://token.example.com","UserInfoEndpoint":"https://userinfo.example.com","SecretName":"my-k8s-secret","AuthenticationRequestExtraParams":{"key":"value"}}`,
+					"alb.ingress.kubernetes.io/auth-on-unauthenticated-request": "deny",
+					"alb.ingress.kubernetes.io/auth-scope":                      "email",
+					"alb.ingress.kubernetes.io/auth-session-cookie":             "my-cookie",
+					"alb.ingress.kubernetes.io/auth-session-timeout":            "86400",
+				},
+			},
+			want: AuthConfig{
+				Type: AuthTypeOIDC,
+				IDPConfigOIDC: &AuthIDPConfigOIDC{
+					Issuer:                "https://example.com",
+					AuthorizationEndpoint: "https://authorization.example.com",
+					TokenEndpoint:         "https://token.example.com",
+					UserInfoEndpoint:      "https://userinfo.example.com",
+					SecretName:            "my-k8s-secret",
+					AuthenticationRequestExtraParams: map[string]string{
+						"key": "value",
+					},
+				},
+				OnUnauthenticatedRequest: "deny",
+				Scope:                    "email",
+				SessionCookieName:        "my-cookie",
+				SessionTimeout:           86400,
+			},
+		},
+		{
+			name: "no auth annotation",
+			args: args{
+				svcAndIngAnnotations: map[string]string{},
+			},
+			want: AuthConfig{
+				Type:                     AuthTypeNone,
+				IDPConfigCognito:         nil,
+				IDPConfigOIDC:            nil,
+				OnUnauthenticatedRequest: defaultAuthOnUnauthenticatedRequest,
+				Scope:                    defaultAuthScope,
+				SessionCookieName:        defaultAuthSessionCookieName,
+				SessionTimeout:           defaultAuthSessionTimeout,
+			},
+		},
+		{
+			// The secret index functionality for Ingress/Service relies on this
+			// since we allow these auth annotations be configured on either Ingress/Service
+			name: "oidc configuration should still be populated even authType is unspecified",
+			args: args{
+				svcAndIngAnnotations: map[string]string{
+					"alb.ingress.kubernetes.io/auth-idp-oidc":                   `{"issuer":"https://example.com","authorizationEndpoint":"https://authorization.example.com","tokenEndpoint":"https://token.example.com","userInfoEndpoint":"https://userinfo.example.com","secretName":"my-k8s-secret","authenticationRequestExtraParams":{"key":"value"}}`,
+					"alb.ingress.kubernetes.io/auth-on-unauthenticated-request": "deny",
+					"alb.ingress.kubernetes.io/auth-scope":                      "email",
+					"alb.ingress.kubernetes.io/auth-session-cookie":             "my-cookie",
+					"alb.ingress.kubernetes.io/auth-session-timeout":            "86400",
+				},
+			},
+			want: AuthConfig{
+				Type: AuthTypeNone,
+				IDPConfigOIDC: &AuthIDPConfigOIDC{
+					Issuer:                "https://example.com",
+					AuthorizationEndpoint: "https://authorization.example.com",
+					TokenEndpoint:         "https://token.example.com",
+					UserInfoEndpoint:      "https://userinfo.example.com",
+					SecretName:            "my-k8s-secret",
+					AuthenticationRequestExtraParams: map[string]string{
+						"key": "value",
+					},
+				},
+				OnUnauthenticatedRequest: "deny",
+				Scope:                    "email",
+				SessionCookieName:        "my-cookie",
+				SessionTimeout:           86400,
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			annotationParser := annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io")
+			b := &defaultAuthConfigBuilder{
+				annotationParser: annotationParser,
+			}
+			got, err := b.Build(context.Background(), tt.args.svcAndIngAnnotations)
+			if tt.wantErr != nil {
+				assert.EqualError(t, err, tt.wantErr.Error())
+			} else {
+				assert.NoError(t, err)
+				assert.Equal(t, tt.want, got)
+			}
+		})
+	}
+}
diff --git a/pkg/ingress/reference_indexer_test.go b/pkg/ingress/reference_indexer_test.go
new file mode 100644
index 000000000..97e4c8790
--- /dev/null
+++ b/pkg/ingress/reference_indexer_test.go
@@ -0,0 +1,315 @@
+package ingress
+
+import (
+	"context"
+	"github.com/stretchr/testify/assert"
+	networking "k8s.io/api/networking/v1beta1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/intstr"
+	"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
+	"sigs.k8s.io/controller-runtime/pkg/log"
+	"testing"
+)
+
+func Test_defaultReferenceIndexer_BuildServiceRefIndexes(t *testing.T) {
+	type args struct {
+		ing *networking.Ingress
+	}
+	tests := []struct {
+		name string
+		args args
+		want []string
+	}{
+		{
+			name: "standard Ingress - with default backend",
+			args: args{
+				ing: &networking.Ingress{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "my-ing",
+					},
+					Spec: networking.IngressSpec{
+						Backend: &networking.IngressBackend{
+							ServiceName: "svc-a",
+							ServicePort: intstr.FromInt(80),
+						},
+						Rules: []networking.IngressRule{
+							{
+								Host: "/hostX",
+								IngressRuleValue: networking.IngressRuleValue{
+									HTTP: &networking.HTTPIngressRuleValue{
+										Paths: []networking.HTTPIngressPath{
+											{
+												Path: "/pathB",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-b",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+											{
+												Path: "/pathC",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-c",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+										},
+									},
+								},
+							},
+							{
+								Host: "/hostY",
+								IngressRuleValue: networking.IngressRuleValue{
+									HTTP: &networking.HTTPIngressRuleValue{
+										Paths: []networking.HTTPIngressPath{
+											{
+												Path: "/pathB",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-b",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+											{
+												Path: "/pathD",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-d",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			want: []string{"svc-a", "svc-b", "svc-c", "svc-d"},
+		},
+		{
+			name: "standard Ingress - without default backend",
+			args: args{
+				ing: &networking.Ingress{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "my-ing",
+					},
+					Spec: networking.IngressSpec{
+						Backend: nil,
+						Rules: []networking.IngressRule{
+							{
+								Host: "/hostX",
+								IngressRuleValue: networking.IngressRuleValue{
+									HTTP: &networking.HTTPIngressRuleValue{
+										Paths: []networking.HTTPIngressPath{
+											{
+												Path: "/pathB",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-b",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+											{
+												Path: "/pathC",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-c",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+										},
+									},
+								},
+							},
+							{
+								Host: "/hostY",
+								IngressRuleValue: networking.IngressRuleValue{
+									HTTP: &networking.HTTPIngressRuleValue{
+										Paths: []networking.HTTPIngressPath{
+											{
+												Path: "/pathB",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-b",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+											{
+												Path: "/pathD",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-d",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			want: []string{"svc-b", "svc-c", "svc-d"},
+		},
+		{
+			name: "empty http path are ignored",
+			args: args{
+				ing: &networking.Ingress{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "my-ing",
+					},
+					Spec: networking.IngressSpec{
+						Rules: []networking.IngressRule{
+							{
+								Host: "/hostX",
+								IngressRuleValue: networking.IngressRuleValue{
+									HTTP: nil,
+								},
+							},
+							{
+								Host: "/hostY",
+								IngressRuleValue: networking.IngressRuleValue{
+									HTTP: &networking.HTTPIngressRuleValue{
+										Paths: []networking.HTTPIngressPath{
+											{
+												Path: "/pathB",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-b",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+											{
+												Path: "/pathD",
+												Backend: networking.IngressBackend{
+													ServiceName: "svc-d",
+													ServicePort: intstr.FromInt(80),
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			want: []string{"svc-b", "svc-d"},
+		},
+		{
+			name: "standard Ingress - actions configured via use-annotation",
+			args: args{
+				ing: &networking.Ingress{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "my-ing",
+						Annotations: map[string]string{
+							"alb.ingress.kubernetes.io/actions.forward-single-svc":   `{"type":"forward","forwardConfig":{"targetGroups":[{"serviceName":"svc-a","servicePort":"80"}]}}`,
+							"alb.ingress.kubernetes.io/actions.forward-multiple-svc": `{"type":"forward","forwardConfig":{"targetGroups":[{"serviceName":"svc-b","servicePort":"80","weight":20},{"serviceName":"svc-c","servicePort":"80","weight":80}]}}`,
+							"alb.ingress.kubernetes.io/actions.forward-single-tg":    `{"type":"forward","forwardConfig":{"targetGroups":[{"targetGroupArn":"tg-a"}]}}`,
+							"alb.ingress.kubernetes.io/actions.forward-multiple-tg":  `{"type":"forward","forwardConfig":{"targetGroups":[{"targetGroupArn":"tg-a","weight":20},{"targetGroupArn":"tg-b","weight":20}]}}`,
+						},
+					},
+					Spec: networking.IngressSpec{
+						Backend: &networking.IngressBackend{
+							ServiceName: "forward-single-svc",
+							ServicePort: intstr.FromString("use-annotation"),
+						},
+						Rules: []networking.IngressRule{
+							{
+								Host: "/hostX",
+								IngressRuleValue: networking.IngressRuleValue{
+									HTTP: &networking.HTTPIngressRuleValue{
+										Paths: []networking.HTTPIngressPath{
+											{
+												Path: "/pathB",
+												Backend: networking.IngressBackend{
+													ServiceName: "forward-multiple-svc",
+													ServicePort: intstr.FromString("use-annotation"),
+												},
+											},
+											{
+												Path: "/pathC",
+												Backend: networking.IngressBackend{
+													ServiceName: "forward-single-tg",
+													ServicePort: intstr.FromString("use-annotation"),
+												},
+											},
+											{
+												Path: "/pathD",
+												Backend: networking.IngressBackend{
+													ServiceName: "forward-multiple-tg",
+													ServicePort: intstr.FromString("use-annotation"),
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			want: []string{"svc-a", "svc-b", "svc-c"},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			annotationParser := annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io")
+			authConfigBuilder := NewDefaultAuthConfigBuilder(annotationParser)
+			enhancedBackendBuilder := NewDefaultEnhancedBackendBuilder(annotationParser)
+			i := &defaultReferenceIndexer{
+				enhancedBackendBuilder: enhancedBackendBuilder,
+				authConfigBuilder:      authConfigBuilder,
+				logger:                 &log.NullLogger{},
+			}
+			got := i.BuildServiceRefIndexes(context.Background(), tt.args.ing)
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}
+
+func Test_defaultReferenceIndexer_BuildSecretRefIndexes(t *testing.T) {
+	type args struct {
+		ingOrSvc metav1.Object
+	}
+	tests := []struct {
+		name string
+		args args
+		want []string
+	}{
+		{
+			name: "ingress with AuthOIDC annotation",
+			args: args{
+				ingOrSvc: &networking.Ingress{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "my-ing",
+						Annotations: map[string]string{
+							"alb.ingress.kubernetes.io/auth-idp-oidc": `{"issuer":"https://example.com","authorizationEndpoint":"https://authorization.example.com","tokenEndpoint":"https://token.example.com","userInfoEndpoint":"https://userinfo.example.com","secretName":"my-k8s-secret","authenticationRequestExtraParams":{"key":"value"}}`,
+						},
+					},
+				},
+			},
+			want: []string{"my-k8s-secret"},
+		},
+		{
+			name: "ingress with no annotation",
+			args: args{
+				ingOrSvc: &networking.Ingress{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "my-ing",
+					},
+				},
+			},
+			want: nil,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			annotationParser := annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io")
+			authConfigBuilder := NewDefaultAuthConfigBuilder(annotationParser)
+			enhancedBackendBuilder := NewDefaultEnhancedBackendBuilder(annotationParser)
+			i := &defaultReferenceIndexer{
+				enhancedBackendBuilder: enhancedBackendBuilder,
+				authConfigBuilder:      authConfigBuilder,
+				logger:                 &log.NullLogger{},
+			}
+			got := i.BuildSecretRefIndexes(context.Background(), tt.args.ingOrSvc)
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}