Repository: kubernetes/ingress-nginx
Pull request #9059 (2022-09-17 02:00:51)

kubewebhookcertgen sha change after go1191
## What this PR does / why we need it:
- Continuation of https://github.com/kubernetes/ingress-nginx/pull/9057 as we built a new image 
- This PR changes the sha of the kube-webhook-certgen image to the new image's sha

## Types of changes
- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] CVE Report (Scanner found CVE and adding report)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation only

## Which issue/s this PR fixes
- No issue created for this as its a bump of go and related to several process related PRs and issues

## How Has This Been Tested?
- Tested locally with `make dev-env`

## Checklist:
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [X] I've read the [CONTRIBUTION](https://github.com/kubernetes/ingress-nginx/blob/main/CONTRIBUTING.md) guide
- [ ] I have added unit and/or e2e tests to cover my changes.
- [ ] All new and existing tests passed.
- [ ] Added Release Notes.

## Does my pull request need a release note?
Any user-visible or operator-visible change qualifies for a release note. This could be a:

- CLI change
- API change
- UI change
- configuration schema change
- behavioral change
- change in non-functional attributes such as efficiency or availability, availability of a new platform
- a warning about a deprecation
- fix of a previous Known Issue
- fix of a vulnerability (CVE)

No release notes are required for changes to the following:

- Tests
- Build infrastructure
- Fixes for unreleased bugs

