Repository: kubernetes/ingress-nginx
Pull request #5320 (2020-04-02 23:58:29)

Change condition order that produces endless loop
<!--- 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: -->
- [x] 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: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes/ingress-nginx/pull/5320#" title="Author self-approved">aledbf</a>*

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

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

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

- ~~[OWNERS](https://github.com/kubernetes/ingress-nginx/blob/master/OWNERS)~~ [aledbf]

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":[]} -->
aledbf: /test pull-ingress-nginx-e2e-1-16 
k8s-ci-robot: @aledbf: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-ingress-nginx-e2e-1-16 | 2e1650865dbc24f4cc4199899a64ed824c17c368 | [link](https://prow.k8s.io/view/gcs/kubernetes-jenkins/pr-logs/pull/ingress-nginx/5320/pull-ingress-nginx-e2e-1-16/1245871111774670848) | `/test pull-ingress-nginx-e2e-1-16`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes&repo=ingress-nginx&pr=5320). [Your PR dashboard](https://gubernator.k8s.io/pr/aledbf). 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 -->
aledbf: /retest
k8s-ci-robot: @aledbf: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-ingress-nginx-e2e-1-16 | 2e1650865dbc24f4cc4199899a64ed824c17c368 | [link](https://prow.k8s.io/view/gcs/kubernetes-jenkins/pr-logs/pull/ingress-nginx/5320/pull-ingress-nginx-e2e-1-16/1245876270634373120) | `/test pull-ingress-nginx-e2e-1-16`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes&repo=ingress-nginx&pr=5320). [Your PR dashboard](https://gubernator.k8s.io/pr/aledbf). 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 -->
aledbf: /retest

Patch:
diff --git a/internal/ingress/controller/template/template.go b/internal/ingress/controller/template/template.go
index a423b86a2..51a9fec1d 100644
--- a/internal/ingress/controller/template/template.go
+++ b/internal/ingress/controller/template/template.go
@@ -1360,12 +1360,6 @@ func buildModSecurityForLocation(cfg config.Configuration, location *ingress.Loc
 
 	if !isMSEnabled {
 		buffer.WriteString(`modsecurity on;
-modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf;
-`)
-	}
-
-	if !cfg.EnableOWASPCoreRules && location.ModSecurity.OWASPRules {
-		buffer.WriteString(`modsecurity_rules_file /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf;
 `)
 	}
 
@@ -1381,6 +1375,16 @@ modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf;
 `, location.ModSecurity.TransactionID))
 	}
 
+	if !isMSEnabled {
+		buffer.WriteString(`modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf;
+`)
+	}
+
+	if !cfg.EnableOWASPCoreRules && location.ModSecurity.OWASPRules {
+		buffer.WriteString(`modsecurity_rules_file /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf;
+`)
+	}
+
 	return buffer.String()
 }
 
diff --git a/rootfs/etc/nginx/lua/lua_ingress.lua b/rootfs/etc/nginx/lua/lua_ingress.lua
index 2d84ce141..e292ade80 100644
--- a/rootfs/etc/nginx/lua/lua_ingress.lua
+++ b/rootfs/etc/nginx/lua/lua_ingress.lua
@@ -61,12 +61,12 @@ local function redirect_to_https(location_config)
     return false
   end
 
-  if ngx.var.pass_access_scheme ~= "http" then
-    return false
+  if location_config.force_ssl_redirect and ngx.var.pass_access_scheme == "http" then
+    return true
   end
 
-  if location_config.force_ssl_redirect then
-    return true
+  if ngx.var.pass_access_scheme ~= "http" then
+    return false
   end
 
   return location_config.ssl_redirect and certificate_configured_for_current_request()
@@ -105,6 +105,7 @@ end
 -- phases or redirection
 function _M.rewrite(location_config)
   ngx.var.pass_access_scheme = ngx.var.scheme
+
   ngx.var.best_http_host = ngx.var.http_host or ngx.var.host
 
   if config.use_forwarded_headers then


Test patch:
diff --git a/internal/ingress/controller/template/template_test.go b/internal/ingress/controller/template/template_test.go
index 782a54d4a..7a8599f67 100644
--- a/internal/ingress/controller/template/template_test.go
+++ b/internal/ingress/controller/template/template_test.go
@@ -1373,7 +1373,9 @@ func TestShouldLoadOpentracingModule(t *testing.T) {
 
 func TestModSecurityForLocation(t *testing.T) {
 	loadModule := `modsecurity on;
-modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf;
+`
+
+	modSecCfg := `modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf;
 `
 
 	modsecOff := "modsecurity off;"
@@ -1411,12 +1413,12 @@ modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf;
 		{"configmap enabled, configmap OWASP disabled, annotation enabled, OWASP enabled, no snippet and no transaction ID", true, false, true, true, true, "", "", owaspRules},
 		{"configmap enabled, configmap OWASP disabled, annotation disabled, OWASP disabled, no snippet and no transaction ID", true, false, false, true, false, "", "", modsecOff},
 		{"configmap enabled, configmap OWASP disabled, annotation enabled, OWASP enabled, with snippet and no transaction ID", true, false, true, true, true, "", "", owaspRules},
-		{"configmap enabled, configmap OWASP disabled, annotation enabled, OWASP enabled, with snippet and transaction ID", true, false, true, true, true, "", transactionID, fmt.Sprintf("%v%v", owaspRules, transactionCfg)},
+		{"configmap enabled, configmap OWASP disabled, annotation enabled, OWASP enabled, with snippet and transaction ID", true, false, true, true, true, "", transactionID, fmt.Sprintf("%v%v", transactionCfg, owaspRules)},
 		{"configmap enabled, configmap OWASP enabled, annotation enabled, OWASP disabled", true, true, true, true, false, "", "", ""},
-		{"configmap disabled, annotation enabled, OWASP disabled", false, false, true, true, false, "", "", loadModule},
+		{"configmap disabled, annotation enabled, OWASP disabled", false, false, true, true, false, "", "", fmt.Sprintf("%v%v", loadModule, modSecCfg)},
 		{"configmap disabled, annotation disabled, OWASP disabled", false, false, false, true, false, "", "", ""},
-		{"configmap disabled, annotation enabled, OWASP disabled", false, false, true, true, false, testRule, "", fmt.Sprintf("%v%v", loadModule, modsecRule)},
-		{"configmap disabled, annotation enabled, OWASP enabled", false, false, true, true, false, testRule, "", fmt.Sprintf("%v%v", loadModule, modsecRule)},
+		{"configmap disabled, annotation enabled, OWASP disabled", false, false, true, true, false, testRule, "", fmt.Sprintf("%v%v%v", loadModule, modsecRule, modSecCfg)},
+		{"configmap disabled, annotation enabled, OWASP enabled", false, false, true, true, false, testRule, "", fmt.Sprintf("%v%v%v", loadModule, modsecRule, modSecCfg)},
 	}
 
 	for _, testCase := range testCases {
diff --git a/test/e2e/annotations/modsecurity.go b/test/e2e/annotations/modsecurity.go
index a3682243f..1f5a2f607 100644
--- a/test/e2e/annotations/modsecurity.go
+++ b/test/e2e/annotations/modsecurity.go
@@ -17,6 +17,7 @@ limitations under the License.
 package annotations
 
 import (
+	"net/http"
 	"strings"
 
 	"github.com/onsi/ginkgo"
@@ -148,4 +149,71 @@ var _ = framework.DescribeAnnotation("modsecurity owasp", func() {
 			})
 	})
 
+	ginkgo.It("should enable modsecurity with snippet and block requests", func() {
+		host := "modsecurity.foo.com"
+		nameSpace := f.Namespace
+
+		snippet := `SecRuleEngine On
+		SecRequestBodyAccess On
+		SecAuditEngine RelevantOnly
+		SecAuditLogParts ABIJDEFHZ
+		SecAuditLog /dev/stdout
+		SecAuditLogType Serial
+		SecRule REQUEST_HEADERS:User-Agent \"block-ua\" \"log,deny,id:107,status:403,msg:\'UA blocked\'\"`
+
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/enable-modsecurity":  "true",
+			"nginx.ingress.kubernetes.io/modsecurity-snippet": snippet,
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, nameSpace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		f.WaitForNginxServer(host,
+			func(server string) bool {
+				return strings.Contains(server, "modsecurity on;") &&
+					strings.Contains(server, "SecRuleEngine On")
+			})
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("User-Agent", "block-ua").
+			Expect().
+			Status(http.StatusForbidden)
+	})
+
+	ginkgo.It("should enable modsecurity globally and with modsecurity-snippet block requests", func() {
+		host := "modsecurity.foo.com"
+		nameSpace := f.Namespace
+
+		snippet := `SecRuleEngine On
+		SecRequestBodyAccess On
+		SecAuditEngine RelevantOnly
+		SecAuditLogParts ABIJDEFHZ
+		SecAuditLog /dev/stdout
+		SecAuditLogType Serial
+		SecRule REQUEST_HEADERS:User-Agent \"block-ua\" \"log,deny,id:107,status:403,msg:\'UA blocked\'\"`
+
+		annotations := map[string]string{
+			"nginx.ingress.kubernetes.io/modsecurity-snippet": snippet,
+		}
+
+		ing := framework.NewSingleIngress(host, "/", host, nameSpace, framework.EchoService, 80, annotations)
+		f.EnsureIngress(ing)
+
+		f.UpdateNginxConfigMapData("enable-modsecurity", "true")
+
+		f.WaitForNginxServer(host,
+			func(server string) bool {
+				return strings.Contains(server, "SecRuleEngine On")
+			})
+
+		f.HTTPTestClient().
+			GET("/").
+			WithHeader("Host", host).
+			WithHeader("User-Agent", "block-ua").
+			Expect().
+			Status(http.StatusForbidden)
+	})
 })