Merge remote-tracking branch 'upstream/master' into fix/collect-metrics-if-metrics-per-host-false

This commit is contained in:
Christian Hoffmeister 2020-05-01 14:57:00 +02:00
commit ef75a2d6fc
1311 changed files with 40786 additions and 85757 deletions

View file

@ -17,12 +17,15 @@ limitations under the License.
package controller
import (
"fmt"
"k8s.io/api/admission/v1beta1"
extensions "k8s.io/api/extensions/v1beta1"
networking "k8s.io/api/networking/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
)
// Checker must return an error if the ingress provided as argument
@ -37,57 +40,82 @@ type IngressAdmission struct {
Checker Checker
}
var (
extensionsResource = metav1.GroupVersionResource{
Group: networking.GroupName,
Version: "v1beta1",
Resource: "ingresses",
}
networkingResource = metav1.GroupVersionResource{
Group: extensions.GroupName,
Version: "v1beta1",
Resource: "ingresses",
}
)
// HandleAdmission populates the admission Response
// with Allowed=false if the Object is an ingress that would prevent nginx to reload the configuration
// with Allowed=true otherwise
func (ia *IngressAdmission) HandleAdmission(ar *v1beta1.AdmissionReview) error {
func (ia *IngressAdmission) HandleAdmission(ar *v1beta1.AdmissionReview) {
if ar.Request == nil {
klog.Infof("rejecting nil request")
ar.Response = &v1beta1.AdmissionResponse{
Allowed: false,
}
return nil
return
}
klog.V(3).Infof("handling ingress admission webhook request for {%s} %s in namespace %s", ar.Request.Resource.String(), ar.Request.Name, ar.Request.Namespace)
ingressResource := v1.GroupVersionResource{Group: networking.SchemeGroupVersion.Group, Version: networking.SchemeGroupVersion.Version, Resource: "ingresses"}
oldIngressResource := v1.GroupVersionResource{Group: extensions.SchemeGroupVersion.Group, Version: extensions.SchemeGroupVersion.Version, Resource: "ingresses"}
if ar.Request.Resource == ingressResource || ar.Request.Resource == oldIngressResource {
if ar.Request.Resource != extensionsResource && ar.Request.Resource != networkingResource {
err := fmt.Errorf("rejecting admission review because the request does not contains an Ingress resource but %s with name %s in namespace %s",
ar.Request.Resource.String(), ar.Request.Name, ar.Request.Namespace)
ar.Response = &v1beta1.AdmissionResponse{
UID: ar.Request.UID,
Allowed: false,
}
ingress := networking.Ingress{}
deserializer := codecs.UniversalDeserializer()
if _, _, err := deserializer.Decode(ar.Request.Object.Raw, nil, &ingress); err != nil {
ar.Response.Result = &v1.Status{Message: err.Error()}
ar.Response.AuditAnnotations = map[string]string{
parser.GetAnnotationWithPrefix("error"): err.Error(),
}
klog.Errorf("failed to decode ingress %s in namespace %s: %s, refusing it", ar.Request.Name, ar.Request.Namespace, err.Error())
return err
Result: &metav1.Status{Message: err.Error()},
}
err := ia.Checker.CheckIngress(&ingress)
if err != nil {
ar.Response.Result = &v1.Status{Message: err.Error()}
ar.Response.AuditAnnotations = map[string]string{
parser.GetAnnotationWithPrefix("error"): err.Error(),
}
klog.Errorf("failed to generate configuration for ingress %s in namespace %s: %s, refusing it", ar.Request.Name, ar.Request.Namespace, err.Error())
return err
}
ar.Response.Allowed = true
klog.Infof("successfully validated configuration, accepting ingress %s in namespace %s", ar.Request.Name, ar.Request.Namespace)
return nil
return
}
klog.Infof("accepting non ingress %s in namespace %s %s", ar.Request.Name, ar.Request.Namespace, ar.Request.Resource.String())
ingress := networking.Ingress{}
deserializer := codecs.UniversalDeserializer()
if _, _, err := deserializer.Decode(ar.Request.Object.Raw, nil, &ingress); err != nil {
klog.Errorf("failed to decode ingress %s in namespace %s: %s, refusing it",
ar.Request.Name, ar.Request.Namespace, err.Error())
ar.Response = &v1beta1.AdmissionResponse{
UID: ar.Request.UID,
Allowed: false,
Result: &metav1.Status{Message: err.Error()},
AuditAnnotations: map[string]string{
parser.GetAnnotationWithPrefix("error"): err.Error(),
},
}
return
}
if err := ia.Checker.CheckIngress(&ingress); err != nil {
klog.Errorf("failed to generate configuration for ingress %s in namespace %s: %s, refusing it",
ar.Request.Name, ar.Request.Namespace, err.Error())
ar.Response = &v1beta1.AdmissionResponse{
UID: ar.Request.UID,
Allowed: false,
Result: &metav1.Status{Message: err.Error()},
AuditAnnotations: map[string]string{
parser.GetAnnotationWithPrefix("error"): err.Error(),
},
}
return
}
klog.Infof("successfully validated configuration, accepting ingress %s in namespace %s",
ar.Request.Name, ar.Request.Namespace)
ar.Response = &v1beta1.AdmissionResponse{
UID: ar.Request.UID,
Allowed: true,
}
return nil
}

View file

@ -58,52 +58,44 @@ func TestHandleAdmission(t *testing.T) {
Resource: v1.GroupVersionResource{Group: "", Version: "v1", Resource: "pod"},
},
}
err := adm.HandleAdmission(review)
if !review.Response.Allowed {
t.Errorf("with a non ingress resource, the check should pass")
}
if err != nil {
t.Errorf("with a non ingress resource, no error should be returned")
adm.HandleAdmission(review)
if review.Response.Allowed {
t.Fatalf("with a non ingress resource, the check should not pass")
}
review.Request.Resource = v1.GroupVersionResource{Group: networking.SchemeGroupVersion.Group, Version: networking.SchemeGroupVersion.Version, Resource: "ingresses"}
review.Request.Resource = v1.GroupVersionResource{Group: networking.GroupName, Version: "v1beta1", Resource: "ingresses"}
review.Request.Object.Raw = []byte{0xff}
err = adm.HandleAdmission(review)
adm.HandleAdmission(review)
if review.Response.Allowed {
t.Errorf("when the request object is not decodable, the request should not be allowed")
}
if err == nil {
t.Errorf("when the request object is not decodable, an error should be returned")
t.Fatalf("when the request object is not decodable, the request should not be allowed")
}
raw, err := json.Marshal(networking.Ingress{ObjectMeta: v1.ObjectMeta{Name: testIngressName}})
if err != nil {
t.Errorf("failed to prepare test ingress data: %v", err.Error())
t.Fatalf("failed to prepare test ingress data: %v", err.Error())
}
review.Request.Object.Raw = raw
adm.Checker = testChecker{
t: t,
err: fmt.Errorf("this is a test error"),
}
err = adm.HandleAdmission(review)
adm.HandleAdmission(review)
if review.Response.Allowed {
t.Errorf("when the checker returns an error, the request should not be allowed")
}
if err == nil {
t.Errorf("when the checker returns an error, an error should be returned")
t.Fatalf("when the checker returns an error, the request should not be allowed")
}
adm.Checker = testChecker{
t: t,
err: nil,
}
err = adm.HandleAdmission(review)
adm.HandleAdmission(review)
if !review.Response.Allowed {
t.Errorf("when the checker returns no error, the request should be allowed")
}
if err != nil {
t.Errorf("when the checker returns no error, no error should be returned")
t.Fatalf("when the checker returns no error, the request should be allowed")
}
}

View file

@ -36,7 +36,7 @@ var (
// AdmissionController checks if an object
// is allowed in the cluster
type AdmissionController interface {
HandleAdmission(*v1beta1.AdmissionReview) error
HandleAdmission(*v1beta1.AdmissionReview)
}
// AdmissionControllerServer implements an HTTP server
@ -58,18 +58,16 @@ func NewAdmissionControllerServer(ac AdmissionController) *AdmissionControllerSe
// ServeHTTP implements http.Server method
func (acs *AdmissionControllerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
klog.Infof("handling admission controller request %s", r.URL.String())
review, err := parseAdmissionReview(acs.Decoder, r.Body)
if err != nil {
klog.Error("Can't decode request", err)
klog.Errorf("Unexpected error decoding request: %v", err)
w.WriteHeader(http.StatusBadRequest)
return
}
acs.AdmissionController.HandleAdmission(review)
if err := writeAdmissionReview(w, review); err != nil {
klog.Error(err)
klog.Errorf("Unexpected returning admission review: %v", err)
}
}

View file

@ -29,12 +29,10 @@ import (
type testAdmissionHandler struct{}
func (testAdmissionHandler) HandleAdmission(ar *v1beta1.AdmissionReview) error {
func (testAdmissionHandler) HandleAdmission(ar *v1beta1.AdmissionReview) {
ar.Response = &v1beta1.AdmissionResponse{
Allowed: true,
}
return nil
}
type errorReader struct{}

View file

@ -17,9 +17,8 @@ limitations under the License.
package class
import (
"k8s.io/klog"
networking "k8s.io/api/networking/v1beta1"
"k8s.io/ingress-nginx/internal/k8s"
)
const (
@ -39,25 +38,30 @@ var (
IngressClass = "nginx"
)
// IsValid returns true if the given Ingress either doesn't specify
// the ingress.class annotation, or it's set to the configured in the
// ingress controller.
// IsValid returns true if the given Ingress specify the ingress.class
// annotation or IngressClassName resource for Kubernetes >= v1.18
func IsValid(ing *networking.Ingress) bool {
// 1. with annotation
ingress, ok := ing.GetAnnotations()[IngressKey]
if !ok {
klog.V(3).Infof("annotation %v is not present in ingress %v/%v", IngressKey, ing.Namespace, ing.Name)
if ok {
// empty annotation and same annotation on ingress
if ingress == "" && IngressClass == DefaultClass {
return true
}
return ingress == IngressClass
}
// we have 2 valid combinations
// 1 - ingress with default class | blank annotation on ingress
// 2 - ingress with specific class | same annotation on ingress
//
// and 2 invalid combinations
// 3 - ingress with default class | fixed annotation on ingress
// 4 - ingress with specific class | different annotation on ingress
if ingress == "" && IngressClass == DefaultClass {
return true
// 2. k8s < v1.18. Check default annotation
if !k8s.IsIngressV1Ready {
return IngressClass == DefaultClass
}
return ingress == IngressClass
// 3. without annotation and IngressClass. Check default annotation
if k8s.IngressClass == nil {
return IngressClass == DefaultClass
}
// 4. with IngressClass
return k8s.IngressClass.Name == *ing.Spec.IngressClassName
}

View file

@ -25,6 +25,7 @@ import (
// Config contains ModSecurity Configuration items
type Config struct {
Enable bool `json:"enable-modsecurity"`
EnableSet bool `json:"enable-modsecurity-set"`
OWASPRules bool `json:"enable-owasp-core-rules"`
TransactionID string `json:"modsecurity-transaction-id"`
Snippet string `json:"modsecurity-snippet"`
@ -41,6 +42,9 @@ func (modsec1 *Config) Equal(modsec2 *Config) bool {
if modsec1.Enable != modsec2.Enable {
return false
}
if modsec1.EnableSet != modsec2.EnableSet {
return false
}
if modsec1.OWASPRules != modsec2.OWASPRules {
return false
}
@ -69,9 +73,11 @@ func (a modSecurity) Parse(ing *networking.Ingress) (interface{}, error) {
var err error
config := &Config{}
config.EnableSet = true
config.Enable, err = parser.GetBoolAnnotation("enable-modsecurity", ing)
if err != nil {
config.Enable = false
config.EnableSet = false
}
config.OWASPRules, err = parser.GetBoolAnnotation("enable-owasp-core-rules", ing)

View file

@ -41,22 +41,22 @@ func TestParse(t *testing.T) {
annotations map[string]string
expected Config
}{
{map[string]string{enable: "true"}, Config{true, false, "", ""}},
{map[string]string{enable: "false"}, Config{false, false, "", ""}},
{map[string]string{enable: ""}, Config{false, false, "", ""}},
{map[string]string{enable: "true"}, Config{true, true, false, "", ""}},
{map[string]string{enable: "false"}, Config{false, true, false, "", ""}},
{map[string]string{enable: ""}, Config{false, false, false, "", ""}},
{map[string]string{owasp: "true"}, Config{false, true, "", ""}},
{map[string]string{owasp: "false"}, Config{false, false, "", ""}},
{map[string]string{owasp: ""}, Config{false, false, "", ""}},
{map[string]string{owasp: "true"}, Config{false, false, true, "", ""}},
{map[string]string{owasp: "false"}, Config{false, false, false, "", ""}},
{map[string]string{owasp: ""}, Config{false, false, false, "", ""}},
{map[string]string{transID: "ok"}, Config{false, false, "ok", ""}},
{map[string]string{transID: ""}, Config{false, false, "", ""}},
{map[string]string{transID: "ok"}, Config{false, false, false, "ok", ""}},
{map[string]string{transID: ""}, Config{false, false, false, "", ""}},
{map[string]string{snippet: "ModSecurity Rule"}, Config{false, false, "", "ModSecurity Rule"}},
{map[string]string{snippet: ""}, Config{false, false, "", ""}},
{map[string]string{snippet: "ModSecurity Rule"}, Config{false, false, false, "", "ModSecurity Rule"}},
{map[string]string{snippet: ""}, Config{false, false, false, "", ""}},
{map[string]string{}, Config{false, false, "", ""}},
{nil, Config{false, false, "", ""}},
{map[string]string{}, Config{false, false, false, "", ""}},
{nil, Config{false, false, false, "", ""}},
}
ing := &networking.Ingress{

View file

@ -17,9 +17,13 @@ limitations under the License.
package rewrite
import (
"net/url"
networking "k8s.io/api/networking/v1beta1"
"k8s.io/klog"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver"
)
@ -90,8 +94,29 @@ func (a rewrite) Parse(ing *networking.Ingress) (interface{}, error) {
config.ForceSSLRedirect = a.r.GetDefaultBackend().ForceSSLRedirect
}
config.AppRoot, _ = parser.GetStringAnnotation("app-root", ing)
config.UseRegex, _ = parser.GetBoolAnnotation("use-regex", ing)
config.AppRoot, err = parser.GetStringAnnotation("app-root", ing)
if err != nil {
if !errors.IsMissingAnnotations(err) && !errors.IsInvalidContent(err) {
klog.Warningf("Annotation app-root contains an invalid value: %v", err)
}
return config, nil
}
u, err := url.ParseRequestURI(config.AppRoot)
if err != nil {
klog.Warningf("Annotation app-root contains an invalid value: %v", err)
config.AppRoot = ""
return config, nil
}
if u.IsAbs() {
klog.Warningf("Annotation app-root only allows absolute paths (%v)", config.AppRoot)
config.AppRoot = ""
return config, nil
}
return config, nil
}

View file

@ -41,8 +41,9 @@ func buildIngress() *networking.Ingress {
return &networking.Ingress{
ObjectMeta: meta_v1.ObjectMeta{
Name: "foo",
Namespace: api.NamespaceDefault,
Name: "foo",
Namespace: api.NamespaceDefault,
Annotations: map[string]string{},
},
Spec: networking.IngressSpec{
Backend: &networking.IngressBackend{
@ -163,19 +164,42 @@ func TestForceSSLRedirect(t *testing.T) {
}
}
func TestAppRoot(t *testing.T) {
ing := buildIngress()
ap := NewParser(mockBackend{redirect: true})
data := map[string]string{}
data[parser.GetAnnotationWithPrefix("app-root")] = "/app1"
ing.SetAnnotations(data)
i, _ := NewParser(mockBackend{redirect: true}).Parse(ing)
redirect, ok := i.(*Config)
if !ok {
t.Errorf("expected a App Context")
testCases := []struct {
title string
path string
expected string
errExpected bool
}{
{"Empty path should return an error", "", "", true},
{"Relative paths are not allowed", "demo", "", true},
{"Path / should pass", "/", "/", false},
{"Path /demo should pass", "/demo", "/demo", false},
}
if redirect.AppRoot != "/app1" {
t.Errorf("Unexpected value got in AppRoot")
for _, testCase := range testCases {
t.Run(testCase.title, func(t *testing.T) {
ing := buildIngress()
ing.Annotations[parser.GetAnnotationWithPrefix("app-root")] = testCase.path
i, err := ap.Parse(ing)
if err != nil {
if testCase.errExpected {
return
}
t.Fatalf("%v: unexpected error obtaining running address/es: %v", testCase.title, err)
}
rewrite, ok := i.(*Config)
if !ok {
t.Fatalf("expected a rewrite Config")
}
if testCase.expected != rewrite.AppRoot {
t.Fatalf("%v: expected AppRoot with value %v but was returned: %v", testCase.title, testCase.expected, rewrite.AppRoot)
}
})
}
}

View file

@ -129,6 +129,10 @@ type Configuration struct {
// By default this is disabled
EnableModsecurity bool `json:"enable-modsecurity"`
// EnableOCSP enables the OCSP support in SSL connections
// By default this is disabled
EnableOCSP bool `json:"enable-ocsp"`
// EnableOWASPCoreRules enables the OWASP ModSecurity Core Rule Set (CRS)
// By default this is disabled
EnableOWASPCoreRules bool `json:"enable-owasp-modsecurity-crs"`
@ -268,6 +272,11 @@ type Configuration struct {
NginxStatusIpv4Whitelist []string `json:"nginx-status-ipv4-whitelist,omitempty"`
NginxStatusIpv6Whitelist []string `json:"nginx-status-ipv6-whitelist,omitempty"`
// Plugins configures plugins to use placed in the directory /etc/nginx/lua/plugins.
// Every plugin has to have main.lua in the root. Every plugin has to bundle all of its dependencies.
// The execution order follows the definition.
Plugins []string `json:"plugins,omitempty"`
// If UseProxyProtocol is enabled ProxyRealIPCIDR defines the default the IP/network address
// of your external load balancer
ProxyRealIPCIDR []string `json:"proxy-real-ip-cidr,omitempty"`
@ -826,6 +835,7 @@ type TemplateConfig struct {
ListenPorts *ListenPorts
PublishService *apiv1.Service
EnableMetrics bool
MaxmindEditionFiles []string
PID string
StatusPath string

View file

@ -26,6 +26,7 @@ import (
"github.com/mitchellh/hashstructure"
apiv1 "k8s.io/api/core/v1"
networking "k8s.io/api/networking/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
@ -98,7 +99,8 @@ type Configuration struct {
ValidationWebhookCertPath string
ValidationWebhookKeyPath string
GlobalExternalAuth *ngx_config.GlobalExternalAuth
GlobalExternalAuth *ngx_config.GlobalExternalAuth
MaxmindEditionFiles []string
}
// GetPublishService returns the Service used to set the load-balancer status of Ingresses.
@ -197,23 +199,18 @@ func (n *NGINXController) syncIngress(interface{}) error {
// CheckIngress returns an error in case the provided ingress, when added
// to the current configuration, generates an invalid configuration
func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
//TODO: this is wrong
if n == nil {
return fmt.Errorf("cannot check ingress on a nil ingress controller")
}
if ing == nil {
// no ingress to add, no state change
return nil
}
if !class.IsValid(ing) {
klog.Infof("ignoring ingress %v in %v based on annotation %v", ing.Name, ing.ObjectMeta.Namespace, class.IngressKey)
klog.Warningf("ignoring ingress %v in %v based on annotation %v", ing.Name, ing.ObjectMeta.Namespace, class.IngressKey)
return nil
}
if n.cfg.Namespace != "" && ing.ObjectMeta.Namespace != n.cfg.Namespace {
klog.Infof("ignoring ingress %v in namespace %v different from the namespace watched %s", ing.Name, ing.ObjectMeta.Namespace, n.cfg.Namespace)
klog.Warningf("ignoring ingress %v in namespace %v different from the namespace watched %s", ing.Name, ing.ObjectMeta.Namespace, n.cfg.Namespace)
return nil
}
@ -222,6 +219,8 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
toCheck.ObjectMeta.Name == ing.ObjectMeta.Name
}
k8s.SetDefaultNGINXPathType(ing)
ings := n.store.ListIngresses(filter)
ings = append(ings, &ingress.Ingress{
Ingress: *ing,
@ -530,6 +529,12 @@ func (n *NGINXController) getBackendServers(ingresses []*ingress.Ingress) ([]*in
addLoc := true
for _, loc := range server.Locations {
if loc.Path == nginxPath {
// Same paths but different types are allowed
// (same type means overlap in the path definition)
if !apiequality.Semantic.DeepEqual(loc.PathType, path.PathType) {
break
}
addLoc = false
if !loc.IsDefBackend {
@ -546,6 +551,7 @@ func (n *NGINXController) getBackendServers(ingresses []*ingress.Ingress) ([]*in
loc.Port = ups.Port
loc.Service = ups.Service
loc.Ingress = ing
locationApplyAnnotations(loc, anns)
if loc.Redirect.FromToWWW {
@ -559,9 +565,9 @@ func (n *NGINXController) getBackendServers(ingresses []*ingress.Ingress) ([]*in
if addLoc {
klog.V(3).Infof("Adding location %q for server %q with upstream %q (Ingress %q)",
nginxPath, server.Hostname, ups.Name, ingKey)
loc := &ingress.Location{
Path: nginxPath,
PathType: path.PathType,
Backend: ups.Name,
IsDefBackend: false,
Service: ups.Service,
@ -956,12 +962,14 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
}
// initialize default server and root location
pathTypePrefix := networking.PathTypePrefix
servers[defServerName] = &ingress.Server{
Hostname: defServerName,
SSLCert: n.getDefaultSSLCertificate(),
Locations: []*ingress.Location{
{
Path: rootLocation,
PathType: &pathTypePrefix,
IsDefBackend: true,
Backend: du.Name,
Proxy: ngxProxy,
@ -1028,8 +1036,10 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
continue
}
pathTypePrefix := networking.PathTypePrefix
loc := &ingress.Location{
Path: rootLocation,
PathType: &pathTypePrefix,
IsDefBackend: true,
Backend: un,
Service: &apiv1.Service{},
@ -1065,7 +1075,7 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
if len(servers[host].Aliases) == 0 {
servers[host].Aliases = anns.Aliases
if _, ok := allAliases[host]; !ok {
if aliases := allAliases[host]; len(aliases) == 0 {
allAliases[host] = anns.Aliases
}
} else {
@ -1314,7 +1324,7 @@ func mergeAlternativeBackends(ing *ingress.Ingress, upstreams map[string]*ingres
break
}
if canMergeBackend(priUps, altUps) && loc.Path == path.Path {
if canMergeBackend(priUps, altUps) && loc.Path == path.Path && *loc.PathType == *path.PathType {
klog.V(2).Infof("matching backend %v found for alternative backend %v",
priUps.Name, altUps.Name)
@ -1340,9 +1350,12 @@ func extractTLSSecretName(host string, ing *ingress.Ingress,
}
// naively return Secret name from TLS spec if host name matches
lowercaseHost := toLowerCaseASCII(host)
for _, tls := range ing.Spec.TLS {
if sets.NewString(tls.Hosts...).Has(host) {
return tls.SecretName
for _, tlsHost := range tls.Hosts {
if toLowerCaseASCII(tlsHost) == lowercaseHost {
return tls.SecretName
}
}
}

View file

@ -17,6 +17,7 @@ limitations under the License.
package controller
import (
"context"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
@ -256,6 +257,8 @@ func TestCheckIngress(t *testing.T) {
})
}
var pathPrefix = networking.PathTypePrefix
func TestMergeAlternativeBackends(t *testing.T) {
testCases := map[string]struct {
ingress *ingress.Ingress
@ -278,7 +281,8 @@ func TestMergeAlternativeBackends(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/",
Path: "/",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "http-svc-canary",
ServicePort: intstr.IntOrString{
@ -313,8 +317,9 @@ func TestMergeAlternativeBackends(t *testing.T) {
Hostname: "example.com",
Locations: []*ingress.Location{
{
Path: "/",
Backend: "example-http-svc-80",
Path: "/",
PathType: &pathPrefix,
Backend: "example-http-svc-80",
},
},
},
@ -338,8 +343,9 @@ func TestMergeAlternativeBackends(t *testing.T) {
Hostname: "example.com",
Locations: []*ingress.Location{
{
Path: "/",
Backend: "example-http-svc-80",
Path: "/",
PathType: &pathPrefix,
Backend: "example-http-svc-80",
},
},
},
@ -359,7 +365,8 @@ func TestMergeAlternativeBackends(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/",
Path: "/",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "foo-http-svc-canary",
ServicePort: intstr.IntOrString{
@ -378,7 +385,8 @@ func TestMergeAlternativeBackends(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/",
Path: "/",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "http-svc-canary",
ServicePort: intstr.IntOrString{
@ -425,8 +433,9 @@ func TestMergeAlternativeBackends(t *testing.T) {
Hostname: "foo.bar",
Locations: []*ingress.Location{
{
Path: "/",
Backend: "example-foo-http-svc-80",
Path: "/",
PathType: &pathPrefix,
Backend: "example-foo-http-svc-80",
},
},
},
@ -434,8 +443,9 @@ func TestMergeAlternativeBackends(t *testing.T) {
Hostname: "example.com",
Locations: []*ingress.Location{
{
Path: "/",
Backend: "example-http-svc-80",
Path: "/",
PathType: &pathPrefix,
Backend: "example-http-svc-80",
},
},
},
@ -471,8 +481,9 @@ func TestMergeAlternativeBackends(t *testing.T) {
Hostname: "example.com",
Locations: []*ingress.Location{
{
Path: "/",
Backend: "example-http-svc-80",
Path: "/",
PathType: &pathPrefix,
Backend: "example-http-svc-80",
},
},
},
@ -492,7 +503,8 @@ func TestMergeAlternativeBackends(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/",
Path: "/",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "http-svc-canary",
ServicePort: intstr.IntOrString{
@ -556,8 +568,9 @@ func TestMergeAlternativeBackends(t *testing.T) {
Hostname: "_",
Locations: []*ingress.Location{
{
Path: "/",
Backend: "example-http-svc-80",
Path: "/",
PathType: &pathPrefix,
Backend: "example-http-svc-80",
},
},
},
@ -581,8 +594,9 @@ func TestMergeAlternativeBackends(t *testing.T) {
Hostname: "_",
Locations: []*ingress.Location{
{
Path: "/",
Backend: "example-http-svc-80",
Path: "/",
PathType: &pathPrefix,
Backend: "example-http-svc-80",
},
},
},
@ -622,8 +636,9 @@ func TestMergeAlternativeBackends(t *testing.T) {
Hostname: "_",
Locations: []*ingress.Location{
{
Path: "/",
Backend: "upstream-default-backend",
Path: "/",
PathType: &pathPrefix,
Backend: "upstream-default-backend",
},
},
},
@ -634,8 +649,9 @@ func TestMergeAlternativeBackends(t *testing.T) {
Hostname: "_",
Locations: []*ingress.Location{
{
Path: "/",
Backend: "upstream-default-backend",
Path: "/",
PathType: &pathPrefix,
Backend: "upstream-default-backend",
},
},
},
@ -802,6 +818,33 @@ func TestExtractTLSSecretName(t *testing.T) {
},
"demo",
},
"ingress tls, hosts, matching cert cn, uppercase host": {
"FOO.BAR",
&ingress.Ingress{
Ingress: networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
},
Spec: networking.IngressSpec{
TLS: []networking.IngressTLS{
{
Hosts: []string{"foo.bar", "example.com"},
SecretName: "demo",
},
},
Rules: []networking.IngressRule{
{
Host: "foo.bar",
},
},
},
},
},
func(string) (*ingress.SSLCert, error) {
return nil, nil
},
"demo",
},
}
for title, tc := range testCases {
@ -942,7 +985,8 @@ func TestGetBackendServers(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/",
Path: "/",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "http-svc-canary",
ServicePort: intstr.IntOrString{
@ -1001,7 +1045,8 @@ func TestGetBackendServers(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/",
Path: "/",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "http-svc",
ServicePort: intstr.IntOrString{
@ -1037,7 +1082,8 @@ func TestGetBackendServers(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/",
Path: "/",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "http-svc-canary",
ServicePort: intstr.IntOrString{
@ -1105,7 +1151,8 @@ func TestGetBackendServers(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/a",
Path: "/a",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "http-svc-1",
ServicePort: intstr.IntOrString{
@ -1141,7 +1188,8 @@ func TestGetBackendServers(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/a",
Path: "/a",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "http-svc-2",
ServicePort: intstr.IntOrString{
@ -1177,7 +1225,8 @@ func TestGetBackendServers(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/b",
Path: "/b",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "http-svc-2",
ServicePort: intstr.IntOrString{
@ -1213,7 +1262,8 @@ func TestGetBackendServers(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/b",
Path: "/b",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "http-svc-1",
ServicePort: intstr.IntOrString{
@ -1249,7 +1299,8 @@ func TestGetBackendServers(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/c",
Path: "/c",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "http-svc-1",
ServicePort: intstr.IntOrString{
@ -1285,7 +1336,8 @@ func TestGetBackendServers(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/c",
Path: "/c",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "http-svc-2",
ServicePort: intstr.IntOrString{
@ -1369,7 +1421,8 @@ func TestGetBackendServers(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/path1",
Path: "/path1",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "path1-svc",
ServicePort: intstr.IntOrString{
@ -1408,7 +1461,8 @@ func TestGetBackendServers(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/path2",
Path: "/path2",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "path2-svc",
ServicePort: intstr.IntOrString{
@ -1472,7 +1526,8 @@ func TestGetBackendServers(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/path1",
Path: "/path1",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "path1-svc",
ServicePort: intstr.IntOrString{
@ -1511,7 +1566,8 @@ func TestGetBackendServers(t *testing.T) {
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/path2",
Path: "/path2",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
ServiceName: "path2-svc",
ServicePort: intstr.IntOrString{
@ -1606,7 +1662,7 @@ func newNGINXController(t *testing.T) *NGINXController {
},
}
_, err := clientSet.CoreV1().ConfigMaps(ns).Create(configMap)
_, err := clientSet.CoreV1().ConfigMaps(ns).Create(context.TODO(), configMap, metav1.CreateOptions{})
if err != nil {
t.Fatalf("error creating the configuration map: %v", err)
}
@ -1662,7 +1718,7 @@ func newDynamicNginxController(t *testing.T, setConfigMap func(string) *v1.Confi
clientSet := fake.NewSimpleClientset()
configMap := setConfigMap(ns)
_, err := clientSet.CoreV1().ConfigMaps(ns).Create(configMap)
_, err := clientSet.CoreV1().ConfigMaps(ns).Create(context.TODO(), configMap, metav1.CreateOptions{})
if err != nil {
t.Fatalf("error creating the configuration map: %v", err)
}

View file

@ -615,12 +615,12 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC
ListenPorts: n.cfg.ListenPorts,
PublishService: n.GetPublishService(),
EnableMetrics: n.cfg.EnableMetrics,
HealthzURI: nginx.HealthPath,
PID: nginx.PID,
StatusPath: nginx.StatusPath,
StatusPort: nginx.StatusPort,
StreamPort: nginx.StreamPort,
MaxmindEditionFiles: n.cfg.MaxmindEditionFiles,
HealthzURI: nginx.HealthPath,
PID: nginx.PID,
StatusPath: nginx.StatusPath,
StatusPort: nginx.StatusPort,
StreamPort: nginx.StreamPort,
}
tc.Cfg.Checksum = ingressCfg.ConfigurationChecksum

View file

@ -17,6 +17,7 @@ limitations under the License.
package store
import (
"context"
"encoding/base64"
"fmt"
"io/ioutil"
@ -25,8 +26,6 @@ import (
"sync"
"time"
"k8s.io/klog"
"github.com/eapache/channels"
corev1 "k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
@ -35,6 +34,7 @@ import (
"k8s.io/apimachinery/pkg/labels"
k8sruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/informers"
clientset "k8s.io/client-go/kubernetes"
@ -42,6 +42,7 @@ import (
clientcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/klog"
"k8s.io/ingress-nginx/internal/file"
"k8s.io/ingress-nginx/internal/ingress"
@ -290,11 +291,11 @@ func New(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (k8sruntime.Object, error) {
options.LabelSelector = labelSelector.String()
return client.CoreV1().Pods(store.pod.Namespace).List(options)
return client.CoreV1().Pods(store.pod.Namespace).List(context.TODO(), options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
options.LabelSelector = labelSelector.String()
return client.CoreV1().Pods(store.pod.Namespace).Watch(options)
return client.CoreV1().Pods(store.pod.Namespace).Watch(context.TODO(), options)
},
},
&corev1.Pod{},
@ -614,16 +615,32 @@ func New(
},
}
serviceHandler := cache.ResourceEventHandlerFuncs{
UpdateFunc: func(old, cur interface{}) {
oldSvc := old.(*corev1.Service)
curSvc := cur.(*corev1.Service)
if reflect.DeepEqual(oldSvc, curSvc) {
return
}
updateCh.In() <- Event{
Type: UpdateEvent,
Obj: cur,
}
},
}
store.informers.Ingress.AddEventHandler(ingEventHandler)
store.informers.Endpoint.AddEventHandler(epEventHandler)
store.informers.Secret.AddEventHandler(secrEventHandler)
store.informers.ConfigMap.AddEventHandler(cmEventHandler)
store.informers.Service.AddEventHandler(cache.ResourceEventHandlerFuncs{})
store.informers.Service.AddEventHandler(serviceHandler)
store.informers.Pod.AddEventHandler(podEventHandler)
// do not wait for informers to read the configmap configuration
ns, name, _ := k8s.ParseNameNS(configmap)
cm, err := client.CoreV1().ConfigMaps(ns).Get(name, metav1.GetOptions{})
cm, err := client.CoreV1().ConfigMaps(ns).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
klog.Warningf("Unexpected error reading configuration configmap: %v", err)
}
@ -638,6 +655,9 @@ func isCatchAllIngress(spec networkingv1beta1.IngressSpec) bool {
return spec.Backend != nil && len(spec.Rules) == 0
}
// Default path type is Prefix to not break existing definitions
var defaultPathType = networkingv1beta1.PathTypePrefix
// syncIngress parses ingress annotations converting the value of the
// annotation to a go struct
func (s *k8sStore) syncIngress(ing *networkingv1beta1.Ingress) {
@ -661,6 +681,8 @@ func (s *k8sStore) syncIngress(ing *networkingv1beta1.Ingress) {
}
}
k8s.SetDefaultNGINXPathType(copyIng)
err := s.listers.IngressWithAnnotation.Update(&ingress.Ingress{
Ingress: *copyIng,
ParsedAnnotations: s.annotations.Extract(ing),
@ -924,8 +946,8 @@ func (s k8sStore) GetRunningControllerPodsCount() int {
var runtimeScheme = k8sruntime.NewScheme()
func init() {
extensionsv1beta1.AddToScheme(runtimeScheme)
networkingv1beta1.AddToScheme(runtimeScheme)
utilruntime.Must(extensionsv1beta1.AddToScheme(runtimeScheme))
utilruntime.Must(networkingv1beta1.AddToScheme(runtimeScheme))
}
func fromExtensions(old *extensionsv1beta1.Ingress) (*networkingv1beta1.Ingress, error) {
@ -948,10 +970,12 @@ func toIngress(obj interface{}) (*networkingv1beta1.Ingress, bool) {
return nil, false
}
k8s.SetDefaultNGINXPathType(ing)
return ing, true
}
if ing, ok := obj.(*networkingv1beta1.Ingress); ok {
k8s.SetDefaultNGINXPathType(ing)
return ing, true
}

View file

@ -18,6 +18,7 @@ package store
import (
"bytes"
"context"
"encoding/base64"
"fmt"
"io/ioutil"
@ -252,7 +253,7 @@ func TestStore(t *testing.T) {
// Secret takes a bit to update
time.Sleep(3 * time.Second)
err = clientSet.NetworkingV1beta1().Ingresses(ni.Namespace).Delete(ni.Name, &metav1.DeleteOptions{})
err = clientSet.NetworkingV1beta1().Ingresses(ni.Namespace).Delete(context.TODO(), ni.Name, metav1.DeleteOptions{})
if err != nil {
t.Errorf("error creating ingress: %v", err)
}
@ -451,7 +452,7 @@ func TestStore(t *testing.T) {
t.Errorf("expected 0 events of type Delete but %v occurred", del)
}
err = clientSet.CoreV1().Secrets(ns).Delete(secretName, &metav1.DeleteOptions{})
err = clientSet.CoreV1().Secrets(ns).Delete(context.TODO(), secretName, metav1.DeleteOptions{})
if err != nil {
t.Errorf("error deleting secret: %v", err)
}
@ -565,7 +566,7 @@ func TestStore(t *testing.T) {
t.Errorf("expected 1 events of type Update but %v occurred", upd)
}
err = clientSet.CoreV1().Secrets(ns).Delete(secretName, &metav1.DeleteOptions{})
err = clientSet.CoreV1().Secrets(ns).Delete(context.TODO(), secretName, metav1.DeleteOptions{})
if err != nil {
t.Errorf("error deleting secret: %v", err)
}
@ -707,7 +708,7 @@ func createNamespace(clientSet kubernetes.Interface, t *testing.T) string {
},
}
ns, err := clientSet.CoreV1().Namespaces().Create(namespace)
ns, err := clientSet.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{})
if err != nil {
t.Errorf("error creating the namespace: %v", err)
}
@ -718,7 +719,7 @@ func createNamespace(clientSet kubernetes.Interface, t *testing.T) string {
func deleteNamespace(ns string, clientSet kubernetes.Interface, t *testing.T) {
t.Helper()
err := clientSet.CoreV1().Namespaces().Delete(ns, &metav1.DeleteOptions{})
err := clientSet.CoreV1().Namespaces().Delete(context.TODO(), ns, metav1.DeleteOptions{})
if err != nil {
t.Errorf("error deleting the namespace: %v", err)
}
@ -733,7 +734,7 @@ func createConfigMap(clientSet kubernetes.Interface, ns string, t *testing.T) st
},
}
cm, err := clientSet.CoreV1().ConfigMaps(ns).Create(configMap)
cm, err := clientSet.CoreV1().ConfigMaps(ns).Create(context.TODO(), configMap, metav1.CreateOptions{})
if err != nil {
t.Errorf("error creating the configuration map: %v", err)
}
@ -743,13 +744,13 @@ func createConfigMap(clientSet kubernetes.Interface, ns string, t *testing.T) st
func ensureIngress(ingress *networking.Ingress, clientSet kubernetes.Interface, t *testing.T) *networking.Ingress {
t.Helper()
ing, err := clientSet.NetworkingV1beta1().Ingresses(ingress.Namespace).Update(ingress)
ing, err := clientSet.NetworkingV1beta1().Ingresses(ingress.Namespace).Update(context.TODO(), ingress, metav1.UpdateOptions{})
if err != nil {
if k8sErrors.IsNotFound(err) {
t.Logf("Ingress %v not found, creating", ingress)
ing, err = clientSet.NetworkingV1beta1().Ingresses(ingress.Namespace).Create(ingress)
ing, err = clientSet.NetworkingV1beta1().Ingresses(ingress.Namespace).Create(context.TODO(), ingress, metav1.CreateOptions{})
if err != nil {
t.Fatalf("error creating ingress %+v: %v", ingress, err)
}
@ -766,7 +767,7 @@ func ensureIngress(ingress *networking.Ingress, clientSet kubernetes.Interface,
func deleteIngress(ingress *networking.Ingress, clientSet kubernetes.Interface, t *testing.T) {
t.Helper()
err := clientSet.NetworkingV1beta1().Ingresses(ingress.Namespace).Delete(ingress.Name, &metav1.DeleteOptions{})
err := clientSet.NetworkingV1beta1().Ingresses(ingress.Namespace).Delete(context.TODO(), ingress.Name, metav1.DeleteOptions{})
if err != nil {
t.Errorf("failed to delete ingress %+v: %v", ingress, err)

View file

@ -61,6 +61,7 @@ const (
globalAuthCacheKey = "global-auth-cache-key"
globalAuthCacheDuration = "global-auth-cache-duration"
luaSharedDictsKey = "lua-shared-dicts"
plugins = "plugins"
)
var (
@ -72,6 +73,7 @@ var (
"balancer_ewma_last_touched_at": 10,
"balancer_ewma_locks": 1,
"certificate_servers": 5,
"ocsp_response_cache": 5, // keep this same as certificate_servers
}
)
@ -107,7 +109,7 @@ func ReadConfig(src map[string]string) config.Configuration {
//parse lua shared dict values
if val, ok := conf[luaSharedDictsKey]; ok {
delete(conf, luaSharedDictsKey)
lsd := strings.Split(val, ",")
lsd := splitAndTrimSpace(val, ",")
for _, v := range lsd {
v = strings.Replace(v, " ", "", -1)
results := strings.SplitN(v, ":", 2)
@ -136,9 +138,10 @@ func ReadConfig(src map[string]string) config.Configuration {
luaSharedDicts[k] = v
}
}
if val, ok := conf[customHTTPErrors]; ok {
delete(conf, customHTTPErrors)
for _, i := range strings.Split(val, ",") {
for _, i := range splitAndTrimSpace(val, ",") {
j, err := strconv.Atoi(i)
if err != nil {
klog.Warningf("%v is not a valid http code: %v", i, err)
@ -147,27 +150,32 @@ func ReadConfig(src map[string]string) config.Configuration {
}
}
}
if val, ok := conf[hideHeaders]; ok {
delete(conf, hideHeaders)
hideHeadersList = strings.Split(val, ",")
hideHeadersList = splitAndTrimSpace(val, ",")
}
if val, ok := conf[skipAccessLogUrls]; ok {
delete(conf, skipAccessLogUrls)
skipUrls = strings.Split(val, ",")
skipUrls = splitAndTrimSpace(val, ",")
}
if val, ok := conf[whitelistSourceRange]; ok {
delete(conf, whitelistSourceRange)
whiteList = append(whiteList, strings.Split(val, ",")...)
whiteList = append(whiteList, splitAndTrimSpace(val, ",")...)
}
if val, ok := conf[proxyRealIPCIDR]; ok {
delete(conf, proxyRealIPCIDR)
proxyList = append(proxyList, strings.Split(val, ",")...)
proxyList = append(proxyList, splitAndTrimSpace(val, ",")...)
} else {
proxyList = append(proxyList, "0.0.0.0/0")
}
if val, ok := conf[bindAddress]; ok {
delete(conf, bindAddress)
for _, i := range strings.Split(val, ",") {
for _, i := range splitAndTrimSpace(val, ",") {
ns := net.ParseIP(i)
if ns != nil {
if ing_net.IsIPV6(ns) {
@ -183,15 +191,17 @@ func ReadConfig(src map[string]string) config.Configuration {
if val, ok := conf[blockCIDRs]; ok {
delete(conf, blockCIDRs)
blockCIDRList = strings.Split(val, ",")
blockCIDRList = splitAndTrimSpace(val, ",")
}
if val, ok := conf[blockUserAgents]; ok {
delete(conf, blockUserAgents)
blockUserAgentList = strings.Split(val, ",")
blockUserAgentList = splitAndTrimSpace(val, ",")
}
if val, ok := conf[blockReferers]; ok {
delete(conf, blockReferers)
blockRefererList = strings.Split(val, ",")
blockRefererList = splitAndTrimSpace(val, ",")
}
if val, ok := conf[httpRedirectCode]; ok {
@ -249,15 +259,12 @@ func ReadConfig(src map[string]string) config.Configuration {
delete(conf, globalAuthResponseHeaders)
if len(val) != 0 {
harr := strings.Split(val, ",")
harr := splitAndTrimSpace(val, ",")
for _, header := range harr {
header = strings.TrimSpace(header)
if len(header) > 0 {
if !authreq.ValidHeader(header) {
klog.Warningf("Global auth location denied - %v.", "invalid headers list")
} else {
responseHeaders = append(responseHeaders, header)
}
if !authreq.ValidHeader(header) {
klog.Warningf("Global auth location denied - %v.", "invalid headers list")
} else {
responseHeaders = append(responseHeaders, header)
}
}
}
@ -266,19 +273,16 @@ func ReadConfig(src map[string]string) config.Configuration {
if val, ok := conf[globalAuthRequestRedirect]; ok {
delete(conf, globalAuthRequestRedirect)
to.GlobalExternalAuth.RequestRedirect = val
}
if val, ok := conf[globalAuthSnippet]; ok {
delete(conf, globalAuthSnippet)
to.GlobalExternalAuth.AuthSnippet = val
}
if val, ok := conf[globalAuthCacheKey]; ok {
delete(conf, globalAuthCacheKey)
to.GlobalExternalAuth.AuthCacheKey = val
}
@ -317,23 +321,17 @@ func ReadConfig(src map[string]string) config.Configuration {
// Nginx Status whitelist
if val, ok := conf[nginxStatusIpv4Whitelist]; ok {
whitelist := make([]string, 0)
whitelist = append(whitelist, strings.Split(val, ",")...)
to.NginxStatusIpv4Whitelist = whitelist
to.NginxStatusIpv4Whitelist = splitAndTrimSpace(val, ",")
delete(conf, nginxStatusIpv4Whitelist)
}
if val, ok := conf[nginxStatusIpv6Whitelist]; ok {
whitelist := make([]string, 0)
whitelist = append(whitelist, strings.Split(val, ",")...)
to.NginxStatusIpv6Whitelist = whitelist
if val, ok := conf[nginxStatusIpv6Whitelist]; ok {
to.NginxStatusIpv6Whitelist = splitAndTrimSpace(val, ",")
delete(conf, nginxStatusIpv6Whitelist)
}
if val, ok := conf[workerProcesses]; ok {
to.WorkerProcesses = val
if val == "auto" {
to.WorkerProcesses = strconv.Itoa(runtime.NumCPU())
}
@ -341,6 +339,11 @@ func ReadConfig(src map[string]string) config.Configuration {
delete(conf, workerProcesses)
}
if val, ok := conf[plugins]; ok {
to.Plugins = splitAndTrimSpace(val, ",")
delete(conf, plugins)
}
to.CustomHTTPErrors = filterErrors(errors)
to.SkipAccessLogURLs = skipUrls
to.WhitelistSourceRange = whiteList
@ -395,3 +398,16 @@ func filterErrors(codes []int) []int {
return fa
}
func splitAndTrimSpace(s, sep string) []string {
f := func(c rune) bool {
return strings.EqualFold(string(c), sep)
}
values := strings.FieldsFunc(s, f)
for i := range values {
values[i] = strings.TrimSpace(values[i])
}
return values
}

View file

@ -358,3 +358,39 @@ func TestLuaSharedDictsParsing(t *testing.T) {
}
}
}
func TestSplitAndTrimSpace(t *testing.T) {
testsCases := []struct {
name string
input string
expect []string
}{
{
name: "empty string",
input: "",
expect: []string{},
},
{
name: "two elements",
input: "el1,el2",
expect: []string{"el1", "el2"},
},
{
name: "two elements with spaces",
input: " el1, el2",
expect: []string{"el1", "el2"},
},
{
name: "empty elements with spaces",
input: " el1, el2,el3,,",
expect: []string{"el1", "el2", "el3"},
},
}
for _, tc := range testsCases {
data := splitAndTrimSpace(tc.input, ",")
if !reflect.DeepEqual(data, tc.expect) {
t.Errorf("Testing %v. Expected \"%v\" but \"%v\" was returned", tc.name, tc.expect, data)
}
}
}

View file

@ -39,6 +39,7 @@ import (
"github.com/pkg/errors"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog"
@ -171,7 +172,6 @@ var (
"proxySetHeader": proxySetHeader,
"buildInfluxDB": buildInfluxDB,
"enforceRegexModifier": enforceRegexModifier,
"stripLocationModifer": stripLocationModifer,
"buildCustomErrorDeps": buildCustomErrorDeps,
"buildCustomErrorLocationsPerServer": buildCustomErrorLocationsPerServer,
"shouldLoadModSecurityModule": shouldLoadModSecurityModule,
@ -373,10 +373,6 @@ func needsRewrite(location *ingress.Location) bool {
return false
}
func stripLocationModifer(path string) string {
return strings.TrimLeft(path, "~* ")
}
// enforceRegexModifier checks if the "rewrite-target" or "use-regex" annotation
// is used on any location path within a server
func enforceRegexModifier(input interface{}) bool {
@ -407,6 +403,11 @@ func buildLocation(input interface{}, enforceRegex bool) string {
if enforceRegex {
return fmt.Sprintf(`~* "^%s"`, path)
}
if location.PathType != nil && *location.PathType == networkingv1beta1.PathTypeExact {
return fmt.Sprintf(`= %s`, path)
}
return path
}
@ -557,7 +558,6 @@ rewrite "(?i)%s" %s break;
return defProxyPass
}
// TODO: Needs Unit Tests
func filterRateLimits(input interface{}) []ratelimit.Config {
ratelimits := []ratelimit.Config{}
found := sets.String{}
@ -578,7 +578,6 @@ func filterRateLimits(input interface{}) []ratelimit.Config {
return ratelimits
}
// TODO: Needs Unit Tests
// buildRateLimitZones produces an array of limit_conn_zone in order to allow
// rate limiting of request. Each Ingress rule could have up to three zones, one
// for connection limit by IP address, one for limiting requests per minute, and
@ -1345,26 +1344,21 @@ func shouldLoadOpentracingModule(c interface{}, s interface{}) bool {
func buildModSecurityForLocation(cfg config.Configuration, location *ingress.Location) string {
isMSEnabledInLoc := location.ModSecurity.Enable
isMSEnableSetInLoc := location.ModSecurity.EnableSet
isMSEnabled := cfg.EnableModsecurity
if !isMSEnabled && !isMSEnabledInLoc {
return ""
}
if !isMSEnabledInLoc {
return ""
if isMSEnableSetInLoc && !isMSEnabledInLoc {
return "modsecurity off;"
}
var buffer bytes.Buffer
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;
`)
}
@ -1380,6 +1374,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()
}

View file

@ -1221,15 +1221,6 @@ func TestEnforceRegexModifier(t *testing.T) {
}
}
func TestStripLocationModifer(t *testing.T) {
expected := "ok.com"
actual := stripLocationModifer("~*ok.com")
if expected != actual {
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
}
}
func TestShouldLoadModSecurityModule(t *testing.T) {
// ### Invalid argument type tests ###
// The first tests should return false.
@ -1373,9 +1364,13 @@ 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;"
modsecRule := `modsecurity_rules '
#RULE#
';
@ -1394,30 +1389,34 @@ modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf;
isEnabledInCM bool
isOwaspEnabledInCM bool
isEnabledInLoc bool
isEnableSetInLoc bool
isOwaspEnabledInLoc bool
snippet string
transactionID string
expected string
}{
{"configmap enabled, configmap OWASP disabled, without annotation, snippet or transaction ID", true, false, false, false, "", "", ""},
{"configmap enabled, configmap OWASP disabled, without annotation, snippet and with transaction ID", true, false, false, false, "", transactionID, ""},
{"configmap enabled, configmap OWASP enabled, without annotation, OWASP enabled", true, true, false, false, "", "", ""},
{"configmap enabled, configmap OWASP enabled, without annotation, OWASP disabled, with snippet and no transaction ID", true, true, true, false, testRule, "", modsecRule},
{"configmap enabled, configmap OWASP enabled, without annotation, OWASP disabled, with snippet and transaction ID", true, true, true, false, testRule, transactionID, fmt.Sprintf("%v%v", modsecRule, transactionCfg)},
{"configmap enabled, with annotation, OWASP disabled", true, false, true, false, "", "", ""},
{"configmap enabled, configmap OWASP disabled, with annotation, OWASP enabled, no snippet and no transaction ID", true, false, true, true, "", "", owaspRules},
{"configmap enabled, configmap OWASP disabled, with annotation, OWASP enabled, with snippet and no transaction ID", true, false, true, true, "", "", owaspRules},
{"configmap enabled, configmap OWASP disabled, with annotation, OWASP enabled, with snippet and transaction ID", true, false, true, true, "", transactionID, fmt.Sprintf("%v%v", owaspRules, transactionCfg)},
{"configmap enabled, OWASP configmap enabled, with annotation, OWASP disabled", true, true, true, false, "", "", ""},
{"configmap disabled, with annotation, OWASP disabled", false, false, true, false, "", "", loadModule},
{"configmap disabled, with annotation, OWASP disabled", false, false, true, false, testRule, "", fmt.Sprintf("%v%v", loadModule, modsecRule)},
{"configmap disabled, with annotation, OWASP enabled", false, false, true, false, testRule, "", fmt.Sprintf("%v%v", loadModule, modsecRule)},
{"configmap enabled, configmap OWASP disabled, without annotation, snippet or transaction ID", true, false, false, false, false, "", "", ""},
{"configmap enabled, configmap OWASP disabled, without annotation, snippet and with transaction ID", true, false, false, false, false, "", transactionID, transactionCfg},
{"configmap enabled, configmap OWASP enabled, without annotation, OWASP enabled", true, true, false, false, false, "", "", ""},
{"configmap enabled, configmap OWASP enabled, without annotation, OWASP disabled, with snippet and no transaction ID", true, true, false, false, false, testRule, "", modsecRule},
{"configmap enabled, configmap OWASP enabled, without annotation, OWASP disabled, with snippet and transaction ID", true, true, false, false, false, testRule, transactionID, fmt.Sprintf("%v%v", modsecRule, transactionCfg)},
{"configmap enabled, configmap OWASP disabled, annotation enabled, OWASP disabled", true, false, true, true, false, "", "", ""},
{"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", 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, "", "", 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%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 {
il := &ingress.Location{
ModSecurity: modsecurity.Config{
Enable: testCase.isEnabledInLoc,
EnableSet: testCase.isEnableSetInLoc,
OWASPRules: testCase.isOwaspEnabledInLoc,
Snippet: testCase.snippet,
TransactionID: testCase.transactionID,

View file

@ -17,6 +17,7 @@ limitations under the License.
package status
import (
"context"
"fmt"
"net"
"sort"
@ -180,7 +181,7 @@ func (s *statusSync) runningAddresses() ([]string, error) {
}
// get information about all the pods running the ingress controller
pods, err := s.Client.CoreV1().Pods(s.pod.Namespace).List(metav1.ListOptions{
pods, err := s.Client.CoreV1().Pods(s.pod.Namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: labels.SelectorFromSet(s.pod.Labels).String(),
})
if err != nil {
@ -204,7 +205,7 @@ func (s *statusSync) runningAddresses() ([]string, error) {
}
func (s *statusSync) isRunningMultiplePods() bool {
pods, err := s.Client.CoreV1().Pods(s.pod.Namespace).List(metav1.ListOptions{
pods, err := s.Client.CoreV1().Pods(s.pod.Namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: labels.SelectorFromSet(s.pod.Labels).String(),
})
if err != nil {
@ -266,27 +267,27 @@ func runUpdate(ing *ingress.Ingress, status []apiv1.LoadBalancerIngress,
if k8s.IsNetworkingIngressAvailable {
ingClient := client.NetworkingV1beta1().Ingresses(ing.Namespace)
currIng, err := ingClient.Get(ing.Name, metav1.GetOptions{})
currIng, err := ingClient.Get(context.TODO(), ing.Name, metav1.GetOptions{})
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("unexpected error searching Ingress %v/%v", ing.Namespace, ing.Name))
}
klog.Infof("updating Ingress %v/%v status from %v to %v", currIng.Namespace, currIng.Name, currIng.Status.LoadBalancer.Ingress, status)
currIng.Status.LoadBalancer.Ingress = status
_, err = ingClient.UpdateStatus(currIng)
_, err = ingClient.UpdateStatus(context.TODO(), currIng, metav1.UpdateOptions{})
if err != nil {
klog.Warningf("error updating ingress rule: %v", err)
}
} else {
ingClient := client.ExtensionsV1beta1().Ingresses(ing.Namespace)
currIng, err := ingClient.Get(ing.Name, metav1.GetOptions{})
currIng, err := ingClient.Get(context.TODO(), ing.Name, metav1.GetOptions{})
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("unexpected error searching Ingress %v/%v", ing.Namespace, ing.Name))
}
klog.Infof("updating Ingress %v/%v status from %v to %v", currIng.Namespace, currIng.Name, currIng.Status.LoadBalancer.Ingress, status)
currIng.Status.LoadBalancer.Ingress = status
_, err = ingClient.UpdateStatus(currIng)
_, err = ingClient.UpdateStatus(context.TODO(), currIng, metav1.UpdateOptions{})
if err != nil {
klog.Warningf("error updating ingress rule: %v", err)
}
@ -327,7 +328,7 @@ func ingressSliceEqual(lhs, rhs []apiv1.LoadBalancerIngress) bool {
func statusAddressFromService(service string, kubeClient clientset.Interface) ([]string, error) {
ns, name, _ := k8s.ParseNameNS(service)
svc, err := kubeClient.CoreV1().Services(ns).Get(name, metav1.GetOptions{})
svc, err := kubeClient.CoreV1().Services(ns).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return nil, err
}

View file

@ -17,6 +17,7 @@ limitations under the License.
package status
import (
"context"
"os"
"reflect"
"testing"
@ -322,7 +323,7 @@ func TestStatusActions(t *testing.T) {
newIPs := []apiv1.LoadBalancerIngress{{
IP: "11.0.0.2",
}}
fooIngress1, err1 := fk.Client.NetworkingV1beta1().Ingresses(apiv1.NamespaceDefault).Get("foo_ingress_1", metav1.GetOptions{})
fooIngress1, err1 := fk.Client.NetworkingV1beta1().Ingresses(apiv1.NamespaceDefault).Get(context.TODO(), "foo_ingress_1", metav1.GetOptions{})
if err1 != nil {
t.Fatalf("unexpected error")
}
@ -337,7 +338,7 @@ func TestStatusActions(t *testing.T) {
fk.Shutdown()
// ingress should be empty
newIPs2 := []apiv1.LoadBalancerIngress{}
fooIngress2, err2 := fk.Client.NetworkingV1beta1().Ingresses(apiv1.NamespaceDefault).Get("foo_ingress_1", metav1.GetOptions{})
fooIngress2, err2 := fk.Client.NetworkingV1beta1().Ingresses(apiv1.NamespaceDefault).Get(context.TODO(), "foo_ingress_1", metav1.GetOptions{})
if err2 != nil {
t.Fatalf("unexpected error")
}
@ -346,7 +347,7 @@ func TestStatusActions(t *testing.T) {
t.Fatalf("returned %v but expected %v", fooIngress2CurIPs, newIPs2)
}
oic, err := fk.Client.NetworkingV1beta1().Ingresses(metav1.NamespaceDefault).Get("foo_ingress_different_class", metav1.GetOptions{})
oic, err := fk.Client.NetworkingV1beta1().Ingresses(metav1.NamespaceDefault).Get(context.TODO(), "foo_ingress_different_class", metav1.GetOptions{})
if err != nil {
t.Fatalf("unexpected error")
}

View file

@ -224,6 +224,8 @@ type Location struct {
// a '/'. If unspecified, the path defaults to a catch all sending
// traffic to the backend.
Path string `json:"path"`
// PathType represents the type of path referred to by a HTTPIngressPath.
PathType *networking.PathType `json:"pathType"`
// IsDefBackend indicates if service specified in the Ingress
// contains active endpoints or not. Returning true means the location
// uses the default backend.

View file

@ -17,6 +17,7 @@ limitations under the License.
package k8s
import (
"context"
"fmt"
"os"
"strings"
@ -24,6 +25,7 @@ import (
"k8s.io/klog"
apiv1 "k8s.io/api/core/v1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/version"
clientset "k8s.io/client-go/kubernetes"
@ -42,7 +44,7 @@ func ParseNameNS(input string) (string, string, error) {
// GetNodeIPOrName returns the IP address or the name of a node in the cluster
func GetNodeIPOrName(kubeClient clientset.Interface, name string, useInternalIP bool) string {
node, err := kubeClient.CoreV1().Nodes().Get(name, metav1.GetOptions{})
node, err := kubeClient.CoreV1().Nodes().Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
klog.Errorf("Error getting node %v: %v", name, err)
return ""
@ -92,7 +94,7 @@ func GetPodDetails(kubeClient clientset.Interface) (*PodInfo, error) {
return nil, fmt.Errorf("unable to get POD information (missing POD_NAME or POD_NAMESPACE environment variable")
}
pod, _ := kubeClient.CoreV1().Pods(podNs).Get(podName, metav1.GetOptions{})
pod, _ := kubeClient.CoreV1().Pods(podNs).Get(context.TODO(), podName, metav1.GetOptions{})
if pod == nil {
return nil, fmt.Errorf("unable to get POD information")
}
@ -117,26 +119,56 @@ func MetaNamespaceKey(obj interface{}) string {
// IsNetworkingIngressAvailable indicates if package "k8s.io/api/networking/v1beta1" is available or not
var IsNetworkingIngressAvailable bool
// NetworkingIngressAvailable checks if the package "k8s.io/api/networking/v1beta1" is available or not
func NetworkingIngressAvailable(client clientset.Interface) bool {
// IsIngressV1Ready indicates if the running Kubernetes version is at least v1.18.0
var IsIngressV1Ready bool
// IngressClass indicates the class of the Ingress to use as filter
var IngressClass *networkingv1beta1.IngressClass
// IngressNGINXController defines the valid value of IngressClass
// Controller field for ingress-nginx
const IngressNGINXController = "k8s.io/ingress-nginx"
// NetworkingIngressAvailable checks if the package "k8s.io/api/networking/v1beta1"
// is available or not and if Ingress V1 is supported (k8s >= v1.18.0)
func NetworkingIngressAvailable(client clientset.Interface) (bool, bool) {
// check kubernetes version to use new ingress package or not
version114, err := version.ParseGeneric("v1.14.0")
if err != nil {
klog.Errorf("unexpected error parsing version: %v", err)
return false
}
version114, _ := version.ParseGeneric("v1.14.0")
version118, _ := version.ParseGeneric("v1.18.0")
serverVersion, err := client.Discovery().ServerVersion()
if err != nil {
klog.Errorf("unexpected error parsing Kubernetes version: %v", err)
return false
return false, false
}
runningVersion, err := version.ParseGeneric(serverVersion.String())
if err != nil {
klog.Errorf("unexpected error parsing running Kubernetes version: %v", err)
return false
return false, false
}
return runningVersion.AtLeast(version114)
return runningVersion.AtLeast(version114), runningVersion.AtLeast(version118)
}
// default path type is Prefix to not break existing definitions
var defaultPathType = networkingv1beta1.PathTypePrefix
// SetDefaultNGINXPathType sets a default PathType when is not defined.
func SetDefaultNGINXPathType(ing *networkingv1beta1.Ingress) {
for _, rule := range ing.Spec.Rules {
if rule.IngressRuleValue.HTTP == nil {
continue
}
for idx := range rule.IngressRuleValue.HTTP.Paths {
p := &rule.IngressRuleValue.HTTP.Paths[idx]
if p.PathType == nil {
p.PathType = &defaultPathType
}
if *p.PathType == networkingv1beta1.PathTypeImplementationSpecific {
p.PathType = &defaultPathType
}
}
}
}

View file

@ -30,12 +30,14 @@ import (
// MaxmindLicenseKey maxmind license key to download databases
var MaxmindLicenseKey = ""
// MaxmindEditionIDs maxmind editions (GeoLite2-City, GeoLite2-Country, GeoIP2-ISP, etc)
var MaxmindEditionIDs = ""
// MaxmindEditionFiles maxmind databases on disk
var MaxmindEditionFiles []string
const (
geoIPPath = "/etc/nginx/geoip"
geoLiteCityDB = "GeoLite2-City"
geoLiteASNDB = "GeoLite2-ASN"
geoIPPath = "/etc/nginx/geoip"
dbExtension = ".mmdb"
maxmindURL = "https://download.maxmind.com/app/geoip_download?license_key=%v&edition_id=%v&suffix=tar.gz"
@ -44,12 +46,10 @@ const (
// 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
for _, dbName := range strings.Split(MaxmindEditionIDs, ",") {
if !fileExists(path.Join(geoIPPath, dbName+dbExtension)) {
return false
}
}
return true
@ -58,16 +58,13 @@ func GeoLite2DBExists() bool {
// 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
for _, dbName := range strings.Split(MaxmindEditionIDs, ",") {
err := downloadDatabase(dbName)
if err != nil {
return err
}
MaxmindEditionFiles = append(MaxmindEditionFiles, dbName+dbExtension)
}
err = downloadDatabase(geoLiteASNDB)
if err != nil {
return err
}
return nil
}
@ -133,6 +130,29 @@ func downloadDatabase(dbName string) error {
fmt.Sprintf(maxmindURL, "XXXXXXX", dbName), mmdbFile)
}
// ValidateGeoLite2DBEditions check provided Maxmind database editions names
func ValidateGeoLite2DBEditions() error {
allowedEditions := map[string]bool{
"GeoIP2-Anonymous-IP": true,
"GeoIP2-Country": true,
"GeoIP2-City": true,
"GeoIP2-Connection-Type": true,
"GeoIP2-Domain": true,
"GeoIP2-ISP": true,
"GeoIP2-ASN": true,
"GeoLite2-ASN": true,
"GeoLite2-Country": true,
"GeoLite2-City": true,
}
for _, edition := range strings.Split(MaxmindEditionIDs, ",") {
if !allowedEditions[edition] {
return fmt.Errorf("unknown Maxmind GeoIP2 edition name: '%s'", edition)
}
}
return nil
}
func fileExists(filePath string) bool {
info, err := os.Stat(filePath)
if os.IsNotExist(err) {