For more tips on writing good release notes, check out the [Release Notes Handbook](https://github.com/kubernetes/sig-release/tree/master/release-team/role-handbooks/release-notes)

<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".

For more information on release notes see: https://git.k8s.io/community/contributors/guide/release-notes.md
-->
```release-note
kubewebhookcertgen image sha changed after bumping go to v1.19.1 
```


k8s-ci-robot: @longwuyuan: This issue is currently awaiting triage.

If Ingress contributors determines this is a relevant issue, they will accept it by applying the `triage/accepted` label and provide further guidance.

The `triage/accepted` label can be added by org members by writing `/triage accepted` in a comment.


<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: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes/ingress-nginx/pull/9059#" title="Author self-approved">longwuyuan</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign chiefalexander for approval by writing `/assign @chiefalexander` in a comment. For more information see:[The Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

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

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

- **[charts/ingress-nginx/OWNERS](https://github.com/kubernetes/ingress-nginx/blob/main/charts/ingress-nginx/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":["chiefalexander"]} -->
longwuyuan: /triage-accepted
/area stabilization

/assign @tao12345666333 @strongjz @rikatz 

The helm test will fail because changes to values.yaml requires running `helm-docs`, that autogenerates a new README for the chart and need to checkin this new README. Will do it after because there is a borderline to starting the release process
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes/ingress-nginx/pull/9059#" title="Author self-approved">longwuyuan</a>*
**Once this PR has been reviewed and has the lgtm label**, please ask for approval from rikatz by writing `/assign @rikatz` in a comment. For more information see:[The Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

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

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

- **[charts/ingress-nginx/OWNERS](https://github.com/kubernetes/ingress-nginx/blob/main/charts/ingress-nginx/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":["rikatz"]} -->
longwuyuan: checked in helm-docs generated README so helm test passed. Did not bump chart version with the intention to bump chart-version latest by before releasing app-version. kube-webhook-certgen will get used on new installs of chart but not new installs of static manifest but that is ok as its only used in the patch job at install.
tao12345666333: Do we still rebuild this image if we need to publish the next release?
longwuyuan: > Do we still rebuild this image if we need to publish the next release?

It got rebuilt this time because of golang version bump. When we make a new release, I don't see any content will change under /images/kube-webhook-certgen because of a release. So I don't think it will rebuild. My guess is either alpine bump or golang bump causes this image rebuild (besides certgen code change of-course but that is not much likely)

Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 9d7941ab5..fc106b495 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -175,7 +175,7 @@ jobs:
         uses: engineerd/setup-kind@aa272fe2a7309878ffc2a81c56cfe3ef108ae7d0 #v0.5.0
         with:
           version: v0.15.0
-          image: kindest/node:v1.24.4
+          image: kindest/node:v1.25.0
 
       - uses: geekyeggo/delete-artifact@b73cb986740e466292a536d0e32e2666c56fdeb3 # v1
         with:
diff --git a/.github/workflows/depreview.yaml b/.github/workflows/depreview.yaml
index b16d44681..d69c1a0c8 100644
--- a/.github/workflows/depreview.yaml
+++ b/.github/workflows/depreview.yaml
@@ -11,4 +11,4 @@ jobs:
       - name: 'Checkout Repository'
         uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b #v3.0.2
       - name: 'Dependency Review'
-        uses: actions/dependency-review-action@23d1ffffb6fa5401173051ec21eba8c35242733f #v2.0.2
+        uses: actions/dependency-review-action@375c5370086bfff256c37f8beec0f437e2e72ae1 #v2.0.2
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 62dafe1f3..2e13deea0 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -25,7 +25,7 @@ jobs:
     
     steps:
       - name: "Checkout code"
-        uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # v3.0.0
+        uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.0
         with:
           persist-credentials: false
 
@@ -57,6 +57,6 @@ jobs:
       
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@6a38b7d4a1af70deb1b561eb77db2b5e5a6a1e69 # v2.1.14
+        uses: github/codeql-action/upload-sarif@86f3159a697a097a813ad9bfa0002412d97690a4 # v2.1.14
         with:
           sarif_file: results.sarif
diff --git a/.github/workflows/vulnerability-scans.yaml b/.github/workflows/vulnerability-scans.yaml
index f47c5d626..0ef654160 100644
--- a/.github/workflows/vulnerability-scans.yaml
+++ b/.github/workflows/vulnerability-scans.yaml
@@ -75,7 +75,7 @@ jobs:
 
     # This step checks out a copy of your repository.
     - name: Upload SARIF file
-      uses: github/codeql-action/upload-sarif@6a38b7d4a1af70deb1b561eb77db2b5e5a6a1e69
+      uses: github/codeql-action/upload-sarif@86f3159a697a097a813ad9bfa0002412d97690a4
       with:
         token: ${{ github.token }}
         # Path to SARIF file relative to the root of the repository
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e73f49fcd..a11435aef 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -2,7 +2,7 @@
 
 Read the following guide if you're interested in contributing to Ingress. [Make Ingress-Nginx Work for you, and the Community](https://youtu.be/GDm-7BlmPPg) from KubeCon Europe 2018 is a great video to get you started!!
 
-Note that this guide refers to contributing to actual sources of the repository. If you interested in contributing through issue triaging, have a look at [this guide](./ISSUE_TRIAGE.md). 
+Note that this guide refers to contributing to actual sources of the repository. If you interested in contributing through issue triaging, have a look at [this guide](./ISSUE_TRIAGE.md).
 
 ## Contributor License Agreements
 
@@ -19,7 +19,7 @@ Follow either of the two links above to access the appropriate CLA and instructi
 
 ## Finding Issues That Need Help
 
-If you're new to the project and want to help, but don't know where to start, we have a semi-curated list of issues that should not need deep knowledge of the system. [Have a look and see if anything sounds interesting](https://github.com/kubernetes/ingress-nginx/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue%20label%3A%22help+wanted%22). 
+If you're new to the project and want to help, but don't know where to start, we have a semi-curated list of issues that should not need deep knowledge of the system. [Have a look and see if anything sounds interesting](https://github.com/kubernetes/ingress-nginx/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue%20label%3A%22help+wanted%22).
 
 Alternatively, search for the label [`triage-accepted`](https://github.com/kubernetes/ingress-nginx/issues?q=is%3Aopen+is%3Aissue+label%3Atriage%2Faccepted+) if you have some experience with ingress-nginx. Note, that it could make sense to grab issues with higher priority first.
 
@@ -34,9 +34,9 @@ All changes must be code reviewed. Coding conventions and standards are explaine
 
 ### Merge Approval
 
-Ingress Nginx collaborators may add "/lgtm" (Looks Good To Me) to indicate that a PR is acceptable. Any change requires at least one LGTM.  No pull requests can be merged until at least one Ingress Nginx collaborator signs off with an LGTM. Adding the "/lgtm" comment result in the prow bot adding the `lgtm` label. Note that a pull request still needs an `approve` label from one of the owners. 
+Ingress Nginx collaborators may add "/lgtm" (Looks Good To Me) to indicate that a PR is acceptable. Any change requires at least one LGTM.  No pull requests can be merged until at least one Ingress Nginx collaborator signs off with an LGTM. Adding the "/lgtm" comment result in the prow bot adding the `lgtm` label. Note that a pull request still needs an `approve` label from one of the owners.
 
-Reviewers or members who want to become reviewers according to the [k8s membership ladder](https://github.com/kubernetes/community/blob/master/community-membership.md), could actively search for [pull requests that need a review](https://github.com/kubernetes/ingress-nginx/pulls?q=is%3Aopen+is%3Apr+label%3Atriage%2Faccepted). 
+Reviewers or members who want to become reviewers according to the [k8s membership ladder](https://github.com/kubernetes/community/blob/master/community-membership.md), could actively search for [pull requests that need a review](https://github.com/kubernetes/ingress-nginx/pulls?q=is%3Aopen+is%3Apr+label%3Atriage%2Faccepted).
 
 ## Support Channels
 
@@ -47,3 +47,6 @@ Whether you are a user or contributor, official support channels include:
 - Post: [Kubernetes Forum](https://discuss.kubernetes.io)
 
 Before opening a new issue or submitting a new pull request, it's helpful to search the project - it's likely that another user has already reported the issue you're facing, or it's a known issue that we're already aware of.
+
+## New Contributor Tips
+If you're a new contributor, you can follow the [New Contributor Tips guide](NEW_CONTRIBUTOR.md)
diff --git a/NEW_CONTRIBUTOR.md b/NEW_CONTRIBUTOR.md
new file mode 100644
index 000000000..816d05778
--- /dev/null
+++ b/NEW_CONTRIBUTOR.md
@@ -0,0 +1,850 @@
+## New Contributor Tips
+
+Welcome to the Ingress Nginx new contributor tips.
+This guide briefly outlines the necessary knowledge & tools, required to start working on Ingress-NGINX Issues.
+
+### Prerequisites
+- Basic understanding of linux
+- Familiarity with the command line on linux
+- OSI Model(Links below)
+
+### Introduction
+It all starts with the OSI model...
+> The Open Systems Interconnection (OSI) model describes seven layers that computer systems use to communicate over a network. It was the first standard model for network communications, adopted by all major computer and telecommunication companies
+
+![Describes the 7 Layers of the OSI Model](https://i.imgur.com/qF0KjBq.png)
+
+#### Reading material for OSI Model
+[OSI Model CertificationKits](https://www.certificationkits.com/cisco-certification/cisco-ccna-640-802-exam-certification-guide/cisco-ccna-the-osi-model/)
+
+### Approaching the problem
+
+
+Not everybody knows everything. But the factors that help are a love/passion for this to begin. But to move forward, its the approach and not the knowledge that sustains prolonged joy, while working on issues. If the approach is simple and powered by good-wishes-for-community, then info & tools are forthcoming and easy.
+
+Here we take a bird's eye-view of the hops in the network plumbing, that a packet takes, from source to destination, when we run `curl`, from a laptop to a nginx webserver process, running in a container, inside a pod, inside a Kubernetes cluster, created using `kind` or `minikube` or any other cluster-management tool.
+
+### [Kind](https://kind.sigs.k8s.io/) cluster example on a Linux Host
+
+#### TL;DR
+The destination of the packet from the curl command, is looked up, in the `routing table`. Based on the route, the the packet first travels to the virtual bridge `172.18.0.1` interface, created by docker, when we created the kind cluster on a laptop. Next the packet is forwarded to `172.18.0.2`(See below on how we got this IP address), within the kind cluster. The `kube-proxy` container creates iptables rules that make sure the packet goes to the correct pod ip in this case `10.244.0.5`
+
+Command:
+```
+# docker ps
+CONTAINER ID   IMAGE                  COMMAND                  CREATED       STATUS          PORTS                       NAMES
+230e7246a32c   kindest/node:v1.24.1   "/usr/local/bin/entr…"   2 weeks ago   Up 54 seconds   127.0.0.1:38143->6443/tcp   kind-control-plane
+
+# docker inspect kind-control-plane -f '{{ .NetworkSettings.Networks.kind.IPAddress }}'
+172.18.0.2
+
+```
+
+
+
+If this part is confusing, you would first need to understand what a [bridge](https://tldp.org/HOWTO/BRIDGE-STP-HOWTO/what-is-a-bridge.html) is and what [docker network](https://docs.docker.com/network/) is.
+
+
+
+#### The journey of a curl packet.
+Let's begin with creating a [Kind](https://kind.sigs.k8s.io/docs/user/quick-start/) Cluster on your laptop
+```
+# kind create cluster
+```
+This will create a cluster called `kind`, to view the clusters type
+```
+# kind get clusters                                                                                                                              
+kind
+```
+Kind ships with `kubectl`, so we can use that to communicate with our clusters.
+```
+# kubectl get no -o wide                                                                                                                             
+NAME                 STATUS   ROLES           AGE     VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE       KERNEL-VERSION    CONTAINER-RUNTIME
+kind-control-plane   Ready    control-plane   5d23h   v1.24.1   172.18.0.2    <none>        Ubuntu 21.10   5.18.12-arch1-1   containerd://1.6.4
+```
+Kind creates  a cluster using docker container as nodes, it does this using [containerd](https://containerd.io/) within the docker container.
+The concept of Docker in Docker is very important here.
+
+To start with simply create a nginx deployment using `kubectl`.
+```
+# kubectl create deployment nginx --image nginx:alpine --port=80
+deployment.apps/nginx created
+```
+Then we expose this as a NodePort Service.
+```
+# kubectl expose deployment/nginx --type=NodePort
+service/nginx-new exposed
+```
+Command: Now we can see that the service has been exposed.
+```
+# kubectl get svc -o wide
+NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE     SELECTOR
+nginx          NodePort    10.96.176.241   <none>        80:32329/TCP   4d8h    app=nginx
+```
+Output Relevance: From the above output, we can see that our nginx pod is being exposed as the `NodePort` service type, and now we can curl the Node IP `172.18.0.2` with the exposed port `32329`
+
+Command: The pod has an IP as shown below
+```
+# kubectl get po -o wide                                                                                                                            
+NAME                               READY   STATUS    RESTARTS      AGE     IP            NODE                 NOMINATED NODE   READINESS GATES
+nginx-6c8b449b8f-pdvdk             1/1     Running   1 (32h ago)   4d8h    10.244.0.5    kind-control-plane   <none>           <none>
+```
+
+Command: We can use `curl` on the laptop to view the nginx container that is running on port `32329`.
+
+```
+# curl  172.18.0.2:32329
+
+<!DOCTYPE html>
+<html>
+<head>
+<title>Welcome to nginx!</title>
+<style>
+html { color-scheme: light dark; }
+body { width: 35em; margin: 0 auto;
+font-family: Tahoma, Verdana, Arial, sans-serif; }
+</style>
+</head>
+<body>
+<h1>Welcome to nginx!</h1>
+<p>If you see this page, the nginx web server is successfully installed and
+working. Further configuration is required.</p>
+
+<p>For online documentation and support please refer to
+<a href="http://nginx.org/">nginx.org</a>.<br/>
+Commercial support is available at
+<a href="http://nginx.com/">nginx.com</a>.</p>
+
+<p><em>Thank you for using nginx.</em></p>
+</body>
+</html>
+```
+Now, we can check the ip interfaces as well subnets for our system is connected to:
+
+```
+$ ifconfig
+ethbr0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
+        inet 192.168.31.9  netmask 255.255.255.0  broadcast 192.168.31.255
+        inet6 fe80::7530:9ae5:3e8d:e45a  prefixlen 64  scopeid 0x20<link>
+        ether 2e:90:b3:e8:52:5b  txqueuelen 1000  (Ethernet)
+        RX packets 31220566  bytes 44930589084 (41.8 GiB)
+        RX errors 0  dropped 0  overruns 0  frame 0
+        TX packets 18104006  bytes 1757183680 (1.6 GiB)
+        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
+
+br-2fffe5cd5d9e: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
+        inet 172.18.0.1  netmask 255.255.0.0  broadcast 172.18.255.255
+        inet6 fc00:f853:ccd:e793::1  prefixlen 64  scopeid 0x0<global>
+        inet6 fe80::42:12ff:fed3:8fb0  prefixlen 64  scopeid 0x20<link>
+        inet6 fe80::1  prefixlen 64  scopeid 0x20<link>
+        ether 02:42:12:d3:8f:b0  txqueuelen 0  (Ethernet)
+        RX packets 3547  bytes 414792 (405.0 KiB)
+        RX errors 0  dropped 0  overruns 0  frame 0
+        TX packets 6267  bytes 8189931 (7.8 MiB)
+        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
+docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
+        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
+        inet6 fe80::42:a2ff:fe09:5edb  prefixlen 64  scopeid 0x20<link>
+        ether 02:42:a2:09:5e:db  txqueuelen 0  (Ethernet)
+        RX packets 14  bytes 2143 (2.0 KiB)
+        RX errors 0  dropped 0  overruns 0  frame 0
+        TX packets 40  bytes 6406 (6.2 KiB)
+        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
+```
+From the above output we can see that, there are two bridges connected to our systems network interface,one is the docker default bridge`docker0` and the other created by kind
+`br-2fffe5cd5d9e`.
+
+Since kind creates nodes as containers, this is easily accessible via `docker ps`.
+```
+$ docker ps
+
+CONTAINER ID   IMAGE                  COMMAND                  CREATED      STATUS        PORTS                       NAMES
+230e7246a32c   kindest/node:v1.24.1   "/usr/local/bin/entr…"   6 days ago   Up 33 hours   127.0.0.1:38143->6443/tcp   kind-control-plane
+```
+If we do a docker `exec` we can enter the container, we can also see the network interfaces within the container.
+```
+# docker exec -it 230e7246a32c bash
+
+# root@kind-control-plane:/# ip a
+1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
+    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+    inet 127.0.0.1/8 scope host lo
+       valid_lft forever preferred_lft forever
+    inet6 ::1/128 scope host
+       valid_lft forever preferred_lft forever
+2: vethdb0d1da1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
+    link/ether a2:a1:ce:08:d2:39 brd ff:ff:ff:ff:ff:ff link-netns cni-ddc25710-030a-cc05-c600-5a183fae01f7
+    inet 10.244.0.1/32 scope global vethdb0d1da1
+       valid_lft forever preferred_lft forever
+3: veth4d76603f@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
+    link/ether 9a:9b:6b:3e:d1:53 brd ff:ff:ff:ff:ff:ff link-netns cni-f2270000-8fc8-6f89-e56b-4759ae10a084
+    inet 10.244.0.1/32 scope global veth4d76603f
+       valid_lft forever preferred_lft forever
+4: vethcc2586d6@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
+    link/ether 52:f9:20:63:62:a2 brd ff:ff:ff:ff:ff:ff link-netns cni-97e337cd-1322-c1fa-7523-789af94f397f
+    inet 10.244.0.1/32 scope global vethcc2586d6
+       valid_lft forever preferred_lft forever
+5: veth783189a9@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
+    link/ether ba:e1:55:1f:6f:12 brd ff:ff:ff:ff:ff:ff link-netns cni-90849001-668a-03d2-7d9e-192de79ccc59
+    inet 10.244.0.1/32 scope global veth783189a9
+       valid_lft forever preferred_lft forever
+6: veth79c98c12@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
+    link/ether 22:05:55:c7:86:e9 brd ff:ff:ff:ff:ff:ff link-netns cni-734dfac9-9f70-ab33-265b-21569d90312a
+    inet 10.244.0.1/32 scope global veth79c98c12
+       valid_lft forever preferred_lft forever
+7: veth5b221c83@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
+    link/ether 92:3f:04:54:72:5a brd ff:ff:ff:ff:ff:ff link-netns cni-d8f6666b-1cfb-ef08-4bf8-237a7fc32da2
+    inet 10.244.0.1/32 scope global veth5b221c83
+       valid_lft forever preferred_lft forever
+8: vethad630fb8@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
+    link/ether 32:78:ec:f6:01:ea brd ff:ff:ff:ff:ff:ff link-netns cni-6cb3c179-cb17-3b81-2051-27231c44a3c4
+    inet 10.244.0.1/32 scope global vethad630fb8
+       valid_lft forever preferred_lft forever
+9: veth573a629b@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
+    link/ether e2:57:f8:c9:bc:94 brd ff:ff:ff:ff:ff:ff link-netns cni-d2dbb903-8310-57b4-7ba4-9f353dbc79dc
+    inet 10.244.0.1/32 scope global veth573a629b
+       valid_lft forever preferred_lft forever
+10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
+    link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
+    inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
+       valid_lft forever preferred_lft forever
+    inet6 fc00:f853:ccd:e793::2/64 scope global nodad
+       valid_lft forever preferred_lft forever
+    inet6 fe80::42:acff:fe12:2/64 scope link
+       valid_lft forever preferred_lft forever
+11: vethd7368e27@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
+    link/ether 8a:74:ec:f6:d6:c9 brd ff:ff:ff:ff:ff:ff link-netns cni-7c7eb9cd-bbb1-65b0-0480-b8f1265f2f36
+    inet 10.244.0.1/32 scope global vethd7368e27
+       valid_lft forever preferred_lft forever
+12: veth7cadbf2b@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
+    link/ether 12:48:10:b7:b8:f5 brd ff:ff:ff:ff:ff:ff link-netns cni-b39e37b5-1bc8-626a-a553-a0be2f94a117
+    inet 10.244.0.1/32 scope global veth7cadbf2b
+       valid_lft forever preferred_lft forever
+
+```
+When we run `curl  172.18.0.2:32329` on the laptop it first needs to figure out where `172.18.0.2`, to do this it refers to the host routing table.
+```
+sudo netstat -rn                                                                                                                           main 
+Kernel IP routing table
+Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
+0.0.0.0         192.168.31.1    0.0.0.0         UG        0 0          0 ethbr0
+172.17.0.0      0.0.0.0         255.255.0.0     U         0 0          0 docker0
+172.18.0.0      0.0.0.0         255.255.0.0     U         0 0          0 br-2fffe5cd5d9e
+172.19.0.0      0.0.0.0         255.255.0.0     U         0 0          0 br-be5b544733a3
+192.168.31.0    0.0.0.0         255.255.255.0   U         0 0          0 ethbr0
+192.168.31.0    0.0.0.0         255.255.255.0   U         0 0          0 ethbr0
+192.168.39.0    0.0.0.0         255.255.255.0   U         0 0          0 virbr2
+192.168.122.0   0.0.0.0         255.255.255.0   U         0 0          0 virbr0
+```
+Output Relevance: From the above output, you can see that the `iface`(Interface) for `172.18.0.0` is `br-2fffe5cd5d9e`, which means traffic that needs to go to `172.18.0.0` will go through `br-2fffe5cd5d9e` which is created by docker for the kind container (this is the node in case of kind cluster).
+
+Now we need to understand how the packet travels from the container interface to the pod with IP `10.244.0.5`. The component that handles this is called kube-proxy
+
+So what exactly is [kube-proxy](https://kubernetes.io/docs/concepts/overview/components/#kube-proxy):
+> Kube-Proxy is a network proxy that runs on each node in your cluster, implementing part of the Kubernetes Service concept.
+kube-proxy maintains network rules on nodes. These network rules allow network communication to your Pods from network sessions inside or outside of your cluster
+
+So, as we can see that kube proxy handles the network rules required to aid the communication to the pods, we will look at the [iptables](https://linux.die.net/man/8/iptables)
+> `iptables` is a command line interface used to set up and maintain tables for the Netfilter firewall for IPv4, included in the Linux kernel. The firewall matches packets with rules defined in these tables and then takes the specified action on a possible match. Tables is the name for a set of chains
+
+Command:
+```
+# iptables -t nat -L PREROUTING -n
+Chain PREROUTING (policy ACCEPT)
+target     prot opt source               destination
+KUBE-SERVICES  all  --  0.0.0.0/0            0.0.0.0/0            /* kubernetes service portals */
+DOCKER_OUTPUT  all  --  0.0.0.0/0            172.18.0.1
+CNI-HOSTPORT-DNAT  all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL
+```
+
+```
+# iptables-save | grep PREROUTING
+-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
+```
+Output Relevance:
+> -A: append new iptable rule
+> -j: jump to the target
+> KUBE-SERVICES: target
+
+> The above output appends a new rule for PREROUTING which every network packet will go through first as they try to access any kubernetes service
+
+
+What is `PREROUTING` in iptables?
+>PREROUTING: This chain is used to make any routing related decisions before (PRE) sending any packets
+
+To dig in further we need to go to the target, `KUBE-SERVICES` for our nginx service.
+```
+# iptables -t nat -L KUBE-SERVICES -n| grep nginx
+KUBE-SVC-2CMXP7HKUVJN7L6M  tcp  --  0.0.0.0/0            10.96.176.241        /* default/nginx cluster IP */ tcp dpt:80
+```
+Command:
+```
+# iptables -t nat -L KUBE-SVC-2CMXP7HKUVJN7L6M -n
+Chain KUBE-SVC-2CMXP7HKUVJN7L6M (2 references)
+target     prot opt source               destination
+KUBE-MARK-MASQ  tcp  -- !10.244.0.0/16        10.96.176.241        /* default/nginx cluster IP */ tcp dpt:80
+KUBE-SEP-4IEO3WJHPKXV3AOH  all  --  0.0.0.0/0            0.0.0.0/0            /* default/nginx -> 10.244.0.5:80 */
+
+# iptables -t nat -L KUBE-MARK-MASQ -n
+Chain KUBE-MARK-MASQ (31 references)
+target     prot opt source               destination
+MARK       all  --  0.0.0.0/0            0.0.0.0/0            MARK or 0x4000
+
+# iptables -t nat -L KUBE-SEP-4IEO3WJHPKXV3AOH -n
+Chain KUBE-SEP-4IEO3WJHPKXV3AOH (1 references)
+target     prot opt source               destination
+KUBE-MARK-MASQ  all  --  10.244.0.5           0.0.0.0/0            /* default/nginx */
+DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            /* default/nginx */ tcp to:10.244.0.5:80
+```
+
+
+```
+iptables-save | grep 10.96.176.241
+
+-A KUBE-SERVICES -d 10.96.176.241/32 -p tcp -m comment --comment "default/nginx cluster IP" -m tcp --dport 80 -j KUBE-SVC-2CMXP7HKUVJN7L6M
+-A KUBE-SVC-2CMXP7HKUVJN7L6M ! -s 10.244.0.0/16 -d 10.96.176.241/32 -p tcp -m comment --comment "default/nginx cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
+```
+
+As you can see the rules added by `kube-proxy` helps the packet reach to the destination service.
+
+### Minikube KVM VM Example on Linux
+
+#### TL;DR
+Now we look at the curl packet journey on minikube. The `routing table` is looked up to know the destination of the curl packet. The packet then first travels to the virtual bridge `192.168.39.1`, created by minikube kvm2 driver, when we created the minikube cluster, on a linux laptop. Then this packet is forwarded to `192.168.39.57`, within the minikube VM. We have docker containers running in the VM. Among them, the `kube-proxy` container creates iptables rules that make sure the packet goes to the correct pod ip, in this case `172.17.0.4`.
+
+
+To begin with the minikube example, we first need to create a minikube cluster on a linux laptop. In this example I'll be using the `kvm2` driver option for `minikube start` command, as default.
+
+```
+minikube start
+😄  minikube v1.26.0 on Arch "rolling"
+🆕  Kubernetes 1.24.2 is now available. If you would like to upgrade, specify: --kubernetes-version=v1.24.2
+✨  Using the kvm2 driver based on existing profile
+👍  Starting control plane node minikube in cluster minikube
+🏃  Updating the running kvm2 "minikube" VM ...
+🐳  Preparing Kubernetes v1.23.3 on Docker 20.10.12 ...
+    ▪ kubelet.housekeeping-interval=5m
+🔎  Verifying Kubernetes components...
+    ▪ Using image k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1
+    ▪ Using image k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1
+    ▪ Using image k8s.gcr.io/ingress-nginx/controller:v1.2.1
+    ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
+🔎  Verifying ingress addon...
+🌟  Enabled addons: ingress, storage-provisioner, default-storageclass
+🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
+```
+**Note**: The KVM driver provides a lot of options on customizing the cluster, however that is currently beyond the scope of this guide.
+
+Next we will get the Node IP.
+```
+$ kubectl get no -o wide                                                                                                                   
+NAME       STATUS   ROLES                  AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE              KERNEL-VERSION   CONTAINER-RUNTIME
+minikube   Ready    control-plane,master   25d   v1.23.3   192.168.39.57   <none>        Buildroot 2021.02.4   4.19.202         docker://20.10.12
+```
+Minikube creates a Virtual Machine using the KVM2 driver(Other drivers such as Virtualbox do exist see `minikube start --help` for more information ), you should be able to see this with the following output(You may have to use sudo to get this output)
+
+```
+$ virsh --connect qemu:///system list
+ Id   Name       State
+--------------------------
+ 1    minikube   running
+
+ or
+
+ $ sudo virsh list
+ Id   Name       State
+--------------------------
+ 1    minikube   running
+
+```
+
+Moving on, simply create a nginx deployment using `kubectl`.
+```
+# kubectl create deployment nginx --image nginx:alpine --port=80
+deployment.apps/nginx created
+```
+Then we expose this as a NodePort Service.
+```
+# kubectl expose deployment/nginx --type=NodePort
+service/nginx-new exposed
+```
+Command: Now we can see that the service has been exposed.
+```
+# kubectl get svc -o wide                                                                                                                            main 
+NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE   SELECTOR
+kubernetes       ClusterIP   10.96.0.1    <none>        443/TCP        25d   <none>
+nginx-minikube   NodePort    10.97.44.4   <none>        80:32007/TCP   45h   app=nginx-minikube
+```
+Output Relevance: From the above output, we can see that our nginx pod is being exposed as the `NodePort` service type, and now we can curl the Node IP `192.168.39.57` with the exposed port `32007`
+
+Command: The pod has an IP as shown below
+```
+# kubectl get po -o wide
+NAME                              READY   STATUS    RESTARTS      AGE   IP           NODE       NOMINATED NODE   READINESS GATES
+nginx-minikube-7546f79bd8-x88bt   1/1     Running   3 (43m ago)   45h   172.17.0.4   minikube   <none>           <none>
+
+```
+
+Command: We can use `curl` on the laptop to view the nginx container that is running on port `32007`.
+```
+curl  192.168.39.57:32007
+<!DOCTYPE html>
+<html>
+<head>
+<title>Welcome to nginx!</title>
+<style>
+html { color-scheme: light dark; }
+body { width: 35em; margin: 0 auto;
+font-family: Tahoma, Verdana, Arial, sans-serif; }
+</style>
+</head>
+<body>
+<h1>Welcome to nginx!</h1>
+<p>If you see this page, the nginx web server is successfully installed and
+working. Further configuration is required.</p>
+
+<p>For online documentation and support please refer to
+<a href="http://nginx.org/">nginx.org</a>.<br/>
+Commercial support is available at
+<a href="http://nginx.com/">nginx.com</a>.</p>
+
+<p><em>Thank you for using nginx.</em></p>
+</body>
+</html>
+```
+
+So, how does this packet travel, lets dive in.
+We can check the ip interfaces as well subnets for our system is connected to:
+```
+$ ifconfig
+virbr2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
+        inet 192.168.39.1  netmask 255.255.255.0  broadcast 192.168.39.255
+        ether 52:54:00:19:29:93  txqueuelen 1000  (Ethernet)
+        RX packets 5132  bytes 1777099 (1.6 MiB)
+        RX errors 0  dropped 0  overruns 0  frame 0
+        TX packets 6113  bytes 998530 (975.1 KiB)
+        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
+
+virbr0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
+        inet 192.168.122.1  netmask 255.255.255.0  broadcast 192.168.122.255
+        ether 52:54:00:48:ee:35  txqueuelen 1000  (Ethernet)
+        RX packets 23648  bytes 1265196 (1.2 MiB)
+        RX errors 0  dropped 0  overruns 0  frame 0
+        TX packets 40751  bytes 60265308 (57.4 MiB)
+        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
+```
+Output Relevance: From the above output you can see there are two Virtual Bridges created by minikube when we created the cluster on the network. Here, `virbr0` is the default NAT network bridge while `virbr2` is a isolated network bridge on which the pods run.
+
+Minikube creates a Virtual Machine, to enter the virtual machine we can simple do:
+```
+# minikube ssh
+```
+
+The interfaces within the Virtual Machine are as follows.
+```
+docker0   Link encap:Ethernet  HWaddr 02:42:03:24:26:78
+          inet addr:172.17.0.1  Bcast:172.17.255.255  Mask:255.255.0.0
+          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
+          RX packets:31478 errors:0 dropped:0 overruns:0 frame:0
+          TX packets:36704 errors:0 dropped:0 overruns:0 carrier:0
+          collisions:0 txqueuelen:0
+          RX bytes:3264056 (3.1 MiB)  TX bytes:14061883 (13.4 MiB)
+
+eth0      Link encap:Ethernet  HWaddr 52:54:00:C9:3A:73
+          inet addr:192.168.39.57  Bcast:192.168.39.255  Mask:255.255.255.0
+          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
+          RX packets:8245 errors:0 dropped:9 overruns:0 frame:0
+          TX packets:3876 errors:0 dropped:0 overruns:0 carrier:0
+          collisions:0 txqueuelen:1000
+          RX bytes:812006 (792.9 KiB)  TX bytes:1044724 (1020.2 KiB)
+
+eth1      Link encap:Ethernet  HWaddr 52:54:00:7B:37:79
+          inet addr:192.168.122.35  Bcast:192.168.122.255  Mask:255.255.255.0
+          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
+          RX packets:4459 errors:0 dropped:9 overruns:0 frame:0
+          TX packets:201 errors:0 dropped:0 overruns:0 carrier:0
+          collisions:0 txqueuelen:1000
+          RX bytes:298528 (291.5 KiB)  TX bytes:25813 (25.2 KiB)
+
+lo        Link encap:Local Loopback
+          inet addr:127.0.0.1  Mask:255.0.0.0
+          UP LOOPBACK RUNNING  MTU:65536  Metric:1
+          RX packets:946772 errors:0 dropped:0 overruns:0 frame:0
+          TX packets:946772 errors:0 dropped:0 overruns:0 carrier:0
+          collisions:0 txqueuelen:1000
+          RX bytes:213465460 (203.5 MiB)  TX bytes:213465460 (203.5 MiB)
+
+vetha4f1dc5 Link encap:Ethernet  HWaddr 3E:1C:FE:C9:75:86
+          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
+          RX packets:10 errors:0 dropped:0 overruns:0 frame:0
+          TX packets:16 errors:0 dropped:0 overruns:0 carrier:0
+          collisions:0 txqueuelen:0
+          RX bytes:1413 (1.3 KiB)  TX bytes:955 (955.0 B)
+
+vethbf35613 Link encap:Ethernet  HWaddr BA:31:7D:AE:2A:BF
+          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
+          RX packets:3526 errors:0 dropped:0 overruns:0 frame:0
+          TX packets:3934 errors:0 dropped:0 overruns:0 carrier:0
+          collisions:0 txqueuelen:0
+          RX bytes:342408 (334.3 KiB)  TX bytes:380193 (371.2 KiB)
+
+vethe092a51 Link encap:Ethernet  HWaddr 8A:37:D3:D9:D9:0E
+          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
+          RX packets:9603 errors:0 dropped:0 overruns:0 frame:0
+          TX packets:11151 errors:0 dropped:0 overruns:0 carrier:0
+          collisions:0 txqueuelen:0
+          RX bytes:1199235 (1.1 MiB)  TX bytes:5449408 (5.1 MiB)
+```
+Output Relevance: Here we have the Virtual Ethernet and we have docker bridges too since docker runs within the Virtual Machine.
+
+When we do a `curl` to `192.168.39.57:32007` on the laptop the packet first goes to the route table
+```
+Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
+0.0.0.0         192.168.31.1    0.0.0.0         UG        0 0          0 ethbr0
+172.17.0.0      0.0.0.0         255.255.0.0     U         0 0          0 docker0
+172.18.0.0      0.0.0.0         255.255.0.0     U         0 0          0 br-2fffe5cd5d9e
+172.19.0.0      0.0.0.0         255.255.0.0     U         0 0          0 br-be5b544733a3
+192.168.31.0    0.0.0.0         255.255.255.0   U         0 0          0 ethbr0
+192.168.31.0    0.0.0.0         255.255.255.0   U         0 0          0 ethbr0
+192.168.39.0    0.0.0.0         255.255.255.0   U         0 0          0 virbr2
+192.168.122.0   0.0.0.0         255.255.255.0   U         0 0          0 virbr0
+```
+Output Relevance: As you can see multiple routes are defined here, of which our Virtual Machine Node IP(192.168.39.57) is also shown in the table, so the packet now knows where it has to go.
+
+With that clear we now know how the packet goes from the laptop to the virtual bridge and then enters the Virtual Machine.
+
+Inside the virtual machine, [kube-proxy](https://kubernetes.io/docs/concepts/overview/components/#kube-proxy) handles the routing using iptables.
+
+So what exactly is [kube-proxy](https://kubernetes.io/docs/concepts/overview/components/#kube-proxy)(For those who skipped the kind example):
+> Kube-Proxy is a network proxy that runs on each node in your cluster, implementing part of the Kubernetes Service concept.
+kube-proxy maintains network rules on nodes. These network rules allow network communication to your Pods from network sessions inside or outside of your cluster
+
+So, as we can see that kube proxy handles the network rules required to aid the communication to the pods, we will look at the [iptables](https://linux.die.net/man/8/iptables)
+> `iptables` is a command line interface used to set up and maintain tables for the Netfilter firewall for IPv4, included in the Linux kernel. The firewall matches packets with rules defined in these tables and then takes the specified action on a possible match. Tables is the name for a set of chains
+
+Command:
+
+```
+# minikube ssh                                                                                                                                      
+                         _             _
+            _         _ ( )           ( )
+  ___ ___  (_)  ___  (_)| |/')  _   _ | |_      __
+/' _ ` _ `\| |/' _ `\| || , <  ( ) ( )| '_`\  /'__`\
+| ( ) ( ) || || ( ) || || |\`\ | (_) || |_) )(  ___/
+(_) (_) (_)(_)(_) (_)(_)(_) (_)`\___/'(_,__/'`\____)
+
+$ sudo iptables -t nat -L PREROUTING -n
+Chain PREROUTING (policy ACCEPT)
+target     prot opt source               destination
+KUBE-SERVICES  all  --  0.0.0.0/0            0.0.0.0/0            /* kubernetes service portals */
+DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL
+
+$ iptables-save | grep PREROUTING
+-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
+
+```
+
+Output Relevance:
+> -A: append new iptable rule
+> -j: jump to the target
+> KUBE-SERVICES: target
+
+> The above output appends a new rule for PREROUTING which every network packet will go through first as they try to access any kubernetes service
+
+
+What is `PREROUTING` in iptables?
+>PREROUTING: This chain is used to make any routing related decisions before (PRE) sending any packets
+
+To dig in further we need to go to the target, `KUBE-SERVICES` for our nginx service.
+```
+# iptables -t nat -L KUBE-SERVICES -n| grep nginx
+KUBE-SVC-NRDCJV6H42SDXARP  tcp  --  0.0.0.0/0            10.97.44.4           /* default/nginx-minikube cluster IP */ tcp dpt:80
+```
+Command:
+```
+$ sudo iptables -t nat -L| grep KUBE-SVC-NRDCJV6H42SDXARP
+KUBE-SVC-NRDCJV6H42SDXARP  tcp  --  0.0.0.0/0            0.0.0.0/0            /* default/nginx-minikube */ tcp dpt:32007
+KUBE-SVC-NRDCJV6H42SDXARP  tcp  --  0.0.0.0/0            10.97.44.4           /* default/nginx-minikube cluster IP */ tcp dpt:80
+
+$ sudo iptables -t nat -L KUBE-MARK-MASQ -n
+Chain KUBE-MARK-MASQ (19 references)
+target     prot opt source               destination
+MARK       all  --  0.0.0.0/0            0.0.0.0/0            MARK or 0x4000
+
+sudo iptables-save | grep 172.17.0.4
+-A KUBE-SEP-AHQQ7ZFXMEBNX76B -s 172.17.0.4/32 -m comment --comment "default/nginx-minikube" -j KUBE-MARK-MASQ
+-A KUBE-SEP-AHQQ7ZFXMEBNX76B -p tcp -m comment --comment "default/nginx-minikube" -m tcp -j DNAT --to-destination 172.17.0.4:80
+```
+As you can see the rules added by kube-proxy helps the packet reach to the destination service.
+
+
+### Connection termination
+Connection termination is a type of event that occurs when there are load balancers present, the information for this is quite scarce, however I've found the following article, [IBM - Network Termination](https://www.ibm.com/docs/en/sva/9.0.4?topic=balancer-network-termination) that describes what it means by connection termination between clients(laptop) and server(load balancer) and the various other services.
+
+### Different types of connection errors.
+The following article on [TCP/IP errors](https://www.ibm.com/docs/en/db2/11.1?topic=message-tcpip-errors) has a list of the important tcp timeout errors that we need to know.
+
+
+| Common TCP/IP errors | Meaning |
+| -------- | -------- |
+| Resource temporarily unavailable.| Self-explanatory.     |
+| No space is left on a device or system table.|The disk partition is full|
+|No route to the host is available.|The routing table doesn't know where to route the packet.|
+|Connection was reset by the partner.|This usually means the packet was dropped as soon as it reached the server can be due to a firewall.|
+|The connection was timed out.|This indicates the firewall blocking your connection or the connection took too long.|
+
+## OSI Model Layer 7 (Application Layer)
+
+[What is layer 7?](https://www.cloudflare.com/learning/ddos/what-is-layer-7/)
+#### Summary
+Layer 7 refers to the seventh and topmost layer of the Open Systems Interconnect (OSI) Model known as the application layer. This is the highest layer which supports end-user processes and applications. Layer 7 identifies the communicating parties and the quality of service between them, considers privacy and user authentication, as well as identifies any constraints on the data syntax. This layer is wholly application-specific.
+
+
+## Setting up Ingress-Nginx Controller
+
+Since we are doing this on our local laptop, we are going to use the following tools:
+- [Minikube using KVM driver](https://minikube.sigs.k8s.io/docs/start/) - The host is linux-based in our example
+- [Metallb](https://metallb.universe.tf/) - Baremetal load-balancer.
+- [KVM](https://www.linux-kvm.org/page/Main_Page) / [Oracle VirtualBox](https://www.virtualbox.org/wiki/Downloads) / [VMWare](https://www.vmware.com/in/products/workstation-pro.html)
+
+
+### So let's begin with Metallb and Ingress-Nginx setup.
+
+For setting up metallb, we are going to follow the below steps:
+
+ - To begin the installation, we will execute:
+```
+minikube start
+```
+- To install Metallb, one can install it using the [manifest](https://metallb.universe.tf/installation/#installation-by-manifest) or by using [helm](https://metallb.universe.tf/installation/#installation-with-helm), for now we will use the Manifest method:
+```
+kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.4/config/manifests/metallb-native.yaml
+```
+
+- We need to now configure Metallb, we are using [Layer 2 configuration](https://metallb.universe.tf/configuration/#announce-the-service-ips), let's head over to the [Metallb Configuration](https://metallb.universe.tf/configuration/) website, here you will see how to setup metallb.
+>Layer 2 mode does not require the IPs to be bound to the network interfaces of your worker nodes. It works by responding to ARP requests on your local network directly, to give the machine’s MAC address to clients.
+In order to advertise the IP coming from an IPAddressPool, an L2Advertisement instance must be associated to the IPAddressPool.
+-  We have modified the IP address pool so that our loadbalancer knows which subnet to choose an IP from.Since we have only one minikube IP we need to modify the code given in the documentation.
+Save this as `metallb-config.yaml`:
+```
+apiVersion: metallb.io/v1beta1
+kind: IPAddressPool
+metadata:
+  name: first-pool
+  namespace: metallb-system
+spec:
+  addresses:
+  # The configuration website show's you this
+
+  #- 192.168.10.0/24
+  #- 192.168.9.1-192.168.9.5
+  #- fc00:f853:0ccd:e799::/124
+
+  # We are going to change this to `minikube ip` as such
+  - 192.168.39.57/32
+```
+Now deploy it using `kubectl`
+```
+kubectl apply -f metallb-config.yaml
+```
+- Now that metallb is setup, let's install [ingress-nginx](https://kubernetes.github.io/ingress-nginx/deploy/#quick-start) on the laptop.
+Note: We are using the install by manifest option from the Installation manual
+```
+kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.3.0/deploy/static/provider/cloud/deploy.yaml
+```
+or one can also install it using the minikube addons:
+```
+minikube addons enable ingress
+```
+ - Once your Ingress-Nginx controller is created you can run the following commands to see the output of the setup done.
+```
+kubectl get pods -n ingress-nginx
+NAME                                        READY   STATUS      RESTARTS   AGE
+ingress-nginx-admission-create-65bld        0/1     Completed   0          14m
+ingress-nginx-admission-patch-rwq4x         0/1     Completed   0          14m
+ingress-nginx-controller-6dc865cd86-7c5zd   1/1     Running     0          14m
+```
+The Ingress controller creates a Service with the type LoadBalancer and metallb provides the IP address.
+
+```
+kubectl -n ingress-nginx get svc
+
+NAME                                 TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)                      AGE
+ingress-nginx-controller             LoadBalancer   10.108.154.53   192.168.39.223   80:30367/TCP,443:31491/TCP   4d15h
+ingress-nginx-controller-admission   ClusterIP      10.98.54.3      <none>           443/TCP                      4d15h
+```
+
+#### Creating an Ingress
+
+We will deploy a `httpd` service in a `httpd` namespace and create a ingress for it.
+
+First, let's create a namespace.
+```
+kubectl create namespace httpd
+```
+
+Next we will create a deployment
+```
+kubectl create deployment httpd -n httpd --image=httpd:alpine
+```
+
+Now, In order to create a service, let's expose this deployment
+```
+kubectl expose deployment -n httpd httpd --port 80
+```
+Let's check the `pod` that is created
+
+```
+kubectl get po -n httpd
+NAME                    READY   STATUS    RESTARTS   AGE
+httpd-fb7fcdc77-w287c   1/1     Running   0          64s
+```
+
+Let's list the services in the `httpd` namespace
+```
+kubectl get svc -n httpd
+NAME    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
+httpd   ClusterIP   10.104.111.0   <none>        80/TCP    13s
+```
+
+Once we have this we can now create a n ingress using the following
+```
+kubectl -n httpd create ingress httpd --class nginx --rule httpd.dev.leonnunes.com/"*"=httpd:80
+```
+The above output, creates an ingress, for us with the rule to match the service if the host is `httpd.dev.leonnunes.com`. The class here is retrieved from the below command.
+
+To list the `ingressclasses` use
+```
+kubectl get ingressclasses
+NAME    CONTROLLER             PARAMETERS   AGE
+nginx   k8s.io/ingress-nginx   <none>       6h49m
+```
+
+The following command shows the ingress created
+```
+$ kubectl get ingress -A -o wide
+
+NAMESPACE   NAME    CLASS   HOSTS                     ADDRESS          PORTS   AGE
+httpd       httpd   nginx   httpd.dev.leonnunes.com   192.168.39.223   80      11d
+```
+
+To test if the rule works we can now do
+```
+$ minikube ip
+192.168.39.223
+
+$ curl --resolve httpd.dev.leonnunes.com:80:192.168.39.223 httpd.dev.leonnunes.com
+<html><body><h1>It works!</h1></body></html>
+
+or
+
+curl -H "Host: httpd.dev.leonnunes.com" 192.168.39.223
+```
+
+#### Example of Information found on layer 7
+We have setup `Ingress-Nginx`, using `nginx` as a class and `httpd` for this example.
+
+In order to display the info on Layer - 7, we have extracted the Layer 7 information from a simple `curl` request, and then using `tcpdump` command within the `httpd` pod we extracted the network packets and opened it using the `Wireshark` utility.
+
+Below given is the output that is important:
+```bash
+Frame 4: 391 bytes on wire (3128 bits), 391 bytes captured (3128 bits)
+Linux cooked capture v2
+Internet Protocol Version 4, Src: 172.17.0.4, Dst: 172.17.0.3
+Transmission Control Protocol, Src Port: 49074, Dst Port: 80, Seq: 1, Ack: 1, Len: 319
+Hypertext Transfer Protocol
+    GET / HTTP/1.1\r\n
+    Host: httpd.dev.leonnunes.com\r\n
+    X-Request-ID: 6e1a790412a0d1615dc0231358dc9c8b\r\n
+    X-Real-IP: 172.17.0.1\r\n
+    X-Forwarded-For: 172.17.0.1\r\n
+    X-Forwarded-Host: httpd.dev.leonnunes.com\r\n
+    X-Forwarded-Port: 80\r\n
+    X-Forwarded-Proto: http\r\n
+    X-Forwarded-Scheme: http\r\n
+    X-Scheme: http\r\n
+    User-Agent: curl/7.84.0\r\n
+    Accept: */*\r\n
+    \r\n
+    [Full request URI: http://httpd.dev.leonnunes.com/]
+    [HTTP request 1/1]
+    [Response in frame: 6]
+
+```
+The above output shows the information that the `httpd` pod recieves. The `curl` command sends the host header, `Host: httpd.dev.leonnunes.com`, to the nginx controller, that then matches the rule and sends the information to the right controller
+
+The following output shows what is sent via the laptop.
+```
+curl --resolve httpd.dev.leonnunes.com:80:192.168.39.57 -H "Host: httpd.dev.leonnunes.com" 192.168.39.57 -vL
+* Added httpd.dev.leonnunes.com:80:192.168.39.57 to DNS cache
+*   Trying 192.168.39.57:80...
+* Connected to 192.168.39.57 (192.168.39.57) port 80 (#0)
+> GET / HTTP/1.1
+> Host: httpd.dev.leonnunes.com
+> User-Agent: curl/7.84.0
+> Accept: */*
+>
+* Mark bundle as not supporting multiuse
+< HTTP/1.1 200 OK
+< Date: Mon, 22 Aug 2022 16:05:27 GMT
+< Content-Type: text/html
+< Content-Length: 45
+< Connection: keep-alive
+< Last-Modified: Mon, 11 Jun 2007 18:53:14 GMT
+< ETag: "2d-432a5e4a73a80"
+< Accept-Ranges: bytes
+<
+<html><body><h1>It works!</h1></body></html>
+* Connection #0 to host 192.168.39.57 left intact
+```
+As you can see from the above output there are several headers added to the curl output after it reaches the `httpd` pod, these headers are added by the Ingress Nginx Controller.
+
+
+### References
+#### Basics of Networking
+   - https://www.cisco.com/en/US/docs/security/vpn5000/manager/reference/guide/appA.html
+   - http://web.stanford.edu/class/cs101/
+   - https://www.geeksforgeeks.org/basics-computer-networking/
+   - Subnetting
+     - https://www.computernetworkingnotes.com/ccna-study-guide/subnetting-tutorial-subnetting-explained-with-examples.html
+
+#### Video Links
+   - https://www.youtube.com/playlist?list=PLhfrWIlLOoKPc2RecyiM_A9nf3fUU3e6g
+   - https://www.youtube.com/watch?v=S7MNX_UD7vY&list=PLIhvC56v63IJVXv0GJcl9vO5Z6znCVb1P
+
+### Topics to read about
+   - Docker in Docker
+   - [Docker/Containers](https://www.oreilly.com/library/view/docker-deep-dive/9781800565135/)
+   - Containers
+
+### Basics of Kubernetes
+#### Reading Material
+- https://nubenetes.com/kubernetes-tutorials/
+- https://kubernetes.io/docs/concepts/
+#### Video Material
+- [Techworld with Nana 101](https://www.youtube.com/playlist?list=PLy7NrYWoggjziYQIDorlXjTvvwweTYoNC)
+- [Jeff Geerling Kubernetes 101](https://www.youtube.com/watch?v=IcslsH7OoYo&list=PL2_OBreMn7FoYmfx27iSwocotjiikS5BD)
+
+#### Hands-On Kubernetes
+- https://kube.academy/
+- https://www.civo.com/academy
+
+### Networking in Kubernetes
+- [Kubernetes Networking 101](https://youtu.be/CYnwBIpvSlM?t=284)
+- [CNCF Kubernetes 101](https://www.youtube.com/watch?v=cUGXu2tiZMc)
+
+### Tools/Commands to help with troubleshooting.
+- [mtr](https://www.redhat.com/sysadmin/linux-mtr-command) - Tracing the packet from the source to destination
+- [tcpdump](https://linuxconfig.org/how-to-use-tcpdump-command-on-linux) - Monitor packets
+- [wireshark](https://www.lifewire.com/wireshark-tutorial-4143298) - Read/Sniff packets
+- [nslookup](https://phoenixnap.com/kb/nslookup-command) - Lookup Nameservers
+- [netstat](https://www.lifewire.com/netstat-command-2618098) - List network details
+- [curl](https://linuxhandbook.com/curl-command-examples/) - Curl a website from the command line
+- [ifconfig](https://www.tecmint.com/ifconfig-command-examples/)/[ip](https://www.geeksforgeeks.org/ip-command-in-linux-with-examples/) - Show ip address configuration
+- [dig](https://www.geeksforgeeks.org/dig-command-in-linux-with-examples/) - Query Nameservers
+- [ipcalc](https://www.linux.com/topic/networking/how-calculate-network-addresses-ipcalc/) - Calculate IP addresses
+- Advanced Tools for troubleshooting
+    - [Netshoot](https://github.com/nicolaka/netshoot) - Troubleshoot Networks
+- Cluster Creation tools
+    - [kind](https://kind.sigs.k8s.io/docs/user/quick-start/)
+    - [minikube](https://minikube.sigs.k8s.io/docs/start/)
diff --git a/charts/ingress-nginx/README.md b/charts/ingress-nginx/README.md
index 4e6a696c6..d689e3d5f 100644
--- a/charts/ingress-nginx/README.md
+++ b/charts/ingress-nginx/README.md
@@ -252,11 +252,11 @@ Kubernetes: `>=1.20.0-0`
 | controller.admissionWebhooks.networkPolicyEnabled | bool | `false` |  |
 | controller.admissionWebhooks.objectSelector | object | `{}` |  |
 | controller.admissionWebhooks.patch.enabled | bool | `true` |  |
-| controller.admissionWebhooks.patch.image.digest | string | `"sha256:549e71a6ca248c5abd51cdb73dbc3083df62cf92ed5e6147c780e30f7e007a47"` |  |
+| controller.admissionWebhooks.patch.image.digest | string | `"sha256:39c5b2e3310dc4264d638ad28d9d1d96c4cbb2b2dcfb52368fe4e3c63f61e10f"` |  |
 | controller.admissionWebhooks.patch.image.image | string | `"ingress-nginx/kube-webhook-certgen"` |  |
 | controller.admissionWebhooks.patch.image.pullPolicy | string | `"IfNotPresent"` |  |
 | controller.admissionWebhooks.patch.image.registry | string | `"registry.k8s.io"` |  |
-| controller.admissionWebhooks.patch.image.tag | string | `"v1.3.0"` |  |
+| controller.admissionWebhooks.patch.image.tag | string | `"v20220916-gd32f8c343"` |  |
 | controller.admissionWebhooks.patch.labels | object | `{}` | Labels to be added to patch job resources |
 | controller.admissionWebhooks.patch.nodeSelector."kubernetes.io/os" | string | `"linux"` |  |
 | controller.admissionWebhooks.patch.podAnnotations | object | `{}` |  |
diff --git a/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml b/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml
index 70edde334..e19c95572 100644
--- a/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml
+++ b/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml
@@ -1,3 +1,4 @@
+{{- if (semverCompare "<1.25.0-0" .Capabilities.KubeVersion.Version) }}
 {{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled .Values.podSecurityPolicy.enabled (empty .Values.controller.admissionWebhooks.existingPsp) -}}
 apiVersion: policy/v1beta1
 kind: PodSecurityPolicy
@@ -37,3 +38,4 @@ spec:
   - secret
   - downwardAPI
 {{- end }}
+{{- end }}
diff --git a/charts/ingress-nginx/templates/clusterrole.yaml b/charts/ingress-nginx/templates/clusterrole.yaml
index 0e725ec06..51bc5002c 100644
--- a/charts/ingress-nginx/templates/clusterrole.yaml
+++ b/charts/ingress-nginx/templates/clusterrole.yaml
@@ -89,6 +89,14 @@ rules:
       - get
       - list
       - watch
+  - apiGroups:
+      - discovery.k8s.io
+    resources:
+      - endpointslices
+    verbs:
+      - list
+      - watch
+      - get
 {{- end }}
 
 {{- end }}
diff --git a/charts/ingress-nginx/templates/controller-deployment.yaml b/charts/ingress-nginx/templates/controller-deployment.yaml
index 5ad186794..c193f776c 100644
--- a/charts/ingress-nginx/templates/controller-deployment.yaml
+++ b/charts/ingress-nginx/templates/controller-deployment.yaml
@@ -147,7 +147,11 @@ spec:
           volumeMounts:
           {{- if .Values.controller.extraModules }}
             - name: modules
+            {{ if .Values.controller.image.chroot }}
+              mountPath: /chroot/modules_mount
+            {{ else }}
               mountPath: /modules_mount
+            {{ end }}
           {{- end }}
           {{- if .Values.controller.customTemplate.configMapName }}
             - mountPath: /etc/nginx/template
diff --git a/charts/ingress-nginx/templates/controller-role.yaml b/charts/ingress-nginx/templates/controller-role.yaml
index 330be8c86..d6da92ec9 100644
--- a/charts/ingress-nginx/templates/controller-role.yaml
+++ b/charts/ingress-nginx/templates/controller-role.yaml
@@ -100,6 +100,14 @@ rules:
     verbs:
       - create
       - patch
+  - apiGroups:
+      - discovery.k8s.io
+    resources:
+      - endpointslices
+    verbs:
+      - list
+      - watch
+      - get
 {{- if .Values.podSecurityPolicy.enabled }}
   - apiGroups:      [{{ template "podSecurityPolicy.apiGroup" . }}]
     resources:      ['podsecuritypolicies']
diff --git a/charts/ingress-nginx/values.yaml b/charts/ingress-nginx/values.yaml
index 9c34e7f6f..301635701 100644
--- a/charts/ingress-nginx/values.yaml
+++ b/charts/ingress-nginx/values.yaml
@@ -646,8 +646,8 @@ controller:
         ## for backwards compatibility consider setting the full image url via the repository value below
         ## use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail
         ## repository:
-        tag: v1.3.0
-        digest: sha256:549e71a6ca248c5abd51cdb73dbc3083df62cf92ed5e6147c780e30f7e007a47
+        tag: v20220916-gd32f8c343
+        digest: sha256:39c5b2e3310dc4264d638ad28d9d1d96c4cbb2b2dcfb52368fe4e3c63f61e10f
         pullPolicy: IfNotPresent
       # -- Provide a priority class name to the webhook patching job
       ##
diff --git a/cmd/plugin/request/request.go b/cmd/plugin/request/request.go
index cae90e9d2..fd47564a9 100644
--- a/cmd/plugin/request/request.go
+++ b/cmd/plugin/request/request.go
@@ -22,11 +22,13 @@ import (
 
 	appsv1 "k8s.io/api/apps/v1"
 	apiv1 "k8s.io/api/core/v1"
+	discoveryv1 "k8s.io/api/discovery/v1"
 	networking "k8s.io/api/networking/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/cli-runtime/pkg/genericclioptions"
 	appsv1client "k8s.io/client-go/kubernetes/typed/apps/v1"
 	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
+	discoveryv1client "k8s.io/client-go/kubernetes/typed/discovery/v1"
 	typednetworking "k8s.io/client-go/kubernetes/typed/networking/v1"
 
 	"k8s.io/ingress-nginx/cmd/plugin/util"
@@ -129,55 +131,61 @@ func GetIngressDefinitions(flags *genericclioptions.ConfigFlags, namespace strin
 	return pods.Items, nil
 }
 
-// GetNumEndpoints counts the number of endpoints for the service with the given name
+// GetNumEndpoints counts the number of endpointslices adresses for the service with the given name
 func GetNumEndpoints(flags *genericclioptions.ConfigFlags, namespace string, serviceName string) (*int, error) {
-	endpoints, err := GetEndpointsByName(flags, namespace, serviceName)
+	epss, err := GetEndpointSlicesByName(flags, namespace, serviceName)
 	if err != nil {
 		return nil, err
 	}
 
-	if endpoints == nil {
+	if len(epss) == 0 {
 		return nil, nil
 	}
 
 	ret := 0
-	for _, subset := range endpoints.Subsets {
-		ret += len(subset.Addresses)
+	for _, eps := range epss {
+		for _, ep := range eps.Endpoints {
+			ret += len(ep.Addresses)
+		}
 	}
 	return &ret, nil
 }
 
-// GetEndpointsByName returns the endpoints for the service with the given name
-func GetEndpointsByName(flags *genericclioptions.ConfigFlags, namespace string, name string) (*apiv1.Endpoints, error) {
-	allEndpoints, err := getEndpoints(flags, namespace)
+// GetEndpointSlicesByName returns the endpointSlices for the service with the given name
+func GetEndpointSlicesByName(flags *genericclioptions.ConfigFlags, namespace string, name string) ([]discoveryv1.EndpointSlice, error) {
+	allEndpointsSlices, err := getEndpointSlices(flags, namespace)
 	if err != nil {
 		return nil, err
 	}
-
-	for _, endpoints := range allEndpoints {
-		if endpoints.Name == name {
-			return &endpoints, nil
+	var eps []discoveryv1.EndpointSlice
+	for _, slice := range allEndpointsSlices {
+		if svcName, ok := slice.ObjectMeta.GetLabels()[discoveryv1.LabelServiceName]; ok {
+			if svcName == name {
+				eps = append(eps, slice)
+			}
 		}
 	}
 
-	return nil, nil
+	return eps, nil
 }
 
-var endpointsCache = make(map[string]*[]apiv1.Endpoints)
+var endpointSlicesCache = make(map[string]*[]discoveryv1.EndpointSlice)
+
+func getEndpointSlices(flags *genericclioptions.ConfigFlags, namespace string) ([]discoveryv1.EndpointSlice, error) {
+	cachedEndpointSlices, ok := endpointSlicesCache[namespace]
 
-func getEndpoints(flags *genericclioptions.ConfigFlags, namespace string) ([]apiv1.Endpoints, error) {
-	cachedEndpoints, ok := endpointsCache[namespace]
 	if ok {
-		return *cachedEndpoints, nil
+		return *cachedEndpointSlices, nil
 	}
 
 	if namespace != "" {
-		tryAllNamespacesEndpointsCache(flags)
+		tryAllNamespacesEndpointSlicesCache(flags)
 	}
 
-	cachedEndpoints = tryFilteringEndpointsFromAllNamespacesCache(flags, namespace)
-	if cachedEndpoints != nil {
-		return *cachedEndpoints, nil
+	cachedEndpointSlices = tryFilteringEndpointSlicesFromAllNamespacesCache(flags, namespace)
+
+	if cachedEndpointSlices != nil {
+		return *cachedEndpointSlices, nil
 	}
 
 	rawConfig, err := flags.ToRESTConfig()
@@ -185,42 +193,41 @@ func getEndpoints(flags *genericclioptions.ConfigFlags, namespace string) ([]api
 		return nil, err
 	}
 
-	api, err := corev1.NewForConfig(rawConfig)
+	api, err := discoveryv1client.NewForConfig(rawConfig)
 	if err != nil {
 		return nil, err
 	}
-
-	endpointsList, err := api.Endpoints(namespace).List(context.TODO(), metav1.ListOptions{})
+	endpointSlicesList, err := api.EndpointSlices(namespace).List(context.TODO(), metav1.ListOptions{})
 	if err != nil {
 		return nil, err
 	}
-	endpoints := endpointsList.Items
+	endpointSlices := endpointSlicesList.Items
 
-	endpointsCache[namespace] = &endpoints
-	return endpoints, nil
+	endpointSlicesCache[namespace] = &endpointSlices
+	return endpointSlices, nil
 }
 
-func tryAllNamespacesEndpointsCache(flags *genericclioptions.ConfigFlags) {
-	_, ok := endpointsCache[""]
+func tryAllNamespacesEndpointSlicesCache(flags *genericclioptions.ConfigFlags) {
+	_, ok := endpointSlicesCache[""]
 	if !ok {
-		_, err := getEndpoints(flags, "")
+		_, err := getEndpointSlices(flags, "")
 		if err != nil {
-			endpointsCache[""] = nil
+			endpointSlicesCache[""] = nil
 		}
 	}
 }
 
-func tryFilteringEndpointsFromAllNamespacesCache(flags *genericclioptions.ConfigFlags, namespace string) *[]apiv1.Endpoints {
-	allEndpoints := endpointsCache[""]
-	if allEndpoints != nil {
-		endpoints := make([]apiv1.Endpoints, 0)
-		for _, thisEndpoints := range *allEndpoints {
-			if thisEndpoints.Namespace == namespace {
-				endpoints = append(endpoints, thisEndpoints)
+func tryFilteringEndpointSlicesFromAllNamespacesCache(flags *genericclioptions.ConfigFlags, namespace string) *[]discoveryv1.EndpointSlice {
+	allEndpointSlices := endpointSlicesCache[""]
+	if allEndpointSlices != nil {
+		endpointSlices := make([]discoveryv1.EndpointSlice, 0)
+		for _, slice := range *allEndpointSlices {
+			if slice.Namespace == namespace {
+				endpointSlices = append(endpointSlices, slice)
 			}
 		}
-		endpointsCache[namespace] = &endpoints
-		return &endpoints
+		endpointSlicesCache[namespace] = &endpointSlices
+		return &endpointSlices
 	}
 	return nil
 }
diff --git a/docs/developer-guide/getting-started.md b/docs/developer-guide/getting-started.md
index 8d500f147..fbf251790 100644
--- a/docs/developer-guide/getting-started.md
+++ b/docs/developer-guide/getting-started.md
@@ -88,3 +88,6 @@ and then publish such version with
 ```console
 docker push $REGISTRY/controller:$TAG
 ```
+
+### New Contributor Tips
+New contributors can refer to the [new contributor tips](https://github.com/kubernetes/ingress-nginx/blob/main/NEW_CONTRIBUTOR.md) which will be updated with examples and tips.
diff --git a/docs/examples/affinity/cookie/README.md b/docs/examples/affinity/cookie/README.md
index 891f828a2..1920d132b 100644
--- a/docs/examples/affinity/cookie/README.md
+++ b/docs/examples/affinity/cookie/README.md
@@ -14,6 +14,7 @@ Session affinity can be configured using the following annotations:
 |nginx.ingress.kubernetes.io/session-cookie-name|Name of the cookie that will be created|string (defaults to `INGRESSCOOKIE`)|
 |nginx.ingress.kubernetes.io/session-cookie-secure|Set the cookie as secure regardless the protocol of the incoming request|`"true"` or `"false"`|
 |nginx.ingress.kubernetes.io/session-cookie-path|Path that will be set on the cookie (required if your [Ingress paths][ingress-paths] use regular expressions)|string (defaults to the currently [matched path][ingress-paths])|
+|nginx.ingress.kubernetes.io/session-cookie-domain|Domain that will be set on the cookie|string|
 |nginx.ingress.kubernetes.io/session-cookie-samesite|`SameSite` attribute to apply to the cookie|Browser accepted values are `None`, `Lax`, and `Strict`|
 |nginx.ingress.kubernetes.io/session-cookie-conditional-samesite-none|Will omit `SameSite=None` attribute for older browsers which reject the more-recently defined `SameSite=None` value|`"true"` or `"false"`
 |nginx.ingress.kubernetes.io/session-cookie-max-age|Time until the cookie expires, corresponds to the `Max-Age` cookie directive|number of seconds|
diff --git a/docs/user-guide/nginx-configuration/annotations.md b/docs/user-guide/nginx-configuration/annotations.md
index 199d156be..131320bf7 100755
--- a/docs/user-guide/nginx-configuration/annotations.md
+++ b/docs/user-guide/nginx-configuration/annotations.md
@@ -98,6 +98,7 @@ You can add these Kubernetes annotations to specific Ingress objects to customiz
 |[nginx.ingress.kubernetes.io/service-upstream](#service-upstream)|"true" or "false"|
 |[nginx.ingress.kubernetes.io/session-cookie-name](#cookie-affinity)|string|
 |[nginx.ingress.kubernetes.io/session-cookie-path](#cookie-affinity)|string|
+|[nginx.ingress.kubernetes.io/session-cookie-domain](#cookie-affinity)|string|
 |[nginx.ingress.kubernetes.io/session-cookie-change-on-failure](#cookie-affinity)|"true" or "false"|
 |[nginx.ingress.kubernetes.io/session-cookie-samesite](#cookie-affinity)|string|
 |[nginx.ingress.kubernetes.io/session-cookie-conditional-samesite-none](#cookie-affinity)|"true" or "false"|
@@ -189,6 +190,8 @@ If you use the ``cookie`` affinity type you can also specify the name of the coo
 
 The NGINX annotation `nginx.ingress.kubernetes.io/session-cookie-path` defines the path that will be set on the cookie. This is optional unless the annotation `nginx.ingress.kubernetes.io/use-regex` is set to true; Session cookie paths do not support regex.
 
+Use `nginx.ingress.kubernetes.io/session-cookie-domain` to set the `Domain` attribute of the sticky cookie.
+
 Use `nginx.ingress.kubernetes.io/session-cookie-samesite` to apply a `SameSite` attribute to the sticky cookie. Browser accepted values are `None`, `Lax`, and `Strict`. Some browsers reject cookies with `SameSite=None`, including those created before the `SameSite=None` specification (e.g. Chrome 5X). Other browsers mistakenly treat `SameSite=None` cookies as `SameSite=Strict` (e.g. Safari running on OSX 14). To omit `SameSite=None` from browsers with these incompatibilities, add the annotation `nginx.ingress.kubernetes.io/session-cookie-conditional-samesite-none: "true"`.
 
 ### Authentication
diff --git a/go.mod b/go.mod
index 521b58fce..471e7749e 100644
--- a/go.mod
+++ b/go.mod
@@ -14,7 +14,7 @@ require (
 	github.com/mitchellh/mapstructure v1.5.0
 	github.com/moul/pb v0.0.0-20220425114252-bca18df4138c
 	github.com/ncabatoff/process-exporter v0.7.10
-	github.com/onsi/ginkgo/v2 v2.1.6
+	github.com/onsi/ginkgo/v2 v2.2.0
 	github.com/opencontainers/runc v1.1.4
 	github.com/pmezard/go-difflib v1.0.0
 	github.com/prometheus/client_golang v1.13.0
@@ -30,14 +30,14 @@ require (
 	google.golang.org/grpc v1.49.0
 	gopkg.in/go-playground/pool.v3 v3.1.1
 	gopkg.in/mcuadros/go-syslog.v2 v2.3.0
-	k8s.io/api v0.25.0
+	k8s.io/api v0.25.2
 	k8s.io/apiextensions-apiserver v0.23.5
-	k8s.io/apimachinery v0.25.0
+	k8s.io/apimachinery v0.25.2
 	k8s.io/apiserver v0.25.0
 	k8s.io/cli-runtime v0.25.0
-	k8s.io/client-go v0.25.0
+	k8s.io/client-go v0.25.2
 	k8s.io/code-generator v0.23.5
-	k8s.io/component-base v0.25.0
+	k8s.io/component-base v0.25.2
 	k8s.io/klog/v2 v2.80.1
 	pault.ag/go/sniff v0.0.0-20200207005214-cf7e4d167732
 	sigs.k8s.io/controller-runtime v0.11.2
diff --git a/go.sum b/go.sum
index 5a5411fae..65fb4ee78 100644
--- a/go.sum
+++ b/go.sum
@@ -456,8 +456,8 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
-github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU=
-github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
+github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
+github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
 github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
@@ -1134,26 +1134,26 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8=
-k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0=
-k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk=
+k8s.io/api v0.25.2 h1:v6G8RyFcwf0HR5jQGIAYlvtRNrxMJQG1xJzaSeVnIS8=
+k8s.io/api v0.25.2/go.mod h1:qP1Rn4sCVFwx/xIhe+we2cwBLTXNcheRyYXwajonhy0=
 k8s.io/apiextensions-apiserver v0.23.5 h1:5SKzdXyvIJKu+zbfPc3kCbWpbxi+O+zdmAJBm26UJqI=
 k8s.io/apiextensions-apiserver v0.23.5/go.mod h1:ntcPWNXS8ZPKN+zTXuzYMeg731CP0heCTl6gYBxLcuQ=
 k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=
-k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU=
-k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0=
+k8s.io/apimachinery v0.25.2 h1:WbxfAjCx+AeN8Ilp9joWnyJ6xu9OMeS/fsfjK/5zaQs=
+k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA=
 k8s.io/apiserver v0.23.5/go.mod h1:7wvMtGJ42VRxzgVI7jkbKvMbuCbVbgsWFT7RyXiRNTw=
 k8s.io/apiserver v0.25.0 h1:8kl2ifbNffD440MyvHtPaIz1mw4mGKVgWqM0nL+oyu4=
 k8s.io/apiserver v0.25.0/go.mod h1:BKwsE+PTC+aZK+6OJQDPr0v6uS91/HWxX7evElAH6xo=
 k8s.io/cli-runtime v0.25.0 h1:XBnTc2Fi+w818jcJGzhiJKQuXl8479sZ4FhtV5hVJ1Q=
 k8s.io/cli-runtime v0.25.0/go.mod h1:bHOI5ZZInRHhbq12OdUiYZQN8ml8aKZLwQgt9QlLINw=
 k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4=
-k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E=
-k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8=
+k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo=
+k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4=
 k8s.io/code-generator v0.23.5 h1:xn3a6J5pUL49AoH6SPrOFtnB5cvdMl76f/bEY176R3c=
 k8s.io/code-generator v0.23.5/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk=
 k8s.io/component-base v0.23.5/go.mod h1:c5Nq44KZyt1aLl0IpHX82fhsn84Sb0jjzwjpcA42bY0=
-k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y=
-k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk=
+k8s.io/component-base v0.25.2 h1:Nve/ZyHLUBHz1rqwkjXm/Re6IniNa5k7KgzxZpTfSQY=
+k8s.io/component-base v0.25.2/go.mod h1:90W21YMr+Yjg7MX+DohmZLzjsBtaxQDDwaX4YxDkl60=
 k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c h1:GohjlNKauSai7gN4wsJkeZ3WAJx4Sh+oT/b5IYn5suA=
 k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
 k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
diff --git a/images/nginx/rootfs/build.sh b/images/nginx/rootfs/build.sh
index f3efb3663..75d765e78 100755
--- a/images/nginx/rootfs/build.sh
+++ b/images/nginx/rootfs/build.sh
@@ -60,10 +60,10 @@ export DATADOG_CPP_VERSION=1.3.2
 export MODSECURITY_VERSION=1.0.2
 
 # Check for recent changes: https://github.com/SpiderLabs/ModSecurity/compare/v3.0.5...v3/master
-export MODSECURITY_LIB_VERSION=v3.0.5
+export MODSECURITY_LIB_VERSION=v3.0.8
 
 # Check for recent changes: https://github.com/coreruleset/coreruleset/compare/v3.3.2...v3.3/master
-export OWASP_MODSECURITY_CRS_VERSION=v3.3.2
+export OWASP_MODSECURITY_CRS_VERSION=v3.3.4
 
 # Check for recent changes: https://github.com/openresty/lua-nginx-module/compare/v0.10.20...master
 export LUA_NGX_VERSION=b721656a9127255003b696b42ccc871c7ec18d59
@@ -548,6 +548,7 @@ Include /etc/nginx/owasp-modsecurity-crs/rules/REQUEST-912-DOS-PROTECTION.conf
 Include /etc/nginx/owasp-modsecurity-crs/rules/REQUEST-913-SCANNER-DETECTION.conf
 Include /etc/nginx/owasp-modsecurity-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf
 Include /etc/nginx/owasp-modsecurity-crs/rules/REQUEST-921-PROTOCOL-ATTACK.conf
+Include /etc/nginx/owasp-modsecurity-crs/rules/REQUEST-922-MULTIPART-ATTACK.conf
 Include /etc/nginx/owasp-modsecurity-crs/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf
 Include /etc/nginx/owasp-modsecurity-crs/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf
 Include /etc/nginx/owasp-modsecurity-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf
diff --git a/internal/ingress/annotations/sessionaffinity/main.go b/internal/ingress/annotations/sessionaffinity/main.go
index 9c4d1d2bc..98a0d64f8 100644
--- a/internal/ingress/annotations/sessionaffinity/main.go
+++ b/internal/ingress/annotations/sessionaffinity/main.go
@@ -52,6 +52,9 @@ const (
 	// This is used to control the cookie path when use-regex is set to true
 	annotationAffinityCookiePath = "session-cookie-path"
 
+	// This is used to control the cookie Domain
+	annotationAffinityCookieDomain = "session-cookie-domain"
+
 	// This is used to control the SameSite attribute of the cookie
 	annotationAffinityCookieSameSite = "session-cookie-samesite"
 
@@ -87,6 +90,8 @@ type Cookie struct {
 	MaxAge string `json:"maxage"`
 	// The path that a cookie will be set on
 	Path string `json:"path"`
+	// The domain that a cookie will be set on
+	Domain string `json:"domain"`
 	// Flag that allows cookie regeneration on request failure
 	ChangeOnFailure bool `json:"changeonfailure"`
 	// Secure flag to be set
@@ -127,6 +132,11 @@ func (a affinity) cookieAffinityParse(ing *networking.Ingress) *Cookie {
 		klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookiePath)
 	}
 
+	cookie.Domain, err = parser.GetStringAnnotation(annotationAffinityCookieDomain, ing)
+	if err != nil {
+		klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookieDomain)
+	}
+
 	cookie.SameSite, err = parser.GetStringAnnotation(annotationAffinityCookieSameSite, ing)
 	if err != nil {
 		klog.V(3).InfoS("Invalid or no annotation value found. Ignoring", "ingress", klog.KObj(ing), "annotation", annotationAffinityCookieSameSite)
diff --git a/internal/ingress/controller/controller.go b/internal/ingress/controller/controller.go
index 5287034c6..f60f7a053 100644
--- a/internal/ingress/controller/controller.go
+++ b/internal/ingress/controller/controller.go
@@ -434,7 +434,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
 				sp := svc.Spec.Ports[i]
 				if sp.Name == svcPort {
 					if sp.Protocol == proto {
-						endps = getEndpoints(svc, &sp, proto, n.store.GetServiceEndpoints)
+						endps = getEndpointsFromSlices(svc, &sp, proto, n.store.GetServiceEndpointsSlices)
 						break
 					}
 				}
@@ -445,7 +445,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
 				sp := svc.Spec.Ports[i]
 				if sp.Port == int32(targetPort) {
 					if sp.Protocol == proto {
-						endps = getEndpoints(svc, &sp, proto, n.store.GetServiceEndpoints)
+						endps = getEndpointsFromSlices(svc, &sp, proto, n.store.GetServiceEndpointsSlices)
 						break
 					}
 				}
@@ -497,7 +497,7 @@ func (n *NGINXController) getDefaultUpstream() *ingress.Backend {
 		return upstream
 	}
 
-	endps := getEndpoints(svc, &svc.Spec.Ports[0], apiv1.ProtocolTCP, n.store.GetServiceEndpoints)
+	endps := getEndpointsFromSlices(svc, &svc.Spec.Ports[0], apiv1.ProtocolTCP, n.store.GetServiceEndpointsSlices)
 	if len(endps) == 0 {
 		klog.Warningf("Service %q does not have any active Endpoint", svcKey)
 		endps = []ingress.Endpoint{n.DefaultEndpoint()}
@@ -767,6 +767,7 @@ func (n *NGINXController) getBackendServers(ingresses []*ingress.Ingress) ([]*in
 					ups.SessionAffinity.CookieSessionAffinity.MaxAge = anns.SessionAffinity.Cookie.MaxAge
 					ups.SessionAffinity.CookieSessionAffinity.Secure = anns.SessionAffinity.Cookie.Secure
 					ups.SessionAffinity.CookieSessionAffinity.Path = cookiePath
+					ups.SessionAffinity.CookieSessionAffinity.Domain = anns.SessionAffinity.Cookie.Domain
 					ups.SessionAffinity.CookieSessionAffinity.SameSite = anns.SessionAffinity.Cookie.SameSite
 					ups.SessionAffinity.CookieSessionAffinity.ConditionalSameSiteNone = anns.SessionAffinity.Cookie.ConditionalSameSiteNone
 					ups.SessionAffinity.CookieSessionAffinity.ChangeOnFailure = anns.SessionAffinity.Cookie.ChangeOnFailure
@@ -824,7 +825,7 @@ func (n *NGINXController) getBackendServers(ingresses []*ingress.Ingress) ([]*in
 				}
 
 				sp := location.DefaultBackend.Spec.Ports[0]
-				endps := getEndpoints(location.DefaultBackend, &sp, apiv1.ProtocolTCP, n.store.GetServiceEndpoints)
+				endps := getEndpointsFromSlices(location.DefaultBackend, &sp, apiv1.ProtocolTCP, n.store.GetServiceEndpointsSlices)
 				// custom backend is valid only if contains at least one endpoint
 				if len(endps) > 0 {
 					name := fmt.Sprintf("custom-default-backend-%v-%v", location.DefaultBackend.GetNamespace(), location.DefaultBackend.GetName())
@@ -1082,7 +1083,6 @@ func (n *NGINXController) serviceEndpoints(svcKey, backendPort string) ([]ingres
 	}
 
 	klog.V(3).Infof("Obtaining ports information for Service %q", svcKey)
-
 	// Ingress with an ExternalName Service and no port defined for that Service
 	if svc.Spec.Type == apiv1.ServiceTypeExternalName {
 		if n.cfg.DisableServiceExternalName {
@@ -1090,7 +1090,7 @@ func (n *NGINXController) serviceEndpoints(svcKey, backendPort string) ([]ingres
 			return upstreams, nil
 		}
 		servicePort := externalNamePorts(backendPort, svc)
-		endps := getEndpoints(svc, servicePort, apiv1.ProtocolTCP, n.store.GetServiceEndpoints)
+		endps := getEndpointsFromSlices(svc, servicePort, apiv1.ProtocolTCP, n.store.GetServiceEndpointsSlices)
 		if len(endps) == 0 {
 			klog.Warningf("Service %q does not have any active Endpoint.", svcKey)
 			return upstreams, nil
@@ -1107,7 +1107,7 @@ func (n *NGINXController) serviceEndpoints(svcKey, backendPort string) ([]ingres
 			servicePort.TargetPort.String() == backendPort ||
 			servicePort.Name == backendPort {
 
-			endps := getEndpoints(svc, &servicePort, apiv1.ProtocolTCP, n.store.GetServiceEndpoints)
+			endps := getEndpointsFromSlices(svc, &servicePort, apiv1.ProtocolTCP, n.store.GetServiceEndpointsSlices)
 			if len(endps) == 0 {
 				klog.Warningf("Service %q does not have any active Endpoint.", svcKey)
 			}
diff --git a/internal/ingress/controller/endpoints.go b/internal/ingress/controller/endpointslices.go
similarity index 55%
rename from internal/ingress/controller/endpoints.go
rename to internal/ingress/controller/endpointslices.go
index 5615fadea..efd2a6afa 100644
--- a/internal/ingress/controller/endpoints.go
+++ b/internal/ingress/controller/endpointslices.go
@@ -28,14 +28,15 @@ import (
 	"k8s.io/klog/v2"
 
 	corev1 "k8s.io/api/core/v1"
+	discoveryv1 "k8s.io/api/discovery/v1"
 
 	"k8s.io/ingress-nginx/internal/k8s"
 	"k8s.io/ingress-nginx/pkg/apis/ingress"
 )
 
 // getEndpoints returns a list of Endpoint structs for a given service/target port combination.
-func getEndpoints(s *corev1.Service, port *corev1.ServicePort, proto corev1.Protocol,
-	getServiceEndpoints func(string) (*corev1.Endpoints, error)) []ingress.Endpoint {
+func getEndpointsFromSlices(s *corev1.Service, port *corev1.ServicePort, proto corev1.Protocol,
+	getServiceEndpointsSlices func(string) ([]*discoveryv1.EndpointSlice, error)) []ingress.Endpoint {
 
 	upsServers := []ingress.Endpoint{}
 
@@ -74,54 +75,63 @@ func getEndpoints(s *corev1.Service, port *corev1.ServicePort, proto corev1.Prot
 		})
 	}
 
-	klog.V(3).Infof("Getting Endpoints for Service %q and port %v", svcKey, port.String())
-	ep, err := getServiceEndpoints(svcKey)
+	klog.V(3).Infof("Getting Endpoints from endpointSlices for Service %q and port %v", svcKey, port.String())
+	epss, err := getServiceEndpointsSlices(svcKey)
 	if err != nil {
 		klog.Warningf("Error obtaining Endpoints for Service %q: %v", svcKey, err)
 		return upsServers
 	}
-
-	for _, ss := range ep.Subsets {
-		matchedPortNameFound := false
-		for i, epPort := range ss.Ports {
-
-			if !reflect.DeepEqual(epPort.Protocol, proto) {
-				continue
-			}
-
-			var targetPort int32
-
-			if port.Name == "" {
-				// port.Name is optional if there is only one port
-				targetPort = epPort.Port
-				matchedPortNameFound = true
-			} else if port.Name == epPort.Name {
-				targetPort = epPort.Port
-				matchedPortNameFound = true
-			}
-
-			if i == len(ss.Ports)-1 && !matchedPortNameFound && port.TargetPort.Type == intstr.Int {
-				// use service target port if it's a number and no port name matched
-				// https://github.com/kubernetes/ingress-nginx/issues/7390
-				targetPort = port.TargetPort.IntVal
+	// loop over all endpointSlices generated for service
+	for _, eps := range epss {
+		var ports []int32
+		if len(eps.Ports) == 0 {
+			// When ports is empty, it indicates that there are no defined ports, using svc targePort <- this could be wrong
+			klog.V(3).Infof("No ports found on endpointSlice, using service TargetPort %v for Service %q", port.String(), svcKey)
+			ports = append(ports, port.TargetPort.IntVal)
+		} else {
+			for _, epPort := range eps.Ports {
+				if !reflect.DeepEqual(*epPort.Protocol, proto) {
+					continue
+				}
+				var targetPort int32 = 0
+				if port.Name == "" {
+					// port.Name is optional if there is only one port
+					targetPort = *epPort.Port
+				} else if port.Name == *epPort.Name {
+					targetPort = *epPort.Port
+				}
+				if targetPort == 0 && port.TargetPort.Type == intstr.Int {
+					// use service target port if it's a number and no port name matched
+					// https://github.com/kubernetes/ingress-nginx/issues/7390
+					targetPort = port.TargetPort.IntVal
+				}
+				if targetPort == 0 {
+					continue
+				}
+				ports = append(ports, targetPort)
 			}
-
-			if targetPort <= 0 {
+		}
+		for _, ep := range eps.Endpoints {
+			if !(*ep.Conditions.Ready) {
 				continue
 			}
 
-			for _, epAddress := range ss.Addresses {
-				ep := net.JoinHostPort(epAddress.IP, strconv.Itoa(int(targetPort)))
-				if _, exists := processedUpstreamServers[ep]; exists {
-					continue
-				}
-				ups := ingress.Endpoint{
-					Address: epAddress.IP,
-					Port:    fmt.Sprintf("%v", targetPort),
-					Target:  epAddress.TargetRef,
+			// ep.Hints
+
+			for _, epPort := range ports {
+				for _, epAddress := range ep.Addresses {
+					hostPort := net.JoinHostPort(epAddress, strconv.Itoa(int(epPort)))
+					if _, exists := processedUpstreamServers[hostPort]; exists {
+						continue
+					}
+					ups := ingress.Endpoint{
+						Address: epAddress,
+						Port:    fmt.Sprintf("%v", epPort),
+						Target:  ep.TargetRef,
+					}
+					upsServers = append(upsServers, ups)
+					processedUpstreamServers[hostPort] = struct{}{}
 				}
-				upsServers = append(upsServers, ups)
-				processedUpstreamServers[ep] = struct{}{}
 			}
 		}
 	}
diff --git a/internal/ingress/controller/store/endpoint.go b/internal/ingress/controller/store/endpoint.go
deleted file mode 100644
index ff6fbd715..000000000
--- a/internal/ingress/controller/store/endpoint.go
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-Copyright 2015 The Kubernetes Authors.
-
-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 store
-
-import (
-	apiv1 "k8s.io/api/core/v1"
-	"k8s.io/client-go/tools/cache"
-)
-
-// EndpointLister makes a Store that lists Endpoints.
-type EndpointLister struct {
-	cache.Store
-}
-
-// ByKey returns the Endpoints of the Service matching key in the local Endpoint Store.
-func (s *EndpointLister) ByKey(key string) (*apiv1.Endpoints, error) {
-	eps, exists, err := s.GetByKey(key)
-	if err != nil {
-		return nil, err
-	}
-	if !exists {
-		return nil, NotExistsError(key)
-	}
-	return eps.(*apiv1.Endpoints), nil
-}
diff --git a/internal/ingress/controller/store/endpointslice.go b/internal/ingress/controller/store/endpointslice.go
new file mode 100644
index 000000000..fdd7374e9
--- /dev/null
+++ b/internal/ingress/controller/store/endpointslice.go
@@ -0,0 +1,55 @@
+/*
+Copyright 2022 The Kubernetes Authors.
+
+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 store
+
+import (
+	"fmt"
+	"strings"
+
+	discoveryv1 "k8s.io/api/discovery/v1"
+	"k8s.io/client-go/tools/cache"
+)
+
+// EndpointSliceLister makes a Store that lists Endpoints.
+type EndpointSliceLister struct {
+	cache.Store
+}
+
+// MatchByKey returns the EndpointsSlices of the Service matching key in the local Endpoint Store.
+func (s *EndpointSliceLister) MatchByKey(key string) ([]*discoveryv1.EndpointSlice, error) {
+	var eps []*discoveryv1.EndpointSlice
+	// filter endpointSlices owned by svc
+	for _, listKey := range s.ListKeys() {
+		if !strings.HasPrefix(listKey, key) {
+			continue
+		}
+		epss, exists, err := s.GetByKey(listKey)
+		if exists && err == nil {
+			// check for svc owner label
+			if svcName, ok := epss.(*discoveryv1.EndpointSlice).ObjectMeta.GetLabels()[discoveryv1.LabelServiceName]; ok {
+				namespace := epss.(*discoveryv1.EndpointSlice).ObjectMeta.GetNamespace()
+				if key == fmt.Sprintf("%s/%s", namespace, svcName) {
+					eps = append(eps, epss.(*discoveryv1.EndpointSlice))
+				}
+			}
+		}
+	}
+	if len(eps) == 0 {
+		return nil, NotExistsError(key)
+	}
+	return eps, nil
+}
diff --git a/internal/ingress/controller/store/store.go b/internal/ingress/controller/store/store.go
index 239fbaaf5..7913eb0de 100644
--- a/internal/ingress/controller/store/store.go
+++ b/internal/ingress/controller/store/store.go
@@ -29,6 +29,7 @@ import (
 
 	"github.com/eapache/channels"
 	corev1 "k8s.io/api/core/v1"
+	discoveryv1 "k8s.io/api/discovery/v1"
 	networkingv1 "k8s.io/api/networking/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/fields"
@@ -78,8 +79,8 @@ type Storer interface {
 	// GetService returns the Service matching key.
 	GetService(key string) (*corev1.Service, error)
 
-	// GetServiceEndpoints returns the Endpoints of a Service matching key.
-	GetServiceEndpoints(key string) (*corev1.Endpoints, error)
+	// GetServiceEndpointsSlices returns the EndpointSlices of a Service matching key.
+	GetServiceEndpointsSlices(key string) ([]*discoveryv1.EndpointSlice, error)
 
 	// ListIngresses returns a list of all Ingresses in the store.
 	ListIngresses() []*ingress.Ingress
@@ -127,13 +128,13 @@ type Event struct {
 
 // Informer defines the required SharedIndexInformers that interact with the API server.
 type Informer struct {
-	Ingress      cache.SharedIndexInformer
-	IngressClass cache.SharedIndexInformer
-	Endpoint     cache.SharedIndexInformer
-	Service      cache.SharedIndexInformer
-	Secret       cache.SharedIndexInformer
-	ConfigMap    cache.SharedIndexInformer
-	Namespace    cache.SharedIndexInformer
+	Ingress       cache.SharedIndexInformer
+	IngressClass  cache.SharedIndexInformer
+	EndpointSlice cache.SharedIndexInformer
+	Service       cache.SharedIndexInformer
+	Secret        cache.SharedIndexInformer
+	ConfigMap     cache.SharedIndexInformer
+	Namespace     cache.SharedIndexInformer
 }
 
 // Lister contains object listers (stores).
@@ -141,7 +142,7 @@ type Lister struct {
 	Ingress               IngressLister
 	IngressClass          IngressClassLister
 	Service               ServiceLister
-	Endpoint              EndpointLister
+	EndpointSlice         EndpointSliceLister
 	Secret                SecretLister
 	ConfigMap             ConfigMapLister
 	Namespace             NamespaceLister
@@ -159,7 +160,7 @@ func (e NotExistsError) Error() string {
 // Run initiates the synchronization of the informers against the API server.
 func (i *Informer) Run(stopCh chan struct{}) {
 	go i.Secret.Run(stopCh)
-	go i.Endpoint.Run(stopCh)
+	go i.EndpointSlice.Run(stopCh)
 	if i.IngressClass != nil {
 		go i.IngressClass.Run(stopCh)
 	}
@@ -169,7 +170,6 @@ func (i *Informer) Run(stopCh chan struct{}) {
 	// wait for all involved caches to be synced before processing items
 	// from the queue
 	if !cache.WaitForCacheSync(stopCh,
-		i.Endpoint.HasSynced,
 		i.Service.HasSynced,
 		i.Secret.HasSynced,
 		i.ConfigMap.HasSynced,
@@ -330,8 +330,8 @@ func New(
 		store.listers.IngressClass.Store = cache.NewStore(cache.MetaNamespaceKeyFunc)
 	}
 
-	store.informers.Endpoint = infFactory.Core().V1().Endpoints().Informer()
-	store.listers.Endpoint.Store = store.informers.Endpoint.GetStore()
+	store.informers.EndpointSlice = infFactory.Discovery().V1().EndpointSlices().Informer()
+	store.listers.EndpointSlice.Store = store.informers.EndpointSlice.GetStore()
 
 	store.informers.Secret = infFactorySecrets.Core().V1().Secrets().Informer()
 	store.listers.Secret.Store = store.informers.Secret.GetStore()
@@ -673,7 +673,7 @@ func New(
 		},
 	}
 
-	epEventHandler := cache.ResourceEventHandlerFuncs{
+	epsEventHandler := cache.ResourceEventHandlerFuncs{
 		AddFunc: func(obj interface{}) {
 			updateCh.In() <- Event{
 				Type: CreateEvent,
@@ -687,9 +687,9 @@ func New(
 			}
 		},
 		UpdateFunc: func(old, cur interface{}) {
-			oep := old.(*corev1.Endpoints)
-			cep := cur.(*corev1.Endpoints)
-			if !reflect.DeepEqual(cep.Subsets, oep.Subsets) {
+			oeps := old.(*discoveryv1.EndpointSlice)
+			ceps := cur.(*discoveryv1.EndpointSlice)
+			if !reflect.DeepEqual(ceps.Endpoints, oeps.Endpoints) {
 				updateCh.In() <- Event{
 					Type: UpdateEvent,
 					Obj:  cur,
@@ -796,7 +796,7 @@ func New(
 	if !icConfig.IgnoreIngressClass {
 		store.informers.IngressClass.AddEventHandler(ingressClassEventHandler)
 	}
-	store.informers.Endpoint.AddEventHandler(epEventHandler)
+	store.informers.EndpointSlice.AddEventHandler(epsEventHandler)
 	store.informers.Secret.AddEventHandler(secrEventHandler)
 	store.informers.ConfigMap.AddEventHandler(cmEventHandler)
 	store.informers.Service.AddEventHandler(serviceHandler)
@@ -1044,9 +1044,8 @@ func (s *k8sStore) GetConfigMap(key string) (*corev1.ConfigMap, error) {
 	return s.listers.ConfigMap.ByKey(key)
 }
 
-// GetServiceEndpoints returns the Endpoints of a Service matching key.
-func (s *k8sStore) GetServiceEndpoints(key string) (*corev1.Endpoints, error) {
-	return s.listers.Endpoint.ByKey(key)
+func (s *k8sStore) GetServiceEndpointsSlices(key string) ([]*discoveryv1.EndpointSlice, error) {
+	return s.listers.EndpointSlice.MatchByKey(key)
 }
 
 // GetAuthCertificate is used by the auth-tls annotations to get a cert from a secret
diff --git a/pkg/apis/ingress/types.go b/pkg/apis/ingress/types.go
index 8460ee684..7c1c825b7 100644
--- a/pkg/apis/ingress/types.go
+++ b/pkg/apis/ingress/types.go
@@ -159,6 +159,7 @@ type CookieSessionAffinity struct {
 	Locations               map[string][]string `json:"locations,omitempty"`
 	Secure                  bool                `json:"secure,omitempty"`
 	Path                    string              `json:"path,omitempty"`
+	Domain                  string              `json:"domain,omitempty"`
 	SameSite                string              `json:"samesite,omitempty"`
 	ConditionalSameSiteNone bool                `json:"conditional_samesite_none,omitempty"`
 	ChangeOnFailure         bool                `json:"change_on_failure,omitempty"`
diff --git a/pkg/apis/ingress/types_equals.go b/pkg/apis/ingress/types_equals.go
index 1ba80d07e..a954a253b 100644
--- a/pkg/apis/ingress/types_equals.go
+++ b/pkg/apis/ingress/types_equals.go
@@ -173,6 +173,9 @@ func (csa1 *CookieSessionAffinity) Equal(csa2 *CookieSessionAffinity) bool {
 	if csa1.Path != csa2.Path {
 		return false
 	}
+	if csa1.Domain != csa2.Domain {
+		return false
+	}
 	if csa1.Expires != csa2.Expires {
 		return false
 	}
@@ -192,7 +195,7 @@ func (csa1 *CookieSessionAffinity) Equal(csa2 *CookieSessionAffinity) bool {
 	return true
 }
 
-//Equal checks the equality between UpstreamByConfig types
+// Equal checks the equality between UpstreamByConfig types
 func (u1 *UpstreamHashByConfig) Equal(u2 *UpstreamHashByConfig) bool {
 	if u1 == u2 {
 		return true
diff --git a/rootfs/Dockerfile-chroot b/rootfs/Dockerfile-chroot
index 64b222890..4805909d5 100644
--- a/rootfs/Dockerfile-chroot
+++ b/rootfs/Dockerfile-chroot
@@ -84,7 +84,7 @@ RUN bash -xeu -c ' \
   # LD_LIBRARY_PATH does not work so below is needed for  opentelemetry/other modules
   # Put libs of newer modules under `/modules_mount/<other>/lib` and add that path below
   # Could get complicated arch specific paths become a need
-  && echo "/lib:/usr/lib:/usr/local/lib:/modules_mount/otel/lib" > /etc/ld-musl-x86_64.path
+  && echo "/lib:/usr/lib:/usr/local/lib:/modules_mount/etc/nginx/modules/otel" > /chroot/etc/ld-musl-x86_64.path
 
 RUN apk add --no-cache libcap \
   && setcap    cap_sys_chroot,cap_net_bind_service=+ep /nginx-ingress-controller \
@@ -113,6 +113,10 @@ RUN  ln -sf /chroot/etc/nginx /etc/nginx \
   && mknod -m 0666 /chroot/dev/zero c 1 5 \
   && mknod -m 0666 /chroot/dev/tty c 5 0
 
+RUN mkdir -p /chroot/modules_mount \
+  && mkdir -p modules_mount \
+  && ln -s /modules_mount /chroot/modules_mount
+
 USER www-data
 
 EXPOSE 80 443
diff --git a/rootfs/etc/nginx/lua/balancer.lua b/rootfs/etc/nginx/lua/balancer.lua
index 2c6a83ae9..00104c89d 100644
--- a/rootfs/etc/nginx/lua/balancer.lua
+++ b/rootfs/etc/nginx/lua/balancer.lua
@@ -64,7 +64,7 @@ local function get_implementation(backend)
 
   local implementation = IMPLEMENTATIONS[name]
   if not implementation then
-    ngx.log(ngx.WARN, backend["load-balance"], "is not supported, ",
+    ngx.log(ngx.WARN, backend["load-balance"], " is not supported, ",
             "falling back to ", DEFAULT_LB_ALG)
     implementation = IMPLEMENTATIONS[DEFAULT_LB_ALG]
   end
diff --git a/rootfs/etc/nginx/lua/balancer/sticky.lua b/rootfs/etc/nginx/lua/balancer/sticky.lua
index 3440d86bd..9d0a54116 100644
--- a/rootfs/etc/nginx/lua/balancer/sticky.lua
+++ b/rootfs/etc/nginx/lua/balancer/sticky.lua
@@ -110,6 +110,10 @@ function _M.set_cookie(self, value)
     cookie_data.max_age = tonumber(self.cookie_session_affinity.maxage)
   end
 
+  if self.cookie_session_affinity.domain and self.cookie_session_affinity.domain ~= "" then
+    cookie_data.domain = self.cookie_session_affinity.domain
+  end
+
   local ok
   ok, err = cookie:set(cookie_data)
   if not ok then


Test patch:
diff --git a/.github/workflows/perftest.yaml b/.github/workflows/perftest.yaml
index aeda0425e..d498a216a 100644
--- a/.github/workflows/perftest.yaml
+++ b/.github/workflows/perftest.yaml
@@ -18,7 +18,7 @@ jobs:
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v1
+        uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
  
       - name: Install K6
         run: |
diff --git a/internal/ingress/annotations/sessionaffinity/main_test.go b/internal/ingress/annotations/sessionaffinity/main_test.go
index 65d11ac2d..cffe57fec 100644
--- a/internal/ingress/annotations/sessionaffinity/main_test.go
+++ b/internal/ingress/annotations/sessionaffinity/main_test.go
@@ -78,6 +78,7 @@ func TestIngressAffinityCookieConfig(t *testing.T) {
 	data[parser.GetAnnotationWithPrefix(annotationAffinityCookieExpires)] = "4500"
 	data[parser.GetAnnotationWithPrefix(annotationAffinityCookieMaxAge)] = "3000"
 	data[parser.GetAnnotationWithPrefix(annotationAffinityCookiePath)] = "/foo"
+	data[parser.GetAnnotationWithPrefix(annotationAffinityCookieDomain)] = "foo.bar"
 	data[parser.GetAnnotationWithPrefix(annotationAffinityCookieChangeOnFailure)] = "true"
 	data[parser.GetAnnotationWithPrefix(annotationAffinityCookieSecure)] = "true"
 	ing.SetAnnotations(data)
@@ -112,6 +113,10 @@ func TestIngressAffinityCookieConfig(t *testing.T) {
 		t.Errorf("expected /foo as session-cookie-path but returned %v", nginxAffinity.Cookie.Path)
 	}
 
+	if nginxAffinity.Cookie.Domain != "foo.bar" {
+		t.Errorf("expected foo.bar as session-cookie-domain but returned %v", nginxAffinity.Cookie.Domain)
+	}
+
 	if !nginxAffinity.Cookie.ChangeOnFailure {
 		t.Errorf("expected change of failure parameter set to true but returned %v", nginxAffinity.Cookie.ChangeOnFailure)
 	}
diff --git a/internal/ingress/controller/controller_test.go b/internal/ingress/controller/controller_test.go
index f875632e0..da9f10e45 100644
--- a/internal/ingress/controller/controller_test.go
+++ b/internal/ingress/controller/controller_test.go
@@ -34,6 +34,7 @@ import (
 	"github.com/eapache/channels"
 	corev1 "k8s.io/api/core/v1"
 	v1 "k8s.io/api/core/v1"
+	discoveryv1 "k8s.io/api/discovery/v1"
 	networking "k8s.io/api/networking/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/labels"
@@ -85,7 +86,7 @@ func (fakeIngressStore) GetService(key string) (*corev1.Service, error) {
 	return nil, fmt.Errorf("test error")
 }
 
-func (fakeIngressStore) GetServiceEndpoints(key string) (*corev1.Endpoints, error) {
+func (fakeIngressStore) GetServiceEndpointsSlices(key string) ([]*discoveryv1.EndpointSlice, error) {
 	return nil, fmt.Errorf("test error")
 }
 
diff --git a/internal/ingress/controller/endpoints_test.go b/internal/ingress/controller/endpointslices_test.go
similarity index 50%
rename from internal/ingress/controller/endpoints_test.go
rename to internal/ingress/controller/endpointslices_test.go
index f38ffabe7..e404c4949 100644
--- a/internal/ingress/controller/endpoints_test.go
+++ b/internal/ingress/controller/endpointslices_test.go
@@ -21,17 +21,19 @@ import (
 	"testing"
 
 	corev1 "k8s.io/api/core/v1"
+	discoveryv1 "k8s.io/api/discovery/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/intstr"
 	"k8s.io/ingress-nginx/pkg/apis/ingress"
 )
 
-func TestGetEndpoints(t *testing.T) {
+func TestGetEndpointsFromSlices(t *testing.T) {
 	tests := []struct {
 		name   string
 		svc    *corev1.Service
 		port   *corev1.ServicePort
 		proto  corev1.Protocol
-		fn     func(string) (*corev1.Endpoints, error)
+		fn     func(string) ([]*discoveryv1.EndpointSlice, error)
 		result []ingress.Endpoint
 	}{
 		{
@@ -39,7 +41,7 @@ func TestGetEndpoints(t *testing.T) {
 			nil,
 			nil,
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
 				return nil, nil
 			},
 			[]ingress.Endpoint{},
@@ -49,7 +51,7 @@ func TestGetEndpoints(t *testing.T) {
 			&corev1.Service{},
 			nil,
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
 				return nil, nil
 			},
 			[]ingress.Endpoint{},
@@ -59,8 +61,8 @@ func TestGetEndpoints(t *testing.T) {
 			&corev1.Service{},
 			&corev1.ServicePort{Name: "default"},
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
-				return &corev1.Endpoints{}, nil
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
+				return []*discoveryv1.EndpointSlice{}, nil
 			},
 			[]ingress.Endpoint{},
 		},
@@ -73,8 +75,8 @@ func TestGetEndpoints(t *testing.T) {
 			},
 			&corev1.ServicePort{Name: "default"},
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
-				return &corev1.Endpoints{}, nil
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
+				return []*discoveryv1.EndpointSlice{}, nil
 			},
 			[]ingress.Endpoint{},
 		},
@@ -97,8 +99,8 @@ func TestGetEndpoints(t *testing.T) {
 				TargetPort: intstr.FromInt(80),
 			},
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
-				return &corev1.Endpoints{}, nil
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
+				return []*discoveryv1.EndpointSlice{}, nil
 			},
 			[]ingress.Endpoint{},
 		},
@@ -121,8 +123,8 @@ func TestGetEndpoints(t *testing.T) {
 				TargetPort: intstr.FromInt(80),
 			},
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
-				return &corev1.Endpoints{}, nil
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
+				return []*discoveryv1.EndpointSlice{}, nil
 			},
 			[]ingress.Endpoint{},
 		},
@@ -145,8 +147,8 @@ func TestGetEndpoints(t *testing.T) {
 				TargetPort: intstr.FromInt(80),
 			},
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
-				return &corev1.Endpoints{}, nil
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
+				return []*discoveryv1.EndpointSlice{}, nil
 			},
 			[]ingress.Endpoint{
 				{
@@ -174,8 +176,8 @@ func TestGetEndpoints(t *testing.T) {
 				TargetPort: intstr.FromInt(80),
 			},
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
-				return &corev1.Endpoints{}, nil
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
+				return []*discoveryv1.EndpointSlice{}, nil
 			},
 			[]ingress.Endpoint{
 				{
@@ -203,8 +205,8 @@ func TestGetEndpoints(t *testing.T) {
 				TargetPort: intstr.FromInt(80),
 			},
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
-				return &corev1.Endpoints{}, nil
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
+				return []*discoveryv1.EndpointSlice{}, nil
 			},
 			[]ingress.Endpoint{},
 		},
@@ -227,7 +229,7 @@ func TestGetEndpoints(t *testing.T) {
 				TargetPort: intstr.FromInt(80),
 			},
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
 				return nil, fmt.Errorf("unexpected error")
 			},
 			[]ingress.Endpoint{},
@@ -251,25 +253,27 @@ func TestGetEndpoints(t *testing.T) {
 				TargetPort: intstr.FromInt(80),
 			},
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
-				nodeName := "dummy"
-				return &corev1.Endpoints{
-					Subsets: []corev1.EndpointSubset{
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
+				return []*discoveryv1.EndpointSlice{{
+					ObjectMeta: metav1.ObjectMeta{
+						Labels: map[string]string{discoveryv1.LabelServiceName: "default"},
+					},
+					Endpoints: []discoveryv1.Endpoint{
 						{
-							Addresses: []corev1.EndpointAddress{
-								{
-									IP:       "1.1.1.1",
-									NodeName: &nodeName,
-								},
-							},
-							Ports: []corev1.EndpointPort{
-								{
-									Protocol: corev1.ProtocolUDP,
-								},
+							Addresses: []string{"1.1.1.1"},
+							Conditions: discoveryv1.EndpointConditions{
+								Ready: &[]bool{true}[0],
 							},
 						},
 					},
-				}, nil
+					Ports: []discoveryv1.EndpointPort{
+						{
+							Name:     &[]string{""}[0],
+							Port:     &[]int32{80}[0],
+							Protocol: &[]corev1.Protocol{corev1.ProtocolUDP}[0],
+						},
+					},
+				}}, nil
 			},
 			[]ingress.Endpoint{},
 		},
@@ -292,30 +296,32 @@ func TestGetEndpoints(t *testing.T) {
 				TargetPort: intstr.FromInt(80),
 			},
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
-				nodeName := "dummy"
-				return &corev1.Endpoints{
-					Subsets: []corev1.EndpointSubset{
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
+				return []*discoveryv1.EndpointSlice{{
+					ObjectMeta: metav1.ObjectMeta{
+						Labels: map[string]string{discoveryv1.LabelServiceName: "default"},
+					},
+					Endpoints: []discoveryv1.Endpoint{
 						{
-							NotReadyAddresses: []corev1.EndpointAddress{
-								{
-									IP:       "1.1.1.1",
-									NodeName: &nodeName,
-								},
-							},
-							Ports: []corev1.EndpointPort{
-								{
-									Protocol: corev1.ProtocolUDP,
-								},
+							Addresses: []string{"1.1.1.1"},
+							Conditions: discoveryv1.EndpointConditions{
+								Ready: &[]bool{false}[0],
 							},
 						},
 					},
-				}, nil
+					Ports: []discoveryv1.EndpointPort{
+						{
+							Name:     &[]string{""}[0],
+							Port:     &[]int32{80}[0],
+							Protocol: &[]corev1.Protocol{corev1.ProtocolTCP}[0],
+						},
+					},
+				}}, nil
 			},
 			[]ingress.Endpoint{},
 		},
 		{
-			"should return no endpoint when the name of the port name do not match any port in the endpoint Subsets and TargetPort is string",
+			"should return no endpoint when the name of the port name do not match any port in the endpointPort and TargetPort is string",
 			&corev1.Service{
 				Spec: corev1.ServiceSpec{
 					Type:      corev1.ServiceTypeClusterIP,
@@ -333,32 +339,32 @@ func TestGetEndpoints(t *testing.T) {
 				TargetPort: intstr.FromString("port-1"),
 			},
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
-				nodeName := "dummy"
-				return &corev1.Endpoints{
-					Subsets: []corev1.EndpointSubset{
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
+				return []*discoveryv1.EndpointSlice{{
+					ObjectMeta: metav1.ObjectMeta{
+						Labels: map[string]string{discoveryv1.LabelServiceName: "default"},
+					},
+					Endpoints: []discoveryv1.Endpoint{
 						{
-							Addresses: []corev1.EndpointAddress{
-								{
-									IP:       "1.1.1.1",
-									NodeName: &nodeName,
-								},
-							},
-							Ports: []corev1.EndpointPort{
-								{
-									Protocol: corev1.ProtocolTCP,
-									Port:     int32(80),
-									Name:     "another-name",
-								},
+							Addresses: []string{"1.1.1.1"},
+							Conditions: discoveryv1.EndpointConditions{
+								Ready: &[]bool{true}[0],
 							},
 						},
 					},
-				}, nil
+					Ports: []discoveryv1.EndpointPort{
+						{
+							Protocol: &[]corev1.Protocol{corev1.ProtocolTCP}[0],
+							Port:     &[]int32{80}[0],
+							Name:     &[]string{"another-name"}[0],
+						},
+					},
+				}}, nil
 			},
 			[]ingress.Endpoint{},
 		},
 		{
-			"should return one endpoint when the name of the port name do not match any port in the endpoint Subsets and TargetPort is int",
+			"should return one endpoint when the name of the port name do not match any port in the endpointPort and TargetPort is int",
 			&corev1.Service{
 				Spec: corev1.ServiceSpec{
 					Type:      corev1.ServiceTypeClusterIP,
@@ -376,27 +382,27 @@ func TestGetEndpoints(t *testing.T) {
 				TargetPort: intstr.FromInt(80),
 			},
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
-				nodeName := "dummy"
-				return &corev1.Endpoints{
-					Subsets: []corev1.EndpointSubset{
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
+				return []*discoveryv1.EndpointSlice{{
+					ObjectMeta: metav1.ObjectMeta{
+						Labels: map[string]string{discoveryv1.LabelServiceName: "default"},
+					},
+					Endpoints: []discoveryv1.Endpoint{
 						{
-							Addresses: []corev1.EndpointAddress{
-								{
-									IP:       "1.1.1.1",
-									NodeName: &nodeName,
-								},
-							},
-							Ports: []corev1.EndpointPort{
-								{
-									Protocol: corev1.ProtocolTCP,
-									Port:     int32(80),
-									Name:     "another-name",
-								},
+							Addresses: []string{"1.1.1.1"},
+							Conditions: discoveryv1.EndpointConditions{
+								Ready: &[]bool{true}[0],
 							},
 						},
 					},
-				}, nil
+					Ports: []discoveryv1.EndpointPort{
+						{
+							Protocol: &[]corev1.Protocol{corev1.ProtocolTCP}[0],
+							Port:     &[]int32{80}[0],
+							Name:     &[]string{"another-name"}[0],
+						},
+					},
+				}}, nil
 			},
 			[]ingress.Endpoint{
 				{
@@ -406,7 +412,7 @@ func TestGetEndpoints(t *testing.T) {
 			},
 		},
 		{
-			"should return one endpoint when the name of the port name match a port in the endpoint Subsets",
+			"should return one endpoint when the name of the port name match a port in the endpointPort",
 			&corev1.Service{
 				Spec: corev1.ServiceSpec{
 					Type:      corev1.ServiceTypeClusterIP,
@@ -424,25 +430,95 @@ func TestGetEndpoints(t *testing.T) {
 				TargetPort: intstr.FromInt(80),
 			},
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
-				nodeName := "dummy"
-				return &corev1.Endpoints{
-					Subsets: []corev1.EndpointSubset{
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
+				return []*discoveryv1.EndpointSlice{{
+					ObjectMeta: metav1.ObjectMeta{
+						Labels: map[string]string{discoveryv1.LabelServiceName: "default"},
+					},
+					Endpoints: []discoveryv1.Endpoint{
+						{
+							Addresses: []string{"1.1.1.1"},
+							Conditions: discoveryv1.EndpointConditions{
+								Ready: &[]bool{true}[0],
+							},
+						},
+					},
+					Ports: []discoveryv1.EndpointPort{
 						{
-							Addresses: []corev1.EndpointAddress{
-								{
-									IP:       "1.1.1.1",
-									NodeName: &nodeName,
+							Protocol: &[]corev1.Protocol{corev1.ProtocolTCP}[0],
+							Port:     &[]int32{80}[0],
+							Name:     &[]string{"default"}[0],
+						},
+					},
+				}}, nil
+			},
+			[]ingress.Endpoint{
+				{
+					Address: "1.1.1.1",
+					Port:    "80",
+				},
+			},
+		},
+		{
+			"should return two endpoints when the name of the port name match a port in the endpointPort",
+			&corev1.Service{
+				Spec: corev1.ServiceSpec{
+					Type:      corev1.ServiceTypeClusterIP,
+					ClusterIP: "1.1.1.1",
+					Ports: []corev1.ServicePort{
+						{
+							Name:       "default",
+							TargetPort: intstr.FromInt(80),
+						},
+					},
+				},
+			},
+			&corev1.ServicePort{
+				Name:       "default",
+				TargetPort: intstr.FromString("port-1"),
+			},
+			corev1.ProtocolTCP,
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
+				return []*discoveryv1.EndpointSlice{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Labels: map[string]string{discoveryv1.LabelServiceName: "default"},
+						},
+						Endpoints: []discoveryv1.Endpoint{
+							{
+								Addresses: []string{"1.1.1.1"},
+								Conditions: discoveryv1.EndpointConditions{
+									Ready: &[]bool{true}[0],
 								},
 							},
-							Ports: []corev1.EndpointPort{
-								{
-									Protocol: corev1.ProtocolTCP,
-									Port:     int32(80),
-									Name:     "default",
+						},
+						Ports: []discoveryv1.EndpointPort{
+							{
+								Protocol: &[]corev1.Protocol{corev1.ProtocolTCP}[0],
+								Port:     &[]int32{80}[0],
+								Name:     &[]string{"default"}[0],
+							},
+						},
+					},
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Labels: map[string]string{discoveryv1.LabelServiceName: "default"},
+						},
+						Endpoints: []discoveryv1.Endpoint{
+							{
+								Addresses: []string{"2.2.2.2"},
+								Conditions: discoveryv1.EndpointConditions{
+									Ready: &[]bool{true}[0],
 								},
 							},
 						},
+						Ports: []discoveryv1.EndpointPort{
+							{
+								Protocol: &[]corev1.Protocol{corev1.ProtocolTCP}[0],
+								Port:     &[]int32{80}[0],
+								Name:     &[]string{"default"}[0],
+							},
+						},
 					},
 				}, nil
 			},
@@ -451,10 +527,14 @@ func TestGetEndpoints(t *testing.T) {
 					Address: "1.1.1.1",
 					Port:    "80",
 				},
+				{
+					Address: "2.2.2.2",
+					Port:    "80",
+				},
 			},
 		},
 		{
-			"should return one endpoint when the name of the port name match more than one port in the endpoint Subsets",
+			"should return one endpoints when the name of the port name match a port in the endpointPort",
 			&corev1.Service{
 				Spec: corev1.ServiceSpec{
 					Type:      corev1.ServiceTypeClusterIP,
@@ -462,7 +542,7 @@ func TestGetEndpoints(t *testing.T) {
 					Ports: []corev1.ServicePort{
 						{
 							Name:       "default",
-							TargetPort: intstr.FromString("port-1"),
+							TargetPort: intstr.FromInt(80),
 						},
 					},
 				},
@@ -472,30 +552,47 @@ func TestGetEndpoints(t *testing.T) {
 				TargetPort: intstr.FromString("port-1"),
 			},
 			corev1.ProtocolTCP,
-			func(string) (*corev1.Endpoints, error) {
-				nodeName := "dummy"
-				return &corev1.Endpoints{
-					Subsets: []corev1.EndpointSubset{
-						{
-							Addresses: []corev1.EndpointAddress{
-								{
-									IP:       "1.1.1.1",
-									NodeName: &nodeName,
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
+				return []*discoveryv1.EndpointSlice{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Labels: map[string]string{discoveryv1.LabelServiceName: "default"},
+						},
+						Endpoints: []discoveryv1.Endpoint{
+							{
+								Addresses: []string{"1.1.1.1"},
+								Conditions: discoveryv1.EndpointConditions{
+									Ready: &[]bool{true}[0],
 								},
 							},
-							Ports: []corev1.EndpointPort{
-								{
-									Name:     "port-1",
-									Protocol: corev1.ProtocolTCP,
-									Port:     80,
-								},
-								{
-									Name:     "port-1",
-									Protocol: corev1.ProtocolTCP,
-									Port:     80,
+						},
+						Ports: []discoveryv1.EndpointPort{
+							{
+								Protocol: &[]corev1.Protocol{corev1.ProtocolTCP}[0],
+								Port:     &[]int32{80}[0],
+								Name:     &[]string{"port-1"}[0],
+							},
+						},
+					},
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Labels: map[string]string{discoveryv1.LabelServiceName: "default"},
+						},
+						Endpoints: []discoveryv1.Endpoint{
+							{
+								Addresses: []string{"2.2.2.2"},
+								Conditions: discoveryv1.EndpointConditions{
+									Ready: &[]bool{true}[0],
 								},
 							},
 						},
+						Ports: []discoveryv1.EndpointPort{
+							{
+								Protocol: &[]corev1.Protocol{corev1.ProtocolTCP}[0],
+								Port:     &[]int32{80}[0],
+								Name:     &[]string{"another-name"}[0],
+							},
+						},
 					},
 				}, nil
 			},
@@ -506,11 +603,64 @@ func TestGetEndpoints(t *testing.T) {
 				},
 			},
 		},
+		{
+			"should return one endpoint when the name of the port name match more than one port in the endpointPort",
+			&corev1.Service{
+				Spec: corev1.ServiceSpec{
+					Type:      corev1.ServiceTypeClusterIP,
+					ClusterIP: "1.1.1.1",
+					Ports: []corev1.ServicePort{
+						{
+							Name:       "default",
+							TargetPort: intstr.FromString("port-1"),
+						},
+					},
+				},
+			},
+			&corev1.ServicePort{
+				Name:       "port-1",
+				TargetPort: intstr.FromString("port-1"),
+			},
+			corev1.ProtocolTCP,
+			func(string) ([]*discoveryv1.EndpointSlice, error) {
+				return []*discoveryv1.EndpointSlice{{
+					ObjectMeta: metav1.ObjectMeta{
+						Labels: map[string]string{discoveryv1.LabelServiceName: "default"},
+					},
+					Endpoints: []discoveryv1.Endpoint{
+						{
+							Addresses: []string{"1.1.1.1"},
+							Conditions: discoveryv1.EndpointConditions{
+								Ready: &[]bool{true}[0],
+							},
+						},
+					},
+					Ports: []discoveryv1.EndpointPort{
+						{
+							Protocol: &[]corev1.Protocol{corev1.ProtocolTCP}[0],
+							Port:     &[]int32{80}[0],
+							Name:     &[]string{"port-1"}[0],
+						},
+						{
+							Protocol: &[]corev1.Protocol{corev1.ProtocolTCP}[0],
+							Port:     &[]int32{80}[0],
+							Name:     &[]string{"port-2"}[0],
+						},
+					},
+				}}, nil
+			},
+			[]ingress.Endpoint{
+				{
+					Address: "1.1.1.1",
+					Port:    "80",
+				},
+			},
+		},
 	}
 
 	for _, testCase := range tests {
 		t.Run(testCase.name, func(t *testing.T) {
-			result := getEndpoints(testCase.svc, testCase.port, testCase.proto, testCase.fn)
+			result := getEndpointsFromSlices(testCase.svc, testCase.port, testCase.proto, testCase.fn)
 			if len(testCase.result) != len(result) {
 				t.Errorf("Expected %d Endpoints but got %d", len(testCase.result), len(result))
 			}
diff --git a/internal/ingress/controller/store/endpoint_test.go b/internal/ingress/controller/store/endpoint_test.go
deleted file mode 100644
index 6c8ae40e2..000000000
--- a/internal/ingress/controller/store/endpoint_test.go
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-Copyright 2017 The Kubernetes Authors.
-
-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 store
-
-import (
-	apiv1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/client-go/tools/cache"
-	"testing"
-)
-
-func newEndpointLister(t *testing.T) *EndpointLister {
-	t.Helper()
-
-	return &EndpointLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}
-}
-
-func TestEndpointLister(t *testing.T) {
-	t.Run("the key does not exist", func(t *testing.T) {
-		el := newEndpointLister(t)
-
-		key := "namespace/endpoint"
-		_, err := el.ByKey(key)
-
-		if err == nil {
-			t.Error("expected an error but nothing has been returned")
-		}
-
-		if _, ok := err.(NotExistsError); !ok {
-			t.Errorf("expected NotExistsError, got %v", err)
-		}
-	})
-
-	t.Run("the key exists", func(t *testing.T) {
-		el := newEndpointLister(t)
-
-		key := "namespace/endpoint"
-		endpoint := &apiv1.Endpoints{ObjectMeta: metav1.ObjectMeta{Namespace: "namespace", Name: "endpoint"}}
-
-		el.Add(endpoint)
-
-		e, err := el.ByKey(key)
-
-		if err != nil {
-			t.Errorf("unexpeted error %v", err)
-		}
-
-		if e != endpoint {
-			t.Errorf("expected %v, error, got %v", e, endpoint)
-		}
-	})
-}
diff --git a/internal/ingress/controller/store/endpointslice_test.go b/internal/ingress/controller/store/endpointslice_test.go
new file mode 100644
index 000000000..fdc51c0e4
--- /dev/null
+++ b/internal/ingress/controller/store/endpointslice_test.go
@@ -0,0 +1,94 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+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 store
+
+import (
+	"testing"
+
+	discoveryv1 "k8s.io/api/discovery/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/client-go/tools/cache"
+)
+
+func newEndpointSliceLister(t *testing.T) *EndpointSliceLister {
+	t.Helper()
+
+	return &EndpointSliceLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}
+}
+
+func TestEndpointSliceLister(t *testing.T) {
+	t.Run("the key does not exist", func(t *testing.T) {
+		el := newEndpointSliceLister(t)
+
+		key := "namespace/svcname"
+		_, err := el.MatchByKey(key)
+
+		if err == nil {
+			t.Error("expected an error but nothing has been returned")
+		}
+
+		if _, ok := err.(NotExistsError); !ok {
+			t.Errorf("expected NotExistsError, got %v", err)
+		}
+	})
+	t.Run("the key exists", func(t *testing.T) {
+		el := newEndpointSliceLister(t)
+
+		key := "namespace/svcname"
+		endpointSlice := &discoveryv1.EndpointSlice{
+			ObjectMeta: metav1.ObjectMeta{
+				Namespace: "namespace",
+				Name:      "anothername-foo",
+				Labels: map[string]string{
+					discoveryv1.LabelServiceName: "svcname",
+				},
+			},
+		}
+		el.Add(endpointSlice)
+		endpointSlice = &discoveryv1.EndpointSlice{
+			ObjectMeta: metav1.ObjectMeta{
+				Namespace: "namespace",
+				Name:      "svcname-bar",
+				Labels: map[string]string{
+					discoveryv1.LabelServiceName: "othersvc",
+				},
+			},
+		}
+		el.Add(endpointSlice)
+		endpointSlice = &discoveryv1.EndpointSlice{
+			ObjectMeta: metav1.ObjectMeta{
+				Namespace: "namespace",
+				Name:      "svcname-buz",
+				Labels: map[string]string{
+					discoveryv1.LabelServiceName: "svcname",
+				},
+			},
+		}
+		el.Add(endpointSlice)
+		eps, err := el.MatchByKey(key)
+
+		if err != nil {
+			t.Errorf("unexpeted error %v", err)
+		}
+		if err == nil && len(eps) != 1 {
+			t.Errorf("expected one slice %v, error, got %d slices", endpointSlice, len(eps))
+		}
+		if len(eps) > 0 && eps[0].GetName() != endpointSlice.GetName() {
+			t.Errorf("expected %v, error, got %v", endpointSlice.GetName(), eps[0].GetName())
+		}
+	})
+}
diff --git a/test/e2e/annotations/affinity.go b/test/e2e/annotations/affinity.go
index 6a0a0bf48..3e1e9e969 100644
--- a/test/e2e/annotations/affinity.go
+++ b/test/e2e/annotations/affinity.go
@@ -222,6 +222,51 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() {
 			Header("Set-Cookie").Contains(fmt.Sprintf("Expires=%s", expected)).Contains("Max-Age=259200")
 	})
 
+	ginkgo.It("should set cookie with domain", func() {
+		host := "cookiedomain.foo.com"
+		annotations := make(map[string]string)
+		annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie"
+		annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "DomainCookie"
+		annotations["nginx.ingress.kubernetes.io/session-cookie-domain"] = "foo.bar"
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		f.WaitForNginxServer(host,
+			func(server string) bool {
+				return strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
+			})
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			Expect().
+			Status(http.StatusOK).
+			Header("Set-Cookie").Contains("Domain=foo.bar")
+	})
+
+	ginkgo.It("should not set cookie without domain annotation", func() {
+		host := "cookienodomain.foo.com"
+		annotations := make(map[string]string)
+		annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie"
+		annotations["nginx.ingress.kubernetes.io/session-cookie-name"] = "NoDomainCookie"
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		f.WaitForNginxServer(host,
+			func(server string) bool {
+				return strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
+			})
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			Expect().
+			Status(http.StatusOK).
+			Header("Set-Cookie").NotContains("; Domain")
+	})
+
 	ginkgo.It("should work with use-regex annotation and session-cookie-path", func() {
 		host := "useregex.foo.com"
 		annotations := make(map[string]string)