Provide possibility to block CIDRs, User-Agents and Referers globally
This commit is contained in:
parent
3f6314aa2f
commit
7212d0081b
6 changed files with 263 additions and 0 deletions
|
@ -144,6 +144,9 @@ The following table shows a configuration option's name, type, and the default v
|
|||
|[limit-req-status-code](#limit-req-status-code)|int|503|
|
||||
|[no-tls-redirect-locations](#no-tls-redirect-locations)|string|"/.well-known/acme-challenge"|
|
||||
|[no-auth-locations](#no-auth-locations)|string|"/.well-known/acme-challenge"|
|
||||
|[block-cidrs](#block-cidrs)|[]string|""|
|
||||
|[block-user-agents](#block-user-agents)|[]string|""|
|
||||
|[block-referers](#block-referers)|[]string|""|
|
||||
|
||||
## add-headers
|
||||
|
||||
|
@ -791,3 +794,26 @@ _**default:**_ "/.well-known/acme-challenge"
|
|||
|
||||
A comma-separated list of locations that should not get authenticated.
|
||||
_**default:**_ "/.well-known/acme-challenge"
|
||||
|
||||
## block-cidrs
|
||||
|
||||
A comma-separated list of IP addresses (or subnets), requestst from which have to be blocked globally.
|
||||
|
||||
_References:_
|
||||
[http://nginx.org/en/docs/http/ngx_http_access_module.html#deny](http://nginx.org/en/docs/http/ngx_http_access_module.html#deny)
|
||||
|
||||
## block-user-agents
|
||||
|
||||
A comma-separated list of User-Agent, requestst from which have to be blocked globally.
|
||||
It's possible to use here full strings and regular expressions. More details about valid patterns can be found at `map` Nginx directive documentation.
|
||||
|
||||
_References:_
|
||||
[http://nginx.org/en/docs/http/ngx_http_map_module.html#map](http://nginx.org/en/docs/http/ngx_http_map_module.html#map)
|
||||
|
||||
## block-referers
|
||||
|
||||
A comma-separated list of Referers, requestst from which have to be blocked globally.
|
||||
It's possible to use here full strings and regular expressions. More details about valid patterns can be found at `map` Nginx directive documentation.
|
||||
|
||||
_References:_
|
||||
[http://nginx.org/en/docs/http/ngx_http_map_module.html#map](http://nginx.org/en/docs/http/ngx_http_map_module.html#map)
|
||||
|
|
|
@ -533,12 +533,22 @@ type Configuration struct {
|
|||
|
||||
// Checksum contains a checksum of the configmap configuration
|
||||
Checksum string `json:"-"`
|
||||
|
||||
// Block all requests from given IPs
|
||||
BlockCIDRs []string `json:"block-cidrs"`
|
||||
|
||||
// Block all requests with given User-Agent headers
|
||||
BlockUserAgents []string `json:"block-user-agents"`
|
||||
|
||||
// Block all requests with given Referer headers
|
||||
BlockReferers []string `json:"block-referers"`
|
||||
}
|
||||
|
||||
// NewDefault returns the default nginx configuration
|
||||
func NewDefault() Configuration {
|
||||
defIPCIDR := make([]string, 0)
|
||||
defBindAddress := make([]string, 0)
|
||||
defBlockEntity := make([]string, 0)
|
||||
defNginxStatusIpv4Whitelist := make([]string, 0)
|
||||
defNginxStatusIpv6Whitelist := make([]string, 0)
|
||||
|
||||
|
@ -552,6 +562,9 @@ func NewDefault() Configuration {
|
|||
AccessLogPath: "/var/log/nginx/access.log",
|
||||
WorkerCpuAffinity: "",
|
||||
ErrorLogPath: "/var/log/nginx/error.log",
|
||||
BlockCIDRs: defBlockEntity,
|
||||
BlockUserAgents: defBlockEntity,
|
||||
BlockReferers: defBlockEntity,
|
||||
BrotliLevel: 4,
|
||||
BrotliTypes: brotliTypes,
|
||||
ClientHeaderBufferSize: "1k",
|
||||
|
|
|
@ -41,6 +41,9 @@ const (
|
|||
proxyRealIPCIDR = "proxy-real-ip-cidr"
|
||||
bindAddress = "bind-address"
|
||||
httpRedirectCode = "http-redirect-code"
|
||||
blockCIDRs = "block-cidrs"
|
||||
blockUserAgents = "block-user-agents"
|
||||
blockReferers = "block-referers"
|
||||
proxyStreamResponses = "proxy-stream-responses"
|
||||
hideHeaders = "hide-headers"
|
||||
nginxStatusIpv4Whitelist = "nginx-status-ipv4-whitelist"
|
||||
|
@ -71,6 +74,10 @@ func ReadConfig(src map[string]string) config.Configuration {
|
|||
bindAddressIpv4List := make([]string, 0)
|
||||
bindAddressIpv6List := make([]string, 0)
|
||||
|
||||
blockCIDRList := make([]string, 0)
|
||||
blockUserAgentList := make([]string, 0)
|
||||
blockRefererList := make([]string, 0)
|
||||
|
||||
if val, ok := conf[customHTTPErrors]; ok {
|
||||
delete(conf, customHTTPErrors)
|
||||
for _, i := range strings.Split(val, ",") {
|
||||
|
@ -116,6 +123,19 @@ func ReadConfig(src map[string]string) config.Configuration {
|
|||
}
|
||||
}
|
||||
|
||||
if val, ok := conf[blockCIDRs]; ok {
|
||||
delete(conf, blockCIDRs)
|
||||
blockCIDRList = strings.Split(val, ",")
|
||||
}
|
||||
if val, ok := conf[blockUserAgents]; ok {
|
||||
delete(conf, blockUserAgents)
|
||||
blockUserAgentList = strings.Split(val, ",")
|
||||
}
|
||||
if val, ok := conf[blockReferers]; ok {
|
||||
delete(conf, blockReferers)
|
||||
blockRefererList = strings.Split(val, ",")
|
||||
}
|
||||
|
||||
if val, ok := conf[httpRedirectCode]; ok {
|
||||
delete(conf, httpRedirectCode)
|
||||
j, err := strconv.Atoi(val)
|
||||
|
@ -184,6 +204,9 @@ func ReadConfig(src map[string]string) config.Configuration {
|
|||
to.ProxyRealIPCIDR = proxyList
|
||||
to.BindAddressIpv4 = bindAddressIpv4List
|
||||
to.BindAddressIpv6 = bindAddressIpv6List
|
||||
to.BlockCIDRs = blockCIDRList
|
||||
to.BlockUserAgents = blockUserAgentList
|
||||
to.BlockReferers = blockRefererList
|
||||
to.HideHeaders = hideHeadersList
|
||||
to.ProxyStreamResponses = streamResponses
|
||||
to.DisableIpv6DNS = !ing_net.IsIPv6Enabled()
|
||||
|
|
|
@ -141,6 +141,7 @@ var (
|
|||
"contains": strings.Contains,
|
||||
"hasPrefix": strings.HasPrefix,
|
||||
"hasSuffix": strings.HasSuffix,
|
||||
"trimSpace": strings.TrimSpace,
|
||||
"toUpper": strings.ToUpper,
|
||||
"toLower": strings.ToLower,
|
||||
"formatIP": formatIP,
|
||||
|
|
|
@ -491,6 +491,28 @@ http {
|
|||
{{ $zone }}
|
||||
{{ end }}
|
||||
|
||||
# Global filters
|
||||
{{ range $ip := $cfg.BlockCIDRs }}deny {{ trimSpace $ip }};
|
||||
{{ end }}
|
||||
|
||||
{{ if gt (len $cfg.BlockUserAgents) 0 }}
|
||||
map $http_user_agent $block_ua {
|
||||
default 0;
|
||||
|
||||
{{ range $ua := $cfg.BlockUserAgents }}{{ trimSpace $ua }} 1;
|
||||
{{ end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if gt (len $cfg.BlockReferers) 0 }}
|
||||
map $http_referer $block_ref {
|
||||
default 0;
|
||||
|
||||
{{ range $ref := $cfg.BlockReferers }}{{ trimSpace $ref }} 1;
|
||||
{{ end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{/* Build server redirects (from/to www) */}}
|
||||
{{ range $hostname, $to := .RedirectServers }}
|
||||
server {
|
||||
|
@ -512,6 +534,17 @@ http {
|
|||
{{ end }}
|
||||
server_name {{ $hostname }};
|
||||
|
||||
{{ if gt (len $cfg.BlockUserAgents) 0 }}
|
||||
if ($block_ua) {
|
||||
return 403;
|
||||
}
|
||||
{{ end }}
|
||||
{{ if gt (len $cfg.BlockReferers) 0 }}
|
||||
if ($block_ref) {
|
||||
return 403;
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if ne $all.ListenPorts.HTTPS 443 }}
|
||||
{{ $redirect_port := (printf ":%v" $all.ListenPorts.HTTPS) }}
|
||||
return {{ $all.Cfg.HTTPRedirectCode }} $scheme://{{ $to }}{{ $redirect_port }}$request_uri;
|
||||
|
@ -526,6 +559,18 @@ http {
|
|||
## start server {{ $server.Hostname }}
|
||||
server {
|
||||
server_name {{ $server.Hostname }} {{ $server.Alias }};
|
||||
|
||||
{{ if gt (len $cfg.BlockUserAgents) 0 }}
|
||||
if ($block_ua) {
|
||||
return 403;
|
||||
}
|
||||
{{ end }}
|
||||
{{ if gt (len $cfg.BlockReferers) 0 }}
|
||||
if ($block_ref) {
|
||||
return 403;
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ template "SERVER" serverConfig $all $server }}
|
||||
|
||||
{{ if not (empty $cfg.ServerSnippet) }}
|
||||
|
@ -545,6 +590,17 @@ http {
|
|||
{{ if $IsIPV6Enabled }}listen [::]:{{ $all.ListenPorts.Status }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }};{{ end }}
|
||||
set $proxy_upstream_name "-";
|
||||
|
||||
{{ if gt (len $cfg.BlockUserAgents) 0 }}
|
||||
if ($block_ua) {
|
||||
return 403;
|
||||
}
|
||||
{{ end }}
|
||||
{{ if gt (len $cfg.BlockReferers) 0 }}
|
||||
if ($block_ref) {
|
||||
return 403;
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
location {{ $healthzURI }} {
|
||||
{{ if $cfg.EnableOpentracing }}
|
||||
opentracing off;
|
||||
|
|
144
test/e2e/settings/global_access_block.go
Normal file
144
test/e2e/settings/global_access_block.go
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
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 (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
|
||||
"k8s.io/ingress-nginx/test/e2e/framework"
|
||||
)
|
||||
|
||||
var _ = framework.IngressNginxDescribe("Global access block", func() {
|
||||
f := framework.NewDefaultFramework("global-access-block")
|
||||
|
||||
host := "global-access-block"
|
||||
|
||||
BeforeEach(func() {
|
||||
err := f.NewEchoDeploymentWithReplicas(1)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
ing, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, "http-svc", 80, nil))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(ing).NotTo(BeNil())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
})
|
||||
|
||||
It("should block CIDRs defined in the ConfigMap", func() {
|
||||
err := f.UpdateNginxConfigMapData("block-cidrs", "172.16.0.0/12,192.168.0.0/16,10.0.0.0/8")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = f.WaitForNginxConfiguration(
|
||||
func(cfg string) bool {
|
||||
return strings.Contains(cfg, "deny 172.16.0.0/12;") &&
|
||||
strings.Contains(cfg, "deny 192.168.0.0/16;") &&
|
||||
strings.Contains(cfg, "deny 10.0.0.0/8;")
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// This test works for minikube, but may have problems with real kubernetes clusters,
|
||||
// especially if connection is done via Internet. In this case, the test should be disabled/removed.
|
||||
resp, _, errs := gorequest.New().
|
||||
Get(f.IngressController.HTTPURL).
|
||||
Set("Host", host).
|
||||
End()
|
||||
Expect(errs).To(BeNil())
|
||||
Expect(resp.StatusCode).Should(Equal(http.StatusForbidden))
|
||||
})
|
||||
|
||||
It("should block User-Agents defined in the ConfigMap", func() {
|
||||
err := f.UpdateNginxConfigMapData("block-user-agents", "~*chrome\\/68\\.0\\.3440\\.106\\ safari\\/537\\.36,AlphaBot")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = f.WaitForNginxConfiguration(
|
||||
func(cfg string) bool {
|
||||
return strings.Contains(cfg, "~*chrome\\/68\\.0\\.3440\\.106\\ safari\\/537\\.36 1;") &&
|
||||
strings.Contains(cfg, "AlphaBot 1;")
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Should be blocked
|
||||
resp, _, errs := gorequest.New().
|
||||
Get(f.IngressController.HTTPURL).
|
||||
Set("Host", host).
|
||||
Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36").
|
||||
End()
|
||||
Expect(errs).To(BeNil())
|
||||
Expect(resp.StatusCode).Should(Equal(http.StatusForbidden))
|
||||
|
||||
resp, _, errs = gorequest.New().
|
||||
Get(f.IngressController.HTTPURL).
|
||||
Set("Host", host).
|
||||
Set("User-Agent", "AlphaBot").
|
||||
End()
|
||||
Expect(errs).To(BeNil())
|
||||
Expect(resp.StatusCode).Should(Equal(http.StatusForbidden))
|
||||
|
||||
// Shouldn't be blocked
|
||||
resp, _, errs = gorequest.New().
|
||||
Get(f.IngressController.HTTPURL).
|
||||
Set("Host", host).
|
||||
Set("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 11_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Mobile/15E148 Safari/604.1").
|
||||
End()
|
||||
Expect(errs).To(BeNil())
|
||||
Expect(resp.StatusCode).Should(Equal(http.StatusOK))
|
||||
})
|
||||
|
||||
It("should block Referers defined in the ConfigMap", func() {
|
||||
err := f.UpdateNginxConfigMapData("block-referers", "~*example\\.com,qwerty")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = f.WaitForNginxConfiguration(
|
||||
func(cfg string) bool {
|
||||
return strings.Contains(cfg, "~*example\\.com 1;") &&
|
||||
strings.Contains(cfg, "qwerty 1;")
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Should be blocked
|
||||
resp, _, errs := gorequest.New().
|
||||
Get(f.IngressController.HTTPURL).
|
||||
Set("Host", host).
|
||||
Set("Referer", "example.com").
|
||||
End()
|
||||
Expect(errs).To(BeNil())
|
||||
Expect(resp.StatusCode).Should(Equal(http.StatusForbidden))
|
||||
|
||||
resp, _, errs = gorequest.New().
|
||||
Get(f.IngressController.HTTPURL).
|
||||
Set("Host", host).
|
||||
Set("Referer", "qwerty").
|
||||
End()
|
||||
Expect(errs).To(BeNil())
|
||||
Expect(resp.StatusCode).Should(Equal(http.StatusForbidden))
|
||||
|
||||
// Shouldn't be blocked
|
||||
resp, _, errs = gorequest.New().
|
||||
Get(f.IngressController.HTTPURL).
|
||||
Set("Host", host).
|
||||
Set("Referer", "qwerty123").
|
||||
End()
|
||||
Expect(errs).To(BeNil())
|
||||
Expect(resp.StatusCode).Should(Equal(http.StatusOK))
|
||||
})
|
||||
})
|
Loading…
Reference in a new issue