Enable download of GeoLite2 databases (#4896)

This commit is contained in:
Manuel Alejandro de Brito Fontes 2020-01-08 19:46:43 -03:00 committed by GitHub
parent 58146c803b
commit 74944b99e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 167 additions and 0 deletions

View file

@ -181,6 +181,9 @@ Takes the form "<host>:port". If not provided, no admission controller is starte
flags.MarkDeprecated("enable-dynamic-certificates", `Only dynamic mode is supported`) flags.MarkDeprecated("enable-dynamic-certificates", `Only dynamic mode is supported`)
flags.StringVar(&nginx.MaxmindLicenseKey, "maxmind-license-key", "", `Maxmind license key to download GeoLite2 Databases.
https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-geolite2-databases`)
flag.Set("logtostderr", "true") flag.Set("logtostderr", "true")
flags.AddGoFlagSet(flag.CommandLine) flags.AddGoFlagSet(flag.CommandLine)
@ -297,5 +300,13 @@ Takes the form "<host>:port". If not provided, no admission controller is starte
config.RootCAFile = *rootCAFile config.RootCAFile = *rootCAFile
} }
if nginx.MaxmindLicenseKey != "" {
klog.Info("downloading maxmind GeoIP2 databases...")
err := nginx.DownloadGeoLite2DB()
if err != nil {
klog.Errorf("unexpected error downloading GeoIP2 database: %v", err)
}
}
return false, config, nil return false, config, nil
} }

View file

@ -563,6 +563,13 @@ _**default:**_ true
## use-geoip2 ## use-geoip2
Enables the [geoip2 module](https://github.com/leev/ngx_http_geoip2_module) for NGINX. Enables the [geoip2 module](https://github.com/leev/ngx_http_geoip2_module) for NGINX.
Since `0.27.0` and due to a [change in the MaxMind databases](https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-geolite2-databases) a license is required to have access to the databases.
For this reason, it is required to define a new flag `--maxmind-license-key` in the ingress controller deployment to download the databases needed during the initialization of the ingress controller.
Alternatively, it is possible to use a volume to mount the files `/etc/nginx/geoip/GeoLite2-City.mmdb` and `/etc/nginx/geoip/GeoLite2-ASN.mmdb`, avoiding the overhead of the download.
!!! Important
If the feature is enabled but the files are missing, GeoIP2 will not be enabled.
_**default:**_ false _**default:**_ false
## enable-brotli ## enable-brotli

View file

@ -54,6 +54,7 @@ import (
"k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
"k8s.io/ingress-nginx/internal/k8s" "k8s.io/ingress-nginx/internal/k8s"
"k8s.io/ingress-nginx/internal/nginx"
) )
// IngressFilterFunc decides if an Ingress should be omitted or not // IngressFilterFunc decides if an Ingress should be omitted or not
@ -896,6 +897,11 @@ func (s *k8sStore) setConfig(cmap *corev1.ConfigMap) {
} }
s.backendConfig = ngx_template.ReadConfig(cmap.Data) s.backendConfig = ngx_template.ReadConfig(cmap.Data)
if s.backendConfig.UseGeoIP2 && !nginx.GeoLite2DBExists() {
klog.Warning("The GeoIP2 feature is enabled but the databases are missing. Disabling.")
s.backendConfig.UseGeoIP2 = false
}
s.writeSSLSessionTicketKey(cmap, "/etc/nginx/tickets.key") s.writeSSLSessionTicketKey(cmap, "/etc/nginx/tickets.key")
} }

143
internal/nginx/maxmind.go Normal file
View file

@ -0,0 +1,143 @@
/*
Copyright 2020 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 nginx
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"net/http"
"os"
"path"
"strings"
)
// MaxmindLicenseKey maxmind license key to download databases
var MaxmindLicenseKey = ""
const (
geoIPPath = "/etc/nginx/geoip"
geoLiteCityDB = "GeoLite2-City"
geoLiteASNDB = "GeoLite2-ASN"
dbExtension = ".mmdb"
maxmindURL = "https://download.maxmind.com/app/geoip_download?license_key=%v&edition_id=%v&suffix=tar.gz"
)
// GeoLite2DBExists checks if the required databases for
// the GeoIP2 NGINX module are present in the filesystem
func GeoLite2DBExists() bool {
if !fileExists(path.Join(geoIPPath, geoLiteASNDB+dbExtension)) {
return false
}
if !fileExists(path.Join(geoIPPath, geoLiteCityDB+dbExtension)) {
return false
}
return true
}
// DownloadGeoLite2DB downloads the required databases by the
// GeoIP2 NGINX module using a license key from MaxMind.
func DownloadGeoLite2DB() error {
err := downloadDatabase(geoLiteCityDB)
if err != nil {
return err
}
err = downloadDatabase(geoLiteASNDB)
if err != nil {
return err
}
return nil
}
func downloadDatabase(dbName string) error {
url := fmt.Sprintf(maxmindURL, MaxmindLicenseKey, dbName)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("HTTP status %v", resp.Status)
}
archive, err := gzip.NewReader(resp.Body)
if err != nil {
return err
}
defer archive.Close()
mmdbFile := dbName + dbExtension
tarReader := tar.NewReader(archive)
for true {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
switch header.Typeflag {
case tar.TypeReg:
if !strings.HasSuffix(header.Name, mmdbFile) {
continue
}
outFile, err := os.Create(path.Join(geoIPPath, mmdbFile))
if err != nil {
return err
}
defer outFile.Close()
if _, err := io.Copy(outFile, tarReader); err != nil {
return err
}
return nil
}
}
return fmt.Errorf("the URL %v does not contains the database %v",
fmt.Sprintf(maxmindURL, "XXXXXXX", dbName), mmdbFile)
}
func fileExists(filePath string) bool {
info, err := os.Stat(filePath)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}