mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-28 10:28:36 +00:00
feat: support exclude block in generating VAPs (#10215)
Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
This commit is contained in:
parent
35494bd8bb
commit
b0cef72df1
48 changed files with 942 additions and 81 deletions
pkg/validatingadmissionpolicy
test/conformance/chainsaw/generate-validating-admission-policy/clusterpolicy/standard
generate
cpol-any-exclude-namespace-match-resource
chainsaw-test.yamlpolicy-assert.yamlpolicy.yamlvalidatingadmissionpolicy.yamlvalidatingadmissionpolicybinding.yaml
cpol-any-exclude-resource-match-with-namespace-selector
chainsaw-test.yamlpolicy-assert.yamlpolicy.yamlvalidatingadmissionpolicy.yamlvalidatingadmissionpolicybinding.yaml
cpol-any-exclude-resource-match-with-object-selector
chainsaw-test.yamlpolicy-assert.yamlpolicy.yamlvalidatingadmissionpolicy.yamlvalidatingadmissionpolicybinding.yaml
cpol-any-exclude-resource
chainsaw-test.yamlpolicy-assert.yamlpolicy.yamlvalidatingadmissionpolicy.yamlvalidatingadmissionpolicybinding.yaml
cpol-match-all-exclude-one
skip-generate
cpol-exclude-resources-in-specific-namespace
chainsaw-test.yamlpolicy-assert.yamlpolicy.yamlvalidatingadmissionpolicy.yamlvalidatingadmissionpolicybinding.yaml
cpol-exclude-resources-with-namespace-selector
chainsaw-test.yamlpolicy-assert.yamlpolicy.yamlvalidatingadmissionpolicy.yamlvalidatingadmissionpolicybinding.yaml
cpol-exclude-resources-with-object-selector
chainsaw-test.yamlpolicy-assert.yamlpolicy.yamlvalidatingadmissionpolicy.yamlvalidatingadmissionpolicybinding.yaml
cpol-exclude-user-and-roles
|
@ -14,7 +14,11 @@ import (
|
|||
)
|
||||
|
||||
// BuildValidatingAdmissionPolicy is used to build a Kubernetes ValidatingAdmissionPolicy from a Kyverno policy
|
||||
func BuildValidatingAdmissionPolicy(discoveryClient dclient.IDiscovery, vap *admissionregistrationv1alpha1.ValidatingAdmissionPolicy, cpol kyvernov1.PolicyInterface) error {
|
||||
func BuildValidatingAdmissionPolicy(
|
||||
discoveryClient dclient.IDiscovery,
|
||||
vap *admissionregistrationv1alpha1.ValidatingAdmissionPolicy,
|
||||
cpol kyvernov1.PolicyInterface,
|
||||
) error {
|
||||
// set owner reference
|
||||
vap.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
|
@ -25,30 +29,51 @@ func BuildValidatingAdmissionPolicy(discoveryClient dclient.IDiscovery, vap *adm
|
|||
},
|
||||
}
|
||||
|
||||
// construct validating admission policy resource rules
|
||||
// construct the rules
|
||||
var matchResources admissionregistrationv1alpha1.MatchResources
|
||||
var matchRules []admissionregistrationv1alpha1.NamedRuleWithOperations
|
||||
var matchRules, excludeRules []admissionregistrationv1alpha1.NamedRuleWithOperations
|
||||
|
||||
rule := cpol.GetSpec().Rules[0]
|
||||
|
||||
// convert the match block
|
||||
match := rule.MatchResources
|
||||
if !match.ResourceDescription.IsEmpty() {
|
||||
if err := translateResource(discoveryClient, &matchResources, &matchRules, match.ResourceDescription); err != nil {
|
||||
if err := translateResource(discoveryClient, &matchResources, &matchRules, match.ResourceDescription, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if match.Any != nil {
|
||||
if err := translateResourceFilters(discoveryClient, &matchResources, &matchRules, match.Any); err != nil {
|
||||
if err := translateResourceFilters(discoveryClient, &matchResources, &matchRules, match.Any, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if match.All != nil {
|
||||
if err := translateResourceFilters(discoveryClient, &matchResources, &matchRules, match.All); err != nil {
|
||||
if err := translateResourceFilters(discoveryClient, &matchResources, &matchRules, match.All, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// set validating admission policy spec
|
||||
// convert the exclude block
|
||||
exclude := rule.ExcludeResources
|
||||
if !exclude.ResourceDescription.IsEmpty() {
|
||||
if err := translateResource(discoveryClient, &matchResources, &excludeRules, exclude.ResourceDescription, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if exclude.Any != nil {
|
||||
if err := translateResourceFilters(discoveryClient, &matchResources, &excludeRules, exclude.Any, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if exclude.All != nil {
|
||||
if err := translateResourceFilters(discoveryClient, &matchResources, &excludeRules, exclude.All, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// set policy spec
|
||||
vap.Spec = admissionregistrationv1alpha1.ValidatingAdmissionPolicySpec{
|
||||
MatchConstraints: &matchResources,
|
||||
ParamKind: rule.Validation.CEL.ParamKind,
|
||||
|
@ -64,7 +89,10 @@ func BuildValidatingAdmissionPolicy(discoveryClient dclient.IDiscovery, vap *adm
|
|||
}
|
||||
|
||||
// BuildValidatingAdmissionPolicyBinding is used to build a Kubernetes ValidatingAdmissionPolicyBinding from a Kyverno policy
|
||||
func BuildValidatingAdmissionPolicyBinding(vapbinding *admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding, cpol kyvernov1.PolicyInterface) error {
|
||||
func BuildValidatingAdmissionPolicyBinding(
|
||||
vapbinding *admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding,
|
||||
cpol kyvernov1.PolicyInterface,
|
||||
) error {
|
||||
// set owner reference
|
||||
vapbinding.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
|
@ -98,9 +126,14 @@ func BuildValidatingAdmissionPolicyBinding(vapbinding *admissionregistrationv1al
|
|||
return nil
|
||||
}
|
||||
|
||||
func translateResourceFilters(discoveryClient dclient.IDiscovery, matchResources *admissionregistrationv1alpha1.MatchResources, rules *[]admissionregistrationv1alpha1.NamedRuleWithOperations, resFilters kyvernov1.ResourceFilters) error {
|
||||
func translateResourceFilters(discoveryClient dclient.IDiscovery,
|
||||
matchResources *admissionregistrationv1alpha1.MatchResources,
|
||||
rules *[]admissionregistrationv1alpha1.NamedRuleWithOperations,
|
||||
resFilters kyvernov1.ResourceFilters,
|
||||
isMatch bool,
|
||||
) error {
|
||||
for _, filter := range resFilters {
|
||||
err := translateResource(discoveryClient, matchResources, rules, filter.ResourceDescription)
|
||||
err := translateResource(discoveryClient, matchResources, rules, filter.ResourceDescription, isMatch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -108,32 +141,47 @@ func translateResourceFilters(discoveryClient dclient.IDiscovery, matchResources
|
|||
return nil
|
||||
}
|
||||
|
||||
func translateResource(discoveryClient dclient.IDiscovery, matchResources *admissionregistrationv1alpha1.MatchResources, rules *[]admissionregistrationv1alpha1.NamedRuleWithOperations, res kyvernov1.ResourceDescription) error {
|
||||
err := constructValidatingAdmissionPolicyRules(discoveryClient, rules, res)
|
||||
func translateResource(
|
||||
discoveryClient dclient.IDiscovery,
|
||||
matchResources *admissionregistrationv1alpha1.MatchResources,
|
||||
rules *[]admissionregistrationv1alpha1.NamedRuleWithOperations,
|
||||
res kyvernov1.ResourceDescription,
|
||||
isMatch bool,
|
||||
) error {
|
||||
err := constructValidatingAdmissionPolicyRules(discoveryClient, rules, res, isMatch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
matchResources.ResourceRules = *rules
|
||||
if len(res.Namespaces) > 0 {
|
||||
namespaceSelector := &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "kubernetes.io/metadata.name",
|
||||
Operator: "In",
|
||||
Values: res.Namespaces,
|
||||
if isMatch {
|
||||
matchResources.ResourceRules = *rules
|
||||
if len(res.Namespaces) > 0 {
|
||||
namespaceSelector := &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "kubernetes.io/metadata.name",
|
||||
Operator: "In",
|
||||
Values: res.Namespaces,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
matchResources.NamespaceSelector = namespaceSelector
|
||||
} else {
|
||||
matchResources.NamespaceSelector = res.NamespaceSelector
|
||||
}
|
||||
matchResources.NamespaceSelector = namespaceSelector
|
||||
matchResources.ObjectSelector = res.Selector
|
||||
} else {
|
||||
matchResources.NamespaceSelector = res.NamespaceSelector
|
||||
matchResources.ExcludeResourceRules = *rules
|
||||
}
|
||||
matchResources.ObjectSelector = res.Selector
|
||||
return nil
|
||||
}
|
||||
|
||||
func constructValidatingAdmissionPolicyRules(discoveryClient dclient.IDiscovery, rules *[]admissionregistrationv1alpha1.NamedRuleWithOperations, res kyvernov1.ResourceDescription) error {
|
||||
func constructValidatingAdmissionPolicyRules(
|
||||
discoveryClient dclient.IDiscovery,
|
||||
rules *[]admissionregistrationv1alpha1.NamedRuleWithOperations,
|
||||
res kyvernov1.ResourceDescription,
|
||||
isMatch bool,
|
||||
) error {
|
||||
// translate operations to their corresponding values in validating admission policy.
|
||||
ops := translateOperations(res.GetOperations())
|
||||
|
||||
|
@ -191,6 +239,22 @@ func constructValidatingAdmissionPolicyRules(discoveryClient dclient.IDiscovery,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if exclude block has namespaces but no kinds, we need to add a rule for the namespaces
|
||||
if !isMatch && len(res.Namespaces) > 0 && len(res.Kinds) == 0 {
|
||||
r := admissionregistrationv1alpha1.NamedRuleWithOperations{
|
||||
ResourceNames: res.Namespaces,
|
||||
RuleWithOperations: admissionregistrationv1.RuleWithOperations{
|
||||
Rule: admissionregistrationv1.Rule{
|
||||
Resources: []string{"namespaces"},
|
||||
APIGroups: []string{""},
|
||||
APIVersions: []string{"v1"},
|
||||
},
|
||||
Operations: ops,
|
||||
},
|
||||
}
|
||||
*rules = append(*rules, r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -227,7 +291,7 @@ func translateOperations(operations []string) []admissionregistrationv1.Operatio
|
|||
}
|
||||
}
|
||||
|
||||
// set default values for operations since it's a required field in validating admission policies
|
||||
// set default values for operations since it's a required field in ValidatingAdmissionPolicies
|
||||
if len(vapOperations) == 0 {
|
||||
vapOperations = append(vapOperations, admissionregistrationv1.Create)
|
||||
vapOperations = append(vapOperations, admissionregistrationv1.Update)
|
||||
|
|
|
@ -8,14 +8,12 @@ import (
|
|||
// CanGenerateVAP check if Kyverno policy can be translated to a Kubernetes ValidatingAdmissionPolicy
|
||||
func CanGenerateVAP(spec *kyvernov1.Spec) (bool, string) {
|
||||
var msg string
|
||||
if len(spec.Rules) > 1 {
|
||||
msg = "skip generating ValidatingAdmissionPolicy: multiple rules are not applicable."
|
||||
if ok, msg := checkRuleCount(spec); !ok {
|
||||
return false, msg
|
||||
}
|
||||
|
||||
rule := spec.Rules[0]
|
||||
if !rule.HasValidateCEL() {
|
||||
msg = "skip generating ValidatingAdmissionPolicy for non CEL rules."
|
||||
if ok, msg := checkRuleType(rule); !ok {
|
||||
return false, msg
|
||||
}
|
||||
|
||||
|
@ -32,65 +30,74 @@ func CanGenerateVAP(spec *kyvernov1.Spec) (bool, string) {
|
|||
|
||||
// check the matched/excluded resources of the CEL rule.
|
||||
match, exclude := rule.MatchResources, rule.ExcludeResources
|
||||
if !exclude.UserInfo.IsEmpty() || !exclude.ResourceDescription.IsEmpty() || exclude.All != nil || exclude.Any != nil {
|
||||
msg = "skip generating ValidatingAdmissionPolicy: Exclude is not applicable."
|
||||
return false, msg
|
||||
}
|
||||
if ok, msg := checkUserInfo(match.UserInfo); !ok {
|
||||
return false, msg
|
||||
}
|
||||
if ok, msg := checkResources(match.ResourceDescription); !ok {
|
||||
if ok, msg := checkUserInfo(exclude.UserInfo); !ok {
|
||||
return false, msg
|
||||
}
|
||||
|
||||
var (
|
||||
containsNamespaceSelector = false
|
||||
containsObjectSelector = false
|
||||
)
|
||||
|
||||
// since 'any' specify resources which will be ORed, it can be converted into multiple NamedRuleWithOperations in ValidatingAdmissionPolicy
|
||||
for _, value := range match.Any {
|
||||
if ok, msg := checkUserInfo(value.UserInfo); !ok {
|
||||
return false, msg
|
||||
}
|
||||
if ok, msg := checkResources(value.ResourceDescription); !ok {
|
||||
return false, msg
|
||||
}
|
||||
|
||||
if value.NamespaceSelector != nil {
|
||||
containsNamespaceSelector = true
|
||||
}
|
||||
if value.Selector != nil {
|
||||
containsObjectSelector = true
|
||||
}
|
||||
if ok, msg := checkResources(match.ResourceDescription, true); !ok {
|
||||
return false, msg
|
||||
}
|
||||
// since namespace/object selectors are applied to all NamedRuleWithOperations in ValidatingAdmissionPolicy, then
|
||||
// we can't have more than one resource with namespace/object selectors.
|
||||
if len(match.Any) > 1 && (containsNamespaceSelector || containsObjectSelector) {
|
||||
msg = "skip generating ValidatingAdmissionPolicy: NamespaceSelector / ObjectSelector across multiple resources are not applicable."
|
||||
if ok, msg := checkResources(exclude.ResourceDescription, false); !ok {
|
||||
return false, msg
|
||||
}
|
||||
|
||||
// since 'all' specify resources which will be ANDed, we can't have more than one resource.
|
||||
if match.All != nil {
|
||||
if len(match.All) > 1 {
|
||||
msg = "skip generating ValidatingAdmissionPolicy: multiple 'all' is not applicable."
|
||||
return false, msg
|
||||
} else {
|
||||
if ok, msg := checkUserInfo(match.All[0].UserInfo); !ok {
|
||||
return false, msg
|
||||
}
|
||||
if ok, msg := checkResources(match.All[0].ResourceDescription); !ok {
|
||||
return false, msg
|
||||
}
|
||||
}
|
||||
if ok, msg := checkResourceFilter(match.Any, true); !ok {
|
||||
return false, msg
|
||||
}
|
||||
|
||||
if len(match.All) > 1 {
|
||||
msg = "skip generating ValidatingAdmissionPolicy: multiple 'all' in the match block is not applicable."
|
||||
return false, msg
|
||||
}
|
||||
if ok, msg := checkResourceFilter(match.All, true); !ok {
|
||||
return false, msg
|
||||
}
|
||||
|
||||
if ok, msg := checkResourceFilter(exclude.Any, false); !ok {
|
||||
return false, msg
|
||||
}
|
||||
|
||||
if len(exclude.All) > 1 {
|
||||
msg = "skip generating ValidatingAdmissionPolicy: multiple 'all' in the exclude block is not applicable."
|
||||
return false, msg
|
||||
}
|
||||
if ok, msg := checkResourceFilter(exclude.All, false); !ok {
|
||||
return false, msg
|
||||
}
|
||||
|
||||
return true, msg
|
||||
}
|
||||
|
||||
func checkResources(resource kyvernov1.ResourceDescription) (bool, string) {
|
||||
func checkRuleCount(spec *kyvernov1.Spec) (bool, string) {
|
||||
var msg string
|
||||
if len(spec.Rules) > 1 {
|
||||
msg = "skip generating ValidatingAdmissionPolicy: multiple rules are not applicable."
|
||||
return false, msg
|
||||
}
|
||||
return true, msg
|
||||
}
|
||||
|
||||
func checkRuleType(rule kyvernov1.Rule) (bool, string) {
|
||||
var msg string
|
||||
if !rule.HasValidateCEL() {
|
||||
msg = "skip generating ValidatingAdmissionPolicy for non CEL rules."
|
||||
return false, msg
|
||||
}
|
||||
return true, msg
|
||||
}
|
||||
|
||||
func checkResources(resource kyvernov1.ResourceDescription, isMatch bool) (bool, string) {
|
||||
var msg string
|
||||
if !isMatch {
|
||||
if len(resource.Kinds) != 0 && len(resource.Namespaces) != 0 {
|
||||
msg = "skip generating ValidatingAdmissionPolicy: excluding a resource within a namespace is not applicable."
|
||||
return false, msg
|
||||
}
|
||||
}
|
||||
|
||||
if len(resource.Annotations) != 0 {
|
||||
msg = "skip generating ValidatingAdmissionPolicy: Annotations in resource description is not applicable."
|
||||
return false, msg
|
||||
|
@ -122,3 +129,38 @@ func checkUserInfo(info kyvernov1.UserInfo) (bool, string) {
|
|||
}
|
||||
return true, msg
|
||||
}
|
||||
|
||||
func checkResourceFilter(resFilters kyvernov1.ResourceFilters, isMatch bool) (bool, string) {
|
||||
var msg string
|
||||
containsNamespaceSelector := false
|
||||
containsObjectSelector := false
|
||||
|
||||
for _, value := range resFilters {
|
||||
if ok, msg := checkUserInfo(value.UserInfo); !ok {
|
||||
return false, msg
|
||||
}
|
||||
if ok, msg := checkResources(value.ResourceDescription, isMatch); !ok {
|
||||
return false, msg
|
||||
}
|
||||
|
||||
if value.NamespaceSelector != nil {
|
||||
containsNamespaceSelector = true
|
||||
}
|
||||
if value.Selector != nil {
|
||||
containsObjectSelector = true
|
||||
}
|
||||
}
|
||||
|
||||
if !isMatch {
|
||||
if containsNamespaceSelector || containsObjectSelector {
|
||||
msg = "skip generating ValidatingAdmissionPolicy: NamespaceSelector / ObjectSelector in the exclude block is not applicable."
|
||||
return false, msg
|
||||
}
|
||||
} else {
|
||||
if len(resFilters) > 1 && (containsNamespaceSelector || containsObjectSelector) {
|
||||
return false, "skip generating ValidatingAdmissionPolicy: NamespaceSelector / ObjectSelector across multiple resources in the match block are not applicable."
|
||||
}
|
||||
}
|
||||
|
||||
return true, msg
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ func Test_Check_Resources(t *testing.T) {
|
|||
var res kyvernov1.ResourceDescription
|
||||
err := json.Unmarshal(test.resource, &res)
|
||||
assert.NilError(t, err)
|
||||
out, _ := checkResources(res)
|
||||
out, _ := checkResources(res, true)
|
||||
assert.Equal(t, out, test.expected)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-any-exclude-namespace-match-resource
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: policy.yaml
|
||||
- assert:
|
||||
file: policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- assert:
|
||||
file: validatingadmissionpolicy.yaml
|
||||
- assert:
|
||||
file: validatingadmissionpolicybinding.yaml
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-host-path-t16
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-host-path-t10
|
||||
name: disallow-host-path-t16
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
|
@ -17,8 +17,10 @@ spec:
|
|||
- UPDATE
|
||||
exclude:
|
||||
any:
|
||||
- clusterRoles:
|
||||
- cluster-admin
|
||||
- resources:
|
||||
namespaces:
|
||||
- testing-ns
|
||||
- staging-ns
|
||||
validate:
|
||||
cel:
|
||||
expressions:
|
|
@ -0,0 +1,41 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: disallow-host-path-t16
|
||||
ownerReferences:
|
||||
- apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
name: disallow-host-path-t16
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
excludeResourceRules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resourceNames:
|
||||
- testing-ns
|
||||
- staging-ns
|
||||
resources:
|
||||
- namespaces
|
||||
resourceRules:
|
||||
- apiGroups:
|
||||
- apps
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- deployments
|
||||
validations:
|
||||
- expression: '!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(volume,
|
||||
!has(volume.hostPath))'
|
||||
message: HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath
|
||||
must be unset.
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicyBinding
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: disallow-host-path-t16-binding
|
||||
ownerReferences:
|
||||
- apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
name: disallow-host-path-t16
|
||||
spec:
|
||||
policyName: disallow-host-path-t16
|
||||
validationActions:
|
||||
- Audit
|
||||
- Warn
|
|
@ -0,0 +1,19 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-any-exclude-resource-match-with-namespace-selector
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: policy.yaml
|
||||
- assert:
|
||||
file: policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- assert:
|
||||
file: validatingadmissionpolicy.yaml
|
||||
- assert:
|
||||
file: validatingadmissionpolicybinding.yaml
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-host-path-t14
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-host-path-t14
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: host-path
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
namespaceSelector:
|
||||
matchLabels:
|
||||
app: critical
|
||||
exclude:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
names:
|
||||
- "testing"
|
||||
validate:
|
||||
cel:
|
||||
expressions:
|
||||
- expression: "!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(volume, !has(volume.hostPath))"
|
||||
message: "HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath must be unset."
|
|
@ -0,0 +1,43 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: disallow-host-path-t14
|
||||
ownerReferences:
|
||||
- apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
name: disallow-host-path-t14
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
excludeResourceRules:
|
||||
- apiGroups:
|
||||
- apps
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resourceNames:
|
||||
- testing
|
||||
resources:
|
||||
- deployments
|
||||
namespaceSelector:
|
||||
matchLabels:
|
||||
app: critical
|
||||
resourceRules:
|
||||
- apiGroups:
|
||||
- apps
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- deployments
|
||||
validations:
|
||||
- expression: '!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(volume,
|
||||
!has(volume.hostPath))'
|
||||
message: HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath
|
||||
must be unset.
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicyBinding
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: disallow-host-path-t14-binding
|
||||
ownerReferences:
|
||||
- apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
name: disallow-host-path-t14
|
||||
spec:
|
||||
policyName: disallow-host-path-t14
|
||||
validationActions:
|
||||
- Audit
|
||||
- Warn
|
|
@ -0,0 +1,19 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-any-exclude-resource-match-with-object-selector
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: policy.yaml
|
||||
- assert:
|
||||
file: policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- assert:
|
||||
file: validatingadmissionpolicy.yaml
|
||||
- assert:
|
||||
file: validatingadmissionpolicybinding.yaml
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-host-path-t15
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-host-path-t15
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: host-path
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
selector:
|
||||
matchLabels:
|
||||
app: critical
|
||||
exclude:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
names:
|
||||
- "testing"
|
||||
validate:
|
||||
cel:
|
||||
expressions:
|
||||
- expression: "!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(volume, !has(volume.hostPath))"
|
||||
message: "HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath must be unset."
|
|
@ -0,0 +1,43 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: disallow-host-path-t15
|
||||
ownerReferences:
|
||||
- apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
name: disallow-host-path-t15
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
excludeResourceRules:
|
||||
- apiGroups:
|
||||
- apps
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resourceNames:
|
||||
- testing
|
||||
resources:
|
||||
- deployments
|
||||
objectSelector:
|
||||
matchLabels:
|
||||
app: critical
|
||||
resourceRules:
|
||||
- apiGroups:
|
||||
- apps
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- deployments
|
||||
validations:
|
||||
- expression: '!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(volume,
|
||||
!has(volume.hostPath))'
|
||||
message: HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath
|
||||
must be unset.
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicyBinding
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: disallow-host-path-t15-binding
|
||||
ownerReferences:
|
||||
- apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
name: disallow-host-path-t15
|
||||
spec:
|
||||
policyName: disallow-host-path-t15
|
||||
validationActions:
|
||||
- Audit
|
||||
- Warn
|
|
@ -0,0 +1,19 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-any-exclude-resource
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: policy.yaml
|
||||
- assert:
|
||||
file: policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- assert:
|
||||
file: validatingadmissionpolicy.yaml
|
||||
- assert:
|
||||
file: validatingadmissionpolicybinding.yaml
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-host-path-t13
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-host-path-t13
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: host-path
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
- StatefulSet
|
||||
- ReplicaSet
|
||||
- DaemonSet
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
exclude:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
names:
|
||||
- "testing"
|
||||
validate:
|
||||
cel:
|
||||
expressions:
|
||||
- expression: "!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(volume, !has(volume.hostPath))"
|
||||
message: "HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath must be unset."
|
|
@ -0,0 +1,43 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: disallow-host-path-t13
|
||||
ownerReferences:
|
||||
- apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
name: disallow-host-path-t13
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
excludeResourceRules:
|
||||
- apiGroups:
|
||||
- apps
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resourceNames:
|
||||
- testing
|
||||
resources:
|
||||
- deployments
|
||||
resourceRules:
|
||||
- apiGroups:
|
||||
- apps
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- deployments
|
||||
- statefulsets
|
||||
- replicasets
|
||||
- daemonsets
|
||||
validations:
|
||||
- expression: '!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(volume,
|
||||
!has(volume.hostPath))'
|
||||
message: HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath
|
||||
must be unset.
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicyBinding
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: disallow-host-path-t13-binding
|
||||
ownerReferences:
|
||||
- apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
name: disallow-host-path-t13
|
||||
spec:
|
||||
policyName: disallow-host-path-t13
|
||||
validationActions:
|
||||
- Audit
|
||||
- Warn
|
|
@ -0,0 +1,19 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-match-kind-with-wildcard
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: policy.yaml
|
||||
- assert:
|
||||
file: policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- assert:
|
||||
file: validatingadmissionpolicy.yaml
|
||||
- assert:
|
||||
file: validatingadmissionpolicybinding.yaml
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: check-label-app5
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: check-label-app5
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: check-label-app
|
||||
match:
|
||||
all:
|
||||
- resources:
|
||||
kinds:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
namespaces:
|
||||
- production
|
||||
- staging
|
||||
exclude:
|
||||
all:
|
||||
- resources:
|
||||
kinds:
|
||||
- "Deployment"
|
||||
operations:
|
||||
- CREATE
|
||||
validate:
|
||||
cel:
|
||||
expressions:
|
||||
- expression: "'app' in object.metadata.labels"
|
|
@ -0,0 +1,41 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: check-label-app5
|
||||
ownerReferences:
|
||||
- apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
name: check-label-app5
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
excludeResourceRules:
|
||||
- apiGroups:
|
||||
- apps
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- deployments
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: kubernetes.io/metadata.name
|
||||
operator: In
|
||||
values:
|
||||
- production
|
||||
- staging
|
||||
resourceRules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- '*'
|
||||
scope: '*'
|
||||
validations:
|
||||
- expression: '''app'' in object.metadata.labels'
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicyBinding
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: check-label-app5-binding
|
||||
ownerReferences:
|
||||
- apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
name: check-label-app5
|
||||
spec:
|
||||
policyName: check-label-app5
|
||||
validationActions:
|
||||
- Audit
|
||||
- Warn
|
|
@ -0,0 +1,19 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-exclude-resources-in-specific-namespace
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: policy.yaml
|
||||
- assert:
|
||||
file: policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- error:
|
||||
file: validatingadmissionpolicy.yaml
|
||||
- error:
|
||||
file: validatingadmissionpolicybinding.yaml
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-host-path-t17
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
validatingadmissionpolicy:
|
||||
generated: false
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-host-path-t17
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: host-path
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
exclude:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
namespaces:
|
||||
- testing-ns
|
||||
- staging-ns
|
||||
validate:
|
||||
cel:
|
||||
expressions:
|
||||
- expression: "!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(volume, !has(volume.hostPath))"
|
||||
message: "HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath must be unset."
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: disallow-host-path-t17
|
||||
spec: {}
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicyBinding
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: disallow-host-path-t17-binding
|
||||
spec: {}
|
|
@ -0,0 +1,19 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-exclude-resources-with-namespace-selector
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: policy.yaml
|
||||
- assert:
|
||||
file: policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- error:
|
||||
file: validatingadmissionpolicy.yaml
|
||||
- error:
|
||||
file: validatingadmissionpolicybinding.yaml
|
|
@ -0,0 +1,36 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-host-path-t10
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: host-path
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
exclude:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: type
|
||||
operator: In
|
||||
values:
|
||||
- connector
|
||||
validate:
|
||||
cel:
|
||||
expressions:
|
||||
- expression: "!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(volume, !has(volume.hostPath))"
|
||||
message: "HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath must be unset."
|
|
@ -2,7 +2,7 @@ apiVersion: chainsaw.kyverno.io/v1alpha1
|
|||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-exclude
|
||||
name: cpol-exclude-resources-with-object-selector
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
|
@ -14,8 +14,11 @@ spec:
|
|||
exclude:
|
||||
any:
|
||||
- resources:
|
||||
namespaces:
|
||||
- default
|
||||
kinds:
|
||||
- Pod
|
||||
selector:
|
||||
matchLabels:
|
||||
app: critical
|
||||
validate:
|
||||
cel:
|
||||
expressions:
|
|
@ -2,7 +2,7 @@ apiVersion: chainsaw.kyverno.io/v1alpha1
|
|||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-exclude-namespace
|
||||
name: cpol-exclude-user-and-roles
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: check-label-app1
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
validatingadmissionpolicy:
|
||||
generated: false
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: check-label-app1
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: check-label-app
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
exclude:
|
||||
any:
|
||||
- clusterRoles:
|
||||
- cluster-admin
|
||||
- subjects:
|
||||
- kind: User
|
||||
name: John
|
||||
validate:
|
||||
cel:
|
||||
expressions:
|
||||
- expression: "'app' in object.metadata.labels"
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: check-label-app1
|
||||
spec: {}
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicyBinding
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: check-label-app1-binding
|
||||
spec: {}
|
Loading…
Add table
Reference in a new issue