mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-08-25 16:07:24 +00:00
feat(rules): improve metadata enforcement and add ingress rules (#2050)
* feat: implement namespace metadata enforcement Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add ingress enforcment Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add ingress enforcment Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add ingress enforcment Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add ingress enforcment Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add ingress enforcment Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add ingress enforcment Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add ingress enforcment Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add ingress enforcment Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add ingress enforcment Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add ingress enforcment Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat: add ingress enforcment Signed-off-by: Oliver Baehler <oliver@sudo-i.net> --------- Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
This commit is contained in:
@@ -182,6 +182,17 @@ func (s VersionKinds) HasWildcard() bool {
|
||||
// Wildcard API groups or wildcard kinds are intentionally skipped because they are selectors,
|
||||
// not concrete Kubernetes resources.
|
||||
func (s VersionKinds) ValidateKnownKinds(mapper apimeta.RESTMapper, fieldPath string) error {
|
||||
return s.ValidateKnownKindsWithScope(mapper, fieldPath, nil)
|
||||
}
|
||||
|
||||
// ValidateKnownKindsWithScope validates concrete targets and optionally their
|
||||
// REST scope. Wildcard selectors are skipped because discovery cannot enumerate
|
||||
// their complete set reliably.
|
||||
func (s VersionKinds) ValidateKnownKindsWithScope(
|
||||
mapper apimeta.RESTMapper,
|
||||
fieldPath string,
|
||||
allowScope func(schema.GroupVersionKind, apimeta.RESTScope) bool,
|
||||
) error {
|
||||
if mapper == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -199,7 +210,8 @@ func (s VersionKinds) ValidateKnownKinds(mapper apimeta.RESTMapper, fieldPath st
|
||||
continue
|
||||
}
|
||||
|
||||
if err := validateKnownKindForAPIGroup(mapper, apiGroup, kind); err != nil {
|
||||
mapping, err := restMappingForAPIGroup(mapper, apiGroup, kind)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"%s.kinds[%d] %q for apiGroups[%d] %q is invalid: %w",
|
||||
fieldPath,
|
||||
@@ -210,12 +222,40 @@ func (s VersionKinds) ValidateKnownKinds(mapper apimeta.RESTMapper, fieldPath st
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
if allowScope != nil && !allowScope(mapping.GroupVersionKind, mapping.Scope) {
|
||||
return fmt.Errorf(
|
||||
"%s.kinds[%d] %q for apiGroups[%d] %q is invalid: GVK %s has unsupported scope %q",
|
||||
fieldPath, kindIndex, kind, apiGroupIndex, apiGroup,
|
||||
mapping.GroupVersionKind.String(), mapping.Scope.Name(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func restMappingForAPIGroup(
|
||||
mapper apimeta.RESTMapper,
|
||||
apiGroup string,
|
||||
kind string,
|
||||
) (*apimeta.RESTMapping, error) {
|
||||
apiGroup = strings.TrimSpace(apiGroup)
|
||||
|
||||
apiGroup = normalizeAPIVersion(apiGroup)
|
||||
|
||||
if apiGroup == CoreAPIVersion {
|
||||
return mapper.RESTMapping(schema.GroupKind{Kind: kind}, CoreAPIVersion)
|
||||
}
|
||||
|
||||
if gv, err := schema.ParseGroupVersion(apiGroup); err == nil && strings.Contains(apiGroup, "/") {
|
||||
return mapper.RESTMapping(schema.GroupKind{Group: gv.Group, Kind: kind}, gv.Version)
|
||||
}
|
||||
|
||||
return mapper.RESTMapping(schema.GroupKind{Group: apiGroup, Kind: kind})
|
||||
}
|
||||
|
||||
func (s VersionKinds) StatusAPIGroups() []string {
|
||||
apiGroups := s.NormalizedAPIGroups()
|
||||
if len(apiGroups) == 0 {
|
||||
@@ -247,52 +287,6 @@ func (s VersionKinds) StatusAPIGroups() []string {
|
||||
return out
|
||||
}
|
||||
|
||||
func validateKnownKindForAPIGroup(
|
||||
mapper apimeta.RESTMapper,
|
||||
apiGroup string,
|
||||
kind string,
|
||||
) error {
|
||||
apiGroup = normalizeAPIVersion(apiGroup)
|
||||
|
||||
if apiGroup == CoreAPIVersion {
|
||||
_, err := mapper.RESTMapping(
|
||||
schema.GroupKind{
|
||||
Group: "",
|
||||
Kind: kind,
|
||||
},
|
||||
CoreAPIVersion,
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.Contains(apiGroup, "/") {
|
||||
gv, err := schema.ParseGroupVersion(apiGroup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = mapper.RESTMapping(
|
||||
schema.GroupKind{
|
||||
Group: gv.Group,
|
||||
Kind: kind,
|
||||
},
|
||||
gv.Version,
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := mapper.RESTMapping(
|
||||
schema.GroupKind{
|
||||
Group: apiGroup,
|
||||
Kind: kind,
|
||||
},
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s VersionKinds) NormalizedAPIGroups() []string {
|
||||
if len(s.APIGroups) == 0 {
|
||||
return []string{CoreAPIVersion}
|
||||
|
||||
@@ -1818,109 +1818,6 @@ func TestVersionKindsValidateKnownKinds(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateKnownKindForAPIGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mapper := newVersionKindTestRESTMapper()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
apiGroup string
|
||||
kind string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "core v1 kind",
|
||||
apiGroup: "",
|
||||
kind: "ConfigMap",
|
||||
},
|
||||
{
|
||||
name: "explicit core v1 kind",
|
||||
apiGroup: "v1",
|
||||
kind: "Service",
|
||||
},
|
||||
{
|
||||
name: "group only kind",
|
||||
apiGroup: "apps",
|
||||
kind: "Deployment",
|
||||
},
|
||||
{
|
||||
name: "exact group version kind",
|
||||
apiGroup: "apps/v1",
|
||||
kind: "Deployment",
|
||||
},
|
||||
{
|
||||
name: "batch exact group version kind",
|
||||
apiGroup: "batch/v1",
|
||||
kind: "Job",
|
||||
},
|
||||
{
|
||||
name: "unknown core kind",
|
||||
apiGroup: "",
|
||||
kind: "NotAThing",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "wrong group kind",
|
||||
apiGroup: "batch/v1",
|
||||
kind: "Deployment",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "wrong exact version",
|
||||
apiGroup: "apps/v1beta1",
|
||||
kind: "StatefulSet",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "unknown group",
|
||||
apiGroup: "example.corp",
|
||||
kind: "Widget",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid group version",
|
||||
apiGroup: "apps/v1/extra",
|
||||
kind: "Deployment",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty kind fails",
|
||||
apiGroup: "v1",
|
||||
kind: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "case-sensitive kind fails",
|
||||
apiGroup: "v1",
|
||||
kind: "configmap",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "case-sensitive group fails",
|
||||
apiGroup: "Apps",
|
||||
kind: "Deployment",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := validateKnownKindForAPIGroup(mapper, tt.apiGroup, tt.kind)
|
||||
|
||||
if tt.wantErr && err == nil {
|
||||
t.Fatalf("expected error")
|
||||
}
|
||||
|
||||
if !tt.wantErr && err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionKindsNormalizedAPIGroups(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user