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|
|
|[limit-req-status-code](#limit-req-status-code)|int|503|
|
||||||
|[no-tls-redirect-locations](#no-tls-redirect-locations)|string|"/.well-known/acme-challenge"|
|
|[no-tls-redirect-locations](#no-tls-redirect-locations)|string|"/.well-known/acme-challenge"|
|
||||||
|[no-auth-locations](#no-auth-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
|
## add-headers
|
||||||
|
|
||||||
|
@ -791,3 +794,26 @@ _**default:**_ "/.well-known/acme-challenge"
|
||||||
|
|
||||||
A comma-separated list of locations that should not get authenticated.
|
A comma-separated list of locations that should not get authenticated.
|
||||||
_**default:**_ "/.well-known/acme-challenge"
|
_**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 contains a checksum of the configmap configuration
|
||||||
Checksum string `json:"-"`
|
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
|
// NewDefault returns the default nginx configuration
|
||||||
func NewDefault() Configuration {
|
func NewDefault() Configuration {
|
||||||
defIPCIDR := make([]string, 0)
|
defIPCIDR := make([]string, 0)
|
||||||
defBindAddress := make([]string, 0)
|
defBindAddress := make([]string, 0)
|
||||||
|
defBlockEntity := make([]string, 0)
|
||||||
defNginxStatusIpv4Whitelist := make([]string, 0)
|
defNginxStatusIpv4Whitelist := make([]string, 0)
|
||||||
defNginxStatusIpv6Whitelist := make([]string, 0)
|
defNginxStatusIpv6Whitelist := make([]string, 0)
|
||||||
|
|
||||||
|
@ -552,6 +562,9 @@ func NewDefault() Configuration {
|
||||||
AccessLogPath: "/var/log/nginx/access.log",
|
AccessLogPath: "/var/log/nginx/access.log",
|
||||||
WorkerCpuAffinity: "",
|
WorkerCpuAffinity: "",
|
||||||
ErrorLogPath: "/var/log/nginx/error.log",
|
ErrorLogPath: "/var/log/nginx/error.log",
|
||||||
|
BlockCIDRs: defBlockEntity,
|
||||||
|
BlockUserAgents: defBlockEntity,
|
||||||
|
BlockReferers: defBlockEntity,
|
||||||
BrotliLevel: 4,
|
BrotliLevel: 4,
|
||||||
BrotliTypes: brotliTypes,
|
BrotliTypes: brotliTypes,
|
||||||
ClientHeaderBufferSize: "1k",
|
ClientHeaderBufferSize: "1k",
|
||||||
|
|
|
@ -41,6 +41,9 @@ const (
|
||||||
proxyRealIPCIDR = "proxy-real-ip-cidr"
|
proxyRealIPCIDR = "proxy-real-ip-cidr"
|
||||||
bindAddress = "bind-address"
|
bindAddress = "bind-address"
|
||||||
httpRedirectCode = "http-redirect-code"
|
httpRedirectCode = "http-redirect-code"
|
||||||
|
blockCIDRs = "block-cidrs"
|
||||||
|
blockUserAgents = "block-user-agents"
|
||||||
|
blockReferers = "block-referers"
|
||||||
proxyStreamResponses = "proxy-stream-responses"
|
proxyStreamResponses = "proxy-stream-responses"
|
||||||
hideHeaders = "hide-headers"
|
hideHeaders = "hide-headers"
|
||||||
nginxStatusIpv4Whitelist = "nginx-status-ipv4-whitelist"
|
nginxStatusIpv4Whitelist = "nginx-status-ipv4-whitelist"
|
||||||
|
@ -71,6 +74,10 @@ func ReadConfig(src map[string]string) config.Configuration {
|
||||||
bindAddressIpv4List := make([]string, 0)
|
bindAddressIpv4List := make([]string, 0)
|
||||||
bindAddressIpv6List := 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 {
|
if val, ok := conf[customHTTPErrors]; ok {
|
||||||
delete(conf, customHTTPErrors)
|
delete(conf, customHTTPErrors)
|
||||||
for _, i := range strings.Split(val, ",") {
|
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 {
|
if val, ok := conf[httpRedirectCode]; ok {
|
||||||
delete(conf, httpRedirectCode)
|
delete(conf, httpRedirectCode)
|
||||||
j, err := strconv.Atoi(val)
|
j, err := strconv.Atoi(val)
|
||||||
|
@ -184,6 +204,9 @@ func ReadConfig(src map[string]string) config.Configuration {
|
||||||
to.ProxyRealIPCIDR = proxyList
|
to.ProxyRealIPCIDR = proxyList
|
||||||
to.BindAddressIpv4 = bindAddressIpv4List
|
to.BindAddressIpv4 = bindAddressIpv4List
|
||||||
to.BindAddressIpv6 = bindAddressIpv6List
|
to.BindAddressIpv6 = bindAddressIpv6List
|
||||||
|
to.BlockCIDRs = blockCIDRList
|
||||||
|
to.BlockUserAgents = blockUserAgentList
|
||||||
|
to.BlockReferers = blockRefererList
|
||||||
to.HideHeaders = hideHeadersList
|
to.HideHeaders = hideHeadersList
|
||||||
to.ProxyStreamResponses = streamResponses
|
to.ProxyStreamResponses = streamResponses
|
||||||
to.DisableIpv6DNS = !ing_net.IsIPv6Enabled()
|
to.DisableIpv6DNS = !ing_net.IsIPv6Enabled()
|
||||||
|
|
|
@ -141,6 +141,7 @@ var (
|
||||||
"contains": strings.Contains,
|
"contains": strings.Contains,
|
||||||
"hasPrefix": strings.HasPrefix,
|
"hasPrefix": strings.HasPrefix,
|
||||||
"hasSuffix": strings.HasSuffix,
|
"hasSuffix": strings.HasSuffix,
|
||||||
|
"trimSpace": strings.TrimSpace,
|
||||||
"toUpper": strings.ToUpper,
|
"toUpper": strings.ToUpper,
|
||||||
"toLower": strings.ToLower,
|
"toLower": strings.ToLower,
|
||||||
"formatIP": formatIP,
|
"formatIP": formatIP,
|
||||||
|
|
|
@ -491,6 +491,28 @@ http {
|
||||||
{{ $zone }}
|
{{ $zone }}
|
||||||
{{ end }}
|
{{ 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) */}}
|
{{/* Build server redirects (from/to www) */}}
|
||||||
{{ range $hostname, $to := .RedirectServers }}
|
{{ range $hostname, $to := .RedirectServers }}
|
||||||
server {
|
server {
|
||||||
|
@ -512,6 +534,17 @@ http {
|
||||||
{{ end }}
|
{{ end }}
|
||||||
server_name {{ $hostname }};
|
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 }}
|
{{ if ne $all.ListenPorts.HTTPS 443 }}
|
||||||
{{ $redirect_port := (printf ":%v" $all.ListenPorts.HTTPS) }}
|
{{ $redirect_port := (printf ":%v" $all.ListenPorts.HTTPS) }}
|
||||||
return {{ $all.Cfg.HTTPRedirectCode }} $scheme://{{ $to }}{{ $redirect_port }}$request_uri;
|
return {{ $all.Cfg.HTTPRedirectCode }} $scheme://{{ $to }}{{ $redirect_port }}$request_uri;
|
||||||
|
@ -526,6 +559,18 @@ http {
|
||||||
## start server {{ $server.Hostname }}
|
## start server {{ $server.Hostname }}
|
||||||
server {
|
server {
|
||||||
server_name {{ $server.Hostname }} {{ $server.Alias }};
|
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 }}
|
{{ template "SERVER" serverConfig $all $server }}
|
||||||
|
|
||||||
{{ if not (empty $cfg.ServerSnippet) }}
|
{{ 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 }}
|
{{ if $IsIPV6Enabled }}listen [::]:{{ $all.ListenPorts.Status }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }};{{ end }}
|
||||||
set $proxy_upstream_name "-";
|
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 }} {
|
location {{ $healthzURI }} {
|
||||||
{{ if $cfg.EnableOpentracing }}
|
{{ if $cfg.EnableOpentracing }}
|
||||||
opentracing off;
|
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