Fix golangci-lint errors (#10196)

* Fix golangci-lint errors

Signed-off-by: z1cheng <imchench@gmail.com>

* Fix dupl errors

Signed-off-by: z1cheng <imchench@gmail.com>

* Fix comments

Signed-off-by: z1cheng <imchench@gmail.com>

* Fix errcheck lint errors

Signed-off-by: z1cheng <imchench@gmail.com>

* Fix assert in e2e test

Signed-off-by: z1cheng <imchench@gmail.com>

* Not interrupt the waitForPodsReady

Signed-off-by: z1cheng <imchench@gmail.com>

* Replace string with constant

Signed-off-by: z1cheng <imchench@gmail.com>

* Fix comments

Signed-off-by: z1cheng <imchench@gmail.com>

* Revert write file permision

Signed-off-by: z1cheng <imchench@gmail.com>

---------

Signed-off-by: z1cheng <imchench@gmail.com>
This commit is contained in:
Chen Chen 2023-08-31 15:36:48 +08:00 committed by GitHub
parent 46d87d3462
commit b3060bfbd0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
253 changed files with 2434 additions and 2113 deletions

View file

@ -42,19 +42,16 @@ type IngressAdmission struct {
Checker Checker
}
var (
ingressResource = metav1.GroupVersionKind{
Group: networking.GroupName,
Version: "v1",
Kind: "Ingress",
}
)
var ingressResource = metav1.GroupVersionKind{
Group: networking.GroupName,
Version: "v1",
Kind: "Ingress",
}
// 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(obj runtime.Object) (runtime.Object, error) {
review, isV1 := obj.(*admissionv1.AdmissionReview)
if !isV1 {
return nil, fmt.Errorf("request is not of type AdmissionReview v1 or v1beta1")

View file

@ -33,12 +33,12 @@ type failTestChecker struct {
t *testing.T
}
func (ftc failTestChecker) CheckIngress(ing *networking.Ingress) error {
func (ftc failTestChecker) CheckIngress(_ *networking.Ingress) error {
ftc.t.Error("checker should not be called")
return nil
}
func (ftc failTestChecker) CheckWarning(ing *networking.Ingress) ([]string, error) {
func (ftc failTestChecker) CheckWarning(_ *networking.Ingress) ([]string, error) {
ftc.t.Error("checker should not be called")
return nil, nil
}

View file

@ -26,9 +26,7 @@ import (
"k8s.io/klog/v2"
)
var (
scheme = runtime.NewScheme()
)
var scheme = runtime.NewScheme()
func init() {
if err := admissionv1.AddToScheme(scheme); err != nil {

View file

@ -72,7 +72,7 @@ func (a alias) Parse(ing *networking.Ingress) (interface{}, error) {
aliases := sets.NewString()
for _, alias := range strings.Split(val, ",") {
alias = strings.TrimSpace(alias)
if len(alias) == 0 {
if alias == "" {
continue
}

View file

@ -65,9 +65,9 @@ func TestParse(t *testing.T) {
if testCase.skipValidation {
parser.EnableAnnotationValidation = false
}
defer func() {
t.Cleanup(func() {
parser.EnableAnnotationValidation = true
}()
})
result, err := ap.Parse(ing)
if (err != nil) != testCase.wantErr {
t.Errorf("ParseAliasAnnotation() annotation: %s, error = %v, wantErr %v", testCase.annotations, err, testCase.wantErr)

View file

@ -86,37 +86,36 @@ type Ingress struct {
CorsConfig cors.Config
CustomHTTPErrors []int
DefaultBackend *apiv1.Service
//TODO: Change this back into an error when https://github.com/imdario/mergo/issues/100 is resolved
FastCGI fastcgi.Config
Denied *string
ExternalAuth authreq.Config
EnableGlobalAuth bool
HTTP2PushPreload bool
Opentracing opentracing.Config
Opentelemetry opentelemetry.Config
Proxy proxy.Config
ProxySSL proxyssl.Config
RateLimit ratelimit.Config
GlobalRateLimit globalratelimit.Config
Redirect redirect.Config
Rewrite rewrite.Config
Satisfy string
ServerSnippet string
ServiceUpstream bool
SessionAffinity sessionaffinity.Config
SSLPassthrough bool
UsePortInRedirects bool
UpstreamHashBy upstreamhashby.Config
LoadBalancing string
UpstreamVhost string
Denylist ipdenylist.SourceRange
XForwardedPrefix string
SSLCipher sslcipher.Config
Logs log.Config
ModSecurity modsecurity.Config
Mirror mirror.Config
StreamSnippet string
Allowlist ipallowlist.SourceRange
FastCGI fastcgi.Config
Denied *string
ExternalAuth authreq.Config
EnableGlobalAuth bool
HTTP2PushPreload bool
Opentracing opentracing.Config
Opentelemetry opentelemetry.Config
Proxy proxy.Config
ProxySSL proxyssl.Config
RateLimit ratelimit.Config
GlobalRateLimit globalratelimit.Config
Redirect redirect.Config
Rewrite rewrite.Config
Satisfy string
ServerSnippet string
ServiceUpstream bool
SessionAffinity sessionaffinity.Config
SSLPassthrough bool
UsePortInRedirects bool
UpstreamHashBy upstreamhashby.Config
LoadBalancing string
UpstreamVhost string
Denylist ipdenylist.SourceRange
XForwardedPrefix string
SSLCipher sslcipher.Config
Logs log.Config
ModSecurity modsecurity.Config
Mirror mirror.Config
StreamSnippet string
Allowlist ipallowlist.SourceRange
}
// Extractor defines the annotation parsers to be used in the extraction of annotations

View file

@ -64,7 +64,11 @@ func (m mockCfg) GetService(name string) (*apiv1.Service, error) {
}
func (m mockCfg) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) {
if secret, _ := m.GetSecret(name); secret != nil {
secret, err := m.GetSecret(name)
if err != nil {
return nil, err
}
if secret != nil {
return &resolver.AuthSSLCert{
Secret: name,
CAFileName: "/opt/ca.pem",
@ -270,9 +274,9 @@ func TestCors(t *testing.T) {
if r.CorsAllowCredentials != foo.credentials {
t.Errorf("Returned %v but expected %v for Cors Credentials", r.CorsAllowCredentials, foo.credentials)
}
}
}
func TestCustomHTTPErrors(t *testing.T) {
ec := NewAnnotationExtractor(mockCfg{})
ing := buildIngress()

View file

@ -50,7 +50,7 @@ var (
)
var AuthSecretConfig = parser.AnnotationConfig{
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars
Documentation: `This annotation defines the name of the Secret that contains the usernames and passwords which are granted access to the paths defined in the Ingress rules. `,
@ -61,20 +61,20 @@ var authSecretAnnotations = parser.Annotation{
Annotations: parser.AnnotationFields{
AuthSecretAnnotation: AuthSecretConfig,
authSecretTypeAnnotation: {
Validator: parser.ValidateRegex(*authSecretTypeRegex, true),
Validator: parser.ValidateRegex(authSecretTypeRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow,
Documentation: `This annotation what is the format of auth-secret value. Can be "auth-file" that defines the content of an htpasswd file, or "auth-map" where each key
is a user and each value is the password.`,
},
authRealmAnnotation: {
Validator: parser.ValidateRegex(*parser.CharsWithSpace, false),
Validator: parser.ValidateRegex(parser.CharsWithSpace, false),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars
Documentation: `This annotation defines the realm (message) that should be shown to user when authentication is requested.`,
},
authTypeAnnotation: {
Validator: parser.ValidateRegex(*authTypeRegex, true),
Validator: parser.ValidateRegex(authTypeRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow,
Documentation: `This annotation defines the basic authentication type. Should be "basic" or "digest"`,
@ -167,14 +167,14 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) {
s, err := parser.GetStringAnnotation(AuthSecretAnnotation, ing, a.annotationConfig.Annotations)
if err != nil {
return nil, ing_errors.LocationDenied{
return nil, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("error reading secret name from annotation: %w", err),
}
}
sns, sname, err := cache.SplitMetaNamespaceKey(s)
if err != nil {
return nil, ing_errors.LocationDenied{
return nil, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("error reading secret name from annotation: %w", err),
}
}
@ -185,7 +185,7 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) {
secCfg := a.r.GetSecurityConfiguration()
// We don't accept different namespaces for secrets.
if !secCfg.AllowCrossNamespaceResources && sns != ing.Namespace {
return nil, ing_errors.LocationDenied{
return nil, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("cross namespace usage of secrets is not allowed"),
}
}
@ -193,7 +193,7 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) {
name := fmt.Sprintf("%v/%v", sns, sname)
secret, err := a.r.GetSecret(name)
if err != nil {
return nil, ing_errors.LocationDenied{
return nil, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("unexpected error reading secret %s: %w", name, err),
}
}
@ -217,7 +217,7 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) {
return nil, err
}
default:
return nil, ing_errors.LocationDenied{
return nil, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("invalid auth-secret-type in annotation, must be 'auth-file' or 'auth-map': %w", err),
}
}
@ -238,14 +238,14 @@ func (a auth) Parse(ing *networking.Ingress) (interface{}, error) {
func dumpSecretAuthFile(filename string, secret *api.Secret) error {
val, ok := secret.Data["auth"]
if !ok {
return ing_errors.LocationDenied{
return ing_errors.LocationDeniedError{
Reason: fmt.Errorf("the secret %s does not contain a key with value auth", secret.Name),
}
}
err := os.WriteFile(filename, val, file.ReadWriteByUser)
if err != nil {
return ing_errors.LocationDenied{
return ing_errors.LocationDeniedError{
Reason: fmt.Errorf("unexpected error creating password file: %w", err),
}
}
@ -264,7 +264,7 @@ func dumpSecretAuthMap(filename string, secret *api.Secret) error {
err := os.WriteFile(filename, []byte(builder.String()), file.ReadWriteByUser)
if err != nil {
return ing_errors.LocationDenied{
return ing_errors.LocationDeniedError{
Reason: fmt.Errorf("unexpected error creating password file: %w", err),
}
}

View file

@ -32,6 +32,15 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver"
)
//nolint:gosec // Ignore hardcoded credentials error in testing
const (
authType = "basic"
authRealm = "-realm-"
defaultDemoSecret = "default/demo-secret"
othernsDemoSecret = "otherns/demo-secret"
demoSecret = "demo-secret"
)
func buildIngress() *networking.Ingress {
defaultBackend := networking.IngressBackend{
Service: &networking.IngressServiceBackend{
@ -80,7 +89,7 @@ type mockSecret struct {
}
func (m mockSecret) GetSecret(name string) (*api.Secret, error) {
if name != "default/demo-secret" && name != "otherns/demo-secret" {
if name != defaultDemoSecret && name != othernsDemoSecret {
return nil, fmt.Errorf("there is no secret with name %v", name)
}
@ -92,7 +101,7 @@ func (m mockSecret) GetSecret(name string) (*api.Secret, error) {
return &api.Secret{
ObjectMeta: meta_v1.ObjectMeta{
Namespace: ns,
Name: "demo-secret",
Name: demoSecret,
},
Data: map[string][]byte{"auth": []byte("foo:$apr1$OFG3Xybp$ckL0FHDAkoXYIlH9.cysT0")},
}, nil
@ -129,9 +138,9 @@ func TestIngressInvalidRealm(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic"
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = "something weird ; location trying to { break }"
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "demo-secret"
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = demoSecret
ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t)
@ -148,14 +157,14 @@ func TestIngressInvalidDifferentNamespace(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic"
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "otherns/demo-secret"
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = othernsDemoSecret
ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t)
defer os.RemoveAll(dir)
expected := ing_errors.LocationDenied{
expected := ing_errors.LocationDeniedError{
Reason: errors.New("cross namespace usage of secrets is not allowed"),
}
_, err := NewParser(dir, &mockSecret{}).Parse(ing)
@ -168,8 +177,8 @@ func TestIngressInvalidDifferentNamespaceAllowed(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic"
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "otherns/demo-secret"
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = othernsDemoSecret
ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t)
@ -187,14 +196,14 @@ func TestIngressInvalidSecretName(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic"
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "demo-secret;xpto"
ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t)
defer os.RemoveAll(dir)
expected := ing_errors.LocationDenied{
expected := ing_errors.LocationDeniedError{
Reason: errors.New("error reading secret name from annotation: annotation nginx.ingress.kubernetes.io/auth-secret contains invalid value"),
}
_, err := NewParser(dir, &mockSecret{}).Parse(ing)
@ -207,13 +216,13 @@ func TestInvalidIngressAuthNoSecret(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic"
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t)
defer os.RemoveAll(dir)
expected := ing_errors.LocationDenied{
expected := ing_errors.LocationDeniedError{
Reason: errors.New("error reading secret name from annotation: ingress rule without annotations"),
}
_, err := NewParser(dir, &mockSecret{}).Parse(ing)
@ -226,9 +235,9 @@ func TestIngressAuth(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic"
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "demo-secret"
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = "-realm-"
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = demoSecret
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = authRealm
ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t)
@ -242,10 +251,10 @@ func TestIngressAuth(t *testing.T) {
if !ok {
t.Errorf("expected a BasicDigest type")
}
if auth.Type != "basic" {
if auth.Type != authType {
t.Errorf("Expected basic as auth type but returned %s", auth.Type)
}
if auth.Realm != "-realm-" {
if auth.Realm != authRealm {
t.Errorf("Expected -realm- as realm but returned %s", auth.Realm)
}
if !auth.Secured {
@ -257,9 +266,9 @@ func TestIngressAuthWithoutSecret(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic"
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "invalid-secret"
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = "-realm-"
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = authRealm
ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t)
@ -275,10 +284,10 @@ func TestIngressAuthInvalidSecretKey(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = "basic"
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = "demo-secret"
data[parser.GetAnnotationWithPrefix(authTypeAnnotation)] = authType
data[parser.GetAnnotationWithPrefix(AuthSecretAnnotation)] = demoSecret
data[parser.GetAnnotationWithPrefix(authSecretTypeAnnotation)] = "invalid-type"
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = "-realm-"
data[parser.GetAnnotationWithPrefix(authRealmAnnotation)] = authRealm
ing.SetAnnotations(data)
_, dir, _ := dummySecretContent(t)
@ -290,7 +299,7 @@ func TestIngressAuthInvalidSecretKey(t *testing.T) {
}
}
func dummySecretContent(t *testing.T) (string, string, *api.Secret) {
func dummySecretContent(t *testing.T) (fileName, dir string, s *api.Secret) {
dir, err := os.MkdirTemp("", fmt.Sprintf("%v", time.Now().Unix()))
if err != nil {
t.Error(err)
@ -301,7 +310,10 @@ func dummySecretContent(t *testing.T) (string, string, *api.Secret) {
t.Error(err)
}
defer tmpfile.Close()
s, _ := mockSecret{}.GetSecret("default/demo-secret")
s, err = mockSecret{}.GetSecret(defaultDemoSecret)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
return tmpfile.Name(), dir, s
}

View file

@ -57,25 +57,25 @@ var authReqAnnotations = parser.Annotation{
Group: "authentication",
Annotations: parser.AnnotationFields{
authReqURLAnnotation: {
Validator: parser.ValidateRegex(*parser.URLWithNginxVariableRegex, true),
Validator: parser.ValidateRegex(parser.URLWithNginxVariableRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskHigh,
Documentation: `This annotation allows to indicate the URL where the HTTP request should be sent`,
},
authReqMethodAnnotation: {
Validator: parser.ValidateRegex(*methodsRegex, true),
Validator: parser.ValidateRegex(methodsRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow,
Documentation: `This annotation allows to specify the HTTP method to use`,
},
authReqSigninAnnotation: {
Validator: parser.ValidateRegex(*parser.URLWithNginxVariableRegex, true),
Validator: parser.ValidateRegex(parser.URLWithNginxVariableRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskHigh,
Documentation: `This annotation allows to specify the location of the error page`,
},
authReqSigninRedirParamAnnotation: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true),
Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation allows to specify the URL parameter in the error page which should contain the original URL for a failed signin request`,
@ -87,7 +87,7 @@ var authReqAnnotations = parser.Annotation{
Documentation: `This annotation allows to specify a custom snippet to use with external authentication`,
},
authReqCacheKeyAnnotation: {
Validator: parser.ValidateRegex(*parser.NGINXVariable, true),
Validator: parser.ValidateRegex(parser.NGINXVariable, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation enables caching for auth requests.`,
@ -117,26 +117,26 @@ var authReqAnnotations = parser.Annotation{
Documentation: `This annotation specifies a duration in seconds which an idle keepalive connection to an upstream server will stay open`,
},
authReqCacheDuration: {
Validator: parser.ValidateRegex(*parser.ExtendedCharsRegex, false),
Validator: parser.ValidateRegex(parser.ExtendedCharsRegex, false),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation allows to specify a caching time for auth responses based on their response codes, e.g. 200 202 30m`,
},
authReqResponseHeadersAnnotation: {
Validator: parser.ValidateRegex(*parser.HeadersVariable, true),
Validator: parser.ValidateRegex(parser.HeadersVariable, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation sets the headers to pass to backend once authentication request completes. They should be separated by comma.`,
},
authReqProxySetHeadersAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation sets the name of a ConfigMap that specifies headers to pass to the authentication service.
Only ConfigMaps on the same namespace are allowed`,
},
authReqRequestRedirectAnnotation: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true),
Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation allows to specify the X-Auth-Request-Redirect header value`,
@ -249,8 +249,8 @@ func (e1 *Config) Equal(e2 *Config) bool {
var (
methodsRegex = regexp.MustCompile("(GET|HEAD|POST|PUT|PATCH|DELETE|CONNECT|OPTIONS|TRACE)")
headerRegexp = regexp.MustCompile(`^[a-zA-Z\d\-_]+$`)
statusCodeRegex = regexp.MustCompile(`^[\d]{3}$`)
durationRegex = regexp.MustCompile(`^[\d]+(ms|s|m|h|d|w|M|y)$`) // see http://nginx.org/en/docs/syntax.html
statusCodeRegex = regexp.MustCompile(`^\d{3}$`)
durationRegex = regexp.MustCompile(`^\d+(ms|s|m|h|d|w|M|y)$`) // see http://nginx.org/en/docs/syntax.html
)
// ValidMethod checks is the provided string a valid HTTP method
@ -273,7 +273,7 @@ func ValidCacheDuration(duration string) bool {
seenDuration := false
for _, element := range elements {
if len(element) == 0 {
if element == "" {
continue
}
if statusCodeRegex.MatchString(element) {
@ -304,6 +304,8 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// ParseAnnotations parses the annotations contained in the ingress
// rule used to use an Config URL as source for authentication
//
//nolint:gocyclo // Ignore function complexity error
func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
// Required Parameters
urlString, err := parser.GetStringAnnotation(authReqURLAnnotation, ing, a.annotationConfig.Annotations)
@ -313,7 +315,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
authURL, err := parser.StringToURL(urlString)
if err != nil {
return nil, ing_errors.LocationDenied{Reason: fmt.Errorf("could not parse auth-url annotation: %v", err)}
return nil, ing_errors.LocationDeniedError{Reason: fmt.Errorf("could not parse auth-url annotation: %v", err)}
}
authMethod, err := parser.GetStringAnnotation(authReqMethodAnnotation, ing, a.annotationConfig.Annotations)
@ -410,7 +412,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
if err != nil && ing_errors.IsValidationError(err) {
return nil, ing_errors.NewLocationDenied("validation error")
}
if len(hstr) != 0 {
if hstr != "" {
harr := strings.Split(hstr, ",")
for _, header := range harr {
header = strings.TrimSpace(header)
@ -430,7 +432,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
cns, _, err := cache.SplitMetaNamespaceKey(proxySetHeaderMap)
if err != nil {
return nil, ing_errors.LocationDenied{
return nil, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("error reading configmap name %s from annotation: %w", proxySetHeaderMap, err),
}
}
@ -442,7 +444,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
secCfg := a.r.GetSecurityConfiguration()
// We don't accept different namespaces for secrets.
if !secCfg.AllowCrossNamespaceResources && cns != ing.Namespace {
return nil, ing_errors.LocationDenied{
return nil, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("cross namespace usage of secrets is not allowed"),
}
}
@ -499,7 +501,7 @@ func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) {
// It will always return at least one duration (the default duration)
func ParseStringToCacheDurations(input string) ([]string, error) {
authCacheDuration := []string{}
if len(input) != 0 {
if input != "" {
arr := strings.Split(input, ",")
for _, duration := range arr {
duration = strings.TrimSpace(duration)

View file

@ -17,7 +17,6 @@ limitations under the License.
package authreq
import (
"fmt"
"reflect"
"testing"
@ -113,7 +112,7 @@ func TestAnnotations(t *testing.T) {
data[parser.GetAnnotationWithPrefix("auth-url")] = test.url
data[parser.GetAnnotationWithPrefix("auth-signin")] = test.signinURL
data[parser.GetAnnotationWithPrefix("auth-signin-redirect-param")] = test.signinURLRedirectParam
data[parser.GetAnnotationWithPrefix("auth-method")] = fmt.Sprintf("%v", test.method)
data[parser.GetAnnotationWithPrefix("auth-method")] = test.method
data[parser.GetAnnotationWithPrefix("auth-request-redirect")] = test.requestRedirect
data[parser.GetAnnotationWithPrefix("auth-snippet")] = test.authSnippet
data[parser.GetAnnotationWithPrefix("auth-cache-key")] = test.authCacheKey
@ -331,7 +330,6 @@ func TestKeepaliveAnnotations(t *testing.T) {
}
func TestParseStringToCacheDurations(t *testing.T) {
tests := []struct {
title string
duration string
@ -346,7 +344,6 @@ func TestParseStringToCacheDurations(t *testing.T) {
}
for _, test := range tests {
dur, err := ParseStringToCacheDurations(test.duration)
if test.expErr {
if err == nil {

View file

@ -55,7 +55,6 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// ParseAnnotations parses the annotations contained in the ingress
// rule used to enable or disable global external authentication
func (a authReqGlobal) Parse(ing *networking.Ingress) (interface{}, error) {
enableGlobalAuth, err := parser.GetBoolAnnotation(enableGlobalAuthAnnotation, ing, a.annotationConfig.Annotations)
if err != nil {
enableGlobalAuth = true

View file

@ -77,7 +77,10 @@ func TestAnnotation(t *testing.T) {
data[parser.GetAnnotationWithPrefix("enable-global-auth")] = "false"
ing.SetAnnotations(data)
i, _ := NewParser(&resolver.Mock{}).Parse(ing)
i, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
u, ok := i.(bool)
if !ok {
t.Errorf("expected a Config type")

View file

@ -18,11 +18,10 @@ package authtls
import (
"fmt"
"regexp"
networking "k8s.io/api/networking/v1"
"regexp"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver"
@ -45,20 +44,20 @@ var (
regexChars = regexp.QuoteMeta(`()|=`)
authVerifyClientRegex = regexp.MustCompile(`on|off|optional|optional_no_ca`)
commonNameRegex = regexp.MustCompile(`^CN=[/\-.\_\~a-zA-Z0-9` + regexChars + `]*$`)
redirectRegex = regexp.MustCompile(`^((https?://)?[A-Za-z0-9\-\.]*(:[0-9]+)?/[A-Za-z0-9\-\.]*)?$`)
redirectRegex = regexp.MustCompile(`^((https?://)?[A-Za-z0-9\-.]*(:\d+)?/[A-Za-z0-9\-.]*)?$`)
)
var authTLSAnnotations = parser.Annotation{
Group: "authentication",
Annotations: parser.AnnotationFields{
annotationAuthTLSSecret: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars
Documentation: `This annotation defines the secret that contains the certificate chain of allowed certs`,
},
annotationAuthTLSVerifyClient: {
Validator: parser.ValidateRegex(*authVerifyClientRegex, true),
Validator: parser.ValidateRegex(authVerifyClientRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, // Medium as it allows a subset of chars
Documentation: `This annotation enables verification of client certificates. Can be "on", "off", "optional" or "optional_no_ca"`,
@ -70,7 +69,7 @@ var authTLSAnnotations = parser.Annotation{
Documentation: `This annotation defines validation depth between the provided client certificate and the Certification Authority chain.`,
},
annotationAuthTLSErrorPage: {
Validator: parser.ValidateRegex(*redirectRegex, true),
Validator: parser.ValidateRegex(redirectRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskHigh,
Documentation: `This annotation defines the URL/Page that user should be redirected in case of a Certificate Authentication Error`,
@ -82,7 +81,7 @@ var authTLSAnnotations = parser.Annotation{
Documentation: `This annotation defines if the received certificates should be passed or not to the upstream server in the header "ssl-client-cert"`,
},
annotationAuthTLSMatchCN: {
Validator: parser.ValidateRegex(*commonNameRegex, true),
Validator: parser.ValidateRegex(commonNameRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskHigh,
Documentation: `This annotation adds a sanity check for the CN of the client certificate that is sent over using a string / regex starting with "CN="`,
@ -130,9 +129,9 @@ func (assl1 *Config) Equal(assl2 *Config) bool {
}
// NewParser creates a new TLS authentication annotation parser
func NewParser(resolver resolver.Resolver) parser.IngressAnnotation {
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return authTLS{
r: resolver,
r: r,
annotationConfig: authTLSAnnotations,
}
}
@ -169,7 +168,7 @@ func (a authTLS) Parse(ing *networking.Ingress) (interface{}, error) {
authCert, err := a.r.GetAuthCertificate(tlsauthsecret)
if err != nil {
e := fmt.Errorf("error obtaining certificate: %w", err)
return &Config{}, ing_errors.LocationDenied{Reason: e}
return &Config{}, ing_errors.LocationDeniedError{Reason: e}
}
config.AuthSSLCert = *authCert

View file

@ -27,6 +27,11 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver"
)
const (
defaultDemoSecret = "default/demo-secret"
off = "off"
)
func buildIngress() *networking.Ingress {
defaultBackend := networking.IngressBackend{
Service: &networking.IngressServiceBackend{
@ -77,23 +82,22 @@ type mockSecret struct {
// GetAuthCertificate from mockSecret mocks the GetAuthCertificate for authTLS
func (m mockSecret) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) {
if name != "default/demo-secret" {
if name != defaultDemoSecret {
return nil, errors.Errorf("there is no secret with name %v", name)
}
return &resolver.AuthSSLCert{
Secret: "default/demo-secret",
Secret: defaultDemoSecret,
CAFileName: "/ssl/ca.crt",
CASHA: "abc",
}, nil
}
func TestAnnotations(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix(annotationAuthTLSSecret)] = "default/demo-secret"
data[parser.GetAnnotationWithPrefix(annotationAuthTLSSecret)] = defaultDemoSecret
ing.SetAnnotations(data)
@ -108,7 +112,7 @@ func TestAnnotations(t *testing.T) {
t.Errorf("expected *Config but got %v", u)
}
secret, err := fakeSecret.GetAuthCertificate("default/demo-secret")
secret, err := fakeSecret.GetAuthCertificate(defaultDemoSecret)
if err != nil {
t.Errorf("unexpected error getting secret %v", err)
}
@ -132,7 +136,7 @@ func TestAnnotations(t *testing.T) {
t.Errorf("expected empty string, but got %v", u.MatchCN)
}
data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyClient)] = "off"
data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyClient)] = off
data[parser.GetAnnotationWithPrefix(annotationAuthTLSVerifyDepth)] = "2"
data[parser.GetAnnotationWithPrefix(annotationAuthTLSErrorPage)] = "ok.com/error"
data[parser.GetAnnotationWithPrefix(annotationAuthTLSPassCertToUpstream)] = "true"
@ -153,8 +157,8 @@ func TestAnnotations(t *testing.T) {
if u.AuthSSLCert.Secret != secret.Secret {
t.Errorf("expected %v but got %v", secret.Secret, u.AuthSSLCert.Secret)
}
if u.VerifyClient != "off" {
t.Errorf("expected %v but got %v", "off", u.VerifyClient)
if u.VerifyClient != off {
t.Errorf("expected %v but got %v", off, u.VerifyClient)
}
if u.ValidationDepth != 2 {
t.Errorf("expected %v but got %v", 2, u.ValidationDepth)
@ -262,28 +266,21 @@ func TestInvalidAnnotations(t *testing.T) {
if u.MatchCN != "" {
t.Errorf("expected empty string but got %v", u.MatchCN)
}
}
func TestEquals(t *testing.T) {
cfg1 := &Config{}
cfg2 := &Config{}
// Same config
result := cfg1.Equal(cfg1)
if result != true {
t.Errorf("Expected true")
}
// compare nil
result = cfg1.Equal(nil)
result := cfg1.Equal(nil)
if result != false {
t.Errorf("Expected false")
}
// Different Certs
sslCert1 := resolver.AuthSSLCert{
Secret: "default/demo-secret",
Secret: defaultDemoSecret,
CAFileName: "/ssl/ca.crt",
CASHA: "abc",
}
@ -302,7 +299,7 @@ func TestEquals(t *testing.T) {
// Different Verify Client
cfg1.VerifyClient = "on"
cfg2.VerifyClient = "off"
cfg2.VerifyClient = off
result = cfg1.Equal(cfg2)
if result != false {
t.Errorf("Expected false")

View file

@ -25,9 +25,7 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver"
)
var (
validProtocols = []string{"auto_http", "http", "https", "grpc", "grpcs", "fcgi"}
)
var validProtocols = []string{"auto_http", "http", "https", "grpc", "grpcs", "fcgi"}
const (
http = "HTTP"

View file

@ -44,6 +44,7 @@ func buildIngress() *networking.Ingress {
},
}
}
func TestParseInvalidAnnotations(t *testing.T) {
ing := buildIngress()
@ -56,7 +57,7 @@ func TestParseInvalidAnnotations(t *testing.T) {
if !ok {
t.Errorf("expected a string type")
}
if val != "HTTP" {
if val != http {
t.Errorf("expected HTTPS but %v returned", val)
}
@ -72,7 +73,7 @@ func TestParseInvalidAnnotations(t *testing.T) {
if !ok {
t.Errorf("expected a string type")
}
if val != "HTTP" {
if val != http {
t.Errorf("expected HTTPS but %v returned", val)
}
@ -88,7 +89,7 @@ func TestParseInvalidAnnotations(t *testing.T) {
if !ok {
t.Errorf("expected a string type")
}
if val != "HTTP" {
if val != http {
t.Errorf("expected HTTPS but %v returned", val)
}
}

View file

@ -57,7 +57,7 @@ var CanaryAnnotations = parser.Annotation{
Documentation: `This annotation The total weight of traffic. If unspecified, it defaults to 100`,
},
canaryByHeaderAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines the header that should be used for notifying the Ingress to route the request to the service specified in the Canary Ingress.
@ -65,7 +65,7 @@ var CanaryAnnotations = parser.Annotation{
For any other value, the header will be ignored and the request compared against the other canary rules by precedence`,
},
canaryByHeaderValueAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines the header value to match for notifying the Ingress to route the request to the service specified in the Canary Ingress.
@ -74,7 +74,7 @@ var CanaryAnnotations = parser.Annotation{
It doesn't have any effect if the 'canary-by-header' annotation is not defined`,
},
canaryByHeaderPatternAnnotation: {
Validator: parser.ValidateRegex(*parser.IsValidRegex, false),
Validator: parser.ValidateRegex(parser.IsValidRegex, false),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation works the same way as canary-by-header-value except it does PCRE Regex matching.
@ -82,7 +82,7 @@ var CanaryAnnotations = parser.Annotation{
When the given Regex causes error during request processing, the request will be considered as not matching.`,
},
canaryByCookieAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines the cookie that should be used for notifying the Ingress to route the request to the service specified in the Canary Ingress.
@ -189,7 +189,7 @@ func (c canary) GetDocumentation() parser.AnnotationFields {
return c.annotationConfig.Annotations
}
func (a canary) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
func (c canary) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(c.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, CanaryAnnotations.Annotations)
}

View file

@ -17,6 +17,7 @@ limitations under the License.
package canary
import (
"strconv"
"testing"
api "k8s.io/api/core/v1"
@ -24,8 +25,6 @@ import (
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"strconv"
"k8s.io/ingress-nginx/internal/ingress/resolver"
)
@ -93,7 +92,6 @@ func TestCanaryInvalid(t *testing.T) {
if val.Weight != 0 {
t.Errorf("Expected %v but got %v", 0, val.Weight)
}
}
func TestAnnotations(t *testing.T) {
@ -133,10 +131,9 @@ func TestAnnotations(t *testing.T) {
}
continue
} else {
if err != nil {
t.Errorf("%v: expected nil but returned error %v", test.title, err)
}
}
if err != nil {
t.Errorf("%v: expected nil but returned error %v", test.title, err)
}
canaryConfig, ok := i.(*Config)

View file

@ -31,7 +31,7 @@ var clientBodyBufferSizeConfig = parser.Annotation{
Group: "backend",
Annotations: parser.AnnotationFields{
clientBodyBufferSizeAnnotation: {
Validator: parser.ValidateRegex(*parser.SizeRegex, true),
Validator: parser.ValidateRegex(parser.SizeRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow, // Low, as it allows just a set of options
Documentation: `Sets buffer size for reading client request body per location.
@ -65,7 +65,7 @@ func (cbbs clientBodyBufferSize) Parse(ing *networking.Ingress) (interface{}, er
return parser.GetStringAnnotation(clientBodyBufferSizeAnnotation, ing, cbbs.annotationConfig.Annotations)
}
func (a clientBodyBufferSize) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
func (cbbs clientBodyBufferSize) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(cbbs.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, clientBodyBufferSizeConfig.Annotations)
}

View file

@ -57,6 +57,7 @@ func TestParse(t *testing.T) {
for _, testCase := range testCases {
ing.SetAnnotations(testCase.annotations)
//nolint:errcheck // Ignore the error since invalid cases will be checked with expected results
result, _ := ap.Parse(ing)
if result != testCase.expected {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)

View file

@ -29,15 +29,13 @@ const (
connectionProxyHeaderAnnotation = "connection-proxy-header"
)
var (
validConnectionHeaderValue = regexp.MustCompile(`^(close|keep-alive)$`)
)
var validConnectionHeaderValue = regexp.MustCompile(`^(close|keep-alive)$`)
var connectionHeadersAnnotations = parser.Annotation{
Group: "backend",
Annotations: parser.AnnotationFields{
connectionProxyHeaderAnnotation: {
Validator: parser.ValidateRegex(*validConnectionHeaderValue, true),
Validator: parser.ValidateRegex(validConnectionHeaderValue, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow,
Documentation: `This annotation allows setting a specific value for "proxy_set_header Connection" directive. Right now it is restricted to "close" or "keep-alive"`,

View file

@ -66,6 +66,5 @@ func TestParse(t *testing.T) {
if !p.Equal(testCase.expected) {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, p, testCase.annotations)
}
}
}

View file

@ -43,9 +43,9 @@ var (
// * Sets a group that can be (https?://)?*?.something.com:port?
// * Allows this to be repeated as much as possible, and separated by comma
// Otherwise it should be '*'
corsOriginRegexValidator = regexp.MustCompile(`^((((https?://)?(\*\.)?[A-Za-z0-9\-\.]*(:[0-9]+)?,?)+)|\*)?$`)
corsOriginRegexValidator = regexp.MustCompile(`^((((https?://)?(\*\.)?[A-Za-z0-9\-.]*(:\d+)?,?)+)|\*)?$`)
// corsOriginRegex defines the regex for validation inside Parse
corsOriginRegex = regexp.MustCompile(`^(https?://(\*\.)?[A-Za-z0-9\-\.]*(:[0-9]+)?|\*)?$`)
corsOriginRegex = regexp.MustCompile(`^(https?://(\*\.)?[A-Za-z0-9\-.]*(:\d+)?|\*)?$`)
// Method must contain valid methods list (PUT, GET, POST, BLA)
// May contain or not spaces between each verb
corsMethodsRegex = regexp.MustCompile(`^([A-Za-z]+,?\s?)+$`)
@ -74,7 +74,7 @@ var corsAnnotation = parser.Annotation{
Documentation: `This annotation enables Cross-Origin Resource Sharing (CORS) in an Ingress rule`,
},
corsAllowOriginAnnotation: {
Validator: parser.ValidateRegex(*corsOriginRegexValidator, true),
Validator: parser.ValidateRegex(corsOriginRegexValidator, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation controls what's the accepted Origin for CORS.
@ -82,14 +82,14 @@ var corsAnnotation = parser.Annotation{
It also supports single level wildcard subdomains and follows this format: http(s)://*.foo.bar, http(s)://*.bar.foo:8080 or http(s)://*.abc.bar.foo:9000`,
},
corsAllowHeadersAnnotation: {
Validator: parser.ValidateRegex(*parser.HeadersVariable, true),
Validator: parser.ValidateRegex(parser.HeadersVariable, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation controls which headers are accepted.
This is a multi-valued field, separated by ',' and accepts letters, numbers, _ and -`,
},
corsAllowMethodsAnnotation: {
Validator: parser.ValidateRegex(*corsMethodsRegex, true),
Validator: parser.ValidateRegex(corsMethodsRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation controls which methods are accepted.
@ -102,7 +102,7 @@ var corsAnnotation = parser.Annotation{
Documentation: `This annotation controls if credentials can be passed during CORS operations.`,
},
corsExposeHeadersAnnotation: {
Validator: parser.ValidateRegex(*corsExposeHeadersRegex, true),
Validator: parser.ValidateRegex(corsExposeHeadersRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation controls which headers are exposed to response.
@ -260,7 +260,7 @@ func (c cors) GetDocumentation() parser.AnnotationFields {
return c.annotationConfig.Annotations
}
func (a cors) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
func (c cors) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(c.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, corsAnnotation.Annotations)
}

View file

@ -31,16 +31,14 @@ const (
customHTTPErrorsAnnotation = "custom-http-errors"
)
var (
// We accept anything between 400 and 599, on a comma separated.
arrayOfHTTPErrors = regexp.MustCompile(`^(?:[4,5][0-9][0-9],?)*$`)
)
// We accept anything between 400 and 599, on a comma separated.
var arrayOfHTTPErrors = regexp.MustCompile(`^(?:[4,5]\d{2},?)*$`)
var customHTTPErrorsAnnotations = parser.Annotation{
Group: "backend",
Annotations: parser.AnnotationFields{
customHTTPErrorsAnnotation: {
Validator: parser.ValidateRegex(*arrayOfHTTPErrors, true),
Validator: parser.ValidateRegex(arrayOfHTTPErrors, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow,
Documentation: `If a default backend annotation is specified on the ingress, the errors code specified on this annotation
@ -72,7 +70,7 @@ func (e customhttperrors) Parse(ing *networking.Ingress) (interface{}, error) {
}
cSplit := strings.Split(c, ",")
var codes []int
codes := make([]int, 0, len(cSplit))
for _, i := range cSplit {
num, err := strconv.Atoi(i)
if err != nil {
@ -88,7 +86,7 @@ func (e customhttperrors) GetDocumentation() parser.AnnotationFields {
return e.annotationConfig.Annotations
}
func (a customhttperrors) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
func (e customhttperrors) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(e.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, customHTTPErrorsAnnotations.Annotations)
}

View file

@ -57,14 +57,14 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// Parse parses the annotations contained in the ingress to use
// a custom default backend
func (db backend) Parse(ing *networking.Ingress) (interface{}, error) {
s, err := parser.GetStringAnnotation(defaultBackendAnnotation, ing, db.annotationConfig.Annotations)
func (b backend) Parse(ing *networking.Ingress) (interface{}, error) {
s, err := parser.GetStringAnnotation(defaultBackendAnnotation, ing, b.annotationConfig.Annotations)
if err != nil {
return nil, err
}
name := fmt.Sprintf("%v/%v", ing.Namespace, s)
svc, err := db.r.GetService(name)
svc, err := b.r.GetService(name)
if err != nil {
return nil, fmt.Errorf("unexpected error reading service %s: %w", name, err)
}
@ -72,11 +72,11 @@ func (db backend) Parse(ing *networking.Ingress) (interface{}, error) {
return svc, nil
}
func (db backend) GetDocumentation() parser.AnnotationFields {
return db.annotationConfig.Annotations
func (b backend) GetDocumentation() parser.AnnotationFields {
return b.annotationConfig.Annotations
}
func (a backend) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
func (b backend) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(b.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, defaultBackendAnnotations.Annotations)
}

View file

@ -35,22 +35,20 @@ const (
fastCGIParamsAnnotation = "fastcgi-params-configmap"
)
var (
// fast-cgi valid parameters is just a single file name (like index.php)
regexValidIndexAnnotationAndKey = regexp.MustCompile(`^[A-Za-z0-9\.\-\_]+$`)
)
// fast-cgi valid parameters is just a single file name (like index.php)
var regexValidIndexAnnotationAndKey = regexp.MustCompile(`^[A-Za-z0-9.\-\_]+$`)
var fastCGIAnnotations = parser.Annotation{
Group: "fastcgi",
Annotations: parser.AnnotationFields{
fastCGIIndexAnnotation: {
Validator: parser.ValidateRegex(*regexValidIndexAnnotationAndKey, true),
Validator: parser.ValidateRegex(regexValidIndexAnnotationAndKey, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation can be used to specify an index file`,
},
fastCGIParamsAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation can be used to specify a ConfigMap containing the fastcgi parameters as a key/value.
@ -98,7 +96,6 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// ParseAnnotations parses the annotations contained in the ingress
// rule used to indicate the fastcgiConfig.
func (a fastcgi) Parse(ing *networking.Ingress) (interface{}, error) {
fcgiConfig := Config{}
if ing.GetAnnotations() == nil {
@ -125,7 +122,7 @@ func (a fastcgi) Parse(ing *networking.Ingress) (interface{}, error) {
cmns, cmn, err := cache.SplitMetaNamespaceKey(cm)
if err != nil {
return fcgiConfig, ing_errors.LocationDenied{
return fcgiConfig, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("error reading configmap name from annotation: %w", err),
}
}
@ -139,7 +136,7 @@ func (a fastcgi) Parse(ing *networking.Ingress) (interface{}, error) {
cm = fmt.Sprintf("%v/%v", ing.Namespace, cmn)
cmap, err := a.r.GetConfigMap(cm)
if err != nil {
return fcgiConfig, ing_errors.LocationDenied{
return fcgiConfig, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("unexpected error reading configmap %s: %w", cm, err),
}
}

View file

@ -155,7 +155,6 @@ func TestParseFastCGIInvalidParamsConfigMapAnnotation(t *testing.T) {
invalidConfigMapList := []string{"unknown/configMap", "unknown/config/map"}
for _, configmap := range invalidConfigMapList {
data := map[string]string{}
data[parser.GetAnnotationWithPrefix("fastcgi-params-configmap")] = configmap
ing.SetAnnotations(data)
@ -239,11 +238,9 @@ func TestParseFastCGIParamsConfigMapAnnotationWithDifferentNS(t *testing.T) {
if err == nil {
t.Errorf("Different namespace configmap should return an error")
}
}
func TestConfigEquality(t *testing.T) {
var nilConfig *Config
config := Config{
@ -297,7 +294,6 @@ func TestConfigEquality(t *testing.T) {
}
func Test_fastcgi_Parse(t *testing.T) {
tests := []struct {
name string
index string
@ -378,7 +374,6 @@ func Test_fastcgi_Parse(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ing := buildIngress()
data := map[string]string{}

View file

@ -25,7 +25,6 @@ import (
"k8s.io/klog/v2"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/errors"
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver"
"k8s.io/ingress-nginx/internal/net"
@ -57,7 +56,7 @@ var globalRateLimitAnnotationConfig = parser.Annotation{
Documentation: `Configures a time window (i.e 1m) that the limit is applied`,
},
globalRateLimitKeyAnnotation: {
Validator: parser.ValidateRegex(*parser.NGINXVariable, true),
Validator: parser.ValidateRegex(parser.NGINXVariable, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskHigh,
Documentation: `This annotation Configures a key for counting the samples. Defaults to $remote_addr.
@ -123,23 +122,23 @@ func (a globalratelimit) Parse(ing *networking.Ingress) (interface{}, error) {
config := &Config{}
limit, err := parser.GetIntAnnotation(globalRateLimitAnnotation, ing, a.annotationConfig.Annotations)
if err != nil && errors.IsInvalidContent(err) {
if err != nil && ing_errors.IsInvalidContent(err) {
return nil, err
}
rawWindowSize, err := parser.GetStringAnnotation(globalRateLimitWindowAnnotation, ing, a.annotationConfig.Annotations)
if err != nil && errors.IsValidationError(err) {
return config, ing_errors.LocationDenied{
if err != nil && ing_errors.IsValidationError(err) {
return config, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("failed to parse 'global-rate-limit-window' value: %w", err),
}
}
if limit == 0 || len(rawWindowSize) == 0 {
if limit == 0 || rawWindowSize == "" {
return config, nil
}
windowSize, err := time.ParseDuration(rawWindowSize)
if err != nil {
return config, ing_errors.LocationDenied{
return config, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("failed to parse 'global-rate-limit-window' value: %w", err),
}
}
@ -148,12 +147,12 @@ func (a globalratelimit) Parse(ing *networking.Ingress) (interface{}, error) {
if err != nil {
klog.Warningf("invalid %s, defaulting to %s", globalRateLimitKeyAnnotation, defaultKey)
}
if len(key) == 0 {
if key == "" {
key = defaultKey
}
rawIgnoredCIDRs, err := parser.GetStringAnnotation(globalRateLimitIgnoredCidrsAnnotation, ing, a.annotationConfig.Annotations)
if err != nil && errors.IsInvalidContent(err) {
if err != nil && ing_errors.IsInvalidContent(err) {
return nil, err
}
ignoredCIDRs, err := net.ParseCIDRs(rawIgnoredCIDRs)
@ -161,7 +160,7 @@ func (a globalratelimit) Parse(ing *networking.Ingress) (interface{}, error) {
return nil, err
}
config.Namespace = strings.Replace(string(ing.UID), "-", "", -1)
config.Namespace = strings.ReplaceAll(string(ing.UID), "-", "")
config.Limit = limit
config.WindowSize = int(windowSize.Seconds())
config.Key = key

View file

@ -30,8 +30,10 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver"
)
const UID = "31285d47-b150-4dcf-bd6f-12c46d769f6e"
const expectedUID = "31285d47b1504dcfbd6f12c46d769f6e"
const (
UID = "31285d47-b150-4dcf-bd6f-12c46d769f6e"
expectedUID = "31285d47b1504dcfbd6f12c46d769f6e"
)
func buildIngress() *networking.Ingress {
defaultBackend := networking.IngressBackend{
@ -190,10 +192,19 @@ func TestGlobalRateLimiting(t *testing.T) {
t.Errorf("expected error '%v' but got '%v'", testCase.expectedErr, actualErr)
}
actualConfig := i.(*Config)
actualConfig, ok := i.(*Config)
if !ok {
t.Errorf("expected Config type but got %T", i)
}
if !testCase.expectedConfig.Equal(actualConfig) {
expectedJSON, _ := json.Marshal(testCase.expectedConfig)
actualJSON, _ := json.Marshal(actualConfig)
expectedJSON, err := json.Marshal(testCase.expectedConfig)
if err != nil {
t.Errorf("failed to marshal expected config: %v", err)
}
actualJSON, err := json.Marshal(actualConfig)
if err != nil {
t.Errorf("failed to marshal actual config: %v", err)
}
t.Errorf("%v: expected config '%s' but got '%s'", testCase.title, expectedJSON, actualJSON)
}
}

View file

@ -62,7 +62,7 @@ func (h2pp http2PushPreload) GetDocumentation() parser.AnnotationFields {
return h2pp.annotationConfig.Annotations
}
func (a http2PushPreload) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
func (h2pp http2PushPreload) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(h2pp.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, http2PushPreloadAnnotations.Annotations)
}

View file

@ -96,16 +96,15 @@ func (a ipallowlist) Parse(ing *networking.Ingress) (interface{}, error) {
return &SourceRange{CIDR: defaultAllowlistSourceRange}, nil
}
return &SourceRange{CIDR: defaultAllowlistSourceRange}, ing_errors.LocationDenied{
return &SourceRange{CIDR: defaultAllowlistSourceRange}, ing_errors.LocationDeniedError{
Reason: err,
}
}
values := strings.Split(val, ",")
ipnets, ips, err := net.ParseIPNets(values...)
if err != nil && len(ips) == 0 {
return &SourceRange{CIDR: defaultAllowlistSourceRange}, ing_errors.LocationDenied{
return &SourceRange{CIDR: defaultAllowlistSourceRange}, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("the annotation does not contain a valid IP address or network: %w", err),
}
}

View file

@ -93,16 +93,15 @@ func (a ipdenylist) Parse(ing *networking.Ingress) (interface{}, error) {
return &SourceRange{CIDR: defaultDenylistSourceRange}, nil
}
return &SourceRange{CIDR: defaultDenylistSourceRange}, ing_errors.LocationDenied{
return &SourceRange{CIDR: defaultDenylistSourceRange}, ing_errors.LocationDeniedError{
Reason: err,
}
}
values := strings.Split(val, ",")
ipnets, ips, err := net.ParseIPNets(values...)
if err != nil && len(ips) == 0 {
return &SourceRange{CIDR: defaultDenylistSourceRange}, ing_errors.LocationDenied{
return &SourceRange{CIDR: defaultDenylistSourceRange}, ing_errors.LocationDeniedError{
Reason: fmt.Errorf("the annotation does not contain a valid IP address or network: %w", err),
}
}

View file

@ -54,6 +54,7 @@ func TestParse(t *testing.T) {
for _, testCase := range testCases {
ing.SetAnnotations(testCase.annotations)
//nolint:errcheck // Ignore the error since invalid cases will be checked with expected results
result, _ := ap.Parse(ing)
if result != testCase.expected {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)

View file

@ -101,7 +101,7 @@ func (l log) GetDocumentation() parser.AnnotationFields {
return l.annotationConfig.Annotations
}
func (a log) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
func (l log) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(l.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, logAnnotations.Annotations)
}

View file

@ -76,7 +76,10 @@ func TestIngressAccessLogConfig(t *testing.T) {
data[parser.GetAnnotationWithPrefix(enableAccessLogAnnotation)] = "false"
ing.SetAnnotations(data)
log, _ := NewParser(&resolver.Mock{}).Parse(ing)
log, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
nginxLogs, ok := log.(*Config)
if !ok {
t.Errorf("expected a Config type")
@ -94,7 +97,10 @@ func TestIngressRewriteLogConfig(t *testing.T) {
data[parser.GetAnnotationWithPrefix(enableRewriteLogAnnotation)] = "true"
ing.SetAnnotations(data)
log, _ := NewParser(&resolver.Mock{}).Parse(ing)
log, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error parsing annotations %v", err)
}
nginxLogs, ok := log.(*Config)
if !ok {
t.Errorf("expected a Config type")
@ -112,7 +118,10 @@ func TestInvalidBoolConfig(t *testing.T) {
data[parser.GetAnnotationWithPrefix(enableRewriteLogAnnotation)] = "blo"
ing.SetAnnotations(data)
log, _ := NewParser(&resolver.Mock{}).Parse(ing)
log, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
nginxLogs, ok := log.(*Config)
if !ok {
t.Errorf("expected a Config type")

View file

@ -34,15 +34,13 @@ const (
mirrorHostAnnotation = "mirror-host"
)
var (
OnOffRegex = regexp.MustCompile(`^(on|off)$`)
)
var OnOffRegex = regexp.MustCompile(`^(on|off)$`)
var mirrorAnnotation = parser.Annotation{
Group: "mirror",
Annotations: parser.AnnotationFields{
mirrorRequestBodyAnnotation: {
Validator: parser.ValidateRegex(*OnOffRegex, true),
Validator: parser.ValidateRegex(OnOffRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskLow,
Documentation: `This annotation defines if the request-body should be sent to the mirror backend. Can be 'on' or 'off'`,

View file

@ -27,7 +27,7 @@ import (
const (
modsecEnableAnnotation = "enable-modsecurity"
modsecEnableOwaspCoreAnnotation = "enable-owasp-core-rules"
modesecTransactionIdAnnotation = "modsecurity-transaction-id"
modesecTransactionIDAnnotation = "modsecurity-transaction-id"
modsecSnippetAnnotation = "modsecurity-snippet"
)
@ -46,8 +46,8 @@ var modsecurityAnnotation = parser.Annotation{
Risk: parser.AnnotationRiskLow,
Documentation: `This annotation enables the OWASP Core Rule Set`,
},
modesecTransactionIdAnnotation: {
Validator: parser.ValidateRegex(*parser.NGINXVariable, true),
modesecTransactionIDAnnotation: {
Validator: parser.ValidateRegex(parser.NGINXVariable, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskHigh,
Documentation: `This annotation enables passing an NGINX variable to ModSecurity.`,
@ -98,9 +98,9 @@ func (modsec1 *Config) Equal(modsec2 *Config) bool {
}
// NewParser creates a new ModSecurity annotation parser
func NewParser(resolver resolver.Resolver) parser.IngressAnnotation {
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return modSecurity{
r: resolver,
r: r,
annotationConfig: modsecurityAnnotation,
}
}
@ -134,10 +134,10 @@ func (a modSecurity) Parse(ing *networking.Ingress) (interface{}, error) {
config.OWASPRules = false
}
config.TransactionID, err = parser.GetStringAnnotation(modesecTransactionIdAnnotation, ing, a.annotationConfig.Annotations)
config.TransactionID, err = parser.GetStringAnnotation(modesecTransactionIDAnnotation, ing, a.annotationConfig.Annotations)
if err != nil {
if errors.IsInvalidContent(err) {
klog.Warningf("annotation %s contains invalid directive, defaulting", modesecTransactionIdAnnotation)
klog.Warningf("annotation %s contains invalid directive, defaulting", modesecTransactionIDAnnotation)
}
config.TransactionID = ""
}

View file

@ -69,8 +69,14 @@ func TestParse(t *testing.T) {
for _, testCase := range testCases {
ing.SetAnnotations(testCase.annotations)
result, _ := ap.Parse(ing)
config := result.(*Config)
result, err := ap.Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
config, ok := result.(*Config)
if !ok {
t.Errorf("unexpected type: %T", result)
}
if !config.Equal(&testCase.expected) {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)
}

View file

@ -51,7 +51,7 @@ var otelAnnotations = parser.Annotation{
Documentation: `This annotation enables or disables using spans from incoming requests as parent for created ones`,
},
otelOperationNameAnnotation: {
Validator: parser.ValidateRegex(*regexOperationName, true),
Validator: parser.ValidateRegex(regexOperationName, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines what operation name should be added to the span`,
@ -75,7 +75,6 @@ type Config struct {
// Equal tests for equality between two Config types
func (bd1 *Config) Equal(bd2 *Config) bool {
if bd1.Set != bd2.Set {
return false
}
@ -150,7 +149,7 @@ func (c opentelemetry) GetDocumentation() parser.AnnotationFields {
return c.annotationConfig.Annotations
}
func (a opentelemetry) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
func (c opentelemetry) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(c.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, otelAnnotations.Annotations)
}

View file

@ -26,6 +26,8 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver"
)
const enableAnnotation = "true"
func buildIngress() *networking.Ingress {
defaultBackend := networking.IngressBackend{
Service: &networking.IngressServiceBackend{
@ -73,10 +75,13 @@ func TestIngressAnnotationOpentelemetrySetTrue(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = "true"
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = enableAnnotation
ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing)
val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
openTelemetry, ok := val.(*Config)
if !ok {
t.Errorf("expected a Config type")
@ -103,7 +108,10 @@ func TestIngressAnnotationOpentelemetrySetFalse(t *testing.T) {
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = "false"
ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing)
val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
openTelemetry, ok := val.(*Config)
if !ok {
t.Errorf("expected a Config type")
@ -123,8 +131,8 @@ func TestIngressAnnotationOpentelemetryTrustSetTrue(t *testing.T) {
data := map[string]string{}
opName := "foo-op"
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = "true"
data[parser.GetAnnotationWithPrefix(otelTrustSpanAnnotation)] = "true"
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = enableAnnotation
data[parser.GetAnnotationWithPrefix(otelTrustSpanAnnotation)] = enableAnnotation
data[parser.GetAnnotationWithPrefix(otelOperationNameAnnotation)] = opName
ing.SetAnnotations(data)
@ -163,7 +171,7 @@ func TestIngressAnnotationOpentelemetryWithBadOpName(t *testing.T) {
data := map[string]string{}
opName := "fooxpto_123$la;"
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = "true"
data[parser.GetAnnotationWithPrefix(enableOpenTelemetryAnnotation)] = enableAnnotation
data[parser.GetAnnotationWithPrefix(otelOperationNameAnnotation)] = opName
ing.SetAnnotations(data)
@ -180,7 +188,10 @@ func TestIngressAnnotationOpentelemetryUnset(t *testing.T) {
data := map[string]string{}
ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing)
val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
_, ok := val.(*Config)
if !ok {
t.Errorf("expected a Config type")

View file

@ -89,13 +89,13 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
}
}
func (s opentracing) Parse(ing *networking.Ingress) (interface{}, error) {
enabled, err := parser.GetBoolAnnotation(enableOpentracingAnnotation, ing, s.annotationConfig.Annotations)
func (o opentracing) Parse(ing *networking.Ingress) (interface{}, error) {
enabled, err := parser.GetBoolAnnotation(enableOpentracingAnnotation, ing, o.annotationConfig.Annotations)
if err != nil {
return &Config{}, nil
}
trustSpan, err := parser.GetBoolAnnotation(opentracingTrustSpanAnnotation, ing, s.annotationConfig.Annotations)
trustSpan, err := parser.GetBoolAnnotation(opentracingTrustSpanAnnotation, ing, o.annotationConfig.Annotations)
if err != nil {
return &Config{Set: true, Enabled: enabled}, nil
}
@ -103,11 +103,11 @@ func (s opentracing) Parse(ing *networking.Ingress) (interface{}, error) {
return &Config{Set: true, Enabled: enabled, TrustSet: true, TrustEnabled: trustSpan}, nil
}
func (s opentracing) GetDocumentation() parser.AnnotationFields {
return s.annotationConfig.Annotations
func (o opentracing) GetDocumentation() parser.AnnotationFields {
return o.annotationConfig.Annotations
}
func (a opentracing) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
func (o opentracing) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(o.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, opentracingAnnotations.Annotations)
}

View file

@ -26,6 +26,8 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver"
)
const enableAnnotation = "true"
func buildIngress() *networking.Ingress {
defaultBackend := networking.IngressBackend{
Service: &networking.IngressServiceBackend{
@ -73,10 +75,13 @@ func TestIngressAnnotationOpentracingSetTrue(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix(enableOpentracingAnnotation)] = "true"
data[parser.GetAnnotationWithPrefix(enableOpentracingAnnotation)] = enableAnnotation
ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing)
val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error %v", err)
}
openTracing, ok := val.(*Config)
if !ok {
t.Errorf("expected a Config type")
@ -95,7 +100,10 @@ func TestIngressAnnotationOpentracingSetFalse(t *testing.T) {
data[parser.GetAnnotationWithPrefix(enableOpentracingAnnotation)] = "false"
ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing)
val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error %v", err)
}
openTracing, ok := val.(*Config)
if !ok {
t.Errorf("expected a Config type")
@ -110,11 +118,14 @@ func TestIngressAnnotationOpentracingTrustSetTrue(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix(enableOpentracingAnnotation)] = "true"
data[parser.GetAnnotationWithPrefix(opentracingTrustSpanAnnotation)] = "true"
data[parser.GetAnnotationWithPrefix(enableOpentracingAnnotation)] = enableAnnotation
data[parser.GetAnnotationWithPrefix(opentracingTrustSpanAnnotation)] = enableAnnotation
ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing)
val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error %v", err)
}
openTracing, ok := val.(*Config)
if !ok {
t.Errorf("expected a Config type")
@ -136,7 +147,11 @@ func TestIngressAnnotationOpentracingUnset(t *testing.T) {
data := map[string]string{}
ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing)
val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
_, ok := val.(*Config)
if !ok {
t.Errorf("expected a Config type")

View file

@ -118,7 +118,7 @@ func (a ingAnnotations) parseString(name string) (string, error) {
val, ok := a[name]
if ok {
s := normalizeString(val)
if len(s) == 0 {
if s == "" {
return "", errors.NewInvalidAnnotationContent(name, val)
}
@ -248,13 +248,14 @@ func StringToURL(input string) (*url.URL, error) {
return nil, fmt.Errorf("%v is not a valid URL: %v", input, err)
}
if parsedURL.Scheme == "" {
switch {
case parsedURL.Scheme == "":
return nil, fmt.Errorf("url scheme is empty")
} else if parsedURL.Host == "" {
case parsedURL.Host == "":
return nil, fmt.Errorf("url host is empty")
} else if strings.Contains(parsedURL.Host, "..") {
case strings.Contains(parsedURL.Host, ".."):
return nil, fmt.Errorf("invalid url host")
default:
return parsedURL, nil
}
return parsedURL, nil
}

View file

@ -93,14 +93,16 @@ func TestGetStringAnnotation(t *testing.T) {
{"valid - A", "string", "A ", "A", false},
{"valid - B", "string", " B", "B", false},
{"empty", "string", " ", "", true},
{"valid multiline", "string", `
{
"valid multiline", "string", `
rewrite (?i)/arcgis/rest/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/rest/services/Utilities/Geometry/GeometryServer$1 break;
rewrite (?i)/arcgis/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/services/Utilities/Geometry/GeometryServer$1 break;
`, `
rewrite (?i)/arcgis/rest/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/rest/services/Utilities/Geometry/GeometryServer$1 break;
rewrite (?i)/arcgis/services/Utilities/Geometry/GeometryServer(.*)$ /arcgis/services/Utilities/Geometry/GeometryServer$1 break;
`,
false},
false,
},
}
data := map[string]string{}
@ -213,8 +215,10 @@ func TestGetIntAnnotation(t *testing.T) {
func TestStringToURL(t *testing.T) {
validURL := "http://bar.foo.com/external-auth"
validParsedURL, _ := url.Parse(validURL)
validParsedURL, err := url.Parse(validURL)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
tests := []struct {
title string
url string

View file

@ -52,7 +52,7 @@ var (
var IsValidRegex = regexp.MustCompile("^[/" + alphaNumericChars + regexEnabledChars + "]*$")
// SizeRegex validates sizes understood by NGINX, like 1000, 100k, 1000M
var SizeRegex = regexp.MustCompile("^(?i)[0-9]+[bkmg]?$")
var SizeRegex = regexp.MustCompile(`^(?i)\d+[bkmg]?$`)
// URLRegex is used to validate a URL but with only a specific set of characters:
// It is alphanumericChar + ":", "?", "&"
@ -103,7 +103,7 @@ func ValidateServerName(value string) error {
// ValidateRegex receives a regex as an argument and uses it to validate
// the value of the field.
// Annotation can define if the spaces should be trimmed before validating the value
func ValidateRegex(regex regexp.Regexp, removeSpace bool) AnnotationValidator {
func ValidateRegex(regex *regexp.Regexp, removeSpace bool) AnnotationValidator {
return func(s string) error {
if removeSpace {
s = strings.ReplaceAll(s, " ", "")
@ -117,7 +117,7 @@ func ValidateRegex(regex regexp.Regexp, removeSpace bool) AnnotationValidator {
// ValidateOptions receives an array of valid options that can be the value of annotation.
// If no valid option is found, it will return an error
func ValidateOptions(options []string, caseSensitive bool, trimSpace bool) AnnotationValidator {
func ValidateOptions(options []string, caseSensitive, trimSpace bool) AnnotationValidator {
return func(s string) error {
if trimSpace {
s = strings.TrimSpace(s)
@ -161,7 +161,7 @@ func ValidateDuration(value string) error {
// ValidateNull always return null values and should not be widely used.
// It is used on the "snippet" annotations, as it is up to the admin to allow its
// usage, knowing it can be critical!
func ValidateNull(value string) error {
func ValidateNull(_ string) error {
return nil
}

View file

@ -223,7 +223,6 @@ func Test_checkAnnotation(t *testing.T) {
}
func TestCheckAnnotationRisk(t *testing.T) {
tests := []struct {
name string
annotations map[string]string

View file

@ -45,9 +45,7 @@ const (
proxyMaxTempFileSizeAnnotation = "proxy-max-temp-file-size"
)
var (
validUpstreamAnnotation = regexp.MustCompile(`^((error|timeout|invalid_header|http_500|http_502|http_503|http_504|http_403|http_404|http_429|non_idempotent|off)\s?)+$`)
)
var validUpstreamAnnotation = regexp.MustCompile(`^((error|timeout|invalid_header|http_500|http_502|http_503|http_504|http_403|http_404|http_429|non_idempotent|off)\s?)+$`)
var proxyAnnotations = parser.Annotation{
Group: "backend",
@ -78,32 +76,32 @@ var proxyAnnotations = parser.Annotation{
By default proxy buffers number is set as 4`,
},
proxyBufferSizeAnnotation: {
Validator: parser.ValidateRegex(*parser.SizeRegex, true),
Validator: parser.ValidateRegex(parser.SizeRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow,
Documentation: `This annotation sets the size of the buffer proxy_buffer_size used for reading the first part of the response received from the proxied server.
By default proxy buffer size is set as "4k".`,
},
proxyCookiePathAnnotation: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true),
Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation sets a text that should be changed in the path attribute of the "Set-Cookie" header fields of a proxied server response.`,
},
proxyCookieDomainAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation ets a text that should be changed in the domain attribute of the "Set-Cookie" header fields of a proxied server response.`,
},
proxyBodySizeAnnotation: {
Validator: parser.ValidateRegex(*parser.SizeRegex, true),
Validator: parser.ValidateRegex(parser.SizeRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation allows setting the maximum allowed size of a client request body.`,
},
proxyNextUpstreamAnnotation: {
Validator: parser.ValidateRegex(*validUpstreamAnnotation, false),
Validator: parser.ValidateRegex(validUpstreamAnnotation, false),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines when the next upstream should be used.
@ -129,13 +127,13 @@ var proxyAnnotations = parser.Annotation{
Documentation: `This annotation enables or disables buffering of a client request body.`,
},
proxyRedirectFromAnnotation: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true),
Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `The annotations proxy-redirect-from and proxy-redirect-to will set the first and second parameters of NGINX's proxy_redirect directive respectively`,
},
proxyRedirectToAnnotation: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true),
Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `The annotations proxy-redirect-from and proxy-redirect-to will set the first and second parameters of NGINX's proxy_redirect directive respectively`,
@ -153,7 +151,7 @@ var proxyAnnotations = parser.Annotation{
Documentation: `This annotations sets the HTTP protocol version for proxying. Can be "1.0" or "1.1".`,
},
proxyMaxTempFileSizeAnnotation: {
Validator: parser.ValidateRegex(*parser.SizeRegex, true),
Validator: parser.ValidateRegex(parser.SizeRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow,
Documentation: `This annotation defines the maximum size of a temporary file when buffering responses.`,
@ -253,7 +251,8 @@ type proxy struct {
// NewParser creates a new reverse proxy configuration annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return proxy{r: r,
return proxy{
r: r,
annotationConfig: proxyAnnotations,
}
}

View file

@ -28,6 +28,12 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver"
)
const (
off = "off"
proxyHTTPVersion = "1.0"
proxyMaxTempFileSize = "128k"
)
func buildIngress() *networking.Ingress {
defaultBackend := networking.IngressBackend{
Service: &networking.IngressServiceBackend{
@ -87,7 +93,7 @@ func (m mockBackend) GetDefaultBackend() defaults.Backend {
ProxyNextUpstreamTimeout: 0,
ProxyNextUpstreamTries: 3,
ProxyRequestBuffering: "on",
ProxyBuffering: "off",
ProxyBuffering: off,
ProxyHTTPVersion: "1.1",
ProxyMaxTempFileSize: "1024m",
}
@ -103,13 +109,13 @@ func TestProxy(t *testing.T) {
data[parser.GetAnnotationWithPrefix("proxy-buffers-number")] = "8"
data[parser.GetAnnotationWithPrefix("proxy-buffer-size")] = "1k"
data[parser.GetAnnotationWithPrefix("proxy-body-size")] = "2k"
data[parser.GetAnnotationWithPrefix("proxy-next-upstream")] = "off"
data[parser.GetAnnotationWithPrefix("proxy-next-upstream")] = off
data[parser.GetAnnotationWithPrefix("proxy-next-upstream-timeout")] = "5"
data[parser.GetAnnotationWithPrefix("proxy-next-upstream-tries")] = "3"
data[parser.GetAnnotationWithPrefix("proxy-request-buffering")] = "off"
data[parser.GetAnnotationWithPrefix("proxy-request-buffering")] = off
data[parser.GetAnnotationWithPrefix("proxy-buffering")] = "on"
data[parser.GetAnnotationWithPrefix("proxy-http-version")] = "1.0"
data[parser.GetAnnotationWithPrefix("proxy-max-temp-file-size")] = "128k"
data[parser.GetAnnotationWithPrefix("proxy-http-version")] = proxyHTTPVersion
data[parser.GetAnnotationWithPrefix("proxy-max-temp-file-size")] = proxyMaxTempFileSize
ing.SetAnnotations(data)
i, err := NewParser(mockBackend{}).Parse(ing)
@ -138,7 +144,7 @@ func TestProxy(t *testing.T) {
if p.BodySize != "2k" {
t.Errorf("expected 2k as body-size but returned %v", p.BodySize)
}
if p.NextUpstream != "off" {
if p.NextUpstream != off {
t.Errorf("expected off as next-upstream but returned %v", p.NextUpstream)
}
if p.NextUpstreamTimeout != 5 {
@ -147,16 +153,16 @@ func TestProxy(t *testing.T) {
if p.NextUpstreamTries != 3 {
t.Errorf("expected 3 as next-upstream-tries but returned %v", p.NextUpstreamTries)
}
if p.RequestBuffering != "off" {
if p.RequestBuffering != off {
t.Errorf("expected off as request-buffering but returned %v", p.RequestBuffering)
}
if p.ProxyBuffering != "on" {
t.Errorf("expected on as proxy-buffering but returned %v", p.ProxyBuffering)
}
if p.ProxyHTTPVersion != "1.0" {
if p.ProxyHTTPVersion != proxyHTTPVersion {
t.Errorf("expected 1.0 as proxy-http-version but returned %v", p.ProxyHTTPVersion)
}
if p.ProxyMaxTempFileSize != "128k" {
if p.ProxyMaxTempFileSize != proxyMaxTempFileSize {
t.Errorf("expected 128k as proxy-max-temp-file-size but returned %v", p.ProxyMaxTempFileSize)
}
}
@ -176,8 +182,8 @@ func TestProxyComplex(t *testing.T) {
data[parser.GetAnnotationWithPrefix("proxy-next-upstream-tries")] = "3"
data[parser.GetAnnotationWithPrefix("proxy-request-buffering")] = "off"
data[parser.GetAnnotationWithPrefix("proxy-buffering")] = "on"
data[parser.GetAnnotationWithPrefix("proxy-http-version")] = "1.0"
data[parser.GetAnnotationWithPrefix("proxy-max-temp-file-size")] = "128k"
data[parser.GetAnnotationWithPrefix("proxy-http-version")] = proxyHTTPVersion
data[parser.GetAnnotationWithPrefix("proxy-max-temp-file-size")] = proxyMaxTempFileSize
ing.SetAnnotations(data)
i, err := NewParser(mockBackend{}).Parse(ing)
@ -221,10 +227,10 @@ func TestProxyComplex(t *testing.T) {
if p.ProxyBuffering != "on" {
t.Errorf("expected on as proxy-buffering but returned %v", p.ProxyBuffering)
}
if p.ProxyHTTPVersion != "1.0" {
if p.ProxyHTTPVersion != proxyHTTPVersion {
t.Errorf("expected 1.0 as proxy-http-version but returned %v", p.ProxyHTTPVersion)
}
if p.ProxyMaxTempFileSize != "128k" {
if p.ProxyMaxTempFileSize != proxyMaxTempFileSize {
t.Errorf("expected 128k as proxy-max-temp-file-size but returned %v", p.ProxyMaxTempFileSize)
}
}

View file

@ -24,7 +24,6 @@ import (
networking "k8s.io/api/networking/v1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/errors"
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver"
"k8s.io/ingress-nginx/internal/k8s"
@ -42,7 +41,7 @@ const (
var (
proxySSLOnOffRegex = regexp.MustCompile(`^(on|off)$`)
proxySSLProtocolRegex = regexp.MustCompile(`^(SSLv2|SSLv3|TLSv1|TLSv1\.1|TLSv1\.2|TLSv1\.3| )*$`)
proxySSLCiphersRegex = regexp.MustCompile(`^[A-Za-z0-9\+\:\_\-\!]*$`)
proxySSLCiphersRegex = regexp.MustCompile(`^[A-Za-z0-9\+:\_\-!]*$`)
)
const (
@ -59,7 +58,7 @@ var proxySSLAnnotation = parser.Annotation{
Group: "proxy",
Annotations: parser.AnnotationFields{
proxySSLSecretAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation specifies a Secret with the certificate tls.crt, key tls.key in PEM format used for authentication to a proxied HTTPS server.
@ -68,14 +67,14 @@ var proxySSLAnnotation = parser.Annotation{
Just secrets on the same namespace of the ingress can be used.`,
},
proxySSLCiphersAnnotation: {
Validator: parser.ValidateRegex(*proxySSLCiphersRegex, true),
Validator: parser.ValidateRegex(proxySSLCiphersRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation Specifies the enabled ciphers for requests to a proxied HTTPS server.
The ciphers are specified in the format understood by the OpenSSL library.`,
},
proxySSLProtocolsAnnotation: {
Validator: parser.ValidateRegex(*proxySSLProtocolRegex, true),
Validator: parser.ValidateRegex(proxySSLProtocolRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskLow,
Documentation: `This annotation enables the specified protocols for requests to a proxied HTTPS server.`,
@ -88,7 +87,7 @@ var proxySSLAnnotation = parser.Annotation{
This value is also passed through SNI when a connection is established to the proxied HTTPS server.`,
},
proxySSLVerifyAnnotation: {
Validator: parser.ValidateRegex(*proxySSLOnOffRegex, true),
Validator: parser.ValidateRegex(proxySSLOnOffRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskLow,
Documentation: `This annotation enables or disables verification of the proxied HTTPS server certificate. (default: off)`,
@ -100,7 +99,7 @@ var proxySSLAnnotation = parser.Annotation{
Documentation: `This annotation Sets the verification depth in the proxied HTTPS server certificates chain. (default: 1).`,
},
proxySSLServerNameAnnotation: {
Validator: parser.ValidateRegex(*proxySSLOnOffRegex, true),
Validator: parser.ValidateRegex(proxySSLOnOffRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskLow,
Documentation: `This annotation enables passing of the server name through TLS Server Name Indication extension (SNI, RFC 6066) when establishing a connection with the proxied HTTPS server.`,
@ -150,10 +149,11 @@ func (pssl1 *Config) Equal(pssl2 *Config) bool {
}
// NewParser creates a new TLS authentication annotation parser
func NewParser(resolver resolver.Resolver) parser.IngressAnnotation {
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return proxySSL{
r: resolver,
annotationConfig: proxySSLAnnotation}
r: r,
annotationConfig: proxySSLAnnotation,
}
}
type proxySSL struct {
@ -208,13 +208,13 @@ func (p proxySSL) Parse(ing *networking.Ingress) (interface{}, error) {
proxyCert, err := p.r.GetAuthCertificate(proxysslsecret)
if err != nil {
e := fmt.Errorf("error obtaining certificate: %w", err)
return &Config{}, ing_errors.LocationDenied{Reason: e}
return &Config{}, ing_errors.LocationDeniedError{Reason: e}
}
config.AuthSSLCert = *proxyCert
config.Ciphers, err = parser.GetStringAnnotation(proxySSLCiphersAnnotation, ing, p.annotationConfig.Annotations)
if err != nil {
if errors.IsValidationError(err) {
if ing_errors.IsValidationError(err) {
klog.Warningf("invalid value passed to proxy-ssl-ciphers, defaulting to %s", defaultProxySSLCiphers)
}
config.Ciphers = defaultProxySSLCiphers
@ -222,7 +222,7 @@ func (p proxySSL) Parse(ing *networking.Ingress) (interface{}, error) {
config.Protocols, err = parser.GetStringAnnotation(proxySSLProtocolsAnnotation, ing, p.annotationConfig.Annotations)
if err != nil {
if errors.IsValidationError(err) {
if ing_errors.IsValidationError(err) {
klog.Warningf("invalid value passed to proxy-ssl-protocols, defaulting to %s", defaultProxySSLProtocols)
}
config.Protocols = defaultProxySSLProtocols
@ -232,7 +232,7 @@ func (p proxySSL) Parse(ing *networking.Ingress) (interface{}, error) {
config.ProxySSLName, err = parser.GetStringAnnotation(proxySSLNameAnnotation, ing, p.annotationConfig.Annotations)
if err != nil {
if errors.IsValidationError(err) {
if ing_errors.IsValidationError(err) {
klog.Warningf("invalid value passed to proxy-ssl-name, defaulting to empty")
}
config.ProxySSLName = ""
@ -260,7 +260,7 @@ func (p proxySSL) GetDocumentation() parser.AnnotationFields {
return p.annotationConfig.Annotations
}
func (a proxySSL) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
func (p proxySSL) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(p.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, proxySSLAnnotation.Annotations)
}

View file

@ -27,6 +27,14 @@ import (
"k8s.io/ingress-nginx/internal/ingress/resolver"
)
const (
defaultDemoSecret = "default/demo-secret"
proxySslCiphers = "HIGH:-SHA"
off = "off"
sslServerName = "w00t"
defaultProtocol = "SSLv2 TLSv1 TLSv1.2 TLSv1.3"
)
func buildIngress() *networking.Ingress {
defaultBackend := networking.IngressBackend{
Service: &networking.IngressServiceBackend{
@ -77,28 +85,27 @@ type mockSecret struct {
// GetAuthCertificate from mockSecret mocks the GetAuthCertificate for backend certificate authentication
func (m mockSecret) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) {
if name != "default/demo-secret" {
if name != defaultDemoSecret {
return nil, errors.Errorf("there is no secret with name %v", name)
}
return &resolver.AuthSSLCert{
Secret: "default/demo-secret",
Secret: defaultDemoSecret,
CAFileName: "/ssl/ca.crt",
CASHA: "abc",
}, nil
}
func TestAnnotations(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix(proxySSLSecretAnnotation)] = "default/demo-secret"
data[parser.GetAnnotationWithPrefix("proxy-ssl-ciphers")] = "HIGH:-SHA"
data[parser.GetAnnotationWithPrefix(proxySSLSecretAnnotation)] = defaultDemoSecret
data[parser.GetAnnotationWithPrefix("proxy-ssl-ciphers")] = proxySslCiphers
data[parser.GetAnnotationWithPrefix("proxy-ssl-name")] = "$host"
data[parser.GetAnnotationWithPrefix("proxy-ssl-protocols")] = "TLSv1.3 SSLv2 TLSv1 TLSv1.2"
data[parser.GetAnnotationWithPrefix("proxy-ssl-server-name")] = "on"
data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = "off"
data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = off
data[parser.GetAnnotationWithPrefix("proxy-ssl-verify")] = "on"
data[parser.GetAnnotationWithPrefix("proxy-ssl-verify-depth")] = "3"
@ -115,7 +122,7 @@ func TestAnnotations(t *testing.T) {
t.Errorf("expected *Config but got %v", u)
}
secret, err := fakeSecret.GetAuthCertificate("default/demo-secret")
secret, err := fakeSecret.GetAuthCertificate(defaultDemoSecret)
if err != nil {
t.Errorf("unexpected error getting secret %v", err)
}
@ -123,11 +130,11 @@ func TestAnnotations(t *testing.T) {
if u.AuthSSLCert.Secret != secret.Secret {
t.Errorf("expected %v but got %v", secret.Secret, u.AuthSSLCert.Secret)
}
if u.Ciphers != "HIGH:-SHA" {
t.Errorf("expected %v but got %v", "HIGH:-SHA", u.Ciphers)
if u.Ciphers != proxySslCiphers {
t.Errorf("expected %v but got %v", proxySslCiphers, u.Ciphers)
}
if u.Protocols != "SSLv2 TLSv1 TLSv1.2 TLSv1.3" {
t.Errorf("expected %v but got %v", "SSLv2 TLSv1 TLSv1.2 TLSv1.3", u.Protocols)
if u.Protocols != defaultProtocol {
t.Errorf("expected %v but got %v", defaultProtocol, u.Protocols)
}
if u.Verify != "on" {
t.Errorf("expected %v but got %v", "on", u.Verify)
@ -141,7 +148,6 @@ func TestAnnotations(t *testing.T) {
if u.ProxySSLServerName != "on" {
t.Errorf("expected %v but got %v", "on", u.ProxySSLServerName)
}
}
func TestInvalidAnnotations(t *testing.T) {
@ -172,11 +178,11 @@ func TestInvalidAnnotations(t *testing.T) {
}
// Invalid optional Annotations
data[parser.GetAnnotationWithPrefix("proxy-ssl-secret")] = "default/demo-secret"
data[parser.GetAnnotationWithPrefix("proxy-ssl-secret")] = defaultDemoSecret
data[parser.GetAnnotationWithPrefix("proxy-ssl-protocols")] = "TLSv111 SSLv1"
data[parser.GetAnnotationWithPrefix("proxy-ssl-server-name")] = "w00t"
data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = "w00t"
data[parser.GetAnnotationWithPrefix("proxy-ssl-verify")] = "w00t"
data[parser.GetAnnotationWithPrefix("proxy-ssl-server-name")] = sslServerName
data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = sslServerName
data[parser.GetAnnotationWithPrefix("proxy-ssl-verify")] = sslServerName
data[parser.GetAnnotationWithPrefix("proxy-ssl-verify-depth")] = "abcd"
ing.SetAnnotations(data)
@ -207,21 +213,15 @@ func TestEquals(t *testing.T) {
cfg1 := &Config{}
cfg2 := &Config{}
// Same config
result := cfg1.Equal(cfg1)
if result != true {
t.Errorf("Expected true")
}
// compare nil
result = cfg1.Equal(nil)
result := cfg1.Equal(nil)
if result != false {
t.Errorf("Expected false")
}
// Different Certs
sslCert1 := resolver.AuthSSLCert{
Secret: "default/demo-secret",
Secret: defaultDemoSecret,
CAFileName: "/ssl/ca.crt",
CASHA: "abc",
}
@ -240,7 +240,7 @@ func TestEquals(t *testing.T) {
// Different Ciphers
cfg1.Ciphers = "DEFAULT"
cfg2.Ciphers = "HIGH:-SHA"
cfg2.Ciphers = proxySslCiphers
result = cfg1.Equal(cfg2)
if result != false {
t.Errorf("Expected false")
@ -248,22 +248,22 @@ func TestEquals(t *testing.T) {
cfg2.Ciphers = "DEFAULT"
// Different Protocols
cfg1.Protocols = "SSLv2 TLSv1 TLSv1.2 TLSv1.3"
cfg1.Protocols = defaultProtocol
cfg2.Protocols = "SSLv3 TLSv1 TLSv1.2 TLSv1.3"
result = cfg1.Equal(cfg2)
if result != false {
t.Errorf("Expected false")
}
cfg2.Protocols = "SSLv2 TLSv1 TLSv1.2 TLSv1.3"
cfg2.Protocols = defaultProtocol
// Different Verify
cfg1.Verify = "off"
cfg1.Verify = off
cfg2.Verify = "on"
result = cfg1.Equal(cfg2)
if result != false {
t.Errorf("Expected false")
}
cfg2.Verify = "off"
cfg2.Verify = off
// Different VerifyDepth
cfg1.VerifyDepth = 1
@ -275,13 +275,13 @@ func TestEquals(t *testing.T) {
cfg2.VerifyDepth = 1
// Different ProxySSLServerName
cfg1.ProxySSLServerName = "off"
cfg1.ProxySSLServerName = off
cfg2.ProxySSLServerName = "on"
result = cfg1.Equal(cfg2)
if result != false {
t.Errorf("Expected false")
}
cfg2.ProxySSLServerName = "off"
cfg2.ProxySSLServerName = off
// Equal Configs
result = cfg1.Equal(cfg2)

View file

@ -288,7 +288,7 @@ func (a ratelimit) Parse(ing *networking.Ingress) (interface{}, error) {
func encode(s string) string {
str := base64.URLEncoding.EncodeToString([]byte(s))
return strings.Replace(str, "=", "", -1)
return strings.ReplaceAll(str, "=", "")
}
func (a ratelimit) GetDocumentation() parser.AnnotationFields {

View file

@ -54,14 +54,14 @@ var redirectAnnotations = parser.Annotation{
Documentation: `In some scenarios is required to redirect from www.domain.com to domain.com or vice versa. To enable this feature use this annotation.`,
},
temporalRedirectAnnotation: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, false),
Validator: parser.ValidateRegex(parser.URLIsValidRegex, false),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, // Medium, as it allows arbitrary URLs that needs to be validated
Documentation: `This annotation allows you to return a temporal redirect (Return Code 302) instead of sending data to the upstream.
For example setting this annotation to https://www.google.com would redirect everything to Google with a Return Code of 302 (Moved Temporarily).`,
},
permanentRedirectAnnotation: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, false),
Validator: parser.ValidateRegex(parser.URLIsValidRegex, false),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium, // Medium, as it allows arbitrary URLs that needs to be validated
Documentation: `This annotation allows to return a permanent redirect (Return Code 301) instead of sending data to the upstream.
@ -174,11 +174,11 @@ func isValidURL(s string) error {
return nil
}
func (a redirect) GetDocumentation() parser.AnnotationFields {
return a.annotationConfig.Annotations
func (r redirect) GetDocumentation() parser.AnnotationFields {
return r.annotationConfig.Annotations
}
func (a redirect) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
func (r redirect) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(r.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, redirectAnnotations.Annotations)
}

View file

@ -136,7 +136,6 @@ func TestTemporalRedirect(t *testing.T) {
}
func TestIsValidURL(t *testing.T) {
invalid := "ok.com"
urlParse, err := url.Parse(invalid)
if err != nil {

View file

@ -40,7 +40,7 @@ var rewriteAnnotations = parser.Annotation{
Group: "rewrite",
Annotations: parser.AnnotationFields{
rewriteTargetAnnotation: {
Validator: parser.ValidateRegex(*parser.RegexPathWithCapture, false),
Validator: parser.ValidateRegex(parser.RegexPathWithCapture, false),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation allows to specify the target URI where the traffic must be redirected. It can contain regular characters and captured
@ -72,7 +72,7 @@ var rewriteAnnotations = parser.Annotation{
the pathType should also be defined as 'ImplementationSpecific'.`,
},
appRootAnnotation: {
Validator: parser.ValidateRegex(*parser.RegexPathWithCapture, false),
Validator: parser.ValidateRegex(parser.RegexPathWithCapture, false),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines the Application Root that the Controller must redirect if it's in / context`,

View file

@ -120,7 +120,10 @@ func TestSSLRedirect(t *testing.T) {
data[parser.GetAnnotationWithPrefix("rewrite-target")] = defRoute
ing.SetAnnotations(data)
i, _ := NewParser(mockBackend{redirect: true}).Parse(ing)
i, err := NewParser(mockBackend{redirect: true}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
redirect, ok := i.(*Config)
if !ok {
t.Errorf("expected a Redirect type")
@ -132,7 +135,10 @@ func TestSSLRedirect(t *testing.T) {
data[parser.GetAnnotationWithPrefix("rewrite-target")] = "/xpto/$1/abc/$2"
ing.SetAnnotations(data)
i, _ = NewParser(mockBackend{redirect: true}).Parse(ing)
i, err = NewParser(mockBackend{redirect: true}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
redirect, ok = i.(*Config)
if !ok {
t.Errorf("expected a Redirect type")
@ -144,7 +150,10 @@ func TestSSLRedirect(t *testing.T) {
data[parser.GetAnnotationWithPrefix("rewrite-target")] = "/xpto/xas{445}"
ing.SetAnnotations(data)
i, _ = NewParser(mockBackend{redirect: true}).Parse(ing)
i, err = NewParser(mockBackend{redirect: true}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
redirect, ok = i.(*Config)
if !ok {
t.Errorf("expected a Redirect type")
@ -156,7 +165,10 @@ func TestSSLRedirect(t *testing.T) {
data[parser.GetAnnotationWithPrefix("ssl-redirect")] = "false"
ing.SetAnnotations(data)
i, _ = NewParser(mockBackend{redirect: false}).Parse(ing)
i, err = NewParser(mockBackend{redirect: false}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
redirect, ok = i.(*Config)
if !ok {
t.Errorf("expected a Redirect type")
@ -173,7 +185,10 @@ func TestForceSSLRedirect(t *testing.T) {
data[parser.GetAnnotationWithPrefix("rewrite-target")] = defRoute
ing.SetAnnotations(data)
i, _ := NewParser(mockBackend{redirect: true}).Parse(ing)
i, err := NewParser(mockBackend{redirect: true}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
redirect, ok := i.(*Config)
if !ok {
t.Errorf("expected a Redirect type")
@ -185,7 +200,10 @@ func TestForceSSLRedirect(t *testing.T) {
data[parser.GetAnnotationWithPrefix("force-ssl-redirect")] = "true"
ing.SetAnnotations(data)
i, _ = NewParser(mockBackend{redirect: false}).Parse(ing)
i, err = NewParser(mockBackend{redirect: false}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
redirect, ok = i.(*Config)
if !ok {
t.Errorf("expected a Redirect type")
@ -194,6 +212,7 @@ func TestForceSSLRedirect(t *testing.T) {
t.Errorf("Expected true but returned false")
}
}
func TestAppRoot(t *testing.T) {
ap := NewParser(mockBackend{redirect: true})
@ -241,7 +260,10 @@ func TestUseRegex(t *testing.T) {
data[parser.GetAnnotationWithPrefix("use-regex")] = "true"
ing.SetAnnotations(data)
i, _ := NewParser(mockBackend{redirect: true}).Parse(ing)
i, err := NewParser(mockBackend{redirect: true}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
redirect, ok := i.(*Config)
if !ok {
t.Errorf("expected a App Context")

View file

@ -69,7 +69,7 @@ func (s satisfy) GetDocumentation() parser.AnnotationFields {
return s.annotationConfig.Annotations
}
func (a satisfy) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
func (s satisfy) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(s.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, satisfyAnnotations.Annotations)
}

View file

@ -54,6 +54,7 @@ func TestParse(t *testing.T) {
for _, testCase := range testCases {
ing.SetAnnotations(testCase.annotations)
//nolint:errcheck // Ignore the error since invalid cases will be checked with expected results
result, _ := ap.Parse(ing)
if result != testCase.expected {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)

View file

@ -69,7 +69,7 @@ func (s serviceUpstream) GetDocumentation() parser.AnnotationFields {
return s.annotationConfig.Annotations
}
func (a serviceUpstream) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
func (s serviceUpstream) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(s.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, serviceUpstreamAnnotations.Annotations)
}

View file

@ -77,7 +77,10 @@ func TestIngressAnnotationServiceUpstreamEnabled(t *testing.T) {
data[parser.GetAnnotationWithPrefix(serviceUpstreamAnnotation)] = "true"
ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing)
val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
enabled, ok := val.(bool)
if !ok {
t.Errorf("expected a bool type")
@ -96,7 +99,10 @@ func TestIngressAnnotationServiceUpstreamSetFalse(t *testing.T) {
data[parser.GetAnnotationWithPrefix(serviceUpstreamAnnotation)] = "false"
ing.SetAnnotations(data)
val, _ := NewParser(&resolver.Mock{}).Parse(ing)
val, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
enabled, ok := val.(bool)
if !ok {
t.Errorf("expected a bool type")
@ -110,7 +116,10 @@ func TestIngressAnnotationServiceUpstreamSetFalse(t *testing.T) {
data = map[string]string{}
ing.SetAnnotations(data)
val, _ = NewParser(&resolver.Mock{}).Parse(ing)
val, err = NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
enabled, ok = val.(bool)
if !ok {
t.Errorf("expected a bool type")
@ -137,7 +146,10 @@ func (m mockBackend) GetDefaultBackend() defaults.Backend {
func TestParseAnnotationsWithDefaultConfig(t *testing.T) {
ing := buildIngress()
val, _ := NewParser(mockBackend{}).Parse(ing)
val, err := NewParser(mockBackend{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
enabled, ok := val.(bool)
if !ok {
@ -158,7 +170,10 @@ func TestParseAnnotationsOverridesDefaultConfig(t *testing.T) {
data[parser.GetAnnotationWithPrefix(serviceUpstreamAnnotation)] = "false"
ing.SetAnnotations(data)
val, _ := NewParser(mockBackend{}).Parse(ing)
val, err := NewParser(mockBackend{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
enabled, ok := val.(bool)
if !ok {

View file

@ -63,13 +63,15 @@ const (
// This is used to control the cookie change after request failure
annotationAffinityCookieChangeOnFailure = "session-cookie-change-on-failure"
cookieAffinity = "cookie"
)
var sessionAffinityAnnotations = parser.Annotation{
Group: "affinity",
Annotations: parser.AnnotationFields{
annotationAffinityType: {
Validator: parser.ValidateOptions([]string{"cookie"}, true, true),
Validator: parser.ValidateOptions([]string{cookieAffinity}, true, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskLow,
Documentation: `This annotation enables and sets the affinity type in all Upstreams of an Ingress. This way, a request will always be directed to the same upstream server. The only affinity type available for NGINX is cookie`,
@ -91,7 +93,7 @@ var sessionAffinityAnnotations = parser.Annotation{
Setting this to legacy will restore original canary behavior, when session affinity was ignored.`,
},
annotationAffinityCookieName: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation allows to specify the name of the cookie that will be used to route the requests`,
@ -103,25 +105,25 @@ var sessionAffinityAnnotations = parser.Annotation{
Documentation: `This annotation set the cookie as secure regardless the protocol of the incoming request`,
},
annotationAffinityCookieExpires: {
Validator: parser.ValidateRegex(*affinityCookieExpiresRegex, true),
Validator: parser.ValidateRegex(affinityCookieExpiresRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation is a legacy version of "session-cookie-max-age" for compatibility with older browsers, generates an "Expires" cookie directive by adding the seconds to the current date`,
},
annotationAffinityCookieMaxAge: {
Validator: parser.ValidateRegex(*affinityCookieExpiresRegex, false),
Validator: parser.ValidateRegex(affinityCookieExpiresRegex, false),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation sets the time until the cookie expires`,
},
annotationAffinityCookiePath: {
Validator: parser.ValidateRegex(*parser.URLIsValidRegex, true),
Validator: parser.ValidateRegex(parser.URLIsValidRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines the Path that will be set on the cookie (required if your Ingress paths use regular expressions)`,
},
annotationAffinityCookieDomain: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines the Domain attribute of the sticky cookie.`,
@ -149,9 +151,7 @@ var sessionAffinityAnnotations = parser.Annotation{
},
}
var (
affinityCookieExpiresRegex = regexp.MustCompile(`(^0|-?[1-9]\d*$)`)
)
var affinityCookieExpiresRegex = regexp.MustCompile(`(^0|-?[1-9]\d*$)`)
// Config describes the per ingress session affinity config
type Config struct {
@ -186,6 +186,11 @@ type Cookie struct {
ConditionalSameSiteNone bool `json:"conditional-samesite-none"`
}
type affinity struct {
r resolver.Resolver
annotationConfig parser.Annotation
}
// cookieAffinityParse gets the annotation values related to Cookie Affinity
// It also sets default values when no value or incorrect value is found
func (a affinity) cookieAffinityParse(ing *networking.Ingress) *Cookie {
@ -252,11 +257,6 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
}
}
type affinity struct {
r resolver.Resolver
annotationConfig parser.Annotation
}
// ParseAnnotations parses the annotations contained in the ingress
// rule used to configure the affinity directives
func (a affinity) Parse(ing *networking.Ingress) (interface{}, error) {
@ -279,11 +279,10 @@ func (a affinity) Parse(ing *networking.Ingress) (interface{}, error) {
}
switch at {
case "cookie":
case cookieAffinity:
cookie = a.cookieAffinityParse(ing)
default:
klog.V(3).InfoS("No default affinity found", "ingress", ing.Name)
}
return &Config{

View file

@ -83,7 +83,11 @@ func TestIngressAffinityCookieConfig(t *testing.T) {
data[parser.GetAnnotationWithPrefix(annotationAffinityCookieSecure)] = "true"
ing.SetAnnotations(data)
affin, _ := NewParser(&resolver.Mock{}).Parse(ing)
affin, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error parsing annotations: %v", err)
}
nginxAffinity, ok := affin.(*Config)
if !ok {
t.Errorf("expected a Config type")

View file

@ -54,6 +54,7 @@ func TestParse(t *testing.T) {
for _, testCase := range testCases {
ing.SetAnnotations(testCase.annotations)
//nolint:errcheck // Ignore the error since invalid cases will be checked with expected results
result, _ := ap.Parse(ing)
if result != testCase.expected {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)

View file

@ -31,10 +31,8 @@ const (
sslCipherAnnotation = "ssl-ciphers"
)
var (
// Should cover something like "ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"
regexValidSSLCipher = regexp.MustCompile(`^[A-Za-z0-9!:+\-]*$`)
)
// Should cover something like "ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"
var regexValidSSLCipher = regexp.MustCompile(`^[A-Za-z0-9!:+\-]*$`)
var sslCipherAnnotations = parser.Annotation{
Group: "backend",
@ -47,7 +45,7 @@ var sslCipherAnnotations = parser.Annotation{
This configuration specifies that server ciphers should be preferred over client ciphers when using the SSLv3 and TLS protocols.`,
},
sslCipherAnnotation: {
Validator: parser.ValidateRegex(*regexValidSSLCipher, true),
Validator: parser.ValidateRegex(regexValidSSLCipher, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskLow,
Documentation: `Using this annotation will set the ssl_ciphers directive at the server level. This configuration is active for all the paths in the host.`,
@ -104,7 +102,7 @@ func (sc sslCipher) GetDocumentation() parser.AnnotationFields {
return sc.annotationConfig.Annotations
}
func (a sslCipher) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
func (sc sslCipher) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(sc.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, sslCipherAnnotations.Annotations)
}

View file

@ -42,8 +42,11 @@ func TestParse(t *testing.T) {
expectErr bool
}{
{map[string]string{annotationSSLCiphers: "ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP"}, Config{"ALL:!aNULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP", ""}, false},
{map[string]string{annotationSSLCiphers: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"},
Config{"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256", ""}, false},
{
map[string]string{annotationSSLCiphers: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"},
Config{"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256", ""},
false,
},
{map[string]string{annotationSSLCiphers: ""}, Config{"", ""}, false},
{map[string]string{annotationSSLPreferServerCiphers: "true"}, Config{"", "on"}, false},
{map[string]string{annotationSSLPreferServerCiphers: "false"}, Config{"", "off"}, false},

View file

@ -47,7 +47,8 @@ type sslpt struct {
// NewParser creates a new SSL passthrough annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return sslpt{r: r,
return sslpt{
r: r,
annotationConfig: sslPassthroughAnnotations,
}
}

View file

@ -38,7 +38,8 @@ func TestParse(t *testing.T) {
annotations map[string]string
expected string
}{
{map[string]string{annotation: "server { listen: 8000; proxy_pass 127.0.0.1:80}"},
{
map[string]string{annotation: "server { listen: 8000; proxy_pass 127.0.0.1:80}"},
"server { listen: 8000; proxy_pass 127.0.0.1:80}",
},
{map[string]string{annotation: "false"}, "false"},
@ -56,6 +57,7 @@ func TestParse(t *testing.T) {
for _, testCase := range testCases {
ing.SetAnnotations(testCase.annotations)
//nolint:errcheck // Ignore the error since invalid cases will be checked with expected results
result, _ := ap.Parse(ing)
if result != testCase.expected {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)

View file

@ -41,7 +41,7 @@ var upstreamHashByAnnotations = parser.Annotation{
Group: "backend",
Annotations: parser.AnnotationFields{
upstreamHashByAnnotation: {
Validator: parser.ValidateRegex(*hashByRegex, true),
Validator: parser.ValidateRegex(hashByRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskHigh, // High, this annotation allows accessing NGINX variables
Documentation: `This annotation defines the nginx variable, text value or any combination thereof to use for consistent hashing.

View file

@ -31,7 +31,7 @@ var xForwardedForAnnotations = parser.Annotation{
Group: "backend",
Annotations: parser.AnnotationFields{
xForwardedForPrefixAnnotation: {
Validator: parser.ValidateRegex(*parser.BasicCharsRegex, true),
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeLocation,
Risk: parser.AnnotationRiskLow, // Low, as it allows regexes but on a very limited set
Documentation: `This annotation can be used to add the non-standard X-Forwarded-Prefix header to the upstream request with a string value`,
@ -54,15 +54,15 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// Parse parses the annotations contained in the ingress rule
// used to add an x-forwarded-prefix header to the request
func (cbbs xforwardedprefix) Parse(ing *networking.Ingress) (interface{}, error) {
return parser.GetStringAnnotation(xForwardedForPrefixAnnotation, ing, cbbs.annotationConfig.Annotations)
func (x xforwardedprefix) Parse(ing *networking.Ingress) (interface{}, error) {
return parser.GetStringAnnotation(xForwardedForPrefixAnnotation, ing, x.annotationConfig.Annotations)
}
func (cbbs xforwardedprefix) GetDocumentation() parser.AnnotationFields {
return cbbs.annotationConfig.Annotations
func (x xforwardedprefix) GetDocumentation() parser.AnnotationFields {
return x.annotationConfig.Annotations
}
func (a xforwardedprefix) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
func (x xforwardedprefix) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(x.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, xForwardedForAnnotations.Annotations)
}

View file

@ -54,6 +54,7 @@ func TestParse(t *testing.T) {
for _, testCase := range testCases {
ing.SetAnnotations(testCase.annotations)
//nolint:errcheck // Ignore the error since invalid cases will be checked with expected results
result, _ := ap.Parse(ing)
if result != testCase.expected {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)

View file

@ -100,7 +100,7 @@ func matchHostnames(pattern, host string) bool {
host = strings.TrimSuffix(host, ".")
pattern = strings.TrimSuffix(pattern, ".")
if len(pattern) == 0 || len(host) == 0 {
if pattern == "" || host == "" {
return false
}

View file

@ -29,7 +29,7 @@ import (
)
// Name returns the healthcheck name
func (n NGINXController) Name() string {
func (n *NGINXController) Name() string {
return "nginx-ingress-controller"
}

View file

@ -32,7 +32,7 @@ import (
)
func TestNginxCheck(t *testing.T) {
var tests = []struct {
tests := []struct {
healthzPath string
}{
{"/healthz"},
@ -42,7 +42,6 @@ func TestNginxCheck(t *testing.T) {
for _, tt := range tests {
testName := fmt.Sprintf("health path: %s", tt.healthzPath)
t.Run(testName, func(t *testing.T) {
mux := http.NewServeMux()
listener, err := tryListen("tcp", fmt.Sprintf(":%v", nginx.StatusPort))
@ -50,7 +49,7 @@ func TestNginxCheck(t *testing.T) {
t.Fatalf("creating tcp listener: %s", err)
}
defer listener.Close()
//nolint:gosec // Ignore not configured ReadHeaderTimeout in testing
server := &httptest.Server{
Listener: listener,
Config: &http.Server{
@ -103,10 +102,10 @@ func TestNginxCheck(t *testing.T) {
}
}()
go func() {
cmd.Wait() //nolint:errcheck
cmd.Wait() //nolint:errcheck // Ignore the error
}()
if _, err := pidFile.Write([]byte(fmt.Sprintf("%v", pid))); err != nil {
if _, err := fmt.Fprintf(pidFile, "%v", pid); err != nil {
t.Errorf("unexpected error writing the pid file: %v", err)
}
@ -121,7 +120,7 @@ func TestNginxCheck(t *testing.T) {
})
// pollute pid file
pidFile.Write([]byte("999999")) //nolint:errcheck
pidFile.WriteString("999999") //nolint:errcheck // Ignore the error
pidFile.Close()
t.Run("bad pid", func(t *testing.T) {
@ -134,7 +133,7 @@ func TestNginxCheck(t *testing.T) {
}
func callHealthz(expErr bool, healthzPath string, mux *http.ServeMux) error {
req, err := http.NewRequest(http.MethodGet, healthzPath, nil)
req, err := http.NewRequest(http.MethodGet, healthzPath, http.NoBody)
if err != nil {
return fmt.Errorf("healthz error: %v", err)
}

View file

@ -29,10 +29,8 @@ import (
"k8s.io/ingress-nginx/pkg/util/runtime"
)
var (
// EnableSSLChainCompletion Autocomplete SSL certificate chains with missing intermediate CA certificates.
EnableSSLChainCompletion = false
)
// EnableSSLChainCompletion Autocomplete SSL certificate chains with missing intermediate CA certificates.
var EnableSSLChainCompletion = false
const (
// http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size
@ -91,7 +89,7 @@ const (
// Configuration represents the content of nginx.conf file
type Configuration struct {
defaults.Backend `json:",squash"` //nolint:staticcheck
defaults.Backend `json:",squash"` //nolint:staticcheck // Ignore unknown JSON option "squash" error
// AllowSnippetAnnotations enable users to add their own snippets via ingress annotation.
// If disabled, only snippets added via ConfigMap are added to ingress.
@ -141,9 +139,9 @@ type Configuration struct {
// By default access logs go to /var/log/nginx/access.log
AccessLogPath string `json:"access-log-path,omitempty"`
// HttpAccessLogPath sets the path of the access logs for http context globally if enabled
// HTTPAccessLogPath sets the path of the access logs for http context globally if enabled
// http://nginx.org/en/docs/http/ngx_http_log_module.html#access_log
HttpAccessLogPath string `json:"http-access-log-path,omitempty"`
HTTPAccessLogPath string `json:"http-access-log-path,omitempty"`
// StreamAccessLogPath sets the path of the access logs for stream context globally if enabled
// http://nginx.org/en/docs/stream/ngx_stream_log_module.html#access_log
@ -230,19 +228,19 @@ type Configuration struct {
// https://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_field_size
// HTTP2MaxFieldSize Limits the maximum size of an HPACK-compressed request header field
// NOTE: Deprecated
// Deprecated: HTTP2MaxFieldSize is deprecated.
HTTP2MaxFieldSize string `json:"http2-max-field-size,omitempty"`
// https://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_header_size
// HTTP2MaxHeaderSize Limits the maximum size of the entire request header list after HPACK decompression
// NOTE: Deprecated
// Deprecated: HTTP2MaxHeaderSize is deprecated.
HTTP2MaxHeaderSize string `json:"http2-max-header-size,omitempty"`
// http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_requests
// HTTP2MaxRequests Sets the maximum number of requests (including push requests) that can be served
// through one HTTP/2 connection, after which the next client request will lead to connection closing
// and the need of establishing a new connection.
// NOTE: Deprecated
// Deprecated: HTTP2MaxRequests is deprecated.
HTTP2MaxRequests int `json:"http2-max-requests,omitempty"`
// http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_concurrent_streams
@ -552,7 +550,7 @@ type Configuration struct {
UseForwardedHeaders bool `json:"use-forwarded-headers"`
// Sets whether to enable the real ip module
EnableRealIp bool `json:"enable-real-ip"`
EnableRealIP bool `json:"enable-real-ip"`
// Sets the header field for identifying the originating IP address of a client
// Default is X-Forwarded-For
@ -621,7 +619,7 @@ type Configuration struct {
// Default: 0.01
OtelSamplerRatio float32 `json:"otel-sampler-ratio"`
//OtelSamplerParentBased specifies the parent based sampler to be use for any traces created
// OtelSamplerParentBased specifies the parent based sampler to be use for any traces created
// Default: true
OtelSamplerParentBased bool `json:"otel-sampler-parent-based"`
@ -891,7 +889,7 @@ func NewDefault() Configuration {
EnableUnderscoresInHeaders: false,
ErrorLogLevel: errorLevel,
UseForwardedHeaders: false,
EnableRealIp: false,
EnableRealIP: false,
ForwardedForHeader: "X-Forwarded-For",
ComputeFullForwardedFor: false,
ProxyAddOriginalURIHeader: false,
@ -1039,41 +1037,41 @@ func NewDefault() Configuration {
// TemplateConfig contains the nginx configuration to render the file nginx.conf
type TemplateConfig struct {
ProxySetHeaders map[string]string
AddHeaders map[string]string
BacklogSize int
Backends []*ingress.Backend
PassthroughBackends []*ingress.SSLPassthroughBackend
Servers []*ingress.Server
TCPBackends []ingress.L4Service
UDPBackends []ingress.L4Service
HealthzURI string
Cfg Configuration
IsIPV6Enabled bool
IsSSLPassthroughEnabled bool
NginxStatusIpv4Whitelist []string
NginxStatusIpv6Whitelist []string
RedirectServers interface{}
ListenPorts *ListenPorts
PublishService *apiv1.Service
EnableMetrics bool
MaxmindEditionFiles *[]string
MonitorMaxBatchSize int
PID string
StatusPath string
StatusPort int
StreamPort int
StreamSnippets []string
ProxySetHeaders map[string]string `json:"ProxySetHeaders"`
AddHeaders map[string]string `json:"AddHeaders"`
BacklogSize int `json:"BacklogSize"`
Backends []*ingress.Backend `json:"Backends"`
PassthroughBackends []*ingress.SSLPassthroughBackend `json:"PassthroughBackends"`
Servers []*ingress.Server `json:"Servers"`
TCPBackends []ingress.L4Service `json:"TCPBackends"`
UDPBackends []ingress.L4Service `json:"UDPBackends"`
HealthzURI string `json:"HealthzURI"`
Cfg Configuration `json:"Cfg"`
IsIPV6Enabled bool `json:"IsIPV6Enabled"`
IsSSLPassthroughEnabled bool `json:"IsSSLPassthroughEnabled"`
NginxStatusIpv4Whitelist []string `json:"NginxStatusIpv4Whitelist"`
NginxStatusIpv6Whitelist []string `json:"NginxStatusIpv6Whitelist"`
RedirectServers interface{} `json:"RedirectServers"`
ListenPorts *ListenPorts `json:"ListenPorts"`
PublishService *apiv1.Service `json:"PublishService"`
EnableMetrics bool `json:"EnableMetrics"`
MaxmindEditionFiles *[]string `json:"MaxmindEditionFiles"`
MonitorMaxBatchSize int `json:"MonitorMaxBatchSize"`
PID string `json:"PID"`
StatusPath string `json:"StatusPath"`
StatusPort int `json:"StatusPort"`
StreamPort int `json:"StreamPort"`
StreamSnippets []string `json:"StreamSnippets"`
}
// ListenPorts describe the ports required to run the
// NGINX Ingress controller
type ListenPorts struct {
HTTP int
HTTPS int
Health int
Default int
SSLProxy int
HTTP int `json:"HTTP"`
HTTPS int `json:"HTTPS"`
Health int `json:"Health"`
Default int `json:"Default"`
SSLProxy int `json:"SSLProxy"`
}
// GlobalExternalAuth describe external authentication configuration for the

View file

@ -114,7 +114,7 @@ type Configuration struct {
DisableCatchAll bool
IngressClassConfiguration *ingressclass.IngressClassConfiguration
IngressClassConfiguration *ingressclass.Configuration
ValidationWebhook string
ValidationWebhookCertPath string
@ -143,7 +143,7 @@ type Configuration struct {
func getIngressPodZone(svc *apiv1.Service) string {
svcKey := k8s.MetaNamespaceKey(svc)
if svcZoneAnnotation, ok := svc.ObjectMeta.GetAnnotations()[apiv1.AnnotationTopologyMode]; ok {
if strings.ToLower(svcZoneAnnotation) == "auto" {
if strings.EqualFold(svcZoneAnnotation, "auto") {
if foundZone, ok := k8s.IngressNodeDetails.GetLabels()[apiv1.LabelTopologyZone]; ok {
klog.V(3).Infof("Svc has topology aware annotation enabled, try to use zone %q where controller pod is running for Service %q ", foundZone, svcKey)
return foundZone
@ -154,7 +154,7 @@ func getIngressPodZone(svc *apiv1.Service) string {
}
// GetPublishService returns the Service used to set the load-balancer status of Ingresses.
func (n NGINXController) GetPublishService() *apiv1.Service {
func (n *NGINXController) GetPublishService() *apiv1.Service {
s, err := n.store.GetService(n.cfg.PublishService)
if err != nil {
return nil
@ -189,13 +189,16 @@ func (n *NGINXController) syncIngress(interface{}) error {
if !utilingress.IsDynamicConfigurationEnough(pcfg, n.runningConfig) {
klog.InfoS("Configuration changes detected, backend reload required")
hash, _ := hashstructure.Hash(pcfg, hashstructure.FormatV1, &hashstructure.HashOptions{
hash, err := hashstructure.Hash(pcfg, hashstructure.FormatV1, &hashstructure.HashOptions{
TagName: "json",
})
if err != nil {
klog.Errorf("unexpected error hashing configuration: %v", err)
}
pcfg.ConfigurationChecksum = fmt.Sprintf("%v", hash)
err := n.OnUpdate(*pcfg)
err = n.OnUpdate(*pcfg)
if err != nil {
n.metricCollector.IncReloadErrorCount()
n.metricCollector.ConfigSuccess(hash, false)
@ -263,7 +266,7 @@ func (n *NGINXController) syncIngress(interface{}) error {
func (n *NGINXController) CheckWarning(ing *networking.Ingress) ([]string, error) {
warnings := make([]string, 0)
var deprecatedAnnotations = sets.NewString()
deprecatedAnnotations := sets.NewString()
deprecatedAnnotations.Insert(
"enable-influxdb",
"influxdb-measurement",
@ -335,7 +338,7 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
}
if n.cfg.DisableCatchAll && ing.Spec.DefaultBackend != nil {
return fmt.Errorf("This deployment is trying to create a catch-all ingress while DisableCatchAll flag is set to true. Remove '.spec.defaultBackend' or set DisableCatchAll flag to false.")
return fmt.Errorf("this deployment is trying to create a catch-all ingress while DisableCatchAll flag is set to true. Remove '.spec.defaultBackend' or set DisableCatchAll flag to false")
}
startRender := time.Now().UnixNano() / 1000000
cfg := n.store.GetBackendConfiguration()
@ -355,10 +358,9 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
}
for key, value := range ing.ObjectMeta.GetAnnotations() {
if parser.AnnotationsPrefix != parser.DefaultAnnotationsPrefix {
if strings.HasPrefix(key, fmt.Sprintf("%s/", parser.DefaultAnnotationsPrefix)) {
return fmt.Errorf("This deployment has a custom annotation prefix defined. Use '%s' instead of '%s'", parser.AnnotationsPrefix, parser.DefaultAnnotationsPrefix)
return fmt.Errorf("this deployment has a custom annotation prefix defined. Use '%s' instead of '%s'", parser.AnnotationsPrefix, parser.DefaultAnnotationsPrefix)
}
}
@ -374,10 +376,9 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
return fmt.Errorf("%s annotation cannot be used. Snippet directives are disabled by the Ingress administrator", key)
}
if len(cfg.GlobalRateLimitMemcachedHost) == 0 && strings.HasPrefix(key, fmt.Sprintf("%s/%s", parser.AnnotationsPrefix, "global-rate-limit")) {
if cfg.GlobalRateLimitMemcachedHost == "" && strings.HasPrefix(key, fmt.Sprintf("%s/%s", parser.AnnotationsPrefix, "global-rate-limit")) {
return fmt.Errorf("'global-rate-limit*' annotations require 'global-rate-limit-memcached-host' settings configured in the global configmap")
}
}
k8s.SetDefaultNGINXPathType(ing)
@ -401,7 +402,7 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
startTest := time.Now().UnixNano() / 1000000
_, servers, pcfg := n.getConfiguration(ings)
err = checkOverlap(ing, allIngresses, servers)
err = checkOverlap(ing, servers)
if err != nil {
n.metricCollector.IncCheckErrorCount(ing.ObjectMeta.Namespace, ing.Name)
return err
@ -452,7 +453,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
return []ingress.L4Service{}
}
var svcs []ingress.L4Service
svcs := make([]ingress.L4Service, 0, len(configmap.Data))
var svcProxyProtocol ingress.ProxyProtocol
rp := []int{
@ -489,10 +490,10 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
svcProxyProtocol.Encode = false
// Proxy Protocol is only compatible with TCP Services
if len(nsSvcPort) >= 3 && proto == apiv1.ProtocolTCP {
if len(nsSvcPort) >= 3 && strings.ToUpper(nsSvcPort[2]) == "PROXY" {
if len(nsSvcPort) >= 3 && strings.EqualFold(nsSvcPort[2], "PROXY") {
svcProxyProtocol.Decode = true
}
if len(nsSvcPort) == 4 && strings.ToUpper(nsSvcPort[3]) == "PROXY" {
if len(nsSvcPort) == 4 && strings.EqualFold(nsSvcPort[3], "PROXY") {
svcProxyProtocol.Encode = true
}
}
@ -532,6 +533,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
klog.V(3).Infof("Searching Endpoints with %v port number %d for Service %q", proto, targetPort, nsName)
for i := range svc.Spec.Ports {
sp := svc.Spec.Ports[i]
//nolint:gosec // Ignore G109 error
if sp.Port == int32(targetPort) {
if sp.Protocol == proto {
endps = getEndpointsFromSlices(svc, &sp, proto, zone, n.store.GetServiceEndpointsSlices)
@ -574,7 +576,7 @@ func (n *NGINXController) getDefaultUpstream() *ingress.Backend {
}
svcKey := n.cfg.DefaultService
if len(svcKey) == 0 {
if svcKey == "" {
upstream.Endpoints = append(upstream.Endpoints, n.DefaultEndpoint())
return upstream
}
@ -690,13 +692,14 @@ func dropSnippetDirectives(anns *annotations.Ingress, ingKey string) {
klog.V(3).Infof("Ingress %q tried to use stream-snippet and the annotation is disabled by the admin. Removing the annotation", ingKey)
anns.StreamSnippet = ""
}
}
}
// getBackendServers returns a list of Upstream and Server to be used by the
// backend. An upstream can be used in multiple servers if the namespace,
// service name and port are the same.
//
//nolint:gocyclo // Ignore function complexity error
func (n *NGINXController) getBackendServers(ingresses []*ingress.Ingress) ([]*ingress.Backend, []*ingress.Server) {
du := n.getDefaultUpstream()
upstreams := n.createUpstreams(ingresses, du)
@ -1030,7 +1033,7 @@ func (n *NGINXController) createUpstreams(data []*ingress.Ingress, du *ingress.B
// configure traffic shaping for canary
if anns.Canary.Enabled {
upstreams[defBackend].NoServer = true
upstreams[defBackend].TrafficShapingPolicy = newTrafficShapingPolicy(anns.Canary)
upstreams[defBackend].TrafficShapingPolicy = newTrafficShapingPolicy(&anns.Canary)
}
if len(upstreams[defBackend].Endpoints) == 0 {
@ -1095,7 +1098,7 @@ func (n *NGINXController) createUpstreams(data []*ingress.Ingress, du *ingress.B
// configure traffic shaping for canary
if anns.Canary.Enabled {
upstreams[name].NoServer = true
upstreams[name].TrafficShapingPolicy = newTrafficShapingPolicy(anns.Canary)
upstreams[name].TrafficShapingPolicy = newTrafficShapingPolicy(&anns.Canary)
}
if len(upstreams[name].Endpoints) == 0 {
@ -1206,7 +1209,6 @@ func (n *NGINXController) serviceEndpoints(svcKey, backendPort string) ([]ingres
if strconv.Itoa(int(servicePort.Port)) == backendPort ||
servicePort.TargetPort.String() == backendPort ||
servicePort.Name == backendPort {
endps := getEndpointsFromSlices(svc, &servicePort, apiv1.ProtocolTCP, zone, n.store.GetServiceEndpointsSlices)
if len(endps) == 0 {
klog.Warningf("Service %q does not have any active Endpoint.", svcKey)
@ -1239,8 +1241,8 @@ func (n *NGINXController) getDefaultSSLCertificate() *ingress.SSLCert {
// one root location, which uses a default backend if left unspecified.
func (n *NGINXController) createServers(data []*ingress.Ingress,
upstreams map[string]*ingress.Backend,
du *ingress.Backend) map[string]*ingress.Server {
du *ingress.Backend,
) map[string]*ingress.Server {
servers := make(map[string]*ingress.Server, len(data))
allAliases := make(map[string][]string, len(data))
@ -1282,7 +1284,8 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
Rewrite: false,
},
},
}}
},
}
// initialize all other servers
for _, ing := range data {
@ -1532,7 +1535,7 @@ func locationApplyAnnotations(loc *ingress.Location, anns *annotations.Ingress)
}
// OK to merge canary ingresses iff there exists one or more ingresses to potentially merge into
func nonCanaryIngressExists(ingresses []*ingress.Ingress, canaryIngresses []*ingress.Ingress) bool {
func nonCanaryIngressExists(ingresses, canaryIngresses []*ingress.Ingress) bool {
return len(ingresses)-len(canaryIngresses) > 0
}
@ -1540,12 +1543,12 @@ func nonCanaryIngressExists(ingresses []*ingress.Ingress, canaryIngresses []*ing
// 1) names of backends do not match and canary doesn't merge into itself
// 2) primary name is not the default upstream
// 3) the primary has a server
func canMergeBackend(primary *ingress.Backend, alternative *ingress.Backend) bool {
func canMergeBackend(primary, alternative *ingress.Backend) bool {
return alternative != nil && primary.Name != alternative.Name && primary.Name != defUpstreamName && !primary.NoServer
}
// Performs the merge action and checks to ensure that one two alternative backends do not merge into each other
func mergeAlternativeBackend(ing *ingress.Ingress, priUps *ingress.Backend, altUps *ingress.Backend) bool {
func mergeAlternativeBackend(ing *ingress.Ingress, priUps, altUps *ingress.Backend) bool {
if priUps.NoServer {
klog.Warningf("unable to merge alternative backend %v into primary backend %v because %v is a primary backend",
altUps.Name, priUps.Name, priUps.Name)
@ -1563,8 +1566,7 @@ func mergeAlternativeBackend(ing *ingress.Ingress, priUps *ingress.Backend, altU
priUps.SessionAffinity.DeepCopyInto(&altUps.SessionAffinity)
}
priUps.AlternativeBackends =
append(priUps.AlternativeBackends, altUps.Name)
priUps.AlternativeBackends = append(priUps.AlternativeBackends, altUps.Name)
return true
}
@ -1574,8 +1576,8 @@ func mergeAlternativeBackend(ing *ingress.Ingress, priUps *ingress.Backend, altU
// to a backend's alternative list.
// If no match is found, then the serverless backend is deleted.
func mergeAlternativeBackends(ing *ingress.Ingress, upstreams map[string]*ingress.Backend,
servers map[string]*ingress.Server) {
servers map[string]*ingress.Server,
) {
// merge catch-all alternative backends
if ing.Spec.DefaultBackend != nil {
upsName := upstreamName(ing.Namespace, ing.Spec.DefaultBackend.Service)
@ -1585,7 +1587,6 @@ func mergeAlternativeBackends(ing *ingress.Ingress, upstreams map[string]*ingres
if altUps == nil {
klog.Warningf("alternative backend %s has already been removed", upsName)
} else {
merged := false
altEqualsPri := false
@ -1676,8 +1677,8 @@ func mergeAlternativeBackends(ing *ingress.Ingress, upstreams map[string]*ingres
// extractTLSSecretName returns the name of the Secret containing a SSL
// certificate for the given host name, or an empty string.
func extractTLSSecretName(host string, ing *ingress.Ingress,
getLocalSSLCert func(string) (*ingress.SSLCert, error)) string {
getLocalSSLCert func(string) (*ingress.SSLCert, error),
) string {
if ing == nil {
return ""
}
@ -1694,7 +1695,6 @@ func extractTLSSecretName(host string, ing *ingress.Ingress,
// no TLS host matching host name, try each TLS host for matching SAN or CN
for _, tls := range ing.Spec.TLS {
if tls.SecretName == "" {
// There's no secretName specified, so it will never be available
continue
@ -1753,6 +1753,7 @@ func externalNamePorts(name string, svc *apiv1.Service) *apiv1.ServicePort {
}
for _, svcPort := range svc.Spec.Ports {
//nolint:gosec // Ignore G109 error
if svcPort.Port != int32(port) {
continue
}
@ -1771,13 +1772,14 @@ func externalNamePorts(name string, svc *apiv1.Service) *apiv1.ServicePort {
// ExternalName without port
return &apiv1.ServicePort{
Protocol: "TCP",
Protocol: "TCP",
//nolint:gosec // Ignore G109 error
Port: int32(port),
TargetPort: intstr.FromInt(port),
}
}
func checkOverlap(ing *networking.Ingress, ingresses []*ingress.Ingress, servers []*ingress.Server) error {
func checkOverlap(ing *networking.Ingress, servers []*ingress.Server) error {
for _, rule := range ing.Spec.Rules {
if rule.HTTP == nil {
continue
@ -1870,7 +1872,7 @@ func (n *NGINXController) getStreamSnippets(ingresses []*ingress.Ingress) []stri
}
// newTrafficShapingPolicy creates new ingress.TrafficShapingPolicy instance using canary configuration
func newTrafficShapingPolicy(cfg canary.Config) ingress.TrafficShapingPolicy {
func newTrafficShapingPolicy(cfg *canary.Config) ingress.TrafficShapingPolicy {
return ingress.TrafficShapingPolicy{
Weight: cfg.Weight,
WeightTotal: cfg.WeightTotal,

View file

@ -60,51 +60,56 @@ import (
"k8s.io/ingress-nginx/pkg/util/file"
)
const (
exampleBackend = "example-http-svc-1-80"
TRUE = "true"
)
type fakeIngressStore struct {
ingresses []*ingress.Ingress
configuration ngx_config.Configuration
}
func (fakeIngressStore) GetIngressClass(ing *networking.Ingress, icConfig *ingressclass.IngressClassConfiguration) (string, error) {
func (fakeIngressStore) GetIngressClass(_ *networking.Ingress, _ *ingressclass.Configuration) (string, error) {
return "nginx", nil
}
func (fis fakeIngressStore) GetBackendConfiguration() ngx_config.Configuration {
func (fis *fakeIngressStore) GetBackendConfiguration() ngx_config.Configuration {
return fis.configuration
}
func (fis fakeIngressStore) GetSecurityConfiguration() defaults.SecurityConfiguration {
func (fis *fakeIngressStore) GetSecurityConfiguration() defaults.SecurityConfiguration {
return defaults.SecurityConfiguration{
AnnotationsRiskLevel: fis.configuration.AnnotationsRiskLevel,
AllowCrossNamespaceResources: fis.configuration.AllowCrossNamespaceResources,
}
}
func (fakeIngressStore) GetConfigMap(key string) (*corev1.ConfigMap, error) {
func (fakeIngressStore) GetConfigMap(_ string) (*corev1.ConfigMap, error) {
return nil, fmt.Errorf("test error")
}
func (fakeIngressStore) GetSecret(key string) (*corev1.Secret, error) {
func (fakeIngressStore) GetSecret(_ string) (*corev1.Secret, error) {
return nil, fmt.Errorf("test error")
}
func (fakeIngressStore) GetService(key string) (*corev1.Service, error) {
func (fakeIngressStore) GetService(_ string) (*corev1.Service, error) {
return nil, fmt.Errorf("test error")
}
func (fakeIngressStore) GetServiceEndpointsSlices(key string) ([]*discoveryv1.EndpointSlice, error) {
func (fakeIngressStore) GetServiceEndpointsSlices(_ string) ([]*discoveryv1.EndpointSlice, error) {
return nil, fmt.Errorf("test error")
}
func (fis fakeIngressStore) ListIngresses() []*ingress.Ingress {
func (fis *fakeIngressStore) ListIngresses() []*ingress.Ingress {
return fis.ingresses
}
func (fis fakeIngressStore) FilterIngresses(ingresses []*ingress.Ingress, filterFunc store.IngressFilterFunc) []*ingress.Ingress {
func (fis *fakeIngressStore) FilterIngresses(ingresses []*ingress.Ingress, _ store.IngressFilterFunc) []*ingress.Ingress {
return ingresses
}
func (fakeIngressStore) GetLocalSSLCert(name string) (*ingress.SSLCert, error) {
func (fakeIngressStore) GetLocalSSLCert(_ string) (*ingress.SSLCert, error) {
return nil, fmt.Errorf("test error")
}
@ -120,7 +125,7 @@ func (fakeIngressStore) GetDefaultBackend() defaults.Backend {
return defaults.Backend{}
}
func (fakeIngressStore) Run(stopCh chan struct{}) {}
func (fakeIngressStore) Run(_ chan struct{}) {}
type testNginxTestCommand struct {
t *testing.T
@ -129,7 +134,7 @@ type testNginxTestCommand struct {
err error
}
func (ntc testNginxTestCommand) ExecCommand(args ...string) *exec.Cmd {
func (ntc testNginxTestCommand) ExecCommand(_ ...string) *exec.Cmd {
return nil
}
@ -152,7 +157,7 @@ func (ntc testNginxTestCommand) Test(cfg string) ([]byte, error) {
type fakeTemplate struct{}
func (fakeTemplate) Write(conf ngx_config.TemplateConfig) ([]byte, error) {
func (fakeTemplate) Write(conf *ngx_config.TemplateConfig) ([]byte, error) {
r := []byte{}
for _, s := range conf.Servers {
if len(r) > 0 {
@ -196,7 +201,7 @@ func TestCheckIngress(t *testing.T) {
nginx.metricCollector = metric.DummyCollector{}
nginx.t = fakeTemplate{}
nginx.store = fakeIngressStore{
nginx.store = &fakeIngressStore{
ingresses: []*ingress.Ingress{},
}
@ -226,7 +231,7 @@ func TestCheckIngress(t *testing.T) {
}
t.Run("When the hostname is updated", func(t *testing.T) {
nginx.store = fakeIngressStore{
nginx.store = &fakeIngressStore{
ingresses: []*ingress.Ingress{
{
Ingress: *ing,
@ -273,7 +278,7 @@ func TestCheckIngress(t *testing.T) {
})
t.Run("When snippets are disabled and user tries to use snippet annotation", func(t *testing.T) {
nginx.store = fakeIngressStore{
nginx.store = &fakeIngressStore{
ingresses: []*ingress.Ingress{},
configuration: ngx_config.Configuration{
AllowSnippetAnnotations: false,
@ -290,7 +295,7 @@ func TestCheckIngress(t *testing.T) {
})
t.Run("When invalid directives are used in annotation values", func(t *testing.T) {
nginx.store = fakeIngressStore{
nginx.store = &fakeIngressStore{
ingresses: []*ingress.Ingress{},
configuration: ngx_config.Configuration{
AnnotationValueWordBlocklist: "invalid_directive, another_directive",
@ -366,12 +371,11 @@ func TestCheckIngress(t *testing.T) {
}
func TestCheckWarning(t *testing.T) {
// Ensure no panic with wrong arguments
var nginx = &NGINXController{}
nginx := &NGINXController{}
nginx.t = fakeTemplate{}
nginx.store = fakeIngressStore{
nginx.store = &fakeIngressStore{
ingresses: []*ingress.Ingress{},
}
@ -390,7 +394,7 @@ func TestCheckWarning(t *testing.T) {
},
}
t.Run("when a deprecated annotation is used a warning should be returned", func(t *testing.T) {
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = "true"
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = TRUE
defer func() {
ing.ObjectMeta.Annotations = map[string]string{}
}()
@ -407,7 +411,6 @@ func TestCheckWarning(t *testing.T) {
})
t.Run("When an invalid pathType is used, a warning should be returned", func(t *testing.T) {
rules := ing.Spec.DeepCopy().Rules
ing.Spec.Rules = []networking.IngressRule{
{
@ -443,8 +446,8 @@ func TestCheckWarning(t *testing.T) {
}
t.Run("adding invalid annotations increases the warning count", func(t *testing.T) {
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = "true"
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("secure-verify-ca-secret")] = "true"
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = TRUE
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("secure-verify-ca-secret")] = TRUE
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("influxdb-host")] = "blabla"
defer func() {
ing.ObjectMeta.Annotations = map[string]string{}
@ -472,6 +475,7 @@ func TestCheckWarning(t *testing.T) {
})
}
//nolint:dupl // Ignore dupl errors for similar test case
func TestMergeAlternativeBackends(t *testing.T) {
testCases := map[string]struct {
ingress *ingress.Ingress
@ -1537,8 +1541,8 @@ func TestExtractTLSSecretName(t *testing.T) {
}
}
//nolint:gocyclo // Ignore function complexity error
func TestGetBackendServers(t *testing.T) {
testCases := []struct {
Ingresses []*ingress.Ingress
Validate func(ingresses []*ingress.Ingress, upstreams []*ingress.Backend, servers []*ingress.Server)
@ -2078,7 +2082,7 @@ func TestGetBackendServers(t *testing.T) {
t.Errorf("server hostname should be 'example.com', got '%s'", s.Hostname)
}
if s.Locations[0].Backend != "example-http-svc-1-80" || s.Locations[1].Backend != "example-http-svc-1-80" || s.Locations[2].Backend != "example-http-svc-1-80" {
if s.Locations[0].Backend != exampleBackend || s.Locations[1].Backend != exampleBackend || s.Locations[2].Backend != exampleBackend {
t.Errorf("all location backend should be 'example-http-svc-1-80'")
}
@ -2087,7 +2091,7 @@ func TestGetBackendServers(t *testing.T) {
return
}
if upstreams[0].Name != "example-http-svc-1-80" {
if upstreams[0].Name != exampleBackend {
t.Errorf("example-http-svc-1-80 should be first upstream, got %s", upstreams[0].Name)
return
}
@ -2101,6 +2105,7 @@ func TestGetBackendServers(t *testing.T) {
SetConfigMap: testConfigMap,
},
{
//nolint:dupl // Ignore dupl errors for similar test case
Ingresses: []*ingress.Ingress{
{
Ingress: networking.Ingress{
@ -2208,6 +2213,7 @@ func TestGetBackendServers(t *testing.T) {
SetConfigMap: testConfigMap,
},
{
//nolint:dupl // Ignore dupl errors for similar test case
Ingresses: []*ingress.Ingress{
{
Ingress: networking.Ingress{
@ -2319,7 +2325,7 @@ func TestGetBackendServers(t *testing.T) {
SelfLink: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/config", ns),
},
Data: map[string]string{
"proxy-ssl-location-only": "true",
"proxy-ssl-location-only": TRUE,
},
}
},
@ -2380,7 +2386,7 @@ func TestGetBackendServers(t *testing.T) {
SelfLink: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/config", ns),
},
Data: map[string]string{
"proxy-ssl-location-only": "true",
"proxy-ssl-location-only": TRUE,
},
}
},
@ -2449,7 +2455,6 @@ func TestGetBackendServers(t *testing.T) {
if len(s.Locations[0].Allowlist.CIDR) != 1 || s.Locations[0].Allowlist.CIDR[0] != "10.0.0.0/24" {
t.Errorf("allow list was incorrectly dropped, len should be 1 and contain 10.0.0.0/24")
}
},
SetConfigMap: func(ns string) *corev1.ConfigMap {
return &corev1.ConfigMap{
@ -2520,7 +2525,7 @@ func newNGINXController(t *testing.T) *NGINXController {
channels.NewRingChannel(10),
false,
true,
&ingressclass.IngressClassConfiguration{
&ingressclass.Configuration{
Controller: "k8s.io/ingress-nginx",
AnnotationValue: "nginx",
},
@ -2586,7 +2591,7 @@ func newDynamicNginxController(t *testing.T, setConfigMap func(string) *corev1.C
channels.NewRingChannel(10),
false,
true,
&ingressclass.IngressClassConfiguration{
&ingressclass.Configuration{
Controller: "k8s.io/ingress-nginx",
AnnotationValue: "nginx",
},

View file

@ -36,8 +36,8 @@ import (
// getEndpointsFromSlices returns a list of Endpoint structs for a given service/target port combination.
func getEndpointsFromSlices(s *corev1.Service, port *corev1.ServicePort, proto corev1.Protocol, zoneForHints string,
getServiceEndpointsSlices func(string) ([]*discoveryv1.EndpointSlice, error)) []ingress.Endpoint {
getServiceEndpointsSlices func(string) ([]*discoveryv1.EndpointSlice, error),
) []ingress.Endpoint {
upsServers := []ingress.Endpoint{}
if s == nil || port == nil {
@ -94,7 +94,7 @@ func getEndpointsFromSlices(s *corev1.Service, port *corev1.ServicePort, proto c
if !reflect.DeepEqual(*epPort.Protocol, proto) {
continue
}
var targetPort int32 = 0
var targetPort int32
if port.Name == "" {
// port.Name is optional if there is only one port
targetPort = *epPort.Port

View file

@ -27,6 +27,7 @@ import (
"k8s.io/ingress-nginx/pkg/apis/ingress"
)
//nolint:dupl // Ignore dupl errors for similar test case
func TestGetEndpointsFromSlices(t *testing.T) {
tests := []struct {
name string

View file

@ -29,9 +29,9 @@ const (
DefaultAnnotationValue = "nginx"
)
// IngressClassConfiguration defines the various aspects of IngressClass parsing
// Configuration defines the various aspects of IngressClass parsing
// and how the controller should behave in each case
type IngressClassConfiguration struct {
type Configuration struct {
// Controller defines the controller value this daemon watch to.
// Defaults to "k8s.io/ingress-nginx" defined in flags
Controller string
@ -45,7 +45,7 @@ type IngressClassConfiguration struct {
// IgnoreIngressClass defines if Controller should ignore the IngressClass Object if no permissions are
// granted on IngressClass
IgnoreIngressClass bool
//IngressClassByName defines if the Controller should watch for Ingress Classes by
// IngressClassByName defines if the Controller should watch for Ingress Classes by
// .metadata.name together with .spec.Controller
IngressClassByName bool
}

View file

@ -113,7 +113,7 @@ func NewNGINXController(config *Configuration, mc metric.Collector) *NGINXContro
if n.cfg.ValidationWebhook != "" {
n.validationWebhookServer = &http.Server{
Addr: config.ValidationWebhook,
//G112 (CWE-400): Potential Slowloris Attack
// G112 (CWE-400): Potential Slowloris Attack
ReadHeaderTimeout: 10 * time.Second,
Handler: adm_controller.NewAdmissionControllerServer(&adm_controller.IngressAdmission{Checker: n}),
TLSConfig: ssl.NewTLSListener(n.cfg.ValidationWebhookCertPath, n.cfg.ValidationWebhookKeyPath).TLSConfig(),
@ -429,7 +429,7 @@ func (n *NGINXController) start(cmd *exec.Cmd) {
}
// DefaultEndpoint returns the default endpoint to be use as default server that returns 404.
func (n NGINXController) DefaultEndpoint() ingress.Endpoint {
func (n *NGINXController) DefaultEndpoint() ingress.Endpoint {
return ingress.Endpoint{
Address: "127.0.0.1",
Port: fmt.Sprintf("%v", n.cfg.ListenPorts.Default),
@ -438,8 +438,9 @@ func (n NGINXController) DefaultEndpoint() ingress.Endpoint {
}
// generateTemplate returns the nginx configuration file content
func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressCfg ingress.Configuration) ([]byte, error) {
//
//nolint:gocritic // the cfg shouldn't be changed, and shouldn't be mutated by other processes while being rendered.
func (n *NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressCfg ingress.Configuration) ([]byte, error) {
if n.cfg.EnableSSLPassthrough {
servers := []*tcpproxy.TCPServer{}
for _, pb := range ingressCfg.PassthroughBackends {
@ -458,6 +459,7 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC
}
} else {
for _, sp := range svc.Spec.Ports {
//nolint:gosec // Ignore G109 error
if sp.Port == int32(port) {
port = int(sp.Port)
break
@ -563,7 +565,7 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC
if err != nil {
klog.Warningf("Error reading Secret %q from local store: %v", secretName, err)
} else {
nsSecName := strings.Replace(secretName, "/", "-", -1)
nsSecName := strings.ReplaceAll(secretName, "/", "-")
dh, ok := secret.Data["dhparam.pem"]
if ok {
pemFileName, err := ssl.AddOrUpdateDHParam(nsSecName, dh)
@ -589,7 +591,7 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC
}
}
tc := ngx_config.TemplateConfig{
tc := &ngx_config.TemplateConfig{
ProxySetHeaders: setHeaders,
AddHeaders: addHeaders,
BacklogSize: sysctlSomaxconn(),
@ -623,7 +625,7 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC
// testTemplate checks if the NGINX configuration inside the byte array is valid
// running the command "nginx -t" using a temporal file.
func (n NGINXController) testTemplate(cfg []byte) error {
func (n *NGINXController) testTemplate(cfg []byte) error {
if len(cfg) == 0 {
return fmt.Errorf("invalid NGINX configuration (empty)")
}
@ -658,6 +660,8 @@ Error: %v
// changes were detected. The received backend Configuration is merged with the
// configuration ConfigMap before generating the final configuration file.
// Returns nil in case the backend was successfully reloaded.
//
//nolint:gocritic // the cfg shouldn't be changed, and shouldn't be mutated by other processes while being rendered.
func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
cfg := n.store.GetBackendConfiguration()
cfg.Resolver = n.resolver
@ -667,12 +671,12 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
return err
}
err = createOpentracingCfg(cfg)
err = createOpentracingCfg(&cfg)
if err != nil {
return err
}
err = createOpentelemetryCfg(cfg)
err = createOpentelemetryCfg(&cfg)
if err != nil {
return err
}
@ -683,7 +687,10 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
}
if klog.V(2).Enabled() {
src, _ := os.ReadFile(cfgPath)
src, err := os.ReadFile(cfgPath)
if err != nil {
return err
}
if !bytes.Equal(src, content) {
tmpfile, err := os.CreateTemp("", "new-nginx-cfg")
if err != nil {
@ -694,11 +701,14 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
if err != nil {
return err
}
//nolint:gosec //Ignore G204 error
diffOutput, err := exec.Command("diff", "-I", "'# Configuration.*'", "-u", cfgPath, tmpfile.Name()).CombinedOutput()
if err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
ws := exitError.Sys().(syscall.WaitStatus)
ws, ok := exitError.Sys().(syscall.WaitStatus)
if !ok {
klog.Errorf("unexpected type: %T", exitError.Sys())
}
if ws.ExitStatus() == 2 {
klog.Warningf("Failed to executing diff command: %v", err)
}
@ -828,9 +838,10 @@ func (n *NGINXController) configureDynamically(pcfg *ingress.Configuration) erro
return nil
}
func updateStreamConfiguration(TCPEndpoints []ingress.L4Service, UDPEndpoints []ingress.L4Service) error {
func updateStreamConfiguration(tcpEndpoints, udpEndpoints []ingress.L4Service) error {
streams := make([]ingress.Backend, 0)
for _, ep := range TCPEndpoints {
for i := range tcpEndpoints {
ep := &tcpEndpoints[i]
var service *apiv1.Service
if ep.Service != nil {
service = &apiv1.Service{Spec: ep.Service.Spec}
@ -844,7 +855,8 @@ func updateStreamConfiguration(TCPEndpoints []ingress.L4Service, UDPEndpoints []
Service: service,
})
}
for _, ep := range UDPEndpoints {
for i := range udpEndpoints {
ep := &udpEndpoints[i]
var service *apiv1.Service
if ep.Service != nil {
service = &apiv1.Service{Spec: ep.Service.Spec}
@ -1034,7 +1046,7 @@ ratio = {{ .OtelSamplerRatio }}
parent_based = {{ .OtelSamplerParentBased }}
`
func datadogOpentracingCfg(cfg ngx_config.Configuration) (string, error) {
func datadogOpentracingCfg(cfg *ngx_config.Configuration) (string, error) {
m := map[string]interface{}{
"service": cfg.DatadogServiceName,
"agent_host": cfg.DatadogCollectorHost,
@ -1058,7 +1070,7 @@ func datadogOpentracingCfg(cfg ngx_config.Configuration) (string, error) {
return string(buf), nil
}
func opentracingCfgFromTemplate(cfg ngx_config.Configuration, tmplName string, tmplText string) (string, error) {
func opentracingCfgFromTemplate(cfg *ngx_config.Configuration, tmplName, tmplText string) (string, error) {
tmpl, err := template.New(tmplName).Parse(tmplText)
if err != nil {
return "", err
@ -1073,17 +1085,18 @@ func opentracingCfgFromTemplate(cfg ngx_config.Configuration, tmplName string, t
return tmplBuf.String(), nil
}
func createOpentracingCfg(cfg ngx_config.Configuration) error {
func createOpentracingCfg(cfg *ngx_config.Configuration) error {
var configData string
var err error
if cfg.ZipkinCollectorHost != "" {
switch {
case cfg.ZipkinCollectorHost != "":
configData, err = opentracingCfgFromTemplate(cfg, "zipkin", zipkinTmpl)
} else if cfg.JaegerCollectorHost != "" || cfg.JaegerEndpoint != "" {
case cfg.JaegerCollectorHost != "" || cfg.JaegerEndpoint != "":
configData, err = opentracingCfgFromTemplate(cfg, "jaeger", jaegerTmpl)
} else if cfg.DatadogCollectorHost != "" {
case cfg.DatadogCollectorHost != "":
configData, err = datadogOpentracingCfg(cfg)
} else {
default:
configData = "{}"
}
@ -1097,8 +1110,7 @@ func createOpentracingCfg(cfg ngx_config.Configuration) error {
return os.WriteFile("/etc/nginx/opentracing.json", []byte(expanded), file.ReadWriteByUser)
}
func createOpentelemetryCfg(cfg ngx_config.Configuration) error {
func createOpentelemetryCfg(cfg *ngx_config.Configuration) error {
tmpl, err := template.New("otel").Parse(otelTmpl)
if err != nil {
return err
@ -1123,7 +1135,10 @@ func cleanTempNginxCfg() error {
return filepath.SkipDir
}
dur, _ := time.ParseDuration("-5m")
dur, err := time.ParseDuration("-5m")
if err != nil {
return err
}
fiveMinutesAgo := time.Now().Add(dur)
if strings.HasPrefix(info.Name(), tempNginxPattern) && info.ModTime().Before(fiveMinutesAgo) {
files = append(files, path)

View file

@ -58,6 +58,7 @@ func TestConfigureDynamically(t *testing.T) {
server := &httptest.Server{
Listener: listener,
//nolint:gosec // Ignore not configured ReadHeaderTimeout in testing
Config: &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusCreated)
@ -76,23 +77,17 @@ func TestConfigureDynamically(t *testing.T) {
switch r.URL.Path {
case "/configuration/backends":
{
if strings.Contains(body, "target") {
t.Errorf("unexpected target reference in JSON content: %v", body)
}
if strings.Contains(body, "target") {
t.Errorf("unexpected target reference in JSON content: %v", body)
}
if !strings.Contains(body, "service") {
t.Errorf("service reference should be present in JSON content: %v", body)
}
if !strings.Contains(body, "service") {
t.Errorf("service reference should be present in JSON content: %v", body)
}
case "/configuration/general":
{
}
case "/configuration/servers":
{
if !strings.Contains(body, `{"certificates":{},"servers":{"myapp.fake":"-1"}}`) {
t.Errorf("should be present in JSON content: %v", body)
}
if !strings.Contains(body, `{"certificates":{},"servers":{"myapp.fake":"-1"}}`) {
t.Errorf("should be present in JSON content: %v", body)
}
default:
t.Errorf("unknown request to %s", r.URL.Path)
@ -218,6 +213,7 @@ func TestConfigureCertificates(t *testing.T) {
server := &httptest.Server{
Listener: listener,
//nolint:gosec // Ignore not configured ReadHeaderTimeout in testing
Config: &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusCreated)
@ -414,6 +410,7 @@ func TestCleanTempNginxCfg(t *testing.T) {
}
}
//nolint:unparam // Ingnore `network` always receives `"tcp"` error
func tryListen(network, address string) (l net.Listener, err error) {
condFunc := func() (bool, error) {
l, err = net.Listen(network, address)

View file

@ -30,7 +30,10 @@ func IsRespawnIfRequired(err error) bool {
return false
}
waitStatus := exitError.Sys().(syscall.WaitStatus)
waitStatus, ok := exitError.Sys().(syscall.WaitStatus)
if !ok {
return false
}
klog.Warningf(`
-------------------------------------------------------------------------------
NGINX master process died (%v): %v

View file

@ -50,7 +50,7 @@ func setupLeaderElection(config *leaderElectionConfig) {
var cancelContext context.CancelFunc
var newLeaderCtx = func(ctx context.Context) context.CancelFunc {
newLeaderCtx := func(ctx context.Context) context.CancelFunc {
// allow to cancel the context in case we stop being the leader
leaderCtx, cancel := context.WithCancel(ctx)
go elector.Run(leaderCtx)
@ -86,8 +86,10 @@ func setupLeaderElection(config *leaderElectionConfig) {
}
broadcaster := record.NewBroadcaster()
hostname, _ := os.Hostname()
hostname, err := os.Hostname()
if err != nil {
klog.Errorf("unexpected error getting hostname: %v", err)
}
recorder := broadcaster.NewRecorder(scheme.Scheme, apiv1.EventSource{
Component: "ingress-leader-elector",
Host: hostname,
@ -107,7 +109,7 @@ func setupLeaderElection(config *leaderElectionConfig) {
ttl := 30 * time.Second
elector, err := leaderelection.NewLeaderElector(leaderelection.LeaderElectionConfig{
elector, err = leaderelection.NewLeaderElector(leaderelection.LeaderElectionConfig{
Lock: lock,
LeaseDuration: ttl,
RenewDeadline: ttl / 2,

View file

@ -88,10 +88,11 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
auth := secret.Data["auth"]
// namespace/secretName -> namespace-secretName
nsSecName := strings.Replace(secretName, "/", "-", -1)
nsSecName := strings.ReplaceAll(secretName, "/", "-")
var sslCert *ingress.SSLCert
if okcert && okkey {
switch {
case okcert && okkey:
if cert == nil {
return nil, fmt.Errorf("key 'tls.crt' missing from Secret %q", secretName)
}
@ -144,7 +145,7 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
}
klog.V(3).InfoS(msg)
} else if len(ca) > 0 {
case len(ca) > 0:
sslCert, err = ssl.CreateCACert(ca)
if err != nil {
return nil, fmt.Errorf("unexpected error creating SSL Cert: %v", err)
@ -166,7 +167,7 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
// makes this secret in 'syncSecret' to be used for Certificate Authentication
// this does not enable Certificate Authentication
klog.V(3).InfoS("Configuring Secret for TLS authentication", "secret", secretName)
} else {
default:
if auth != nil {
return nil, ErrSecretForAuth
}

View file

@ -38,7 +38,7 @@ func (s *EndpointSliceLister) MatchByKey(key string) ([]*discoveryv1.EndpointSli
keyNsLen = 0
} else {
// count '/' char
keyNsLen += 1
keyNsLen++
}
// filter endpointSlices owned by svc
for _, listKey := range s.ListKeys() {

View file

@ -87,7 +87,6 @@ func TestEndpointSliceLister(t *testing.T) {
t.Errorf("unexpected error %v", err)
}
eps, err := el.MatchByKey(key)
if err != nil {
t.Errorf("unexpeted error %v", err)
}

View file

@ -105,7 +105,7 @@ type Storer interface {
Run(stopCh chan struct{})
// GetIngressClass validates given ingress against ingress class configuration and returns the ingress class.
GetIngressClass(ing *networkingv1.Ingress, icConfig *ingressclass.IngressClassConfiguration) (string, error)
GetIngressClass(ing *networkingv1.Ingress, icConfig *ingressclass.Configuration) (string, error)
}
// EventType type of event associated with an informer
@ -242,7 +242,9 @@ type k8sStore struct {
defaultSSLCertificate string
}
// New creates a new object store to be used in the ingress controller
// New creates a new object store to be used in the ingress controller.
//
//nolint:gocyclo // Ignore function complexity error.
func New(
namespace string,
namespaceSelector labels.Selector,
@ -252,9 +254,9 @@ func New(
updateCh *channels.RingChannel,
disableCatchAll bool,
deepInspector bool,
icConfig *ingressclass.IngressClassConfiguration,
disableSyncEvents bool) Storer {
icConfig *ingressclass.Configuration,
disableSyncEvents bool,
) Storer {
store := &k8sStore{
informers: &Informer{},
listers: &Lister{},
@ -474,7 +476,8 @@ func New(
_, errOld = store.GetIngressClass(oldIng, icConfig)
classCur, errCur = store.GetIngressClass(curIng, icConfig)
}
if errOld != nil && errCur == nil {
switch {
case errOld != nil && errCur == nil:
if hasCatchAllIngressRule(curIng.Spec) && disableCatchAll {
klog.InfoS("ignoring update for catch-all ingress because of --disable-catch-all", "ingress", klog.KObj(curIng))
return
@ -482,11 +485,11 @@ func New(
klog.InfoS("creating ingress", "ingress", klog.KObj(curIng), "ingressclass", classCur)
recorder.Eventf(curIng, corev1.EventTypeNormal, "Sync", "Scheduled for sync")
} else if errOld == nil && errCur != nil {
case errOld == nil && errCur != nil:
klog.InfoS("removing ingress because of unknown ingressclass", "ingress", klog.KObj(curIng))
ingDeleteHandler(old)
return
} else if errCur == nil && !reflect.DeepEqual(old, cur) {
case errCur == nil && !reflect.DeepEqual(old, cur):
if hasCatchAllIngressRule(curIng.Spec) && disableCatchAll {
klog.InfoS("ignoring update for catch-all ingress and delete old one because of --disable-catch-all", "ingress", klog.KObj(curIng))
ingDeleteHandler(old)
@ -494,7 +497,7 @@ func New(
}
recorder.Eventf(curIng, corev1.EventTypeNormal, "Sync", "Scheduled for sync")
} else {
default:
klog.V(3).InfoS("No changes on ingress. Skipping update", "ingress", klog.KObj(curIng))
return
}
@ -519,7 +522,10 @@ func New(
ingressClassEventHandler := cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
ingressclass := obj.(*networkingv1.IngressClass)
ingressclass, ok := obj.(*networkingv1.IngressClass)
if !ok {
klog.Errorf("unexpected type: %T", obj)
}
foundClassByName := false
if icConfig.IngressClassByName && ingressclass.Name == icConfig.AnnotationValue {
klog.InfoS("adding ingressclass as ingress-class-by-name is configured", "ingressclass", klog.KObj(ingressclass))
@ -541,7 +547,10 @@ func New(
}
},
DeleteFunc: func(obj interface{}) {
ingressclass := obj.(*networkingv1.IngressClass)
ingressclass, ok := obj.(*networkingv1.IngressClass)
if !ok {
klog.Errorf("unexpected type: %T", obj)
}
if ingressclass.Spec.Controller != icConfig.Controller {
klog.InfoS("ignoring ingressclass as the spec.controller is not the same of this ingress", "ingressclass", klog.KObj(ingressclass))
return
@ -557,8 +566,14 @@ func New(
}
},
UpdateFunc: func(old, cur interface{}) {
oic := old.(*networkingv1.IngressClass)
cic := cur.(*networkingv1.IngressClass)
oic, ok := old.(*networkingv1.IngressClass)
if !ok {
klog.Errorf("unexpected type: %T", old)
}
cic, ok := cur.(*networkingv1.IngressClass)
if !ok {
klog.Errorf("unexpected type: %T", cur)
}
if cic.Spec.Controller != icConfig.Controller {
klog.InfoS("ignoring ingressclass as the spec.controller is not the same of this ingress", "ingressclass", klog.KObj(cic))
return
@ -581,7 +596,10 @@ func New(
secrEventHandler := cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
sec := obj.(*corev1.Secret)
sec, ok := obj.(*corev1.Secret)
if !ok {
klog.Errorf("unexpected type: %T", obj)
}
key := k8s.MetaNamespaceKey(sec)
if store.defaultSSLCertificate == key {
@ -608,7 +626,10 @@ func New(
},
UpdateFunc: func(old, cur interface{}) {
if !reflect.DeepEqual(old, cur) {
sec := cur.(*corev1.Secret)
sec, ok := cur.(*corev1.Secret)
if !ok {
klog.Errorf("unexpected type: %T", cur)
}
key := k8s.MetaNamespaceKey(sec)
if !watchedNamespace(sec.Namespace) {
@ -695,8 +716,14 @@ func New(
}
},
UpdateFunc: func(old, cur interface{}) {
oeps := old.(*discoveryv1.EndpointSlice)
ceps := cur.(*discoveryv1.EndpointSlice)
oeps, ok := old.(*discoveryv1.EndpointSlice)
if !ok {
klog.Errorf("unexpected type: %T", old)
}
ceps, ok := cur.(*discoveryv1.EndpointSlice)
if !ok {
klog.Errorf("unexpected type: %T", cur)
}
if !reflect.DeepEqual(ceps.Endpoints, oeps.Endpoints) {
updateCh.In() <- Event{
Type: UpdateEvent,
@ -750,7 +777,10 @@ func New(
cmEventHandler := cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
cfgMap := obj.(*corev1.ConfigMap)
cfgMap, ok := obj.(*corev1.ConfigMap)
if !ok {
klog.Errorf("unexpected type: %T", obj)
}
key := k8s.MetaNamespaceKey(cfgMap)
handleCfgMapEvent(key, cfgMap, "CREATE")
},
@ -759,7 +789,10 @@ func New(
return
}
cfgMap := cur.(*corev1.ConfigMap)
cfgMap, ok := cur.(*corev1.ConfigMap)
if !ok {
klog.Errorf("unexpected type: %T", cur)
}
key := k8s.MetaNamespaceKey(cfgMap)
handleCfgMapEvent(key, cfgMap, "UPDATE")
},
@ -767,7 +800,10 @@ func New(
serviceHandler := cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
svc := obj.(*corev1.Service)
svc, ok := obj.(*corev1.Service)
if !ok {
klog.Errorf("unexpected type: %T", obj)
}
if svc.Spec.Type == corev1.ServiceTypeExternalName {
updateCh.In() <- Event{
Type: CreateEvent,
@ -776,7 +812,10 @@ func New(
}
},
DeleteFunc: func(obj interface{}) {
svc := obj.(*corev1.Service)
svc, ok := obj.(*corev1.Service)
if !ok {
klog.Errorf("unexpected type: %T", obj)
}
if svc.Spec.Type == corev1.ServiceTypeExternalName {
updateCh.In() <- Event{
Type: DeleteEvent,
@ -785,8 +824,14 @@ func New(
}
},
UpdateFunc: func(old, cur interface{}) {
oldSvc := old.(*corev1.Service)
curSvc := cur.(*corev1.Service)
oldSvc, ok := old.(*corev1.Service)
if !ok {
klog.Errorf("unexpected type: %T", old)
}
curSvc, ok := cur.(*corev1.Service)
if !ok {
klog.Errorf("unexpected type: %T", cur)
}
if reflect.DeepEqual(oldSvc, curSvc) {
return
@ -821,7 +866,10 @@ func New(
}
// do not wait for informers to read the configmap configuration
ns, name, _ := k8s.ParseNameNS(configmap)
ns, name, err := k8s.ParseNameNS(configmap)
if err != nil {
klog.Errorf("unexpected error parsing name and ns: %v", err)
}
cm, err := client.CoreV1().ConfigMaps(ns).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
klog.Warningf("Unexpected error reading configuration configmap: %v", err)
@ -837,10 +885,10 @@ func hasCatchAllIngressRule(spec networkingv1.IngressSpec) bool {
return spec.DefaultBackend != nil
}
func checkBadAnnotationValue(annotations map[string]string, badwords string) error {
func checkBadAnnotationValue(annotationMap map[string]string, badwords string) error {
arraybadWords := strings.Split(strings.TrimSpace(badwords), ",")
for annotation, value := range annotations {
for annotation, value := range annotationMap {
if strings.HasPrefix(annotation, fmt.Sprintf("%s/", parser.AnnotationsPrefix)) {
for _, forbiddenvalue := range arraybadWords {
if strings.Contains(value, forbiddenvalue) {
@ -999,7 +1047,7 @@ func (s *k8sStore) GetService(key string) (*corev1.Service, error) {
return s.listers.Service.ByKey(key)
}
func (s *k8sStore) GetIngressClass(ing *networkingv1.Ingress, icConfig *ingressclass.IngressClassConfiguration) (string, error) {
func (s *k8sStore) GetIngressClass(ing *networkingv1.Ingress, icConfig *ingressclass.Configuration) (string, error) {
// First we try ingressClassName
if !icConfig.IgnoreIngressClass && ing.Spec.IngressClassName != nil {
iclass, err := s.listers.IngressClass.ByKey(*ing.Spec.IngressClassName)
@ -1010,11 +1058,11 @@ func (s *k8sStore) GetIngressClass(ing *networkingv1.Ingress, icConfig *ingressc
}
// Then we try annotation
if ingressclass, ok := ing.GetAnnotations()[ingressclass.IngressKey]; ok {
if ingressclass != icConfig.AnnotationValue {
if class, ok := ing.GetAnnotations()[ingressclass.IngressKey]; ok {
if class != icConfig.AnnotationValue {
return "", fmt.Errorf("ingress class annotation is not equal to the expected by Ingress Controller")
}
return ingressclass, nil
return class, nil
}
// Then we accept if the WithoutClass is enabled
@ -1055,7 +1103,10 @@ func (s *k8sStore) ListIngresses() []*ingress.Ingress {
// filter ingress rules
ingresses := make([]*ingress.Ingress, 0)
for _, item := range s.listers.IngressWithAnnotation.List() {
ing := item.(*ingress.Ingress)
ing, ok := item.(*ingress.Ingress)
if !ok {
klog.Errorf("unexpected type: %T", item)
}
ingresses = append(ingresses, ing)
}

View file

@ -44,29 +44,27 @@ import (
var pathPrefix networking.PathType = networking.PathTypePrefix
var DefaultClassConfig = &ingressclass.IngressClassConfiguration{
var DefaultClassConfig = &ingressclass.Configuration{
Controller: ingressclass.DefaultControllerName,
AnnotationValue: ingressclass.DefaultAnnotationValue,
WatchWithoutClass: false,
}
var (
commonIngressSpec = networking.IngressSpec{
Rules: []networking.IngressRule{
{
Host: "dummy",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
Service: &networking.IngressServiceBackend{
Name: "http-svc",
Port: networking.ServiceBackendPort{
Number: 80,
},
var commonIngressSpec = networking.IngressSpec{
Rules: []networking.IngressRule{
{
Host: "dummy",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/",
PathType: &pathPrefix,
Backend: networking.IngressBackend{
Service: &networking.IngressServiceBackend{
Name: "http-svc",
Port: networking.ServiceBackendPort{
Number: 80,
},
},
},
@ -75,12 +73,15 @@ var (
},
},
},
}
)
},
}
const updateDummyHost = "update-dummy"
//nolint:gocyclo // Ignore function complexity error
func TestStore(t *testing.T) {
//TODO: move env definition to docker image?
os.Setenv("KUBEBUILDER_ASSETS", "/usr/local/bin")
// TODO: move env definition to docker image?
t.Setenv("KUBEBUILDER_ASSETS", "/usr/local/bin")
pathPrefix = networking.PathTypePrefix
@ -90,9 +91,12 @@ func TestStore(t *testing.T) {
t.Fatalf("error: %v", err)
}
emptySelector, _ := labels.Parse("")
emptySelector, err := labels.Parse("")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
defer te.Stop() //nolint:errcheck
defer te.Stop() //nolint:errcheck // Ignore the error
clientSet, err := kubernetes.NewForConfig(cfg)
if err != nil {
@ -176,7 +180,11 @@ func TestStore(t *testing.T) {
return
}
e := evt.(Event)
e, ok := evt.(Event)
if !ok {
return
}
if e.Obj == nil {
continue
}
@ -230,7 +238,7 @@ func TestStore(t *testing.T) {
time.Sleep(1 * time.Second)
ni := ing.DeepCopy()
ni.Spec.Rules[0].Host = "update-dummy"
ni.Spec.Rules[0].Host = updateDummyHost
_ = ensureIngress(ni, clientSet, t)
if err != nil {
t.Errorf("error creating ingress: %v", err)
@ -281,7 +289,10 @@ func TestStore(t *testing.T) {
return
}
e := evt.(Event)
e, ok := evt.(Event)
if !ok {
return
}
if e.Obj == nil {
continue
}
@ -343,7 +354,7 @@ func TestStore(t *testing.T) {
defer deleteIngress(invalidIngress, clientSet, t)
ni := ing.DeepCopy()
ni.Spec.Rules[0].Host = "update-dummy"
ni.Spec.Rules[0].Host = updateDummyHost
_ = ensureIngress(ni, clientSet, t)
if err != nil {
t.Errorf("error creating ingress: %v", err)
@ -392,7 +403,10 @@ func TestStore(t *testing.T) {
return
}
e := evt.(Event)
e, ok := evt.(Event)
if !ok {
return
}
if e.Obj == nil {
continue
}
@ -411,7 +425,7 @@ func TestStore(t *testing.T) {
}
}(updateCh)
ingressClassconfig := &ingressclass.IngressClassConfiguration{
ingressClassconfig := &ingressclass.Configuration{
Controller: ingressclass.DefaultControllerName,
AnnotationValue: ingressclass.DefaultAnnotationValue,
WatchWithoutClass: true,
@ -463,7 +477,7 @@ func TestStore(t *testing.T) {
time.Sleep(1 * time.Second)
validIngressUpdated := validIngress1.DeepCopy()
validIngressUpdated.Spec.Rules[0].Host = "update-dummy"
validIngressUpdated.Spec.Rules[0].Host = updateDummyHost
_ = ensureIngress(validIngressUpdated, clientSet, t)
if err != nil {
t.Errorf("error updating ingress: %v", err)
@ -523,7 +537,10 @@ func TestStore(t *testing.T) {
return
}
e := evt.(Event)
e, ok := evt.(Event)
if !ok {
return
}
if e.Obj == nil {
continue
}
@ -542,7 +559,7 @@ func TestStore(t *testing.T) {
}
}(updateCh)
ingressClassconfig := &ingressclass.IngressClassConfiguration{
ingressClassconfig := &ingressclass.Configuration{
Controller: ingressclass.DefaultControllerName,
AnnotationValue: ic,
IngressClassByName: true,
@ -581,7 +598,7 @@ func TestStore(t *testing.T) {
time.Sleep(1 * time.Second)
ingressUpdated := ing.DeepCopy()
ingressUpdated.Spec.Rules[0].Host = "update-dummy"
ingressUpdated.Spec.Rules[0].Host = updateDummyHost
_ = ensureIngress(ingressUpdated, clientSet, t)
if err != nil {
t.Errorf("error updating ingress: %v", err)
@ -630,7 +647,10 @@ func TestStore(t *testing.T) {
return
}
e := evt.(Event)
e, ok := evt.(Event)
if !ok {
return
}
if e.Obj == nil {
continue
}
@ -684,7 +704,7 @@ func TestStore(t *testing.T) {
time.Sleep(1 * time.Second)
invalidIngressUpdated := invalidIngress.DeepCopy()
invalidIngressUpdated.Spec.Rules[0].Host = "update-dummy"
invalidIngressUpdated.Spec.Rules[0].Host = updateDummyHost
_ = ensureIngress(invalidIngressUpdated, clientSet, t)
if err != nil {
t.Errorf("error creating ingress: %v", err)
@ -725,7 +745,10 @@ func TestStore(t *testing.T) {
return
}
e := evt.(Event)
e, ok := evt.(Event)
if !ok {
return
}
if e.Obj == nil {
continue
}
@ -778,7 +801,7 @@ func TestStore(t *testing.T) {
time.Sleep(1 * time.Second)
invalidIngressUpdated := invalidIngress.DeepCopy()
invalidIngressUpdated.Spec.Rules[0].Host = "update-dummy"
invalidIngressUpdated.Spec.Rules[0].Host = updateDummyHost
_ = ensureIngress(invalidIngressUpdated, clientSet, t)
if err != nil {
t.Errorf("error creating ingress: %v", err)
@ -816,7 +839,10 @@ func TestStore(t *testing.T) {
return
}
e := evt.(Event)
e, ok := evt.(Event)
if !ok {
return
}
if e.Obj == nil {
continue
}
@ -908,7 +934,10 @@ func TestStore(t *testing.T) {
return
}
e := evt.(Event)
e, ok := evt.(Event)
if !ok {
return
}
if e.Obj == nil {
continue
}
@ -1008,7 +1037,6 @@ func TestStore(t *testing.T) {
if atomic.LoadUint64(&del) != 1 {
t.Errorf("expected 1 events of type Delete but %v occurred", del)
}
})
t.Run("should create an ingress with a secret which does not exist", func(t *testing.T) {
@ -1032,7 +1060,10 @@ func TestStore(t *testing.T) {
return
}
e := evt.(Event)
e, ok := evt.(Event)
if !ok {
return
}
if e.Obj == nil {
continue
}
@ -1159,7 +1190,10 @@ func TestStore(t *testing.T) {
return
}
e := evt.(Event)
e, ok := evt.(Event)
if !ok {
return
}
if e.Obj == nil {
continue
}
@ -1174,7 +1208,10 @@ func TestStore(t *testing.T) {
}
}(updateCh)
namesapceSelector, _ := labels.Parse("foo=bar")
namesapceSelector, err := labels.Parse("foo=bar")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
storer := New(
ns,
namesapceSelector,
@ -1236,7 +1273,6 @@ func TestStore(t *testing.T) {
if atomic.LoadUint64(&del) != 0 {
t.Errorf("expected 0 events of type Delete but %v occurred", del)
}
})
// test add ingress with secret it doesn't exists and then add secret
// check secret is generated on fs
@ -1274,16 +1310,16 @@ func deleteNamespace(ns string, clientSet kubernetes.Interface, t *testing.T) {
func createIngressClass(clientSet kubernetes.Interface, t *testing.T, controller string) string {
t.Helper()
ingressclass := &networking.IngressClass{
class := &networking.IngressClass{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("ingress-nginx-%v", time.Now().Unix()),
//Namespace: "xpto" // TODO: We don't support namespaced ingress-class yet
// Namespace: "xpto" // TODO: We don't support namespaced ingress-class yet
},
Spec: networking.IngressClassSpec{
Controller: controller,
},
}
ic, err := clientSet.NetworkingV1().IngressClasses().Create(context.TODO(), ingressclass, metav1.CreateOptions{})
ic, err := clientSet.NetworkingV1().IngressClasses().Create(context.TODO(), class, metav1.CreateOptions{})
if err != nil {
t.Errorf("error creating ingress class: %v", err)
}
@ -1299,7 +1335,7 @@ func deleteIngressClass(ic string, clientSet kubernetes.Interface, t *testing.T)
}
}
func createConfigMap(clientSet kubernetes.Interface, ns string, t *testing.T) string {
func createConfigMap(clientSet kubernetes.Interface, ns string, t *testing.T) {
t.Helper()
configMap := &v1.ConfigMap{
@ -1308,51 +1344,47 @@ func createConfigMap(clientSet kubernetes.Interface, ns string, t *testing.T) st
},
}
cm, err := clientSet.CoreV1().ConfigMaps(ns).Create(context.TODO(), configMap, metav1.CreateOptions{})
_, err := clientSet.CoreV1().ConfigMaps(ns).Create(context.TODO(), configMap, metav1.CreateOptions{})
if err != nil {
t.Errorf("error creating the configuration map: %v", err)
}
return cm.Name
}
func ensureIngress(ingress *networking.Ingress, clientSet kubernetes.Interface, t *testing.T) *networking.Ingress {
func ensureIngress(ing *networking.Ingress, clientSet kubernetes.Interface, t *testing.T) *networking.Ingress {
t.Helper()
ing, err := clientSet.NetworkingV1().Ingresses(ingress.Namespace).Update(context.TODO(), ingress, metav1.UpdateOptions{})
newIngress, err := clientSet.NetworkingV1().Ingresses(ing.Namespace).Update(context.TODO(), ing, metav1.UpdateOptions{})
if err != nil {
if k8sErrors.IsNotFound(err) {
t.Logf("Ingress %v not found, creating", ingress)
t.Logf("Ingress %v not found, creating", ing)
ing, err = clientSet.NetworkingV1().Ingresses(ingress.Namespace).Create(context.TODO(), ingress, metav1.CreateOptions{})
newIngress, err = clientSet.NetworkingV1().Ingresses(ing.Namespace).Create(context.TODO(), ing, metav1.CreateOptions{})
if err != nil {
t.Fatalf("error creating ingress %+v: %v", ingress, err)
t.Fatalf("error creating ingress %+v: %v", ing, err)
}
t.Logf("Ingress %+v created", ingress)
return ing
t.Logf("Ingress %+v created", ing)
return newIngress
}
t.Fatalf("error updating ingress %+v: %v", ingress, err)
t.Fatalf("error updating ingress %+v: %v", ing, err)
}
return ing
return newIngress
}
func deleteIngress(ingress *networking.Ingress, clientSet kubernetes.Interface, t *testing.T) {
func deleteIngress(ing *networking.Ingress, clientSet kubernetes.Interface, t *testing.T) {
t.Helper()
err := clientSet.NetworkingV1().Ingresses(ingress.Namespace).Delete(context.TODO(), ingress.Name, metav1.DeleteOptions{})
err := clientSet.NetworkingV1().Ingresses(ing.Namespace).Delete(context.TODO(), ing.Name, metav1.DeleteOptions{})
if err != nil {
t.Errorf("failed to delete ingress %+v: %v", ingress, err)
t.Errorf("failed to delete ingress %+v: %v", ing, err)
}
t.Logf("Ingress %+v deleted", ingress)
t.Logf("Ingress %+v deleted", ing)
}
// newStore creates a new mock object store for tests which do not require the
// use of Informers.
func newStore(t *testing.T) *k8sStore {
func newStore() *k8sStore {
return &k8sStore{
listers: &Lister{
// add more listers if needed
@ -1369,7 +1401,7 @@ func newStore(t *testing.T) *k8sStore {
}
func TestUpdateSecretIngressMap(t *testing.T) {
s := newStore(t)
s := newStore()
ingTpl := &networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
@ -1431,7 +1463,9 @@ func TestUpdateSecretIngressMap(t *testing.T) {
ing.ObjectMeta.SetAnnotations(map[string]string{
parser.GetAnnotationWithPrefix("auth-secret"): "anotherns/auth",
})
s.listers.Ingress.Update(ing)
if err := s.listers.Ingress.Update(ing); err != nil {
t.Errorf("error updating the Ingress: %v", err)
}
s.updateSecretIngressMap(ing)
if l := s.secretIngressMap.Len(); l != 0 {
@ -1456,7 +1490,7 @@ func TestUpdateSecretIngressMap(t *testing.T) {
}
func TestListIngresses(t *testing.T) {
s := newStore(t)
s := newStore()
invalidIngressClass := "something"
validIngressClass := ingressclass.DefaultControllerName
@ -1586,7 +1620,7 @@ func TestWriteSSLSessionTicketKey(t *testing.T) {
}
for _, test := range tests {
s := newStore(t)
s := newStore()
cmap := &v1.ConfigMap{
Data: map[string]string{

View file

@ -91,6 +91,8 @@ const (
)
// ReadConfig obtains the configuration defined by the user merged with the defaults.
//
//nolint:gocyclo // Ignore function complexity error
func ReadConfig(src map[string]string) config.Configuration {
conf := map[string]string{}
// we need to copy the configmap data because the content is altered
@ -116,12 +118,12 @@ func ReadConfig(src map[string]string) config.Configuration {
luaSharedDicts := make(map[string]int)
debugConnectionsList := make([]string, 0)
//parse lua shared dict values
// parse lua shared dict values
if val, ok := conf[luaSharedDictsKey]; ok {
delete(conf, luaSharedDictsKey)
lsd := splitAndTrimSpace(val, ",")
for _, v := range lsd {
v = strings.Replace(v, " ", "", -1)
v = strings.ReplaceAll(v, " ", "")
results := strings.SplitN(v, ":", 2)
dictName := results[0]
size := dictStrToKb(results[1])
@ -196,7 +198,7 @@ func ReadConfig(src map[string]string) config.Configuration {
if ing_net.IsIPV6(ns) {
bindAddressIpv6List = append(bindAddressIpv6List, fmt.Sprintf("[%v]", ns))
} else {
bindAddressIpv4List = append(bindAddressIpv4List, fmt.Sprintf("%v", ns))
bindAddressIpv4List = append(bindAddressIpv4List, ns.String())
}
} else {
klog.Warningf("%v is not a valid textual representation of an IP address", i)
@ -250,7 +252,7 @@ func ReadConfig(src map[string]string) config.Configuration {
if val, ok := conf[globalAuthMethod]; ok {
delete(conf, globalAuthMethod)
if len(val) != 0 && !authreq.ValidMethod(val) {
if val != "" && !authreq.ValidMethod(val) {
klog.Warningf("Global auth location denied - %v.", "invalid HTTP method")
} else {
to.GlobalExternalAuth.Method = val
@ -261,7 +263,10 @@ func ReadConfig(src map[string]string) config.Configuration {
if val, ok := conf[globalAuthSignin]; ok {
delete(conf, globalAuthSignin)
signinURL, _ := parser.StringToURL(val)
signinURL, err := parser.StringToURL(val)
if err != nil {
klog.Errorf("string to URL conversion failed: %v", err)
}
if signinURL == nil {
klog.Warningf("Global auth location denied - %v.", "global-auth-signin setting is undefined and will not be set")
} else {
@ -274,7 +279,10 @@ func ReadConfig(src map[string]string) config.Configuration {
delete(conf, globalAuthSigninRedirectParam)
redirectParam := strings.TrimSpace(val)
dummySigninURL, _ := parser.StringToURL(fmt.Sprintf("%s?%s=dummy", to.GlobalExternalAuth.SigninURL, redirectParam))
dummySigninURL, err := parser.StringToURL(fmt.Sprintf("%s?%s=dummy", to.GlobalExternalAuth.SigninURL, redirectParam))
if err != nil {
klog.Errorf("string to URL conversion failed: %v", err)
}
if dummySigninURL == nil {
klog.Warningf("Global auth redirect parameter denied - %v.", "global-auth-signin-redirect-param setting is invalid and will not be set")
} else {
@ -286,7 +294,7 @@ func ReadConfig(src map[string]string) config.Configuration {
if val, ok := conf[globalAuthResponseHeaders]; ok {
delete(conf, globalAuthResponseHeaders)
if len(val) != 0 {
if val != "" {
harr := splitAndTrimSpace(val, ",")
for _, header := range harr {
if !authreq.ValidHeader(header) {
@ -385,8 +393,8 @@ func ReadConfig(src map[string]string) config.Configuration {
if val, ok := conf[debugConnections]; ok {
delete(conf, debugConnections)
for _, i := range splitAndTrimSpace(val, ",") {
validIp := net.ParseIP(i)
if validIp != nil {
validIP := net.ParseIP(i)
if validIP != nil {
debugConnectionsList = append(debugConnectionsList, i)
} else {
_, _, err := net.ParseCIDR(i)
@ -415,14 +423,14 @@ func ReadConfig(src map[string]string) config.Configuration {
to.DisableIpv6DNS = !ing_net.IsIPv6Enabled()
to.LuaSharedDicts = luaSharedDicts
config := &mapstructure.DecoderConfig{
decoderConfig := &mapstructure.DecoderConfig{
Metadata: nil,
WeaklyTypedInput: true,
Result: &to,
TagName: "json",
}
decoder, err := mapstructure.NewDecoder(config)
decoder, err := mapstructure.NewDecoder(decoderConfig)
if err != nil {
klog.Warningf("unexpected error merging defaults: %v", err)
}
@ -456,6 +464,7 @@ func filterErrors(codes []int) []int {
return fa
}
//nolint:unparam // Ignore `sep` always receives `,` error
func splitAndTrimSpace(s, sep string) []string {
f := func(c rune) bool {
return strings.EqualFold(string(c), sep)
@ -474,8 +483,11 @@ func dictStrToKb(sizeStr string) int {
if sizeMatch == nil {
return -1
}
size, _ := strconv.Atoi(sizeMatch[1]) // validated already with regex
if sizeMatch[2] == "" || strings.ToLower(sizeMatch[2]) == "m" {
size, err := strconv.Atoi(sizeMatch[1]) // validated already with regex
if err != nil {
klog.Errorf("unexpected error converting size string %s to int: %v", sizeStr, err)
}
if sizeMatch[2] == "" || strings.EqualFold(sizeMatch[2], "m") {
size *= 1024
}
return size

View file

@ -52,6 +52,12 @@ const (
nonIdempotent = "non_idempotent"
defBufferSize = 65535
writeIndentOnEmptyLines = true // backward-compatibility
httpProtocol = "HTTP"
autoHTTPProtocol = "AUTO_HTTP"
httpsProtocol = "HTTPS"
grpcProtocol = "GRPC"
grpcsProtocol = "GRPCS"
fcgiProtocol = "FCGI"
)
const (
@ -64,13 +70,13 @@ type Writer interface {
// Write renders the template.
// NOTE: Implementors must ensure that the content of the returned slice is not modified by the implementation
// after the return of this function.
Write(conf config.TemplateConfig) ([]byte, error)
Write(conf *config.TemplateConfig) ([]byte, error)
}
// Template ...
// Template ingress template
type Template struct {
tmpl *text_template.Template
//fw watch.FileWatcher
bp *BufferPool
}
@ -97,7 +103,7 @@ func NewTemplate(file string) (*Template, error) {
// 2. Collapses multiple empty lines to single one
// 3. Re-indent
// (ATW: always returns nil)
func cleanConf(in *bytes.Buffer, out *bytes.Buffer) error {
func cleanConf(in, out *bytes.Buffer) error {
depth := 0
lineStarted := false
emptyLineWritten := false
@ -176,7 +182,7 @@ func cleanConf(in *bytes.Buffer, out *bytes.Buffer) error {
// Write populates a buffer using a template with NGINX configuration
// and the servers and upstreams created by Ingress rules
func (t *Template) Write(conf config.TemplateConfig) ([]byte, error) {
func (t *Template) Write(conf *config.TemplateConfig) ([]byte, error) {
tmplBuf := t.bp.Get()
defer t.bp.Put(tmplBuf)
@ -184,14 +190,14 @@ func (t *Template) Write(conf config.TemplateConfig) ([]byte, error) {
defer t.bp.Put(outCmdBuf)
if klog.V(3).Enabled() {
b, err := json.Marshal(conf)
b, err := json.Marshal(*conf)
if err != nil {
klog.Errorf("unexpected error: %v", err)
}
klog.InfoS("NGINX", "configuration", string(b))
}
err := t.tmpl.Execute(tmplBuf, conf)
err := t.tmpl.Execute(tmplBuf, *conf)
if err != nil {
return nil, err
}
@ -211,78 +217,76 @@ func (t *Template) Write(conf config.TemplateConfig) ([]byte, error) {
return res, nil
}
var (
funcMap = text_template.FuncMap{
"empty": func(input interface{}) bool {
check, ok := input.(string)
if ok {
return len(check) == 0
}
return true
},
"escapeLiteralDollar": escapeLiteralDollar,
"buildLuaSharedDictionaries": buildLuaSharedDictionaries,
"luaConfigurationRequestBodySize": luaConfigurationRequestBodySize,
"buildLocation": buildLocation,
"buildAuthLocation": buildAuthLocation,
"shouldApplyGlobalAuth": shouldApplyGlobalAuth,
"buildAuthResponseHeaders": buildAuthResponseHeaders,
"buildAuthUpstreamLuaHeaders": buildAuthUpstreamLuaHeaders,
"buildAuthProxySetHeaders": buildAuthProxySetHeaders,
"buildAuthUpstreamName": buildAuthUpstreamName,
"shouldApplyAuthUpstream": shouldApplyAuthUpstream,
"extractHostPort": extractHostPort,
"changeHostPort": changeHostPort,
"buildProxyPass": buildProxyPass,
"filterRateLimits": filterRateLimits,
"buildRateLimitZones": buildRateLimitZones,
"buildRateLimit": buildRateLimit,
"configForLua": configForLua,
"locationConfigForLua": locationConfigForLua,
"buildResolvers": buildResolvers,
"buildUpstreamName": buildUpstreamName,
"isLocationInLocationList": isLocationInLocationList,
"isLocationAllowed": isLocationAllowed,
"buildDenyVariable": buildDenyVariable,
"getenv": os.Getenv,
"contains": strings.Contains,
"split": strings.Split,
"hasPrefix": strings.HasPrefix,
"hasSuffix": strings.HasSuffix,
"trimSpace": strings.TrimSpace,
"toUpper": strings.ToUpper,
"toLower": strings.ToLower,
"formatIP": formatIP,
"quote": quote,
"buildNextUpstream": buildNextUpstream,
"getIngressInformation": getIngressInformation,
"serverConfig": func(all config.TemplateConfig, server *ingress.Server) interface{} {
return struct{ First, Second interface{} }{all, server}
},
"isValidByteSize": isValidByteSize,
"buildForwardedFor": buildForwardedFor,
"buildAuthSignURL": buildAuthSignURL,
"buildAuthSignURLLocation": buildAuthSignURLLocation,
"buildOpentracing": buildOpentracing,
"buildOpentelemetry": buildOpentelemetry,
"proxySetHeader": proxySetHeader,
"enforceRegexModifier": enforceRegexModifier,
"buildCustomErrorDeps": buildCustomErrorDeps,
"buildCustomErrorLocationsPerServer": buildCustomErrorLocationsPerServer,
"shouldLoadModSecurityModule": shouldLoadModSecurityModule,
"buildHTTPListener": buildHTTPListener,
"buildHTTPSListener": buildHTTPSListener,
"buildOpentracingForLocation": buildOpentracingForLocation,
"buildOpentelemetryForLocation": buildOpentelemetryForLocation,
"shouldLoadOpentracingModule": shouldLoadOpentracingModule,
"shouldLoadOpentelemetryModule": shouldLoadOpentelemetryModule,
"buildModSecurityForLocation": buildModSecurityForLocation,
"buildMirrorLocations": buildMirrorLocations,
"shouldLoadAuthDigestModule": shouldLoadAuthDigestModule,
"buildServerName": buildServerName,
"buildCorsOriginRegex": buildCorsOriginRegex,
}
)
var funcMap = text_template.FuncMap{
"empty": func(input interface{}) bool {
check, ok := input.(string)
if ok {
return check == ""
}
return true
},
"escapeLiteralDollar": escapeLiteralDollar,
"buildLuaSharedDictionaries": buildLuaSharedDictionaries,
"luaConfigurationRequestBodySize": luaConfigurationRequestBodySize,
"buildLocation": buildLocation,
"buildAuthLocation": buildAuthLocation,
"shouldApplyGlobalAuth": shouldApplyGlobalAuth,
"buildAuthResponseHeaders": buildAuthResponseHeaders,
"buildAuthUpstreamLuaHeaders": buildAuthUpstreamLuaHeaders,
"buildAuthProxySetHeaders": buildAuthProxySetHeaders,
"buildAuthUpstreamName": buildAuthUpstreamName,
"shouldApplyAuthUpstream": shouldApplyAuthUpstream,
"extractHostPort": extractHostPort,
"changeHostPort": changeHostPort,
"buildProxyPass": buildProxyPass,
"filterRateLimits": filterRateLimits,
"buildRateLimitZones": buildRateLimitZones,
"buildRateLimit": buildRateLimit,
"configForLua": configForLua,
"locationConfigForLua": locationConfigForLua,
"buildResolvers": buildResolvers,
"buildUpstreamName": buildUpstreamName,
"isLocationInLocationList": isLocationInLocationList,
"isLocationAllowed": isLocationAllowed,
"buildDenyVariable": buildDenyVariable,
"getenv": os.Getenv,
"contains": strings.Contains,
"split": strings.Split,
"hasPrefix": strings.HasPrefix,
"hasSuffix": strings.HasSuffix,
"trimSpace": strings.TrimSpace,
"toUpper": strings.ToUpper,
"toLower": strings.ToLower,
"formatIP": formatIP,
"quote": quote,
"buildNextUpstream": buildNextUpstream,
"getIngressInformation": getIngressInformation,
"serverConfig": func(all config.TemplateConfig, server *ingress.Server) interface{} {
return struct{ First, Second interface{} }{all, server}
},
"isValidByteSize": isValidByteSize,
"buildForwardedFor": buildForwardedFor,
"buildAuthSignURL": buildAuthSignURL,
"buildAuthSignURLLocation": buildAuthSignURLLocation,
"buildOpentracing": buildOpentracing,
"buildOpentelemetry": buildOpentelemetry,
"proxySetHeader": proxySetHeader,
"enforceRegexModifier": enforceRegexModifier,
"buildCustomErrorDeps": buildCustomErrorDeps,
"buildCustomErrorLocationsPerServer": buildCustomErrorLocationsPerServer,
"shouldLoadModSecurityModule": shouldLoadModSecurityModule,
"buildHTTPListener": buildHTTPListener,
"buildHTTPSListener": buildHTTPSListener,
"buildOpentracingForLocation": buildOpentracingForLocation,
"buildOpentelemetryForLocation": buildOpentelemetryForLocation,
"shouldLoadOpentracingModule": shouldLoadOpentracingModule,
"shouldLoadOpentelemetryModule": shouldLoadOpentelemetryModule,
"buildModSecurityForLocation": buildModSecurityForLocation,
"buildMirrorLocations": buildMirrorLocations,
"shouldLoadAuthDigestModule": shouldLoadAuthDigestModule,
"buildServerName": buildServerName,
"buildCorsOriginRegex": buildCorsOriginRegex,
}
// escapeLiteralDollar will replace the $ character with ${literal_dollar}
// which is made to work via the following configuration in the http section of
@ -296,7 +300,7 @@ func escapeLiteralDollar(input interface{}) string {
if !ok {
return ""
}
return strings.Replace(inputStr, `$`, `${literal_dollar}`, -1)
return strings.ReplaceAll(inputStr, `$`, `${literal_dollar}`)
}
// formatIP will wrap IPv6 addresses in [] and return IPv4 addresses
@ -328,9 +332,7 @@ func quote(input interface{}) string {
return fmt.Sprintf("%q", inputStr)
}
func buildLuaSharedDictionaries(c interface{}, s interface{}) string {
var out []string
func buildLuaSharedDictionaries(c, s interface{}) string {
cfg, ok := c.(config.Configuration)
if !ok {
klog.Errorf("expected a 'config.Configuration' type but %T was returned", c)
@ -343,6 +345,7 @@ func buildLuaSharedDictionaries(c interface{}, s interface{}) string {
return ""
}
out := make([]string, 0, len(cfg.LuaSharedDicts))
for name, size := range cfg.LuaSharedDicts {
sizeStr := dictKbToStr(size)
out = append(out, fmt.Sprintf("lua_shared_dict %s %s", name, sizeStr))
@ -364,7 +367,7 @@ func luaConfigurationRequestBodySize(c interface{}) string {
if size < cfg.LuaSharedDicts["certificate_data"] {
size = cfg.LuaSharedDicts["certificate_data"]
}
size = size + 1024
size += 1024
return dictKbToStr(size)
}
@ -418,7 +421,7 @@ func configForLua(input interface{}) string {
}
// locationConfigForLua formats some location specific configuration into Lua table represented as string
func locationConfigForLua(l interface{}, a interface{}) string {
func locationConfigForLua(l, a interface{}) string {
location, ok := l.(*ingress.Location)
if !ok {
klog.Errorf("expected an '*ingress.Location' type but %T was given", l)
@ -459,7 +462,7 @@ func locationConfigForLua(l interface{}, a interface{}) string {
}
// buildResolvers returns the resolvers reading the /etc/resolv.conf file
func buildResolvers(res interface{}, disableIpv6 interface{}) string {
func buildResolvers(res, disableIpv6 interface{}) string {
// NGINX need IPV6 addresses to be surrounded by brackets
nss, ok := res.([]net.IP)
if !ok {
@ -484,7 +487,7 @@ func buildResolvers(res interface{}, disableIpv6 interface{}) string {
}
r = append(r, fmt.Sprintf("[%v]", ns))
} else {
r = append(r, fmt.Sprintf("%v", ns))
r = append(r, ns.String())
}
}
r = append(r, "valid=30s")
@ -554,7 +557,7 @@ func buildAuthLocation(input interface{}, globalExternalAuthURL string) string {
str := base64.URLEncoding.EncodeToString([]byte(location.Path))
// removes "=" after encoding
str = strings.Replace(str, "=", "", -1)
str = strings.ReplaceAll(str, "=", "")
pathType := "default"
if location.PathType != nil {
@ -644,7 +647,7 @@ func buildAuthUpstreamName(input interface{}, host string) string {
// shouldApplyAuthUpstream returns true only in case when ExternalAuth.URL and
// ExternalAuth.KeepaliveConnections are all set
func shouldApplyAuthUpstream(l interface{}, c interface{}) bool {
func shouldApplyAuthUpstream(l, c interface{}) bool {
location, ok := l.(*ingress.Location)
if !ok {
klog.Errorf("expected an '*ingress.Location' type but %T was returned", l)
@ -672,14 +675,14 @@ func shouldApplyAuthUpstream(l interface{}, c interface{}) bool {
}
// extractHostPort will extract the host:port part from the URL specified by url
func extractHostPort(url string) string {
if url == "" {
func extractHostPort(newURL string) string {
if newURL == "" {
return ""
}
authURL, err := parser.StringToURL(url)
authURL, err := parser.StringToURL(newURL)
if err != nil {
klog.Errorf("expected a valid URL but %s was returned", url)
klog.Errorf("expected a valid URL but %s was returned", newURL)
return ""
}
@ -687,14 +690,14 @@ func extractHostPort(url string) string {
}
// changeHostPort will change the host:port part of the url to value
func changeHostPort(url string, value string) string {
if url == "" {
func changeHostPort(newURL, value string) string {
if newURL == "" {
return ""
}
authURL, err := parser.StringToURL(url)
authURL, err := parser.StringToURL(newURL)
if err != nil {
klog.Errorf("expected a valid URL but %s was returned", url)
klog.Errorf("expected a valid URL but %s was returned", newURL)
return ""
}
@ -707,7 +710,7 @@ func changeHostPort(url string, value string) string {
// (specified through the nginx.ingress.kubernetes.io/rewrite-target annotation)
// If the annotation nginx.ingress.kubernetes.io/add-base-url:"true" is specified it will
// add a base tag in the head of the response from the service
func buildProxyPass(host string, b interface{}, loc interface{}) string {
func buildProxyPass(_ string, b, loc interface{}) string {
backends, ok := b.([]*ingress.Backend)
if !ok {
klog.Errorf("expected an '[]*ingress.Backend' type but %T was returned", b)
@ -726,17 +729,17 @@ func buildProxyPass(host string, b interface{}, loc interface{}) string {
proxyPass := "proxy_pass"
switch location.BackendProtocol {
case "AUTO_HTTP":
case autoHTTPProtocol:
proto = "$scheme://"
case "HTTPS":
case httpsProtocol:
proto = "https://"
case "GRPC":
case grpcProtocol:
proto = "grpc://"
proxyPass = "grpc_pass"
case "GRPCS":
case grpcsProtocol:
proto = "grpcs://"
proxyPass = "grpc_pass"
case "FCGI":
case fcgiProtocol:
proto = ""
proxyPass = "fastcgi_pass"
}
@ -748,7 +751,7 @@ func buildProxyPass(host string, b interface{}, loc interface{}) string {
if backend.SSLPassthrough {
proto = "https://"
if location.BackendProtocol == "GRPCS" {
if location.BackendProtocol == grpcsProtocol {
proto = "grpcs://"
}
}
@ -775,7 +778,7 @@ func buildProxyPass(host string, b interface{}, loc interface{}) string {
var xForwardedPrefix string
if len(location.XForwardedPrefix) > 0 {
xForwardedPrefix = fmt.Sprintf("%s X-Forwarded-Prefix \"%s\";\n", proxySetHeader(location), location.XForwardedPrefix)
xForwardedPrefix = fmt.Sprintf("%s X-Forwarded-Prefix %q;\n", proxySetHeader(location), location.XForwardedPrefix)
}
return fmt.Sprintf(`
@ -935,9 +938,7 @@ func isLocationAllowed(input interface{}) bool {
return loc.Denied == nil
}
var (
denyPathSlugMap = map[string]string{}
)
var denyPathSlugMap = map[string]string{}
// buildDenyVariable returns a nginx variable for a location in a
// server to be used in the whitelist check
@ -977,7 +978,11 @@ func buildNextUpstream(i, r interface{}) string {
return ""
}
retryNonIdempotent := r.(bool)
retryNonIdempotent, ok := r.(bool)
if !ok {
klog.Errorf("expected a 'bool' type but %T was returned", i)
return ""
}
parts := strings.Split(nextUpstream, " ")
@ -1002,8 +1007,10 @@ func buildNextUpstream(i, r interface{}) string {
// refer to http://nginx.org/en/docs/syntax.html
// Nginx differentiates between size and offset
// offset directives support gigabytes in addition
var nginxSizeRegex = regexp.MustCompile("^[0-9]+[kKmM]{0,1}$")
var nginxOffsetRegex = regexp.MustCompile("^[0-9]+[kKmMgG]{0,1}$")
var (
nginxSizeRegex = regexp.MustCompile(`^\d+[kKmM]?$`)
nginxOffsetRegex = regexp.MustCompile(`^\d+[kKmMgG]?$`)
)
// isValidByteSize validates size units valid in nginx
// http://nginx.org/en/docs/syntax.html
@ -1153,13 +1160,17 @@ func buildForwardedFor(input interface{}) string {
return ""
}
ffh := strings.Replace(s, "-", "_", -1)
ffh := strings.ReplaceAll(s, "-", "_")
ffh = strings.ToLower(ffh)
return fmt.Sprintf("$http_%v", ffh)
}
func buildAuthSignURL(authSignURL, authRedirectParam string) string {
u, _ := url.Parse(authSignURL)
u, err := url.Parse(authSignURL)
if err != nil {
klog.Errorf("error parsing authSignURL: %v", err)
return ""
}
q := u.Query()
if authRedirectParam == "" {
authRedirectParam = defaultGlobalAuthRedirectParam
@ -1198,7 +1209,7 @@ func randomString() string {
return string(b)
}
func buildOpentracing(c interface{}, s interface{}) string {
func buildOpentracing(c, s interface{}) string {
cfg, ok := c.(config.Configuration)
if !ok {
klog.Errorf("expected a 'config.Configuration' type but %T was returned", c)
@ -1217,6 +1228,7 @@ func buildOpentracing(c interface{}, s interface{}) string {
buf := bytes.NewBufferString("")
//nolint:gocritic // rewriting if-else to switch statement is not more readable
if cfg.DatadogCollectorHost != "" {
buf.WriteString("opentracing_load_tracer /usr/local/lib/libdd_opentracing.so /etc/nginx/opentracing.json;")
} else if cfg.ZipkinCollectorHost != "" {
@ -1228,16 +1240,16 @@ func buildOpentracing(c interface{}, s interface{}) string {
buf.WriteString("\r\n")
if cfg.OpentracingOperationName != "" {
buf.WriteString(fmt.Sprintf("opentracing_operation_name \"%s\";\n", cfg.OpentracingOperationName))
fmt.Fprintf(buf, "opentracing_operation_name \"%s\";\n", cfg.OpentracingOperationName)
}
if cfg.OpentracingLocationOperationName != "" {
buf.WriteString(fmt.Sprintf("opentracing_location_operation_name \"%s\";\n", cfg.OpentracingLocationOperationName))
fmt.Fprintf(buf, "opentracing_location_operation_name \"%s\";\n", cfg.OpentracingLocationOperationName)
}
return buf.String()
}
func buildOpentelemetry(c interface{}, s interface{}) string {
func buildOpentelemetry(c, s interface{}) string {
cfg, ok := c.(config.Configuration)
if !ok {
klog.Errorf("expected a 'config.Configuration' type but %T was returned", c)
@ -1259,7 +1271,7 @@ func buildOpentelemetry(c interface{}, s interface{}) string {
buf.WriteString("\r\n")
if cfg.OpentelemetryOperationName != "" {
buf.WriteString(fmt.Sprintf("opentelemetry_operation_name \"%s\";\n", cfg.OpentelemetryOperationName))
fmt.Fprintf(buf, "opentelemetry_operation_name \"%s\";\n", cfg.OpentelemetryOperationName)
}
return buf.String()
}
@ -1271,7 +1283,7 @@ func proxySetHeader(loc interface{}) string {
return "proxy_set_header"
}
if location.BackendProtocol == "GRPC" || location.BackendProtocol == "GRPCS" {
if location.BackendProtocol == grpcProtocol || location.BackendProtocol == grpcsProtocol {
return "grpc_set_header"
}
@ -1280,7 +1292,7 @@ func proxySetHeader(loc interface{}) string {
// buildCustomErrorDeps is a utility function returning a struct wrapper with
// the data required to build the 'CUSTOM_ERRORS' template
func buildCustomErrorDeps(upstreamName string, errorCodes []int, enableMetrics bool, modsecurityEnabled bool) interface{} {
func buildCustomErrorDeps(upstreamName string, errorCodes []int, enableMetrics, modsecurityEnabled bool) interface{} {
return struct {
UpstreamName string
ErrorCodes []int
@ -1355,7 +1367,7 @@ func opentracingPropagateContext(location *ingress.Location) string {
return ""
}
if location.BackendProtocol == "GRPC" || location.BackendProtocol == "GRPCS" {
if location.BackendProtocol == grpcProtocol || location.BackendProtocol == grpcsProtocol {
return "opentracing_grpc_propagate_context;"
}
@ -1372,7 +1384,7 @@ func opentelemetryPropagateContext(location *ingress.Location) string {
// shouldLoadModSecurityModule determines whether or not the ModSecurity module needs to be loaded.
// First, it checks if `enable-modsecurity` is set in the ConfigMap. If it is not, it iterates over all locations to
// check if ModSecurity is enabled by the annotation `nginx.ingress.kubernetes.io/enable-modsecurity`.
func shouldLoadModSecurityModule(c interface{}, s interface{}) bool {
func shouldLoadModSecurityModule(c, s interface{}) bool {
cfg, ok := c.(config.Configuration)
if !ok {
klog.Errorf("expected a 'config.Configuration' type but %T was returned", c)
@ -1403,7 +1415,7 @@ func shouldLoadModSecurityModule(c interface{}, s interface{}) bool {
return false
}
func buildHTTPListener(t interface{}, s interface{}) string {
func buildHTTPListener(t, s interface{}) string {
var out []string
tc, ok := t.(config.TemplateConfig)
@ -1423,9 +1435,9 @@ func buildHTTPListener(t interface{}, s interface{}) string {
addrV4 = tc.Cfg.BindAddressIpv4
}
co := commonListenOptions(tc, hostname)
co := commonListenOptions(&tc, hostname)
out = append(out, httpListener(addrV4, co, tc)...)
out = append(out, httpListener(addrV4, co, &tc)...)
if !tc.IsIPV6Enabled {
return strings.Join(out, "\n")
@ -1436,12 +1448,12 @@ func buildHTTPListener(t interface{}, s interface{}) string {
addrV6 = tc.Cfg.BindAddressIpv6
}
out = append(out, httpListener(addrV6, co, tc)...)
out = append(out, httpListener(addrV6, co, &tc)...)
return strings.Join(out, "\n")
}
func buildHTTPSListener(t interface{}, s interface{}) string {
func buildHTTPSListener(t, s interface{}) string {
var out []string
tc, ok := t.(config.TemplateConfig)
@ -1456,14 +1468,14 @@ func buildHTTPSListener(t interface{}, s interface{}) string {
return ""
}
co := commonListenOptions(tc, hostname)
co := commonListenOptions(&tc, hostname)
addrV4 := []string{""}
if len(tc.Cfg.BindAddressIpv4) > 0 {
addrV4 = tc.Cfg.BindAddressIpv4
}
out = append(out, httpsListener(addrV4, co, tc)...)
out = append(out, httpsListener(addrV4, co, &tc)...)
if !tc.IsIPV6Enabled {
return strings.Join(out, "\n")
@ -1474,12 +1486,12 @@ func buildHTTPSListener(t interface{}, s interface{}) string {
addrV6 = tc.Cfg.BindAddressIpv6
}
out = append(out, httpsListener(addrV6, co, tc)...)
out = append(out, httpsListener(addrV6, co, &tc)...)
return strings.Join(out, "\n")
}
func commonListenOptions(template config.TemplateConfig, hostname string) string {
func commonListenOptions(template *config.TemplateConfig, hostname string) string {
var out []string
if template.Cfg.UseProxyProtocol {
@ -1503,7 +1515,7 @@ func commonListenOptions(template config.TemplateConfig, hostname string) string
return strings.Join(out, " ")
}
func httpListener(addresses []string, co string, tc config.TemplateConfig) []string {
func httpListener(addresses []string, co string, tc *config.TemplateConfig) []string {
out := make([]string, 0)
for _, address := range addresses {
lo := []string{"listen"}
@ -1514,15 +1526,14 @@ func httpListener(addresses []string, co string, tc config.TemplateConfig) []str
lo = append(lo, fmt.Sprintf("%v:%v", address, tc.ListenPorts.HTTP))
}
lo = append(lo, co)
lo = append(lo, ";")
lo = append(lo, co, ";")
out = append(out, strings.Join(lo, " "))
}
return out
}
func httpsListener(addresses []string, co string, tc config.TemplateConfig) []string {
func httpsListener(addresses []string, co string, tc *config.TemplateConfig) []string {
out := make([]string, 0)
for _, address := range addresses {
lo := []string{"listen"}
@ -1545,8 +1556,7 @@ func httpsListener(addresses []string, co string, tc config.TemplateConfig) []st
}
}
lo = append(lo, co)
lo = append(lo, "ssl")
lo = append(lo, co, "ssl")
if tc.Cfg.UseHTTP2 {
lo = append(lo, "http2")
@ -1559,7 +1569,7 @@ func httpsListener(addresses []string, co string, tc config.TemplateConfig) []st
return out
}
func buildOpentracingForLocation(isOTEnabled bool, isOTTrustSet bool, location *ingress.Location) string {
func buildOpentracingForLocation(isOTEnabled, isOTTrustSet bool, location *ingress.Location) string {
isOTEnabledInLoc := location.Opentracing.Enabled
isOTSetInLoc := location.Opentracing.Set
@ -1578,13 +1588,13 @@ func buildOpentracingForLocation(isOTEnabled bool, isOTTrustSet bool, location *
if (!isOTTrustSet && !location.Opentracing.TrustSet) ||
(location.Opentracing.TrustSet && !location.Opentracing.TrustEnabled) {
opc = opc + "\nopentracing_trust_incoming_span off;"
opc += "\nopentracing_trust_incoming_span off;"
}
return opc
}
func buildOpentelemetryForLocation(isOTEnabled bool, isOTTrustSet bool, location *ingress.Location) string {
func buildOpentelemetryForLocation(isOTEnabled, isOTTrustSet bool, location *ingress.Location) string {
isOTEnabledInLoc := location.Opentelemetry.Enabled
isOTSetInLoc := location.Opentelemetry.Set
@ -1602,14 +1612,14 @@ func buildOpentelemetryForLocation(isOTEnabled bool, isOTTrustSet bool, location
}
if location.Opentelemetry.OperationName != "" {
opc = opc + "\nopentelemetry_operation_name " + location.Opentelemetry.OperationName + ";"
opc += "\nopentelemetry_operation_name " + location.Opentelemetry.OperationName + ";"
}
if (!isOTTrustSet && !location.Opentelemetry.TrustSet) ||
(location.Opentelemetry.TrustSet && !location.Opentelemetry.TrustEnabled) {
opc = opc + "\nopentelemetry_trust_incoming_spans off;"
opc += "\nopentelemetry_trust_incoming_spans off;"
} else {
opc = opc + "\nopentelemetry_trust_incoming_spans on;"
opc += "\nopentelemetry_trust_incoming_spans on;"
}
return opc
}
@ -1617,7 +1627,7 @@ func buildOpentelemetryForLocation(isOTEnabled bool, isOTTrustSet bool, location
// shouldLoadOpentracingModule determines whether or not the Opentracing module needs to be loaded.
// First, it checks if `enable-opentracing` is set in the ConfigMap. If it is not, it iterates over all locations to
// check if Opentracing is enabled by the annotation `nginx.ingress.kubernetes.io/enable-opentracing`.
func shouldLoadOpentracingModule(c interface{}, s interface{}) bool {
func shouldLoadOpentracingModule(c, s interface{}) bool {
cfg, ok := c.(config.Configuration)
if !ok {
klog.Errorf("expected a 'config.Configuration' type but %T was returned", c)
@ -1647,7 +1657,7 @@ func shouldLoadOpentracingModule(c interface{}, s interface{}) bool {
// shouldLoadOpentelemetryModule determines whether or not the Opentelemetry module needs to be loaded.
// It checks if `enable-opentelemetry` is set in the ConfigMap.
func shouldLoadOpentelemetryModule(c interface{}, s interface{}) bool {
func shouldLoadOpentelemetryModule(c, s interface{}) bool {
cfg, ok := c.(config.Configuration)
if !ok {
klog.Errorf("expected a 'config.Configuration' type but %T was returned", c)
@ -1674,6 +1684,7 @@ func shouldLoadOpentelemetryModule(c interface{}, s interface{}) bool {
return false
}
//nolint:gocritic // Ignore passing cfg by pointer error
func buildModSecurityForLocation(cfg config.Configuration, location *ingress.Location) string {
isMSEnabledInLoc := location.ModSecurity.Enable
isMSEnableSetInLoc := location.ModSecurity.EnableSet
@ -1807,7 +1818,7 @@ func convertGoSliceIntoLuaTable(goSliceInterface interface{}, emptyStringAsNil b
switch kind {
case reflect.String:
if emptyStringAsNil && len(goSlice.Interface().(string)) == 0 {
if emptyStringAsNil && goSlice.Interface().(string) == "" {
return "nil", nil
}
return fmt.Sprintf(`"%v"`, goSlice.Interface()), nil
@ -1840,17 +1851,17 @@ func buildCorsOriginRegex(corsOrigins []string) string {
return "set $http_origin *;\nset $cors 'true';"
}
var originsRegex string = "if ($http_origin ~* ("
originsRegex := "if ($http_origin ~* ("
for i, origin := range corsOrigins {
originTrimmed := strings.TrimSpace(origin)
if len(originTrimmed) > 0 {
builtOrigin := buildOriginRegex(originTrimmed)
originsRegex += builtOrigin
if i != len(corsOrigins)-1 {
originsRegex = originsRegex + "|"
originsRegex += "|"
}
}
}
originsRegex = originsRegex + ")$ ) { set $cors 'true'; }"
originsRegex += ")$ ) { set $cors 'true'; }"
return originsRegex
}

View file

@ -48,9 +48,9 @@ import (
func init() {
// the default value of nginx.TemplatePath assumes the template exists in
// the root filesystem and not in the rootfs directory
path, err := filepath.Abs(filepath.Join("../../../../rootfs/", nginx.TemplatePath))
absPath, err := filepath.Abs(filepath.Join("..", "..", "..", "..", "rootfs", nginx.TemplatePath))
if err == nil {
nginx.TemplatePath = path
nginx.TemplatePath = absPath
}
}
@ -63,7 +63,7 @@ var (
Target string
Location string
ProxyPass string
AutoHttpProxyPass string
AutoHTTPProxyPass string
Sticky bool
XForwardedPrefix string
SecureBackend bool
@ -200,6 +200,12 @@ proxy_pass $scheme://upstream_balancer;`,
}
)
const (
defaultBackend = "upstream-name"
defaultHost = "example.com"
fooAuthHost = "foo.com/auth"
)
func getTestDataDir() (string, error) {
pwd, err := os.Getwd()
if err != nil {
@ -326,9 +332,6 @@ func TestBuildLocation(t *testing.T) {
}
func TestBuildProxyPass(t *testing.T) {
defaultBackend := "upstream-name"
defaultHost := "example.com"
for k, tc := range tmplFuncTestcases {
loc := &ingress.Location{
Path: tc.Path,
@ -339,7 +342,7 @@ func TestBuildProxyPass(t *testing.T) {
}
if tc.SecureBackend {
loc.BackendProtocol = "HTTPS"
loc.BackendProtocol = httpsProtocol
}
backend := &ingress.Backend{
@ -367,9 +370,6 @@ func TestBuildProxyPass(t *testing.T) {
}
func TestBuildProxyPassAutoHttp(t *testing.T) {
defaultBackend := "upstream-name"
defaultHost := "example.com"
for k, tc := range tmplFuncTestcases {
loc := &ingress.Location{
Path: tc.Path,
@ -379,9 +379,9 @@ func TestBuildProxyPassAutoHttp(t *testing.T) {
}
if tc.SecureBackend {
loc.BackendProtocol = "HTTPS"
loc.BackendProtocol = httpsProtocol
} else {
loc.BackendProtocol = "AUTO_HTTP"
loc.BackendProtocol = autoHTTPProtocol
}
backend := &ingress.Backend{
@ -402,7 +402,7 @@ func TestBuildProxyPassAutoHttp(t *testing.T) {
backends := []*ingress.Backend{backend}
pp := buildProxyPass(defaultHost, backends, loc)
if !strings.EqualFold(tc.AutoHttpProxyPass, pp) {
if !strings.EqualFold(tc.AutoHTTPProxyPass, pp) {
t.Errorf("%s: expected \n'%v'\nbut returned \n'%v'", k, tc.ProxyPass, pp)
}
}
@ -417,7 +417,7 @@ func TestBuildAuthLocation(t *testing.T) {
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
}
authURL := "foo.com/auth"
authURL := fooAuthHost
globalAuthURL := "foo.com/global-auth"
loc := &ingress.Location{
@ -428,7 +428,7 @@ func TestBuildAuthLocation(t *testing.T) {
EnableGlobalAuth: true,
}
encodedAuthURL := strings.Replace(base64.URLEncoding.EncodeToString([]byte(loc.Path)), "=", "", -1)
encodedAuthURL := strings.ReplaceAll(base64.URLEncoding.EncodeToString([]byte(loc.Path)), "=", "")
externalAuthPath := fmt.Sprintf("/_external-auth-%v-default", encodedAuthURL)
testCases := []struct {
@ -460,8 +460,7 @@ func TestBuildAuthLocation(t *testing.T) {
}
func TestShouldApplyGlobalAuth(t *testing.T) {
authURL := "foo.com/auth"
authURL := fooAuthHost
globalAuthURL := "foo.com/global-auth"
loc := &ingress.Location{
@ -579,12 +578,12 @@ func TestBuildAuthUpstreamName(t *testing.T) {
loc := &ingress.Location{
ExternalAuth: authreq.Config{
URL: "foo.com/auth",
URL: fooAuthHost,
},
Path: "/cat",
}
encodedAuthURL := strings.Replace(base64.URLEncoding.EncodeToString([]byte(loc.Path)), "=", "", -1)
encodedAuthURL := strings.ReplaceAll(base64.URLEncoding.EncodeToString([]byte(loc.Path)), "=", "")
externalAuthPath := fmt.Sprintf("external-auth-%v-default", encodedAuthURL)
testCases := []struct {
@ -606,7 +605,7 @@ func TestBuildAuthUpstreamName(t *testing.T) {
}
func TestShouldApplyAuthUpstream(t *testing.T) {
authURL := "foo.com/auth"
authURL := fooAuthHost
loc := &ingress.Location{
ExternalAuth: authreq.Config{
@ -702,7 +701,10 @@ func TestChangeHostPort(t *testing.T) {
}
func TestTemplateWithData(t *testing.T) {
pwd, _ := os.Getwd()
pwd, err := os.Getwd()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
f, err := os.Open(path.Join(pwd, "../../../../test/data/config.json"))
if err != nil {
t.Errorf("unexpected error reading json file: %v", err)
@ -727,7 +729,7 @@ func TestTemplateWithData(t *testing.T) {
dat.Cfg.DefaultSSLCertificate = &ingress.SSLCert{}
rt, err := ngxTpl.Write(dat)
rt, err := ngxTpl.Write(&dat)
if err != nil {
t.Errorf("invalid NGINX template: %v", err)
}
@ -746,7 +748,10 @@ func TestTemplateWithData(t *testing.T) {
}
func BenchmarkTemplateWithData(b *testing.B) {
pwd, _ := os.Getwd()
pwd, err := os.Getwd()
if err != nil {
b.Errorf("unexpected error: %v", err)
}
f, err := os.Open(path.Join(pwd, "../../../../test/data/config.json"))
if err != nil {
b.Errorf("unexpected error reading json file: %v", err)
@ -767,7 +772,7 @@ func BenchmarkTemplateWithData(b *testing.B) {
}
for i := 0; i < b.N; i++ {
if _, err := ngxTpl.Write(dat); err != nil {
if _, err := ngxTpl.Write(&dat); err != nil {
b.Errorf("unexpected error writing template: %v", err)
}
}
@ -1066,9 +1071,6 @@ func TestBuildUpstreamName(t *testing.T) {
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
}
defaultBackend := "upstream-name"
defaultHost := "example.com"
for k, tc := range tmplFuncTestcases {
loc := &ingress.Location{
Path: tc.Path,
@ -1079,7 +1081,7 @@ func TestBuildUpstreamName(t *testing.T) {
}
if tc.SecureBackend {
loc.BackendProtocol = "HTTPS"
loc.BackendProtocol = httpsProtocol
}
backend := &ingress.Backend{
@ -1134,13 +1136,13 @@ func TestEscapeLiteralDollar(t *testing.T) {
func TestOpentracingPropagateContext(t *testing.T) {
tests := map[*ingress.Location]string{
{BackendProtocol: "HTTP"}: "opentracing_propagate_context;",
{BackendProtocol: "HTTPS"}: "opentracing_propagate_context;",
{BackendProtocol: "AUTO_HTTP"}: "opentracing_propagate_context;",
{BackendProtocol: "GRPC"}: "opentracing_grpc_propagate_context;",
{BackendProtocol: "GRPCS"}: "opentracing_grpc_propagate_context;",
{BackendProtocol: "FCGI"}: "opentracing_propagate_context;",
nil: "",
{BackendProtocol: httpProtocol}: "opentracing_propagate_context;",
{BackendProtocol: httpsProtocol}: "opentracing_propagate_context;",
{BackendProtocol: autoHTTPProtocol}: "opentracing_propagate_context;",
{BackendProtocol: grpcProtocol}: "opentracing_grpc_propagate_context;",
{BackendProtocol: grpcsProtocol}: "opentracing_grpc_propagate_context;",
{BackendProtocol: fcgiProtocol}: "opentracing_propagate_context;",
nil: "",
}
for loc, expectedDirective := range tests {
@ -1153,13 +1155,13 @@ func TestOpentracingPropagateContext(t *testing.T) {
func TestOpentelemetryPropagateContext(t *testing.T) {
tests := map[*ingress.Location]string{
{BackendProtocol: "HTTP"}: "opentelemetry_propagate;",
{BackendProtocol: "HTTPS"}: "opentelemetry_propagate;",
{BackendProtocol: "AUTO_HTTP"}: "opentelemetry_propagate;",
{BackendProtocol: "GRPC"}: "opentelemetry_propagate;",
{BackendProtocol: "GRPCS"}: "opentelemetry_propagate;",
{BackendProtocol: "FCGI"}: "opentelemetry_propagate;",
nil: "",
{BackendProtocol: httpProtocol}: "opentelemetry_propagate;",
{BackendProtocol: httpsProtocol}: "opentelemetry_propagate;",
{BackendProtocol: autoHTTPProtocol}: "opentelemetry_propagate;",
{BackendProtocol: grpcProtocol}: "opentelemetry_propagate;",
{BackendProtocol: grpcsProtocol}: "opentelemetry_propagate;",
{BackendProtocol: fcgiProtocol}: "opentelemetry_propagate;",
nil: "",
}
for loc, expectedDirective := range tests {
@ -1171,7 +1173,6 @@ func TestOpentelemetryPropagateContext(t *testing.T) {
}
func TestGetIngressInformation(t *testing.T) {
testcases := map[string]struct {
Ingress interface{}
Host string
@ -1625,7 +1626,7 @@ func TestProxySetHeader(t *testing.T) {
{
name: "gRPC backend",
loc: &ingress.Location{
BackendProtocol: "GRPC",
BackendProtocol: grpcProtocol,
},
expected: "grpc_set_header",
},
@ -1716,7 +1717,6 @@ func TestBuildOpenTracing(t *testing.T) {
if expected != actual {
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
}
}
func TestBuildOpenTelemetry(t *testing.T) {
@ -1777,6 +1777,7 @@ func TestEnforceRegexModifier(t *testing.T) {
}
}
//nolint:dupl // Ignore dupl errors for similar test case
func TestShouldLoadModSecurityModule(t *testing.T) {
// ### Invalid argument type tests ###
// The first tests should return false.
@ -1877,6 +1878,7 @@ opentracing_trust_incoming_span off;`
}
}
//nolint:dupl // Ignore dupl errors for similar test case
func TestShouldLoadOpentracingModule(t *testing.T) {
// ### Invalid argument type tests ###
// The first tests should return false.
@ -1978,6 +1980,7 @@ opentelemetry_trust_incoming_spans off;`
}
}
//nolint:dupl // Ignore dupl errors for similar test case
func TestShouldLoadOpentelemetryModule(t *testing.T) {
// ### Invalid argument type tests ###
// The first tests should return false.
@ -2104,7 +2107,6 @@ func TestModSecurityForLocation(t *testing.T) {
}
func TestBuildServerName(t *testing.T) {
testCases := []struct {
title string
hostname string

View file

@ -135,11 +135,13 @@ func (nc NginxCommand) ExecCommand(args ...string) *exec.Cmd {
cmdArgs = append(cmdArgs, "-c", cfgPath)
cmdArgs = append(cmdArgs, args...)
//nolint:gosec // Ignore G204 error
return exec.Command(nc.Binary, cmdArgs...)
}
// Test checks if config file is a syntax valid nginx configuration
func (nc NginxCommand) Test(cfg string) ([]byte, error) {
//nolint:gosec // Ignore G204 error
return exec.Command(nc.Binary, "-c", cfg, "-t").CombinedOutput()
}

View file

@ -100,7 +100,7 @@ type Backend struct {
// Name server/s used to resolve names of upstream servers into IP addresses.
// The file /etc/resolv.conf is used as DNS resolution configuration.
Resolver []net.IP
Resolver []net.IP `json:"Resolver"`
// SkipAccessLogURLs sets a list of URLs that should not appear in the NGINX access log
// This is useful with urls like `/health` or `health-check` that make "complex" reading the logs

View file

@ -33,57 +33,57 @@ var (
// NewInvalidAnnotationConfiguration returns a new InvalidConfiguration error for use when
// annotations are not correctly configured
func NewInvalidAnnotationConfiguration(name string, reason string) error {
return InvalidConfiguration{
func NewInvalidAnnotationConfiguration(name, reason string) error {
return InvalidConfigurationError{
Name: fmt.Sprintf("the annotation %v does not contain a valid configuration: %v", name, reason),
}
}
// NewInvalidAnnotationContent returns a new InvalidContent error
func NewInvalidAnnotationContent(name string, val interface{}) error {
return InvalidContent{
return InvalidContentError{
Name: fmt.Sprintf("the annotation %v does not contain a valid value (%v)", name, val),
}
}
// NewLocationDenied returns a new LocationDenied error
func NewLocationDenied(reason string) error {
return LocationDenied{
Reason: fmt.Errorf("Location denied, reason: %v", reason),
return LocationDeniedError{
Reason: fmt.Errorf("location denied, reason: %v", reason),
}
}
// InvalidConfiguration Error
type InvalidConfiguration struct {
// InvalidConfigurationError
type InvalidConfigurationError struct {
Name string
}
func (e InvalidConfiguration) Error() string {
func (e InvalidConfigurationError) Error() string {
return e.Name
}
// InvalidContent error
type InvalidContent struct {
// InvalidContentError
type InvalidContentError struct {
Name string
}
func (e InvalidContent) Error() string {
func (e InvalidContentError) Error() string {
return e.Name
}
// LocationDenied error
type LocationDenied struct {
// LocationDeniedError
type LocationDeniedError struct {
Reason error
}
func (e LocationDenied) Error() string {
func (e LocationDeniedError) Error() string {
return e.Reason.Error()
}
// IsLocationDenied checks if the err is an error which
// indicates a location should return HTTP code 503
func IsLocationDenied(e error) bool {
_, ok := e.(LocationDenied)
_, ok := e.(LocationDeniedError)
return ok
}
@ -96,7 +96,7 @@ func IsMissingAnnotations(e error) bool {
// IsInvalidContent checks if the err is an error which
// indicates an annotations value is not valid
func IsInvalidContent(e error) bool {
_, ok := e.(InvalidContent)
_, ok := e.(InvalidContentError)
return ok
}

View file

@ -24,7 +24,6 @@ import (
)
func makeSimpleIngress(hostname string, paths ...string) *networking.Ingress {
newIngress := networking.Ingress{
ObjectMeta: v1.ObjectMeta{
Name: "test1",

View file

@ -40,9 +40,7 @@ func DeepInspect(obj interface{}) error {
}
}
var (
implSpecific = networking.PathTypeImplementationSpecific
)
var implSpecific = networking.PathTypeImplementationSpecific
func ValidatePathType(ing *networking.Ingress) error {
if ing == nil {

View file

@ -34,7 +34,7 @@ var (
// the group [[:alnum:]\_\-\/]* says that any amount of characters (A-Za-z0-9), _, - and /
// are accepted until the end of the line
// Nothing else is accepted.
validPathType = regexp.MustCompile(`(?i)^/[[:alnum:]\_\-\/]*$`)
validPathType = regexp.MustCompile(`(?i)^/[[:alnum:]\_\-/]*$`)
invalidRegex = []regexp.Regexp{}
)
@ -52,8 +52,8 @@ func init() {
// CheckRegex receives a value/configuration and validates if it matches with one of the
// forbidden regexes.
func CheckRegex(value string) error {
for _, regex := range invalidRegex {
if regex.MatchString(value) {
for i := range invalidRegex {
if invalidRegex[i].MatchString(value) {
return fmt.Errorf("invalid value found: %s", value)
}
}

View file

@ -19,7 +19,6 @@ package inspector
import "testing"
func TestCheckRegex(t *testing.T) {
tests := []struct {
name string
value string

View file

@ -21,6 +21,6 @@ import (
)
// InspectService will be used to inspect service objects for possible invalid configurations
func InspectService(svc *corev1.Service) error {
func InspectService(_ *corev1.Service) error {
return nil
}

View file

@ -104,7 +104,7 @@ func NewAdmissionCollector(pod, namespace, class string) *AdmissionCollector {
}
// Describe implements prometheus.Collector
func (am AdmissionCollector) Describe(ch chan<- *prometheus.Desc) {
func (am *AdmissionCollector) Describe(ch chan<- *prometheus.Desc) {
am.testedIngressLength.Describe(ch)
am.testedIngressTime.Describe(ch)
am.renderingIngressLength.Describe(ch)
@ -114,7 +114,7 @@ func (am AdmissionCollector) Describe(ch chan<- *prometheus.Desc) {
}
// Collect implements the prometheus.Collector interface.
func (am AdmissionCollector) Collect(ch chan<- prometheus.Metric) {
func (am *AdmissionCollector) Collect(ch chan<- prometheus.Metric) {
am.testedIngressLength.Collect(ch)
am.testedIngressTime.Collect(ch)
am.renderingIngressLength.Collect(ch)
@ -139,7 +139,7 @@ func ByteFormat(bytes int64) string {
}
// SetAdmissionMetrics sets the values for AdmissionMetrics that can be called externally
func (am *AdmissionCollector) SetAdmissionMetrics(testedIngressLength float64, testedIngressTime float64, renderingIngressLength float64, renderingIngressTime float64, testedConfigurationSize float64, admissionTime float64) {
func (am *AdmissionCollector) SetAdmissionMetrics(testedIngressLength, testedIngressTime, renderingIngressLength, renderingIngressTime, testedConfigurationSize, admissionTime float64) {
am.testedIngressLength.Set(testedIngressLength)
am.testedIngressTime.Set(testedIngressTime)
am.renderingIngressLength.Set(renderingIngressLength)

Some files were not shown because too many files have changed in this diff Show more