diff --git a/docs/user-guide/nginx-configuration/configmap.md b/docs/user-guide/nginx-configuration/configmap.md index d4cdc16c2..85615f26b 100644 --- a/docs/user-guide/nginx-configuration/configmap.md +++ b/docs/user-guide/nginx-configuration/configmap.md @@ -86,6 +86,7 @@ The following table shows a configuration option's name, type, and the default v |[proxy-protocol-header-timeout](#proxy-protocol-header-timeout)|string|"5s"| |[use-gzip](#use-gzip)|bool|"true"| |[use-geoip](#use-geoip)|bool|"true"| +|[use-geoip2](#use-geoip2)|bool|"false"| |[enable-brotli](#enable-brotli)|bool|"false"| |[brotli-level](#brotli-level)|int|4| |[brotli-types](#brotli-types)|string|"application/xml+rss application/atom+xml application/javascript application/x-javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/plain text/x-component"| @@ -498,6 +499,13 @@ The default mime type list to compress is: `application/atom+xml application/jav Enables or disables ["geoip" module](http://nginx.org/en/docs/http/ngx_http_geoip_module.html) that creates variables with values depending on the client IP address, using the precompiled MaxMind databases. _**default:**_ true +> __Note:__ MaxMind legacy databases are discontinued and will not receive updates after 2019-01-02, cf. [discontinuation notice](https://support.maxmind.com/geolite-legacy-discontinuation-notice/). Consider [use-geoip2](#use-geoip2) below. + +## use-geoip2 + +Enables the [geoip2 module](https://github.com/leev/ngx_http_geoip2_module) for NGINX. +_**default:**_ false + ## enable-brotli Enables or disables compression of HTTP responses using the ["brotli" module](https://github.com/google/ngx_brotli). diff --git a/internal/ingress/controller/config/config.go b/internal/ingress/controller/config/config.go index 74cfac4cf..d6520dd5e 100644 --- a/internal/ingress/controller/config/config.go +++ b/internal/ingress/controller/config/config.go @@ -347,6 +347,10 @@ type Configuration struct { // http://nginx.org/en/docs/http/ngx_http_geoip_module.html UseGeoIP bool `json:"use-geoip,omitempty"` + // UseGeoIP2 enables the geoip2 module for NGINX + // By default this is disabled + UseGeoIP2 bool `json:"use-geoip2,omitempty"` + // Enables or disables the use of the NGINX Brotli Module for compression // https://github.com/google/ngx_brotli EnableBrotli bool `json:"enable-brotli,omitempty"` @@ -630,6 +634,7 @@ func NewDefault() Configuration { EnableBrotli: false, UseGzip: true, UseGeoIP: true, + UseGeoIP2: false, WorkerProcesses: strconv.Itoa(runtime.NumCPU()), WorkerShutdownTimeout: "10s", LoadBalanceAlgorithm: defaultLoadBalancerAlgorithm, diff --git a/rootfs/etc/nginx/template/nginx.tmpl b/rootfs/etc/nginx/template/nginx.tmpl index ca62f1672..0e855953d 100644 --- a/rootfs/etc/nginx/template/nginx.tmpl +++ b/rootfs/etc/nginx/template/nginx.tmpl @@ -12,6 +12,10 @@ # setup custom paths that do not require root access pid /tmp/nginx.pid; +{{ if $cfg.UseGeoIP2 }} +load_module /etc/nginx/modules/ngx_http_geoip2_module.so; +{{ end }} + {{ if $cfg.EnableModsecurity }} load_module /etc/nginx/modules/ngx_http_modsecurity_module.so; {{ end }} @@ -123,6 +127,26 @@ http { geoip_proxy_recursive on; {{ end }} + {{ if $cfg.UseGeoIP2 }} + # https://github.com/leev/ngx_http_geoip2_module#example-usage + + geoip2 /etc/nginx/geoip/GeoLite2-City.mmdb { + $geoip2_city_country_code source=$the_real_ip country iso_code; + $geoip2_city_country_name source=$the_real_ip country names en; + $geoip2_city source=$the_real_ip city names en; + $geoip2_postal_code source=$the_real_ip postal code; + $geoip2_dma_code source=$the_real_ip location metro_code; + $geoip2_latitude source=$the_real_ip location latitude; + $geoip2_longitude source=$the_real_ip location longitude; + $geoip2_region_code source=$the_real_ip subdivisions 0 iso_code; + $geoip2_region_name source=$the_real_ip subdivisions 0 names en; + } + + geoip2 /etc/nginx/geoip/GeoLite2-ASN.mmdb { + $geoip2_asn source=$the_real_ip autonomous_system_number; + } + {{ end }} + aio threads; aio_write on; diff --git a/test/e2e/settings/geoip2.go b/test/e2e/settings/geoip2.go new file mode 100644 index 000000000..f802fd575 --- /dev/null +++ b/test/e2e/settings/geoip2.go @@ -0,0 +1,96 @@ +/* +Copyright 2018 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 ( + "strings" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/parnurzeal/gorequest" + "k8s.io/ingress-nginx/test/e2e/framework" + "net/http" +) + +var _ = framework.IngressNginxDescribe("Geoip2", func() { + f := framework.NewDefaultFramework("geoip2") + + host := "geoip2" + + BeforeEach(func() { + err := f.NewEchoDeployment() + Expect(err).NotTo(HaveOccurred()) + }) + + It("should only allow requests from specific countries", func() { + err := f.UpdateNginxConfigMapData("use-geoip2", "true") + Expect(err).NotTo(HaveOccurred()) + + httpSnippetAllowingOnlyAustralia := + `map $geoip2_city_country_code $blocked_country { + default 1; + AU 0; +}` + err = f.UpdateNginxConfigMapData("http-snippet", httpSnippetAllowingOnlyAustralia) + Expect(err).NotTo(HaveOccurred()) + + err = f.WaitForNginxConfiguration( + func(cfg string) bool { + return strings.Contains(cfg, "map $geoip2_city_country_code $blocked_country") + }) + Expect(err).NotTo(HaveOccurred()) + + configSnippet := + `if ($blocked_country) { + return 403; +}` + + annotations := map[string]string{ + "nginx.ingress.kubernetes.io/configuration-snippet": configSnippet, + } + + ing, err := f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.IngressController.Namespace, "http-svc", 80, &annotations)) + Expect(err).NotTo(HaveOccurred()) + Expect(ing).NotTo(BeNil()) + + err = f.WaitForNginxConfiguration( + func(cfg string) bool { + return strings.Contains(cfg, "if ($blocked_country)") + }) + Expect(err).NotTo(HaveOccurred()) + + // Should be blocked + usIp := "8.8.8.8" + resp, _, errs := gorequest.New(). + Get(f.IngressController.HTTPURL). + Set("Host", host). + Set("X-Forwarded-For", usIp). + End() + Expect(errs).To(BeNil()) + Expect(resp.StatusCode).Should(Equal(http.StatusForbidden)) + + // Shouldn't be blocked + australianIp := "1.1.1.1" + resp, _, errs = gorequest.New(). + Get(f.IngressController.HTTPURL). + Set("Host", host). + Set("X-Forwarded-For", australianIp). + End() + Expect(errs).To(BeNil()) + Expect(resp.StatusCode).Should(Equal(http.StatusOK)) + }) +})