Merge remote-tracking branch 'upstream/master' into fix/collect-metrics-if-metrics-per-host-false
This commit is contained in:
commit
ef75a2d6fc
1311 changed files with 40786 additions and 85757 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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{}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue