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

@ -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)