Repository: kubernetes/ingress-nginx
Pull request #5887 (2020-07-13 17:46:16)

Add force-enable-realip-module
<!--- Provide a general summary of your changes in the Title above --->
<!--- Please don't @-mention people in PR or commit messages (do so in an additional comment). --->

## What this PR does / why we need it:
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Which issue/s this PR fixes
<!--
(optional, in `fixes #<issue number>` format, will close that issue when PR gets merged):

fixes #
-->

## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I've read the [CONTRIBUTION](https://github.com/kubernetes/ingress-nginx/blob/master/CONTRIBUTING.md) guide
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.


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

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

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

---

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

<!-- need_sender_cla -->

<details>

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

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

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

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=kubernetes%2Fingress-nginx).

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

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

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

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

- **[OWNERS](https://github.com/kubernetes/ingress-nginx/blob/master/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":["bowei"]} -->
ElvinEfendi: /ok-to-test
k8s-ci-robot: @dschwar: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-ingress-nginx-gofmt | 6ea6599a9725c91633bc6d2df43a62871c75b6cb | [link](https://prow.k8s.io/view/gcs/kubernetes-jenkins/pr-logs/pull/ingress-nginx/5887/pull-ingress-nginx-gofmt/1282733400628662281) | `/test pull-ingress-nginx-gofmt`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes&repo=ingress-nginx&pr=5887). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3dschwar). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes/ingress-nginx/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
dschwar: /check-cla
k8s-ci-robot: @dschwar: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-ingress-nginx-gofmt | 6ea6599a9725c91633bc6d2df43a62871c75b6cb | [link](https://prow.k8s.io/view/gcs/kubernetes-jenkins/pr-logs/pull/ingress-nginx/5887/pull-ingress-nginx-gofmt/1282733400628662281) | `/test pull-ingress-nginx-gofmt`
pull-ingress-nginx-test | 6ea6599a9725c91633bc6d2df43a62871c75b6cb | [link](https://prow.k8s.io/view/gcs/kubernetes-jenkins/pr-logs/pull/ingress-nginx/5887/pull-ingress-nginx-test/1282733400628662280) | `/test pull-ingress-nginx-test`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes&repo=ingress-nginx&pr=5887). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3dschwar). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes/ingress-nginx/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
ElvinEfendi: @aledbf what do you think of the naming? The idea here is to be able to control enablement of Real IP module independent UseForwardedHeaders and Proxy Protocol.
ElvinEfendi: @dschwar can you also add another test case that tests this new setting in combination with UseForwardedHeaders setting?
ElvinEfendi: @dschwar we will also need a docs adjustment once we agree on the change.

Alternative to this would be relying on https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#proxy-real-ip-cidr to toggle the configuration of Real IP module. But that would be implicit and would introduce backward incompatibility.
ElvinEfendi: Given we have `proxy-real-ip-cidr`, let's keep the new name as close to it as possible, so let's call it `enable-real-ip` instead.

Also @dschwar I think it would be a lot cleaner to implement @XaF's idea about setting the value of `EnableRealIp` attribute base on the values of `UseForwardedHeaders` and `UseProxyProtocol`. Then in the template we can simply do if `$cfg.EnableRealIp`.

Once these two things are addressed we can merge this PR (you will also need to squash the commits).
dschwar: >  Also @dschwar I think it would be a lot cleaner to implement @XaF's idea about setting the value of EnableRealIp attribute base on the values of UseForwardedHeaders and UseProxyProtocol. Then in the template we can simply do if $cfg.EnableRealIp.

I played around with this and I feel like it introduces a lot of complexity and fragility to determine if we should enable realip. I'd prefer not to go with this.

We'd end up with code like

```
	if val, ok := conf[useForwardedHeaders]; ok {
		b, _ := strconv.ParseBool(val)
		if b {
			shouldEnableRealIp = true
		}
	}

	if val, ok := conf[useProxyProtocol]; ok {
		b, _ := strconv.ParseBool(val)
		if b {
			shouldEnableRealIp = true
		}
	}

	if val, ok := conf[enableRealIp]; ok {
		b, _ := strconv.ParseBool(val)
		if shouldEnableRealIp || b {
			delete(conf, enableRealIp)
		}
		if b {
			shouldEnableRealIp = true
		}
	}
```

which isn't cleaner IMO.

Patch:
diff --git a/build/dev-env.sh b/build/dev-env.sh
index 45c6d9733..51a3dd1dc 100755
--- a/build/dev-env.sh
+++ b/build/dev-env.sh
@@ -102,7 +102,6 @@ controller:
   image:
     repository: ${REGISTRY}/controller
     tag: ${TAG}
-    digest:
   config:
     worker-processes: "1"
   podLabels:
diff --git a/build/run-in-docker.sh b/build/run-in-docker.sh
index 4e49a77fb..049be7fb7 100755
--- a/build/run-in-docker.sh
+++ b/build/run-in-docker.sh
@@ -34,7 +34,7 @@ function cleanup {
 }
 trap cleanup EXIT
 
-E2E_IMAGE=${E2E_IMAGE:-us.gcr.io/k8s-artifacts-prod/ingress-nginx/e2e-test-runner@sha256:bf31d6f13984ef0818312f9247780e234a6d50322e362217dabe4b3299f202ef}
+E2E_IMAGE=${E2E_IMAGE:-us.gcr.io/k8s-artifacts-prod/ingress-nginx/e2e-test-runner@sha256:7dece116c5cc1a51496095de97cc9249a07e9f1a4ed0dc378630074c6857ff46}
 
 DOCKER_OPTS=${DOCKER_OPTS:-}
 DOCKER_IN_DOCKER_ENABLED=${DOCKER_IN_DOCKER_ENABLED:-}
diff --git a/docs/user-guide/nginx-configuration/configmap.md b/docs/user-guide/nginx-configuration/configmap.md
index eec4ab782..2cec835f1 100755
--- a/docs/user-guide/nginx-configuration/configmap.md
+++ b/docs/user-guide/nginx-configuration/configmap.md
@@ -115,6 +115,7 @@ The following table shows a configuration option's name, type, and the default v
 |[proxy-stream-responses](#proxy-stream-responses)|int|1|
 |[bind-address](#bind-address)|[]string|""|
 |[use-forwarded-headers](#use-forwarded-headers)|bool|"false"|
+|[enable-real-ip](#enable-real-ip)|bool|"false"|
 |[forwarded-for-header](#forwarded-for-header)|string|"X-Forwarded-For"|
 |[compute-full-forwarded-for](#compute-full-forwarded-for)|bool|"false"|
 |[proxy-add-original-uri-header](#proxy-add-original-uri-header)|bool|"false"|
@@ -745,6 +746,10 @@ If true, NGINX passes the incoming `X-Forwarded-*` headers to upstreams. Use thi
 
 If false, NGINX ignores incoming `X-Forwarded-*` headers, filling them with the request information it sees. Use this option if NGINX is exposed directly to the internet, or it's behind a L3/packet-based load balancer that doesn't alter the source IP in the packets.
 
+## enable-real-ip
+
+`enable-real-ip` enables the configuration of [http://nginx.org/en/docs/http/ngx_http_realip_module.html](http://nginx.org/en/docs/http/ngx_http_realip_module.html). Specific attributes of the module can be configured further by using `forwarded-for-header` and `proxy-real-ip-cidr` settings.
+
 ## forwarded-for-header
 
 Sets the header field for identifying the originating IP address of a client. _**default:**_ X-Forwarded-For
diff --git a/internal/ingress/controller/config/config.go b/internal/ingress/controller/config/config.go
index e089a1191..64b98ef09 100644
--- a/internal/ingress/controller/config/config.go
+++ b/internal/ingress/controller/config/config.go
@@ -479,6 +479,9 @@ type Configuration struct {
 	// Sets whether to use incoming X-Forwarded headers.
 	UseForwardedHeaders bool `json:"use-forwarded-headers"`
 
+	// Sets whether to enable the real ip module
+	EnableRealIp bool `json:"enable-real-ip"`
+
 	// Sets the header field for identifying the originating IP address of a client
 	// Default is X-Forwarded-For
 	ForwardedForHeader string `json:"forwarded-for-header,omitempty"`
@@ -712,6 +715,7 @@ func NewDefault() Configuration {
 		EnableUnderscoresInHeaders:       false,
 		ErrorLogLevel:                    errorLevel,
 		UseForwardedHeaders:              false,
+		EnableRealIp:                     false,
 		ForwardedForHeader:               "X-Forwarded-For",
 		ComputeFullForwardedFor:          false,
 		ProxyAddOriginalURIHeader:        false,
diff --git a/internal/ingress/controller/template/template.go b/internal/ingress/controller/template/template.go
index 3666defbf..a0746be8a 100644
--- a/internal/ingress/controller/template/template.go
+++ b/internal/ingress/controller/template/template.go
@@ -171,7 +171,6 @@ var (
 		"buildOpentracing":                   buildOpentracing,
 		"proxySetHeader":                     proxySetHeader,
 		"buildInfluxDB":                      buildInfluxDB,
-		"enforceRegexModifier":               enforceRegexModifier,
 		"buildCustomErrorDeps":               buildCustomErrorDeps,
 		"buildCustomErrorLocationsPerServer": buildCustomErrorLocationsPerServer,
 		"shouldLoadModSecurityModule":        shouldLoadModSecurityModule,
@@ -373,26 +372,9 @@ func needsRewrite(location *ingress.Location) bool {
 	return false
 }
 
-// enforceRegexModifier checks if the "rewrite-target" or "use-regex" annotation
-// is used on any location path within a server
-func enforceRegexModifier(input interface{}) bool {
-	locations, ok := input.([]*ingress.Location)
-	if !ok {
-		klog.Errorf("expected an '[]*ingress.Location' type but %T was returned", input)
-		return false
-	}
-
-	for _, location := range locations {
-		if needsRewrite(location) || location.Rewrite.UseRegex {
-			return true
-		}
-	}
-	return false
-}
-
 // buildLocation produces the location string, if the ingress has redirects
 // (specified through the nginx.ingress.kubernetes.io/rewrite-target annotation)
-func buildLocation(input interface{}, enforceRegex bool) string {
+func buildLocation(input interface{}) string {
 	location, ok := input.(*ingress.Location)
 	if !ok {
 		klog.Errorf("expected an '*ingress.Location' type but %T was returned", input)
@@ -400,7 +382,7 @@ func buildLocation(input interface{}, enforceRegex bool) string {
 	}
 
 	path := location.Path
-	if enforceRegex {
+	if location.Rewrite.UseRegex {
 		return fmt.Sprintf(`~* "^%s"`, path)
 	}
 
diff --git a/rootfs/etc/nginx/template/nginx.tmpl b/rootfs/etc/nginx/template/nginx.tmpl
index 834a331ae..21b6ff24c 100755
--- a/rootfs/etc/nginx/template/nginx.tmpl
+++ b/rootfs/etc/nginx/template/nginx.tmpl
@@ -119,7 +119,7 @@ http {
 
     {{/* Enable the real_ip module only if we use either X-Forwarded headers or Proxy Protocol. */}}
     {{/* we use the value of the real IP for the geo_ip module */}}
-    {{ if or $cfg.UseForwardedHeaders $cfg.UseProxyProtocol }}
+    {{ if or (or $cfg.UseForwardedHeaders $cfg.UseProxyProtocol) $cfg.EnableRealIp }}
     {{ if $cfg.UseProxyProtocol }}
     real_ip_header      proxy_protocol;
     {{ else }}
@@ -903,9 +903,8 @@ stream {
 
         {{ buildMirrorLocations $server.Locations }}
 
-        {{ $enforceRegex := enforceRegexModifier $server.Locations }}
         {{ range $location := $server.Locations }}
-        {{ $path := buildLocation $location $enforceRegex }}
+        {{ $path := buildLocation $location }}
         {{ $proxySetHeader := proxySetHeader $location }}
         {{ $authPath := buildAuthLocation $location $all.Cfg.GlobalExternalAuth.URL }}
         {{ $applyGlobalAuth := shouldApplyGlobalAuth $location $all.Cfg.GlobalExternalAuth.URL }}


Test patch:
diff --git a/images/test-runner/Makefile b/images/test-runner/Makefile
index e00fb6957..ab59b32ed 100644
--- a/images/test-runner/Makefile
+++ b/images/test-runner/Makefile
@@ -37,7 +37,7 @@ build: ensure-buildx
 		--progress=$(PROGRESS) \
 		--pull \
 		--build-arg BASE_IMAGE=us.gcr.io/k8s-artifacts-prod/ingress-nginx/nginx:v20200708-g00f4a215d@sha256:b0727cebafc35f5ab50f3fa0be5fda7f79937f1683f525f40982e75b882195d8 \
-		--build-arg GOLANG_VERSION=1.14.5 \
+		--build-arg GOLANG_VERSION=1.14.4 \
 		--build-arg ETCD_VERSION=3.4.3-0 \
 		--build-arg K8S_RELEASE=v1.15.7 \
 		--build-arg RESTY_CLI_VERSION=0.25 \
diff --git a/internal/ingress/controller/template/template_test.go b/internal/ingress/controller/template/template_test.go
index 2f2dd670a..3243dbda3 100644
--- a/internal/ingress/controller/template/template_test.go
+++ b/internal/ingress/controller/template/template_test.go
@@ -64,7 +64,7 @@ var (
 		Sticky           bool
 		XForwardedPrefix string
 		SecureBackend    bool
-		enforceRegex     bool
+		UseRegex         bool
 	}{
 		"when secure backend enabled": {
 			"/",
@@ -275,7 +275,7 @@ func TestQuote(t *testing.T) {
 func TestBuildLocation(t *testing.T) {
 	invalidType := &ingress.Ingress{}
 	expected := "/"
-	actual := buildLocation(invalidType, true)
+	actual := buildLocation(invalidType)
 
 	if !reflect.DeepEqual(expected, actual) {
 		t.Errorf("Expected '%v' but returned '%v'", expected, actual)
@@ -284,10 +284,10 @@ func TestBuildLocation(t *testing.T) {
 	for k, tc := range tmplFuncTestcases {
 		loc := &ingress.Location{
 			Path:    tc.Path,
-			Rewrite: rewrite.Config{Target: tc.Target},
+			Rewrite: rewrite.Config{Target: tc.Target, UseRegex: tc.UseRegex},
 		}
 
-		newLoc := buildLocation(loc, tc.enforceRegex)
+		newLoc := buildLocation(loc)
 		if tc.Location != newLoc {
 			t.Errorf("%s: expected '%v' but returned %v", k, tc.Location, newLoc)
 		}
@@ -1195,32 +1195,6 @@ func TestBuildOpenTracing(t *testing.T) {
 
 }
 
-func TestEnforceRegexModifier(t *testing.T) {
-	invalidType := &ingress.Ingress{}
-	expected := false
-	actual := enforceRegexModifier(invalidType)
-
-	if expected != actual {
-		t.Errorf("Expected '%v' but returned '%v'", expected, actual)
-	}
-
-	locs := []*ingress.Location{
-		{
-			Rewrite: rewrite.Config{
-				Target:   "/alright",
-				UseRegex: true,
-			},
-			Path: "/ok",
-		},
-	}
-	expected = true
-	actual = enforceRegexModifier(locs)
-
-	if expected != actual {
-		t.Errorf("Expected '%v' but returned '%v'", expected, actual)
-	}
-}
-
 func TestShouldLoadModSecurityModule(t *testing.T) {
 	// ### Invalid argument type tests ###
 	// The first tests should return false.
diff --git a/test/e2e-image/Dockerfile b/test/e2e-image/Dockerfile
index ae7cff0b2..86ecad6f2 100644
--- a/test/e2e-image/Dockerfile
+++ b/test/e2e-image/Dockerfile
@@ -1,4 +1,4 @@
-FROM us.gcr.io/k8s-artifacts-prod/ingress-nginx/e2e-test-runner@sha256:bf31d6f13984ef0818312f9247780e234a6d50322e362217dabe4b3299f202ef AS BASE
+FROM us.gcr.io/k8s-artifacts-prod/ingress-nginx/e2e-test-runner@sha256:7dece116c5cc1a51496095de97cc9249a07e9f1a4ed0dc378630074c6857ff46 AS BASE
 
 FROM alpine:3.12
 
diff --git a/test/e2e/annotations/rewrite.go b/test/e2e/annotations/rewrite.go
index 37c5a75b9..b357ce940 100644
--- a/test/e2e/annotations/rewrite.go
+++ b/test/e2e/annotations/rewrite.go
@@ -95,7 +95,8 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo
 
 		f.WaitForNginxServer(host,
 			func(server string) bool {
-				return strings.Contains(server, `location ~* "^/" {`) && strings.Contains(server, `location ~* "^/.well-known/acme/challenge" {`)
+				return strings.Contains(server, `location / {`) &&
+					strings.Contains(server, `location /.well-known/acme/challenge {`)
 			})
 
 		ginkgo.By("making a second request to the non-rewritten location")
@@ -129,7 +130,7 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo
 
 		f.WaitForNginxServer(host,
 			func(server string) bool {
-				return strings.Contains(server, `location ~* "^/foo" {`) && strings.Contains(server, `location ~* "^/foo.+" {`)
+				return strings.Contains(server, `location /foo {`) && strings.Contains(server, `location ~* "^/foo.+" {`)
 			})
 
 		ginkgo.By("ensuring '/foo' matches '~* ^/foo'")
@@ -170,7 +171,7 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo
 
 		f.WaitForNginxServer(host,
 			func(server string) bool {
-				return strings.Contains(server, `location ~* "^/foo/bar/bar" {`) &&
+				return strings.Contains(server, `location /foo/bar/bar {`) &&
 					strings.Contains(server, `location ~* "^/foo/bar/[a-z]{3}" {`)
 			})
 
diff --git a/test/e2e/settings/enable_real_ip.go b/test/e2e/settings/enable_real_ip.go
new file mode 100644
index 000000000..027f4c1f8
--- /dev/null
+++ b/test/e2e/settings/enable_real_ip.go
@@ -0,0 +1,113 @@
+/*
+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 settings
+
+import (
+	"fmt"
+	"net/http"
+	"strings"
+
+	"github.com/onsi/ginkgo"
+	"github.com/stretchr/testify/assert"
+
+	"k8s.io/ingress-nginx/test/e2e/framework"
+)
+
+var _ = framework.DescribeSetting("enable-real-ip", func() {
+	f := framework.NewDefaultFramework("enable-real-ip")
+
+	setting := "enable-real-ip"
+
+	ginkgo.BeforeEach(func() {
+		f.NewEchoDeployment()
+		f.UpdateNginxConfigMapData(setting, "false")
+	})
+
+	ginkgo.It("trusts X-Forwarded-For header only when setting is true", func() {
+		host := "forwarded-for-header"
+
+		f.UpdateNginxConfigMapData(setting, "true")
+
+		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil)
+		f.EnsureIngress(ing)
+
+		f.WaitForNginxServer(host,
+			func(server string) bool {
+				return strings.Contains(server, "server_name "+host) &&
+					!strings.Contains(server, "proxy_set_header X-Forwarded-Proto $full_x_forwarded_proto;")
+			})
+
+		ginkgo.By("ensuring single values are parsed correctly")
+		body := f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("X-Forwarded-Port", "1234").
+			WithHeader("X-Forwarded-Proto", "myproto").
+			WithHeader("X-Forwarded-For", "1.2.3.4").
+			WithHeader("X-Forwarded-Host", "myhost").
+			Expect().
+			Status(http.StatusOK).
+			Body().
+			Raw()
+
+		assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=myhost"))
+		assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-host=myhost"))
+		assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-proto=myproto"))
+		assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-port=1234"))
+		assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=%s", host))
+		assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-host=%s", host))
+		assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-proto=http"))
+		assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-port=80"))
+		assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-for=1.2.3.4"))
+	})
+
+	ginkgo.It("should not trust X-Forwarded-For header when setting is false", func() {
+		host := "forwarded-for-header"
+
+		f.UpdateNginxConfigMapData(setting, "false")
+
+		f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil))
+
+		f.WaitForNginxServer(host,
+			func(server string) bool {
+				return strings.Contains(server, "server_name "+host) &&
+					strings.Contains(server, "proxy_set_header X-Forwarded-Proto $pass_access_scheme;")
+			})
+
+		body := f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("X-Forwarded-Port", "1234").
+			WithHeader("X-Forwarded-Proto", "myproto").
+			WithHeader("X-Forwarded-For", "1.2.3.4").
+			WithHeader("X-Forwarded-Host", "myhost").
+			Expect().
+			Status(http.StatusOK).
+			Body().
+			Raw()
+
+		assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=%s", host))
+		assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-port=80"))
+		assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-proto=http"))
+		assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-original-forwarded-for=1.2.3.4"))
+		assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("host=myhost"))
+		assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-host=myhost"))
+		assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-proto=myproto"))
+		assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-port=1234"))
+		assert.NotContains(ginkgo.GinkgoT(), body, fmt.Sprintf("x-forwarded-for=1.2.3.4"))
+	})
+})