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

@ -69,6 +69,9 @@ type Storer interface {
// GetBackendConfiguration returns the nginx configuration stored in a configmap
GetBackendConfiguration() ngx_config.Configuration
// GetSecurityConfiguration returns the configuration options from Ingress
GetSecurityConfiguration() defaults.SecurityConfiguration
// GetConfigMap returns the ConfigMap matching key.
GetConfigMap(key string) (*corev1.ConfigMap, error)
@ -882,9 +885,14 @@ func (s *k8sStore) syncIngress(ing *networkingv1.Ingress) {
k8s.SetDefaultNGINXPathType(copyIng)
err := s.listers.IngressWithAnnotation.Update(&ingress.Ingress{
parsed, err := s.annotations.Extract(ing)
if err != nil {
klog.Error(err)
return
}
err = s.listers.IngressWithAnnotation.Update(&ingress.Ingress{
Ingress: *copyIng,
ParsedAnnotations: s.annotations.Extract(ing),
ParsedAnnotations: parsed,
})
if err != nil {
klog.Error(err)
@ -920,8 +928,10 @@ func (s *k8sStore) updateSecretIngressMap(ing *networkingv1.Ingress) {
"proxy-ssl-secret",
"secure-verify-ca-secret",
}
secConfig := s.GetSecurityConfiguration().AllowCrossNamespaceResources
for _, ann := range secretAnnotations {
secrKey, err := objectRefAnnotationNsKey(ann, ing)
secrKey, err := objectRefAnnotationNsKey(ann, ing, secConfig)
if err != nil && !errors.IsMissingAnnotations(err) {
klog.Errorf("error reading secret reference in annotation %q: %s", ann, err)
continue
@ -937,8 +947,9 @@ func (s *k8sStore) updateSecretIngressMap(ing *networkingv1.Ingress) {
// objectRefAnnotationNsKey returns an object reference formatted as a
// 'namespace/name' key from the given annotation name.
func objectRefAnnotationNsKey(ann string, ing *networkingv1.Ingress) (string, error) {
annValue, err := parser.GetStringAnnotation(ann, ing)
func objectRefAnnotationNsKey(ann string, ing *networkingv1.Ingress, allowCrossNamespace bool) (string, error) {
// We pass nil fields, as this is an internal process and we don't need to validate it.
annValue, err := parser.GetStringAnnotation(ann, ing, nil)
if err != nil {
return "", err
}
@ -951,6 +962,9 @@ func objectRefAnnotationNsKey(ann string, ing *networkingv1.Ingress) (string, er
if secrNs == "" {
return fmt.Sprintf("%v/%v", ing.Namespace, secrName), nil
}
if !allowCrossNamespace && secrNs != ing.Namespace {
return "", fmt.Errorf("cross namespace secret is not supported")
}
return annValue, nil
}
@ -1125,6 +1139,17 @@ func (s *k8sStore) GetBackendConfiguration() ngx_config.Configuration {
return s.backendConfig
}
func (s *k8sStore) GetSecurityConfiguration() defaults.SecurityConfiguration {
s.backendConfigMu.RLock()
defer s.backendConfigMu.RUnlock()
secConfig := defaults.SecurityConfiguration{
AllowCrossNamespaceResources: s.backendConfig.AllowCrossNamespaceResources,
AnnotationsRiskLevel: s.backendConfig.AnnotationsRiskLevel,
}
return secConfig
}
func (s *k8sStore) setConfig(cmap *corev1.ConfigMap) {
s.backendConfigMu.Lock()
defer s.backendConfigMu.Unlock()

View file

@ -1414,18 +1414,31 @@ func TestUpdateSecretIngressMap(t *testing.T) {
t.Run("with annotation in namespace/name format", func(t *testing.T) {
ing := ingTpl.DeepCopy()
ing.ObjectMeta.SetAnnotations(map[string]string{
parser.GetAnnotationWithPrefix("auth-secret"): "otherns/auth",
parser.GetAnnotationWithPrefix("auth-secret"): "testns/auth",
})
if err := s.listers.Ingress.Update(ing); err != nil {
t.Errorf("error updating the Ingress: %v", err)
}
s.updateSecretIngressMap(ing)
if l := s.secretIngressMap.Len(); !(l == 1 && s.secretIngressMap.Has("otherns/auth")) {
if l := s.secretIngressMap.Len(); !(l == 1 && s.secretIngressMap.Has("testns/auth")) {
t.Errorf("Expected \"otherns/auth\" to be the only referenced Secret (got %d)", l)
}
})
t.Run("with annotation in namespace/name format should not be supported", func(t *testing.T) {
ing := ingTpl.DeepCopy()
ing.ObjectMeta.SetAnnotations(map[string]string{
parser.GetAnnotationWithPrefix("auth-secret"): "anotherns/auth",
})
s.listers.Ingress.Update(ing)
s.updateSecretIngressMap(ing)
if l := s.secretIngressMap.Len(); l != 0 {
t.Errorf("Expected \"otherns/auth\" to be denied as it contains a different namespace (got %d)", l)
}
})
t.Run("with annotation in invalid format", func(t *testing.T) {
ing := ingTpl.DeepCopy()
ing.ObjectMeta.SetAnnotations(map[string]string{