Implement annotation validation (#9673)

* Add validation to all annotations

* Add annotation validation for fcgi

* Fix reviews and fcgi e2e

* Add flag to disable cross namespace validation

* Add risk, flag for validation, tests

* Add missing formating

* Enable validation by default on tests

* Test validation flag

* remove ajp from list

* Finalize validation changes

* Add validations to CI

* Update helm docs

* Fix code review

* Use a better name for annotation risk
This commit is contained in:
Ricardo Katz 2023-07-22 00:32:07 -03:00 committed by GitHub
parent 86c00a2310
commit c5f348ea2e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
109 changed files with 4320 additions and 586 deletions

View file

@ -18,13 +18,50 @@ package mirror
import (
"fmt"
"regexp"
"strings"
networking "k8s.io/api/networking/v1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver"
"k8s.io/klog/v2"
)
const (
mirrorRequestBodyAnnotation = "mirror-request-body"
mirrorTargetAnnotation = "mirror-target"
mirrorHostAnnotation = "mirror-host"
)
var (
OnOffRegex = regexp.MustCompile(`^(on|off)$`)
)
var mirrorAnnotation = parser.Annotation{
Group: "mirror",
Annotations: parser.AnnotationFields{
mirrorRequestBodyAnnotation: {
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'`,
},
mirrorTargetAnnotation: {
Validator: parser.ValidateServerName,
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskHigh,
Documentation: `This annotation enables a request to be mirrored to a mirror backend.`,
},
mirrorHostAnnotation: {
Validator: parser.ValidateServerName,
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskHigh,
Documentation: `This annotation defines if a specific Host header should be set for mirrored request.`,
},
},
}
// Config returns the mirror to use in a given location
type Config struct {
Source string `json:"source"`
@ -63,12 +100,16 @@ func (m1 *Config) Equal(m2 *Config) bool {
}
type mirror struct {
r resolver.Resolver
r resolver.Resolver
annotationConfig parser.Annotation
}
// NewParser creates a new mirror configuration annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return mirror{r}
return mirror{
r: r,
annotationConfig: mirrorAnnotation,
}
}
// ParseAnnotations parses the annotations contained in the ingress
@ -79,19 +120,29 @@ func (a mirror) Parse(ing *networking.Ingress) (interface{}, error) {
}
var err error
config.RequestBody, err = parser.GetStringAnnotation("mirror-request-body", ing)
config.RequestBody, err = parser.GetStringAnnotation(mirrorRequestBodyAnnotation, ing, a.annotationConfig.Annotations)
if err != nil || config.RequestBody != "off" {
if errors.IsValidationError(err) {
klog.Warningf("annotation %s contains invalid value", mirrorRequestBodyAnnotation)
}
config.RequestBody = "on"
}
config.Target, err = parser.GetStringAnnotation("mirror-target", ing)
config.Target, err = parser.GetStringAnnotation(mirrorTargetAnnotation, ing, a.annotationConfig.Annotations)
if err != nil {
config.Target = ""
config.Source = ""
if errors.IsValidationError(err) {
klog.Warningf("annotation %s contains invalid value, defaulting", mirrorTargetAnnotation)
} else {
config.Target = ""
config.Source = ""
}
}
config.Host, err = parser.GetStringAnnotation("mirror-host", ing)
config.Host, err = parser.GetStringAnnotation(mirrorHostAnnotation, ing, a.annotationConfig.Annotations)
if err != nil {
if errors.IsValidationError(err) {
klog.Warningf("annotation %s contains invalid value, defaulting", mirrorHostAnnotation)
}
if config.Target != "" {
target := strings.Split(config.Target, "$")
@ -106,3 +157,12 @@ func (a mirror) Parse(ing *networking.Ingress) (interface{}, error) {
return config, nil
}
func (a mirror) GetDocumentation() parser.AnnotationFields {
return a.annotationConfig.Annotations
}
func (a mirror) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(a.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, mirrorAnnotation.Annotations)
}

View file

@ -94,13 +94,13 @@ func TestParse(t *testing.T) {
Source: ngxURI,
RequestBody: "on",
Target: "http://some.test.env.com",
Host: "someInvalidParm.%^&*()_=!@#'\"",
Host: "some.test.env.com",
}},
{map[string]string{backendURL: "http://some.test.env.com", host: "_sbrubles-i\"@xpto:12345"}, &Config{
Source: ngxURI,
RequestBody: "on",
Target: "http://some.test.env.com",
Host: "_sbrubles-i\"@xpto:12345",
Host: "some.test.env.com",
}},
}
@ -115,9 +115,12 @@ func TestParse(t *testing.T) {
for _, testCase := range testCases {
ing.SetAnnotations(testCase.annotations)
result, _ := ap.Parse(ing)
result, err := ap.Parse(ing)
if err != nil {
t.Errorf(err.Error())
}
if !reflect.DeepEqual(result, testCase.expected) {
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)
t.Errorf("expected %+v but returned %+v, annotations: %s", testCase.expected, result, testCase.annotations)
}
}
}