...

Source file src/k8s.io/kubernetes/test/integration/apiserver/cel/validatingadmissionpolicy_test.go

Documentation: k8s.io/kubernetes/test/integration/apiserver/cel

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cel
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"net/http"
    24  	"net/http/httptest"
    25  	"os"
    26  	"strconv"
    27  	"strings"
    28  	"sync"
    29  	"testing"
    30  	"text/template"
    31  	"time"
    32  
    33  	authenticationv1 "k8s.io/api/authentication/v1"
    34  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    35  	apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
    36  	"k8s.io/apiextensions-apiserver/test/integration/fixtures"
    37  	auditinternal "k8s.io/apiserver/pkg/apis/audit"
    38  	auditv1 "k8s.io/apiserver/pkg/apis/audit/v1"
    39  	genericfeatures "k8s.io/apiserver/pkg/features"
    40  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    41  	"k8s.io/client-go/rest"
    42  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    43  	"k8s.io/utils/ptr"
    44  
    45  	"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
    46  	apiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
    47  	authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
    48  	"k8s.io/kubernetes/test/integration/authutil"
    49  	"k8s.io/kubernetes/test/integration/etcd"
    50  	"k8s.io/kubernetes/test/integration/framework"
    51  	"k8s.io/kubernetes/test/utils"
    52  
    53  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    54  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    55  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    56  	"k8s.io/apimachinery/pkg/runtime/schema"
    57  	"k8s.io/apimachinery/pkg/types"
    58  	"k8s.io/apimachinery/pkg/util/sets"
    59  	"k8s.io/apimachinery/pkg/util/wait"
    60  	"k8s.io/client-go/dynamic"
    61  	clientset "k8s.io/client-go/kubernetes"
    62  
    63  	admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
    64  	authorizationv1 "k8s.io/api/authorization/v1"
    65  	v1 "k8s.io/api/core/v1"
    66  	rbacv1 "k8s.io/api/rbac/v1"
    67  )
    68  
    69  // Test_ValidateNamespace_NoParams tests a ValidatingAdmissionPolicy that validates creation of a Namespace with no params.
    70  func Test_ValidateNamespace_NoParams(t *testing.T) {
    71  	forbiddenReason := metav1.StatusReasonForbidden
    72  
    73  	testcases := []struct {
    74  		name          string
    75  		policy        *admissionregistrationv1.ValidatingAdmissionPolicy
    76  		policyBinding *admissionregistrationv1.ValidatingAdmissionPolicyBinding
    77  		namespace     *v1.Namespace
    78  		err           string
    79  		failureReason metav1.StatusReason
    80  	}{
    81  		{
    82  			name: "namespace name contains suffix enforced by validating admission policy, using object metadata fields",
    83  			policy: withValidations([]admissionregistrationv1.Validation{
    84  				{
    85  					Expression: "object.metadata.name.endsWith('k8s')",
    86  				},
    87  			}, withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
    88  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
    89  			namespace: &v1.Namespace{
    90  				ObjectMeta: metav1.ObjectMeta{
    91  					Name: "test-k8s",
    92  				},
    93  			},
    94  			err: "",
    95  		},
    96  		{
    97  			name: "namespace name does NOT contain suffix enforced by validating admission policyusing, object metadata fields",
    98  			policy: withValidations([]admissionregistrationv1.Validation{
    99  				{
   100  					Expression: "object.metadata.name.endsWith('k8s')",
   101  				},
   102  			}, withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
   103  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   104  			namespace: &v1.Namespace{
   105  				ObjectMeta: metav1.ObjectMeta{
   106  					Name: "test-foobar",
   107  				},
   108  			},
   109  			err:           "namespaces \"test-foobar\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: failed expression: object.metadata.name.endsWith('k8s')",
   110  			failureReason: metav1.StatusReasonInvalid,
   111  		},
   112  		{
   113  			name: "namespace name does NOT contain suffix enforced by validating admission policy using object metadata fields, AND validating expression returns StatusReasonForbidden",
   114  			policy: withValidations([]admissionregistrationv1.Validation{
   115  				{
   116  					Expression: "object.metadata.name.endsWith('k8s')",
   117  					Reason:     &forbiddenReason,
   118  				},
   119  			}, withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
   120  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   121  			namespace: &v1.Namespace{
   122  				ObjectMeta: metav1.ObjectMeta{
   123  					Name: "forbidden-test-foobar",
   124  				},
   125  			},
   126  			err:           "namespaces \"forbidden-test-foobar\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: failed expression: object.metadata.name.endsWith('k8s')",
   127  			failureReason: metav1.StatusReasonForbidden,
   128  		},
   129  		{
   130  			name: "namespace name contains suffix enforced by validating admission policy, using request field",
   131  			policy: withValidations([]admissionregistrationv1.Validation{
   132  				{
   133  					Expression: "request.name.endsWith('k8s')",
   134  				},
   135  			}, withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
   136  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   137  			namespace: &v1.Namespace{
   138  				ObjectMeta: metav1.ObjectMeta{
   139  					Name: "test-k8s",
   140  				},
   141  			},
   142  			err: "",
   143  		},
   144  		{
   145  			name: "namespace name does NOT contains suffix enforced by validating admission policy, using request field",
   146  			policy: withValidations([]admissionregistrationv1.Validation{
   147  				{
   148  					Expression: "request.name.endsWith('k8s')",
   149  				},
   150  			}, withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
   151  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   152  			namespace: &v1.Namespace{
   153  				ObjectMeta: metav1.ObjectMeta{
   154  					Name: "test-k8s",
   155  				},
   156  			},
   157  			err: "",
   158  		},
   159  		{
   160  			name: "runtime error when validating namespace, but failurePolicy=Ignore",
   161  			policy: withValidations([]admissionregistrationv1.Validation{
   162  				{
   163  					Expression: "object.nonExistentProperty == 'someval'",
   164  				},
   165  			}, withFailurePolicy(admissionregistrationv1.Ignore, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
   166  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   167  			namespace: &v1.Namespace{
   168  				ObjectMeta: metav1.ObjectMeta{
   169  					Name: "test-k8s",
   170  				},
   171  			},
   172  			err: "",
   173  		},
   174  		{
   175  			name: "runtime error when validating namespace, but failurePolicy=Fail",
   176  			policy: withValidations([]admissionregistrationv1.Validation{
   177  				{
   178  					Expression: "object.nonExistentProperty == 'someval'",
   179  				},
   180  			}, withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
   181  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   182  			namespace: &v1.Namespace{
   183  				ObjectMeta: metav1.ObjectMeta{
   184  					Name: "test-k8s",
   185  				},
   186  			},
   187  			err:           "namespaces \"test-k8s\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: expression 'object.nonExistentProperty == 'someval'' resulted in error: no such key: nonExistentProperty",
   188  			failureReason: metav1.StatusReasonInvalid,
   189  		},
   190  		{
   191  			name: "runtime error due to unguarded params",
   192  			policy: withValidations([]admissionregistrationv1.Validation{
   193  				{
   194  					Expression: "object.metadata.name.startsWith(params.metadata.name)",
   195  				},
   196  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   197  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   198  			namespace: &v1.Namespace{
   199  				ObjectMeta: metav1.ObjectMeta{
   200  					Name: "test-k8s",
   201  				},
   202  			},
   203  			err:           "namespaces \"test-k8s\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: expression 'object.metadata.name.startsWith(params.metadata.name)' resulted in error: no such key: metadata",
   204  			failureReason: metav1.StatusReasonInvalid,
   205  		},
   206  		{
   207  			name: "with check against unguarded params using has()",
   208  			policy: withValidations([]admissionregistrationv1.Validation{
   209  				{
   210  					Expression: "has(params.metadata) && has(params.metadata.name) && object.metadata.name.endsWith(params.metadata.name)",
   211  				},
   212  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   213  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   214  			namespace: &v1.Namespace{
   215  				ObjectMeta: metav1.ObjectMeta{
   216  					Name: "test-k8s",
   217  				},
   218  			},
   219  			err:           "namespaces \"test-k8s\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: failed expression: has(params.metadata) && has(params.metadata.name) && object.metadata.name.endsWith(params.metadata.name)",
   220  			failureReason: metav1.StatusReasonInvalid,
   221  		},
   222  		{
   223  			name: "with check against null params",
   224  			policy: withValidations([]admissionregistrationv1.Validation{
   225  				{
   226  					Expression: "(params != null && object.metadata.name.endsWith(params.metadata.name))",
   227  				},
   228  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   229  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   230  			namespace: &v1.Namespace{
   231  				ObjectMeta: metav1.ObjectMeta{
   232  					Name: "test-k8s",
   233  				},
   234  			},
   235  			err:           "namespaces \"test-k8s\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: failed expression: (params != null && object.metadata.name.endsWith(params.metadata.name))",
   236  			failureReason: metav1.StatusReasonInvalid,
   237  		},
   238  		{
   239  			name: "with check against unguarded params using has() and default check",
   240  			policy: withValidations([]admissionregistrationv1.Validation{
   241  				{
   242  					Expression: "(has(params.metadata) && has(params.metadata.name) && object.metadata.name.startsWith(params.metadata.name)) || object.metadata.name.endsWith('k8s')",
   243  				},
   244  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   245  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   246  			namespace: &v1.Namespace{
   247  				ObjectMeta: metav1.ObjectMeta{
   248  					Name: "test-k8s",
   249  				},
   250  			},
   251  			err: "",
   252  		},
   253  		{
   254  			name: "with check against null params and default check",
   255  			policy: withValidations([]admissionregistrationv1.Validation{
   256  				{
   257  					Expression: "(params != null && object.metadata.name.startsWith(params.metadata.name)) || object.metadata.name.endsWith('k8s')",
   258  				},
   259  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   260  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   261  			namespace: &v1.Namespace{
   262  				ObjectMeta: metav1.ObjectMeta{
   263  					Name: "test-k8s",
   264  				},
   265  			},
   266  			err: "",
   267  		},
   268  		{
   269  			name: "with check against namespaceObject",
   270  			policy: withValidations([]admissionregistrationv1.Validation{
   271  				{
   272  					Expression: "namespaceObject == null", // because namespace itself is cluster-scoped.
   273  				},
   274  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   275  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   276  			namespace: &v1.Namespace{
   277  				ObjectMeta: metav1.ObjectMeta{
   278  					Name: "test-k8s",
   279  				},
   280  			},
   281  			err: "",
   282  		},
   283  	}
   284  	for _, testcase := range testcases {
   285  		t.Run(testcase.name, func(t *testing.T) {
   286  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
   287  
   288  			server, err := apiservertesting.StartTestServer(t, nil, []string{
   289  				"--enable-admission-plugins", "ValidatingAdmissionPolicy",
   290  			}, framework.SharedEtcd())
   291  			if err != nil {
   292  				t.Fatal(err)
   293  			}
   294  			defer server.TearDownFn()
   295  
   296  			config := server.ClientConfig
   297  
   298  			client, err := clientset.NewForConfig(config)
   299  			if err != nil {
   300  				t.Fatal(err)
   301  			}
   302  			policy := withWaitReadyConstraintAndExpression(testcase.policy)
   303  			if _, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
   304  				t.Fatal(err)
   305  			}
   306  			if err := createAndWaitReady(t, client, testcase.policyBinding, nil); err != nil {
   307  				t.Fatal(err)
   308  			}
   309  
   310  			_, err = client.CoreV1().Namespaces().Create(context.TODO(), testcase.namespace, metav1.CreateOptions{})
   311  
   312  			checkExpectedError(t, err, testcase.err)
   313  			checkFailureReason(t, err, testcase.failureReason)
   314  		})
   315  	}
   316  }
   317  func Test_ValidateAnnotationsAndWarnings(t *testing.T) {
   318  	testcases := []struct {
   319  		name             string
   320  		policy           *admissionregistrationv1.ValidatingAdmissionPolicy
   321  		policyBinding    *admissionregistrationv1.ValidatingAdmissionPolicyBinding
   322  		object           *v1.ConfigMap
   323  		err              string
   324  		failureReason    metav1.StatusReason
   325  		auditAnnotations map[string]string
   326  		warnings         sets.Set[string]
   327  	}{
   328  		{
   329  			name: "with audit annotations",
   330  			policy: withAuditAnnotations([]admissionregistrationv1.AuditAnnotation{
   331  				{
   332  					Key:             "example-key",
   333  					ValueExpression: "'object name: ' + object.metadata.name",
   334  				},
   335  				{
   336  					Key:             "exclude-key",
   337  					ValueExpression: "null",
   338  				},
   339  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1.Fail, withConfigMapMatch(makePolicy("validate-audit-annotations"))))),
   340  			policyBinding: makeBinding("validate-audit-annotations-binding", "validate-audit-annotations", ""),
   341  			object: &v1.ConfigMap{
   342  				ObjectMeta: metav1.ObjectMeta{
   343  					Name: "test1-k8s",
   344  				},
   345  			},
   346  			err: "",
   347  			auditAnnotations: map[string]string{
   348  				"validate-audit-annotations/example-key": `object name: test1-k8s`,
   349  			},
   350  		},
   351  		{
   352  			name: "with audit annotations with invalid expression",
   353  			policy: withAuditAnnotations([]admissionregistrationv1.AuditAnnotation{
   354  				{
   355  					Key:             "example-key",
   356  					ValueExpression: "string(params.metadata.name)", // runtime error, params is null
   357  				},
   358  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1.Fail, withConfigMapMatch(makePolicy("validate-audit-annotations-invalid"))))),
   359  			policyBinding: makeBinding("validate-audit-annotations-invalid-binding", "validate-audit-annotations-invalid", ""),
   360  			object: &v1.ConfigMap{
   361  				ObjectMeta: metav1.ObjectMeta{
   362  					Name: "test2-k8s",
   363  				},
   364  			},
   365  			err:           "configmaps \"test2-k8s\" is forbidden: ValidatingAdmissionPolicy 'validate-audit-annotations-invalid' with binding 'validate-audit-annotations-invalid-binding' denied request: expression 'string(params.metadata.name)' resulted in error: no such key: metadata",
   366  			failureReason: metav1.StatusReasonInvalid,
   367  		},
   368  		{
   369  			name: "with audit annotations with invalid expression and ignore failure policy",
   370  			policy: withAuditAnnotations([]admissionregistrationv1.AuditAnnotation{
   371  				{
   372  					Key:             "example-key",
   373  					ValueExpression: "string(params.metadata.name)", // runtime error, params is null
   374  				},
   375  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1.Ignore, withConfigMapMatch(makePolicy("validate-audit-annotations-invalid-ignore"))))),
   376  			policyBinding: makeBinding("validate-audit-annotations-invalid-ignore-binding", "validate-audit-annotations-invalid-ignore", ""),
   377  			object: &v1.ConfigMap{
   378  				ObjectMeta: metav1.ObjectMeta{
   379  					Name: "test3-k8s",
   380  				},
   381  			},
   382  			err: "",
   383  		},
   384  		{
   385  			name: "with warn validationActions",
   386  			policy: withValidations([]admissionregistrationv1.Validation{
   387  				{
   388  					Expression: "object.metadata.name.endsWith('k8s')",
   389  				},
   390  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1.Fail, withConfigMapMatch(makePolicy("validate-actions-warn"))))),
   391  			policyBinding: withValidationActions([]admissionregistrationv1.ValidationAction{admissionregistrationv1.Warn}, makeBinding("validate-actions-warn-binding", "validate-actions-warn", "")),
   392  			object: &v1.ConfigMap{
   393  				ObjectMeta: metav1.ObjectMeta{
   394  					Name: "test4-nope",
   395  				},
   396  			},
   397  			warnings: sets.New("Validation failed for ValidatingAdmissionPolicy 'validate-actions-warn' with binding 'validate-actions-warn-binding': failed expression: object.metadata.name.endsWith('k8s')"),
   398  		},
   399  		{
   400  			name: "with audit validationActions",
   401  			policy: withValidations([]admissionregistrationv1.Validation{
   402  				{
   403  					Expression: "object.metadata.name.endsWith('k8s')",
   404  				},
   405  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1.Fail, withConfigMapMatch(makePolicy("validate-actions-audit"))))),
   406  			policyBinding: withValidationActions([]admissionregistrationv1.ValidationAction{admissionregistrationv1.Deny, admissionregistrationv1.Audit}, makeBinding("validate-actions-audit-binding", "validate-actions-audit", "")),
   407  			object: &v1.ConfigMap{
   408  				ObjectMeta: metav1.ObjectMeta{
   409  					Name: "test5-nope",
   410  				},
   411  			},
   412  			err:           "configmaps \"test5-nope\" is forbidden: ValidatingAdmissionPolicy 'validate-actions-audit' with binding 'validate-actions-audit-binding' denied request: failed expression: object.metadata.name.endsWith('k8s')",
   413  			failureReason: metav1.StatusReasonInvalid,
   414  			auditAnnotations: map[string]string{
   415  				"validation.policy.admission.k8s.io/validation_failure": `[{"message":"failed expression: object.metadata.name.endsWith('k8s')","policy":"validate-actions-audit","binding":"validate-actions-audit-binding","expressionIndex":1,"validationActions":["Deny","Audit"]}]`,
   416  			},
   417  		},
   418  	}
   419  
   420  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
   421  
   422  	// prepare audit policy file
   423  	policyFile, err := os.CreateTemp("", "audit-policy.yaml")
   424  	if err != nil {
   425  		t.Fatalf("Failed to create audit policy file: %v", err)
   426  	}
   427  	defer os.Remove(policyFile.Name())
   428  	if _, err := policyFile.Write([]byte(auditPolicy)); err != nil {
   429  		t.Fatalf("Failed to write audit policy file: %v", err)
   430  	}
   431  	if err := policyFile.Close(); err != nil {
   432  		t.Fatalf("Failed to close audit policy file: %v", err)
   433  	}
   434  
   435  	// prepare audit log file
   436  	logFile, err := os.CreateTemp("", "audit.log")
   437  	if err != nil {
   438  		t.Fatalf("Failed to create audit log file: %v", err)
   439  	}
   440  	defer os.Remove(logFile.Name())
   441  
   442  	server, err := apiservertesting.StartTestServer(t, nil, []string{
   443  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
   444  		"--audit-policy-file", policyFile.Name(),
   445  		"--audit-log-version", "audit.k8s.io/v1",
   446  		"--audit-log-mode", "blocking",
   447  		"--audit-log-path", logFile.Name(),
   448  	}, framework.SharedEtcd())
   449  	if err != nil {
   450  		t.Fatal(err)
   451  	}
   452  	defer server.TearDownFn()
   453  
   454  	config := server.ClientConfig
   455  
   456  	warnHandler := newWarningHandler()
   457  	config.WarningHandler = warnHandler
   458  	config.Impersonate.UserName = testReinvocationClientUsername
   459  	client, err := clientset.NewForConfig(config)
   460  	if err != nil {
   461  		t.Fatal(err)
   462  	}
   463  	for i, testcase := range testcases {
   464  		t.Run(testcase.name, func(t *testing.T) {
   465  			testCaseID := strconv.Itoa(i)
   466  			ns := "auditannotations-" + testCaseID
   467  			_, err = client.CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}}, metav1.CreateOptions{})
   468  			if err != nil {
   469  				t.Fatal(err)
   470  			}
   471  
   472  			policy := withWaitReadyConstraintAndExpression(testcase.policy)
   473  			if _, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
   474  				t.Fatal(err)
   475  			}
   476  
   477  			if err := createAndWaitReadyNamespacedWithWarnHandler(t, client, withMatchNamespace(testcase.policyBinding, ns), nil, ns, warnHandler); err != nil {
   478  				t.Fatal(err)
   479  			}
   480  			warnHandler.reset()
   481  			testcase.object.Namespace = ns
   482  			_, err = client.CoreV1().ConfigMaps(ns).Create(context.TODO(), testcase.object, metav1.CreateOptions{})
   483  
   484  			code := int32(201)
   485  			if testcase.err != "" {
   486  				code = 422
   487  			}
   488  
   489  			auditAnnotationFilter := func(key, val string) bool {
   490  				_, ok := testcase.auditAnnotations[key]
   491  				return ok
   492  			}
   493  
   494  			checkExpectedError(t, err, testcase.err)
   495  			checkFailureReason(t, err, testcase.failureReason)
   496  			checkExpectedWarnings(t, warnHandler, testcase.warnings)
   497  			checkAuditEvents(t, logFile, expectedAuditEvents(testcase.auditAnnotations, ns, code), auditAnnotationFilter)
   498  		})
   499  	}
   500  }
   501  
   502  // Test_ValidateNamespace_WithConfigMapParams tests a ValidatingAdmissionPolicy that validates creation of a Namespace,
   503  // using ConfigMap as a param reference.
   504  func Test_ValidateNamespace_WithConfigMapParams(t *testing.T) {
   505  	testcases := []struct {
   506  		name          string
   507  		policy        *admissionregistrationv1.ValidatingAdmissionPolicy
   508  		policyBinding *admissionregistrationv1.ValidatingAdmissionPolicyBinding
   509  		configMap     *v1.ConfigMap
   510  		namespace     *v1.Namespace
   511  		err           string
   512  		failureReason metav1.StatusReason
   513  	}{
   514  		{
   515  			name: "namespace name contains suffix enforced by validating admission policy",
   516  			policy: withValidations([]admissionregistrationv1.Validation{
   517  				{
   518  					Expression: "object.metadata.name.endsWith(params.data.namespaceSuffix)",
   519  				},
   520  			}, withFailurePolicy(admissionregistrationv1.Fail, withParams(configParamKind(), withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   521  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", "validate-namespace-suffix-param"),
   522  			configMap: makeConfigParams("validate-namespace-suffix-param", map[string]string{
   523  				"namespaceSuffix": "k8s",
   524  			}),
   525  			namespace: &v1.Namespace{
   526  				ObjectMeta: metav1.ObjectMeta{
   527  					Name: "test-k8s",
   528  				},
   529  			},
   530  			err: "",
   531  		},
   532  		{
   533  			name: "namespace name does NOT contain suffix enforced by validating admission policy",
   534  			policy: withValidations([]admissionregistrationv1.Validation{
   535  				{
   536  					Expression: "object.metadata.name.endsWith(params.data.namespaceSuffix)",
   537  				},
   538  			}, withFailurePolicy(admissionregistrationv1.Fail, withParams(configParamKind(), withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   539  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", "validate-namespace-suffix-param"),
   540  			configMap: makeConfigParams("validate-namespace-suffix-param", map[string]string{
   541  				"namespaceSuffix": "k8s",
   542  			}),
   543  			namespace: &v1.Namespace{
   544  				ObjectMeta: metav1.ObjectMeta{
   545  					Name: "test-foo",
   546  				},
   547  			},
   548  			err:           "namespaces \"test-foo\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: failed expression: object.metadata.name.endsWith(params.data.namespaceSuffix)",
   549  			failureReason: metav1.StatusReasonInvalid,
   550  		},
   551  	}
   552  
   553  	for _, testcase := range testcases {
   554  		t.Run(testcase.name, func(t *testing.T) {
   555  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
   556  			server, err := apiservertesting.StartTestServer(t, nil, []string{
   557  				"--enable-admission-plugins", "ValidatingAdmissionPolicy",
   558  			}, framework.SharedEtcd())
   559  			if err != nil {
   560  				t.Fatal(err)
   561  			}
   562  			defer server.TearDownFn()
   563  
   564  			config := server.ClientConfig
   565  
   566  			client, err := clientset.NewForConfig(config)
   567  			if err != nil {
   568  				t.Fatal(err)
   569  			}
   570  
   571  			if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), testcase.configMap, metav1.CreateOptions{}); err != nil {
   572  				t.Fatal(err)
   573  			}
   574  
   575  			policy := withWaitReadyConstraintAndExpression(testcase.policy)
   576  			if _, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
   577  				t.Fatal(err)
   578  			}
   579  			if err := createAndWaitReady(t, client, testcase.policyBinding, nil); err != nil {
   580  				t.Fatal(err)
   581  			}
   582  
   583  			_, err = client.CoreV1().Namespaces().Create(context.TODO(), testcase.namespace, metav1.CreateOptions{})
   584  
   585  			checkExpectedError(t, err, testcase.err)
   586  			checkFailureReason(t, err, testcase.failureReason)
   587  		})
   588  	}
   589  }
   590  
   591  func TestMultiplePolicyBindings(t *testing.T) {
   592  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
   593  	server, err := apiservertesting.StartTestServer(t, nil, nil, framework.SharedEtcd())
   594  	if err != nil {
   595  		t.Fatal(err)
   596  	}
   597  	defer server.TearDownFn()
   598  
   599  	config := server.ClientConfig
   600  
   601  	client, err := clientset.NewForConfig(config)
   602  	if err != nil {
   603  		t.Fatal(err)
   604  	}
   605  
   606  	paramKind := &admissionregistrationv1.ParamKind{
   607  		APIVersion: "v1",
   608  		Kind:       "ConfigMap",
   609  	}
   610  	policy := withPolicyExistsLabels([]string{"paramIdent"}, withParams(paramKind, withPolicyMatch("secrets", withFailurePolicy(admissionregistrationv1.Fail, makePolicy("test-policy")))))
   611  	policy.Spec.Validations = []admissionregistrationv1.Validation{
   612  		{
   613  			Expression: "params.data.autofail != 'true' && (params.data.conditional == 'false' || object.metadata.name.startsWith(params.data.check))",
   614  		},
   615  	}
   616  	policy = withWaitReadyConstraintAndExpression(policy)
   617  	if _, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
   618  		t.Fatal(err)
   619  	}
   620  
   621  	autoFailParams := makeConfigParams("autofail-params", map[string]string{
   622  		"autofail": "true",
   623  	})
   624  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), autoFailParams, metav1.CreateOptions{}); err != nil {
   625  		t.Fatal(err)
   626  	}
   627  	autofailBinding := withBindingExistsLabels([]string{"autofail-binding-label"}, policy, makeBinding("autofail-binding", "test-policy", "autofail-params"))
   628  	if err := createAndWaitReady(t, client, autofailBinding, map[string]string{"paramIdent": "true", "autofail-binding-label": "true"}); err != nil {
   629  		t.Fatal(err)
   630  	}
   631  
   632  	autoPassParams := makeConfigParams("autopass-params", map[string]string{
   633  		"autofail":    "false",
   634  		"conditional": "false",
   635  	})
   636  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), autoPassParams, metav1.CreateOptions{}); err != nil {
   637  		t.Fatal(err)
   638  	}
   639  	autopassBinding := withBindingExistsLabels([]string{"autopass-binding-label"}, policy, makeBinding("autopass-binding", "test-policy", "autopass-params"))
   640  	if err := createAndWaitReady(t, client, autopassBinding, map[string]string{"paramIdent": "true", "autopass-binding-label": "true"}); err != nil {
   641  		t.Fatal(err)
   642  	}
   643  
   644  	condpassParams := makeConfigParams("condpass-params", map[string]string{
   645  		"autofail":    "false",
   646  		"conditional": "true",
   647  		"check":       "prefix-",
   648  	})
   649  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), condpassParams, metav1.CreateOptions{}); err != nil {
   650  		t.Fatal(err)
   651  	}
   652  	condpassBinding := withBindingExistsLabels([]string{"condpass-binding-label"}, policy, makeBinding("condpass-binding", "test-policy", "condpass-params"))
   653  	if err := createAndWaitReady(t, client, condpassBinding, map[string]string{"paramIdent": "true", "condpass-binding-label": "true"}); err != nil {
   654  		t.Fatal(err)
   655  	}
   656  
   657  	autofailingSecret := &v1.Secret{
   658  		ObjectMeta: metav1.ObjectMeta{
   659  			Name: "autofailing-secret",
   660  			Labels: map[string]string{
   661  				"paramIdent":             "someVal",
   662  				"autofail-binding-label": "true",
   663  			},
   664  		},
   665  	}
   666  	_, err = client.CoreV1().Secrets("default").Create(context.TODO(), autofailingSecret, metav1.CreateOptions{})
   667  	if err == nil {
   668  		t.Fatal("expected secret creation to fail due to autofail-binding")
   669  	}
   670  	checkForFailedRule(t, err)
   671  	checkFailureReason(t, err, metav1.StatusReasonInvalid)
   672  
   673  	autopassingSecret := &v1.Secret{
   674  		ObjectMeta: metav1.ObjectMeta{
   675  			Name: "autopassing-secret",
   676  			Labels: map[string]string{
   677  				"paramIdent":             "someVal",
   678  				"autopass-binding-label": "true",
   679  			},
   680  		},
   681  	}
   682  	if _, err := client.CoreV1().Secrets("default").Create(context.TODO(), autopassingSecret, metav1.CreateOptions{}); err != nil {
   683  		t.Fatalf("expected secret creation to succeed, got: %s", err)
   684  	}
   685  
   686  	condpassingSecret := &v1.Secret{
   687  		ObjectMeta: metav1.ObjectMeta{
   688  			Name: "prefix-condpassing-secret",
   689  			Labels: map[string]string{
   690  				"paramIdent":             "someVal",
   691  				"condpass-binding-label": "true",
   692  			},
   693  		},
   694  	}
   695  	if _, err := client.CoreV1().Secrets("default").Create(context.TODO(), condpassingSecret, metav1.CreateOptions{}); err != nil {
   696  		t.Fatalf("expected secret creation to succeed, got: %s", err)
   697  	}
   698  
   699  	condfailingSecret := &v1.Secret{
   700  		ObjectMeta: metav1.ObjectMeta{
   701  			Name: "condfailing-secret",
   702  			Labels: map[string]string{
   703  				"paramIdent":             "someVal",
   704  				"condpass-binding-label": "true",
   705  			},
   706  		},
   707  	}
   708  	_, err = client.CoreV1().Secrets("default").Create(context.TODO(), condfailingSecret, metav1.CreateOptions{})
   709  	if err == nil {
   710  		t.Fatal("expected secret creation to fail due to autofail-binding")
   711  	}
   712  	checkForFailedRule(t, err)
   713  	checkFailureReason(t, err, metav1.StatusReasonInvalid)
   714  }
   715  
   716  // Test_PolicyExemption tests that ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding resources
   717  // are exempt from policy rules.
   718  func Test_PolicyExemption(t *testing.T) {
   719  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
   720  	server, err := apiservertesting.StartTestServer(t, nil, []string{
   721  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
   722  	}, framework.SharedEtcd())
   723  	if err != nil {
   724  		t.Fatal(err)
   725  	}
   726  	defer server.TearDownFn()
   727  
   728  	config := server.ClientConfig
   729  
   730  	client, err := clientset.NewForConfig(config)
   731  	if err != nil {
   732  		t.Fatal(err)
   733  	}
   734  
   735  	policy := makePolicy("test-policy")
   736  	policy.Spec.MatchConstraints = &admissionregistrationv1.MatchResources{
   737  		ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{
   738  			{
   739  				RuleWithOperations: admissionregistrationv1.RuleWithOperations{
   740  					Operations: []admissionregistrationv1.OperationType{
   741  						"*",
   742  					},
   743  					Rule: admissionregistrationv1.Rule{
   744  						APIGroups: []string{
   745  							"*",
   746  						},
   747  						APIVersions: []string{
   748  							"*",
   749  						},
   750  						Resources: []string{
   751  							"*",
   752  						},
   753  					},
   754  				},
   755  			},
   756  		},
   757  	}
   758  
   759  	policy.Spec.Validations = []admissionregistrationv1.Validation{{
   760  		Expression: "false",
   761  		Message:    "marker denied; policy is ready",
   762  	}}
   763  
   764  	policy, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
   765  	if err != nil {
   766  		t.Fatal(err)
   767  	}
   768  
   769  	policyBinding := makeBinding("test-policy-binding", "test-policy", "")
   770  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
   771  		t.Fatal(err)
   772  	}
   773  
   774  	// validate that operations to ValidatingAdmissionPolicy are exempt from an existing policy that catches all resources
   775  	policy, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Get(context.TODO(), policy.Name, metav1.GetOptions{})
   776  	if err != nil {
   777  		t.Fatal(err)
   778  	}
   779  	ignoreFailurePolicy := admissionregistrationv1.Ignore
   780  	policy.Spec.FailurePolicy = &ignoreFailurePolicy
   781  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Update(context.TODO(), policy, metav1.UpdateOptions{})
   782  	if err != nil {
   783  		t.Error(err)
   784  	}
   785  
   786  	policyBinding, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Get(context.TODO(), policyBinding.Name, metav1.GetOptions{})
   787  	if err != nil {
   788  		t.Fatal(err)
   789  	}
   790  
   791  	// validate that operations to ValidatingAdmissionPolicyBindings are exempt from an existing policy that catches all resources
   792  	policyBindingCopy := policyBinding.DeepCopy()
   793  	policyBindingCopy.Spec.PolicyName = "different-binding"
   794  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Update(context.TODO(), policyBindingCopy, metav1.UpdateOptions{})
   795  	if err != nil {
   796  		t.Error(err)
   797  	}
   798  }
   799  
   800  // Test_ValidatingAdmissionPolicy_UpdateParamKind validates the behavior of ValidatingAdmissionPolicy when
   801  // only the ParamKind is updated. This test creates a policy where namespaces must have a prefix that matches
   802  // the ParamKind set in the policy. Switching the ParamKind should result in only namespaces with prefixes matching
   803  // the new ParamKind to be allowed. For example, when Paramkind is v1/ConfigMap, only namespaces prefixed with "configmap"
   804  // is allowed and when ParamKind is updated to v1/Secret, only namespaces prefixed with "secret" is allowed, etc.
   805  func Test_ValidatingAdmissionPolicy_UpdateParamKind(t *testing.T) {
   806  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
   807  	server, err := apiservertesting.StartTestServer(t, nil, []string{
   808  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
   809  	}, framework.SharedEtcd())
   810  	if err != nil {
   811  		t.Fatal(err)
   812  	}
   813  	defer server.TearDownFn()
   814  
   815  	config := server.ClientConfig
   816  
   817  	client, err := clientset.NewForConfig(config)
   818  	if err != nil {
   819  		t.Fatal(err)
   820  	}
   821  
   822  	allowedPrefixesParamsConfigMap := &v1.ConfigMap{
   823  		ObjectMeta: metav1.ObjectMeta{
   824  			Name: "allowed-prefixes",
   825  		},
   826  	}
   827  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), allowedPrefixesParamsConfigMap, metav1.CreateOptions{}); err != nil {
   828  		t.Fatal(err)
   829  	}
   830  
   831  	allowedPrefixesParamSecret := &v1.Secret{
   832  		ObjectMeta: metav1.ObjectMeta{
   833  			Name: "allowed-prefixes",
   834  		},
   835  	}
   836  	if _, err := client.CoreV1().Secrets("default").Create(context.TODO(), allowedPrefixesParamSecret, metav1.CreateOptions{}); err != nil {
   837  		t.Fatal(err)
   838  	}
   839  
   840  	paramKind := &admissionregistrationv1.ParamKind{
   841  		APIVersion: "v1",
   842  		Kind:       "ConfigMap",
   843  	}
   844  
   845  	policy := withValidations([]admissionregistrationv1.Validation{
   846  		{
   847  			Expression: "object.metadata.name.startsWith(params.kind.lowerAscii())",
   848  			Message:    "wrong paramKind",
   849  		},
   850  	}, withParams(paramKind, withNamespaceMatch(withFailurePolicy(admissionregistrationv1.Fail, makePolicy("allowed-prefixes")))))
   851  	policy = withWaitReadyConstraintAndExpression(policy)
   852  	policy, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
   853  	if err != nil {
   854  		t.Fatal(err)
   855  	}
   856  
   857  	allowedPrefixesBinding := makeBinding("allowed-prefixes-binding", "allowed-prefixes", "allowed-prefixes")
   858  	if err := createAndWaitReady(t, client, allowedPrefixesBinding, nil); err != nil {
   859  		t.Fatal(err)
   860  	}
   861  
   862  	// validate that namespaces starting with "configmap-" are allowed
   863  	// and namespaces starting with "secret-" are disallowed
   864  	allowedNamespace := &v1.Namespace{
   865  		ObjectMeta: metav1.ObjectMeta{
   866  			GenerateName: "configmap-",
   867  		},
   868  	}
   869  	_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
   870  	if err != nil {
   871  		t.Error(err)
   872  	}
   873  
   874  	disallowedNamespace := &v1.Namespace{
   875  		ObjectMeta: metav1.ObjectMeta{
   876  			GenerateName: "secret-",
   877  		},
   878  	}
   879  	_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
   880  	if err == nil {
   881  		t.Error("unexpected nil error")
   882  	}
   883  	if !strings.Contains(err.Error(), "wrong paramKind") {
   884  		t.Errorf("unexpected error message: %v", err)
   885  	}
   886  	checkFailureReason(t, err, metav1.StatusReasonInvalid)
   887  
   888  	// update the policy ParamKind to reference a Secret
   889  	paramKind = &admissionregistrationv1.ParamKind{
   890  		APIVersion: "v1",
   891  		Kind:       "Secret",
   892  	}
   893  	policy, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Get(context.TODO(), policy.Name, metav1.GetOptions{})
   894  	if err != nil {
   895  		t.Error(err)
   896  	}
   897  	policy.Spec.ParamKind = paramKind
   898  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Update(context.TODO(), policy, metav1.UpdateOptions{})
   899  	if err != nil {
   900  		t.Error(err)
   901  	}
   902  
   903  	// validate that namespaces starting with "secret-" are allowed
   904  	// and namespaces starting with "configmap-" are disallowed
   905  	// wait loop is required here since ConfigMaps were previousy allowed and we need to wait for the new policy
   906  	// to be enforced
   907  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
   908  		disallowedNamespace = &v1.Namespace{
   909  			ObjectMeta: metav1.ObjectMeta{
   910  				GenerateName: "configmap-",
   911  			},
   912  		}
   913  
   914  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
   915  		if err == nil {
   916  			return false, nil
   917  		}
   918  
   919  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
   920  			return false, nil
   921  		}
   922  
   923  		if !strings.Contains(err.Error(), "wrong paramKind") {
   924  			return false, err
   925  		}
   926  
   927  		return true, nil
   928  	}); waitErr != nil {
   929  		t.Errorf("timed out waiting: %v", err)
   930  	}
   931  
   932  	allowedNamespace = &v1.Namespace{
   933  		ObjectMeta: metav1.ObjectMeta{
   934  			GenerateName: "secret-",
   935  		},
   936  	}
   937  	_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
   938  	if err != nil {
   939  		t.Error(err)
   940  	}
   941  }
   942  
   943  // Test_ValidatingAdmissionPolicy_UpdateParamRef validates the behavior of ValidatingAdmissionPolicy when
   944  // only the ParamRef in the binding is updated. This test creates a policy where namespaces must have a prefix that matches
   945  // the ParamRef set in the policy binding. The paramRef in the binding is then updated to a different object.
   946  func Test_ValidatingAdmissionPolicy_UpdateParamRef(t *testing.T) {
   947  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
   948  	server, err := apiservertesting.StartTestServer(t, nil, []string{
   949  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
   950  	}, framework.SharedEtcd())
   951  	if err != nil {
   952  		t.Fatal(err)
   953  	}
   954  	defer server.TearDownFn()
   955  
   956  	config := server.ClientConfig
   957  
   958  	client, err := clientset.NewForConfig(config)
   959  	if err != nil {
   960  		t.Fatal(err)
   961  	}
   962  
   963  	allowedPrefixesParamsConfigMap1 := &v1.ConfigMap{
   964  		ObjectMeta: metav1.ObjectMeta{
   965  			Name:      "test-1",
   966  			Namespace: "default",
   967  		},
   968  	}
   969  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), allowedPrefixesParamsConfigMap1, metav1.CreateOptions{}); err != nil {
   970  		t.Fatal(err)
   971  	}
   972  
   973  	allowedPrefixesParamsConfigMap2 := &v1.ConfigMap{
   974  		ObjectMeta: metav1.ObjectMeta{
   975  			Name:      "test-2",
   976  			Namespace: "default",
   977  		},
   978  	}
   979  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), allowedPrefixesParamsConfigMap2, metav1.CreateOptions{}); err != nil {
   980  		t.Fatal(err)
   981  	}
   982  
   983  	policy := withValidations([]admissionregistrationv1.Validation{
   984  		{
   985  			Expression: "object.metadata.name.startsWith(params.metadata.name)",
   986  			Message:    "wrong paramRef",
   987  		},
   988  	}, withParams(configParamKind(), withNamespaceMatch(withFailurePolicy(admissionregistrationv1.Fail, makePolicy("allowed-prefixes")))))
   989  	policy = withWaitReadyConstraintAndExpression(policy)
   990  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
   991  	if err != nil {
   992  		t.Fatal(err)
   993  	}
   994  
   995  	// validate that namespaces starting with "test-1" are allowed
   996  	// and namespaces starting with "test-2-" are disallowed
   997  	allowedPrefixesBinding := makeBinding("allowed-prefixes-binding", "allowed-prefixes", "test-1")
   998  	if err := createAndWaitReady(t, client, allowedPrefixesBinding, nil); err != nil {
   999  		t.Fatal(err)
  1000  	}
  1001  
  1002  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1003  		disallowedNamespace := &v1.Namespace{
  1004  			ObjectMeta: metav1.ObjectMeta{
  1005  				GenerateName: "test-2-",
  1006  			},
  1007  		}
  1008  
  1009  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1010  		if err == nil {
  1011  			return false, nil
  1012  		}
  1013  
  1014  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1015  			return false, nil
  1016  		}
  1017  
  1018  		if !strings.Contains(err.Error(), "wrong paramRef") {
  1019  			return false, err
  1020  		}
  1021  
  1022  		return true, nil
  1023  	}); waitErr != nil {
  1024  		t.Errorf("timed out waiting: %v", err)
  1025  	}
  1026  
  1027  	allowedNamespace := &v1.Namespace{
  1028  		ObjectMeta: metav1.ObjectMeta{
  1029  			GenerateName: "test-1-",
  1030  		},
  1031  	}
  1032  	_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
  1033  	if err != nil {
  1034  		t.Error(err)
  1035  	}
  1036  
  1037  	// Update the paramRef in the policy binding to use the test-2 ConfigMap
  1038  	policyBinding, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Get(context.TODO(), allowedPrefixesBinding.Name, metav1.GetOptions{})
  1039  	if err != nil {
  1040  		t.Fatal(err)
  1041  	}
  1042  
  1043  	denyAction := admissionregistrationv1.DenyAction
  1044  	policyBindingCopy := policyBinding.DeepCopy()
  1045  	policyBindingCopy.Spec.ParamRef = &admissionregistrationv1.ParamRef{
  1046  		Name:                    "test-2",
  1047  		Namespace:               "default",
  1048  		ParameterNotFoundAction: &denyAction,
  1049  	}
  1050  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Update(context.TODO(), policyBindingCopy, metav1.UpdateOptions{})
  1051  	if err != nil {
  1052  		t.Error(err)
  1053  	}
  1054  
  1055  	// validate that namespaces starting with "test-2" are allowed
  1056  	// and namespaces starting with "test-1" are disallowed
  1057  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1058  		disallowedNamespace := &v1.Namespace{
  1059  			ObjectMeta: metav1.ObjectMeta{
  1060  				GenerateName: "test-1-",
  1061  			},
  1062  		}
  1063  
  1064  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1065  		if err == nil {
  1066  			return false, nil
  1067  		}
  1068  
  1069  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1070  			return false, nil
  1071  		}
  1072  
  1073  		if !strings.Contains(err.Error(), "wrong paramRef") {
  1074  			return false, err
  1075  		}
  1076  
  1077  		return true, nil
  1078  	}); waitErr != nil {
  1079  		t.Errorf("timed out waiting: %v", err)
  1080  	}
  1081  
  1082  	allowedNamespace = &v1.Namespace{
  1083  		ObjectMeta: metav1.ObjectMeta{
  1084  			GenerateName: "test-2-",
  1085  		},
  1086  	}
  1087  	_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
  1088  	if err != nil {
  1089  		t.Error(err)
  1090  	}
  1091  }
  1092  
  1093  // Test_ValidatingAdmissionPolicy_UpdateParamResource validates behavior of a policy after updates to the param resource.
  1094  func Test_ValidatingAdmissionPolicy_UpdateParamResource(t *testing.T) {
  1095  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1096  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1097  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1098  	}, framework.SharedEtcd())
  1099  	if err != nil {
  1100  		t.Fatal(err)
  1101  	}
  1102  	defer server.TearDownFn()
  1103  
  1104  	config := server.ClientConfig
  1105  
  1106  	client, err := clientset.NewForConfig(config)
  1107  	if err != nil {
  1108  		t.Fatal(err)
  1109  	}
  1110  
  1111  	paramConfigMap := &v1.ConfigMap{
  1112  		ObjectMeta: metav1.ObjectMeta{
  1113  			Name:      "allowed-prefix",
  1114  			Namespace: "default",
  1115  		},
  1116  		Data: map[string]string{
  1117  			"prefix": "test-1",
  1118  		},
  1119  	}
  1120  	paramConfigMap, err = client.CoreV1().ConfigMaps(paramConfigMap.Namespace).Create(context.TODO(), paramConfigMap, metav1.CreateOptions{})
  1121  	if err != nil {
  1122  		t.Fatal(err)
  1123  	}
  1124  
  1125  	policy := withValidations([]admissionregistrationv1.Validation{
  1126  		{
  1127  			Expression: "object.metadata.name.startsWith(params.data['prefix'])",
  1128  			Message:    "wrong prefix",
  1129  		},
  1130  	}, withParams(configParamKind(), withNamespaceMatch(withFailurePolicy(admissionregistrationv1.Fail, makePolicy("allowed-prefixes")))))
  1131  	policy = withWaitReadyConstraintAndExpression(policy)
  1132  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1133  	if err != nil {
  1134  		t.Fatal(err)
  1135  	}
  1136  
  1137  	// validate that namespaces starting with "test-1" are allowed
  1138  	// and namespaces starting with "test-2-" are disallowed
  1139  	allowedPrefixesBinding := makeBinding("allowed-prefixes-binding", "allowed-prefixes", "allowed-prefix")
  1140  	if err := createAndWaitReady(t, client, allowedPrefixesBinding, nil); err != nil {
  1141  		t.Fatal(err)
  1142  	}
  1143  
  1144  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1145  		disallowedNamespace := &v1.Namespace{
  1146  			ObjectMeta: metav1.ObjectMeta{
  1147  				GenerateName: "test-2-",
  1148  			},
  1149  		}
  1150  
  1151  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1152  		if err == nil {
  1153  			return false, nil
  1154  		}
  1155  
  1156  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1157  			return false, nil
  1158  		}
  1159  
  1160  		if !strings.Contains(err.Error(), "wrong prefix") {
  1161  			return false, err
  1162  		}
  1163  
  1164  		return true, nil
  1165  	}); waitErr != nil {
  1166  		t.Errorf("timed out waiting: %v", err)
  1167  	}
  1168  
  1169  	allowedNamespace := &v1.Namespace{
  1170  		ObjectMeta: metav1.ObjectMeta{
  1171  			GenerateName: "test-1-",
  1172  		},
  1173  	}
  1174  	_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
  1175  	if err != nil {
  1176  		t.Error(err)
  1177  	}
  1178  
  1179  	// Update the param resource to use "test-2" as the new allwoed prefix
  1180  	paramConfigMapCopy := paramConfigMap.DeepCopy()
  1181  	paramConfigMapCopy.Data = map[string]string{
  1182  		"prefix": "test-2",
  1183  	}
  1184  	_, err = client.CoreV1().ConfigMaps(paramConfigMapCopy.Namespace).Update(context.TODO(), paramConfigMapCopy, metav1.UpdateOptions{})
  1185  	if err != nil {
  1186  		t.Fatal(err)
  1187  	}
  1188  
  1189  	// validate that namespaces starting with "test-2" are allowed
  1190  	// and namespaces starting with "test-1" are disallowed
  1191  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1192  		disallowedNamespace := &v1.Namespace{
  1193  			ObjectMeta: metav1.ObjectMeta{
  1194  				GenerateName: "test-1-",
  1195  			},
  1196  		}
  1197  
  1198  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1199  		if err == nil {
  1200  			return false, nil
  1201  		}
  1202  
  1203  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1204  			return false, nil
  1205  		}
  1206  
  1207  		if !strings.Contains(err.Error(), "wrong prefix") {
  1208  			return false, err
  1209  		}
  1210  
  1211  		return true, nil
  1212  	}); waitErr != nil {
  1213  		t.Errorf("timed out waiting: %v", err)
  1214  	}
  1215  
  1216  	allowedNamespace = &v1.Namespace{
  1217  		ObjectMeta: metav1.ObjectMeta{
  1218  			GenerateName: "test-2-",
  1219  		},
  1220  	}
  1221  	_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
  1222  	if err != nil {
  1223  		t.Error(err)
  1224  	}
  1225  }
  1226  
  1227  func Test_ValidatingAdmissionPolicy_MatchByObjectSelector(t *testing.T) {
  1228  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1229  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1230  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1231  	}, framework.SharedEtcd())
  1232  	if err != nil {
  1233  		t.Fatal(err)
  1234  	}
  1235  	defer server.TearDownFn()
  1236  
  1237  	config := server.ClientConfig
  1238  
  1239  	client, err := clientset.NewForConfig(config)
  1240  	if err != nil {
  1241  		t.Fatal(err)
  1242  	}
  1243  
  1244  	labelSelector := &metav1.LabelSelector{
  1245  		MatchLabels: map[string]string{
  1246  			"foo": "bar",
  1247  		},
  1248  	}
  1249  
  1250  	policy := withValidations([]admissionregistrationv1.Validation{
  1251  		{
  1252  			Expression: "false",
  1253  			Message:    "matched by object selector!",
  1254  		},
  1255  	}, withConfigMapMatch(withFailurePolicy(admissionregistrationv1.Fail, makePolicy("match-by-object-selector"))))
  1256  	policy = withObjectSelector(labelSelector, policy)
  1257  	policy = withWaitReadyConstraintAndExpression(policy)
  1258  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1259  	if err != nil {
  1260  		t.Fatal(err)
  1261  	}
  1262  
  1263  	policyBinding := makeBinding("match-by-object-selector-binding", "match-by-object-selector", "")
  1264  	if err := createAndWaitReady(t, client, policyBinding, map[string]string{"foo": "bar"}); err != nil {
  1265  		t.Fatal(err)
  1266  	}
  1267  
  1268  	matchedConfigMap := &v1.ConfigMap{
  1269  		ObjectMeta: metav1.ObjectMeta{
  1270  			Name:      "denied",
  1271  			Namespace: "default",
  1272  			Labels: map[string]string{
  1273  				"foo": "bar",
  1274  			},
  1275  		},
  1276  	}
  1277  
  1278  	_, err = client.CoreV1().ConfigMaps(matchedConfigMap.Namespace).Create(context.TODO(), matchedConfigMap, metav1.CreateOptions{})
  1279  	if !strings.Contains(err.Error(), "matched by object selector!") {
  1280  		t.Errorf("unexpected error: %v", err)
  1281  	}
  1282  
  1283  	allowedConfigMap := &v1.ConfigMap{
  1284  		ObjectMeta: metav1.ObjectMeta{
  1285  			Name:      "allowed",
  1286  			Namespace: "default",
  1287  		},
  1288  	}
  1289  
  1290  	if _, err := client.CoreV1().ConfigMaps(allowedConfigMap.Namespace).Create(context.TODO(), allowedConfigMap, metav1.CreateOptions{}); err != nil {
  1291  		t.Errorf("unexpected error: %v", err)
  1292  	}
  1293  }
  1294  
  1295  func Test_ValidatingAdmissionPolicy_MatchByNamespaceSelector(t *testing.T) {
  1296  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1297  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1298  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1299  	}, framework.SharedEtcd())
  1300  	if err != nil {
  1301  		t.Fatal(err)
  1302  	}
  1303  	defer server.TearDownFn()
  1304  
  1305  	config := server.ClientConfig
  1306  
  1307  	client, err := clientset.NewForConfig(config)
  1308  	if err != nil {
  1309  		t.Fatal(err)
  1310  	}
  1311  
  1312  	// only configmaps in default will be allowed.
  1313  	labelSelector := &metav1.LabelSelector{
  1314  		MatchExpressions: []metav1.LabelSelectorRequirement{
  1315  			{
  1316  				Key:      "kubernetes.io/metadata.name",
  1317  				Operator: "NotIn",
  1318  				Values:   []string{"default"},
  1319  			},
  1320  		},
  1321  	}
  1322  
  1323  	policy := withValidations([]admissionregistrationv1.Validation{
  1324  		{
  1325  			Expression: "false",
  1326  			Message:    "matched by namespace selector!",
  1327  		},
  1328  	}, withConfigMapMatch(withFailurePolicy(admissionregistrationv1.Fail, makePolicy("match-by-namespace-selector"))))
  1329  	policy = withNamespaceSelector(labelSelector, policy)
  1330  	policy = withWaitReadyConstraintAndExpression(policy)
  1331  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1332  	if err != nil {
  1333  		t.Fatal(err)
  1334  	}
  1335  
  1336  	policyBinding := makeBinding("match-by-namespace-selector-binding", "match-by-namespace-selector", "")
  1337  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Create(context.TODO(), policyBinding, metav1.CreateOptions{})
  1338  	if err != nil {
  1339  		t.Fatal(err)
  1340  	}
  1341  
  1342  	namespace := &v1.Namespace{
  1343  		ObjectMeta: metav1.ObjectMeta{
  1344  			Name: "not-default",
  1345  		},
  1346  	}
  1347  	if _, err := client.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{}); err != nil {
  1348  		t.Fatal(err)
  1349  	}
  1350  
  1351  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1352  		matchedConfigMap := &v1.ConfigMap{
  1353  			ObjectMeta: metav1.ObjectMeta{
  1354  				GenerateName: "denied-",
  1355  				Namespace:    "not-default",
  1356  			},
  1357  		}
  1358  
  1359  		_, err := client.CoreV1().ConfigMaps(matchedConfigMap.Namespace).Create(context.TODO(), matchedConfigMap, metav1.CreateOptions{})
  1360  		// policy not enforced yet, try again
  1361  		if err == nil {
  1362  			return false, nil
  1363  		}
  1364  
  1365  		if !strings.Contains(err.Error(), "matched by namespace selector!") {
  1366  			return false, err
  1367  		}
  1368  
  1369  		return true, nil
  1370  
  1371  	}); waitErr != nil {
  1372  		t.Errorf("timed out waiting: %v", waitErr)
  1373  	}
  1374  
  1375  	allowedConfigMap := &v1.ConfigMap{
  1376  		ObjectMeta: metav1.ObjectMeta{
  1377  			Name:      "allowed",
  1378  			Namespace: "default",
  1379  		},
  1380  	}
  1381  
  1382  	if _, err := client.CoreV1().ConfigMaps(allowedConfigMap.Namespace).Create(context.TODO(), allowedConfigMap, metav1.CreateOptions{}); err != nil {
  1383  		t.Errorf("unexpected error: %v", err)
  1384  	}
  1385  }
  1386  
  1387  func Test_ValidatingAdmissionPolicy_MatchByResourceNames(t *testing.T) {
  1388  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1389  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1390  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1391  	}, framework.SharedEtcd())
  1392  	if err != nil {
  1393  		t.Fatal(err)
  1394  	}
  1395  	defer server.TearDownFn()
  1396  
  1397  	config := server.ClientConfig
  1398  
  1399  	client, err := clientset.NewForConfig(config)
  1400  	if err != nil {
  1401  		t.Fatal(err)
  1402  	}
  1403  
  1404  	policy := withValidations([]admissionregistrationv1.Validation{
  1405  		{
  1406  			Expression: "false",
  1407  			Message:    "matched by resource names!",
  1408  		},
  1409  	}, withConfigMapMatch(withFailurePolicy(admissionregistrationv1.Fail, makePolicy("match-by-resource-names"))))
  1410  	policy.Spec.MatchConstraints.ResourceRules[0].ResourceNames = []string{"matched-by-resource-name"}
  1411  	policy = withWaitReadyConstraintAndExpression(policy)
  1412  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1413  	if err != nil {
  1414  		t.Fatal(err)
  1415  	}
  1416  
  1417  	policyBinding := makeBinding("match-by-resource-names-binding", "match-by-resource-names", "")
  1418  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  1419  		t.Fatal(err)
  1420  	}
  1421  
  1422  	matchedConfigMap := &v1.ConfigMap{
  1423  		ObjectMeta: metav1.ObjectMeta{
  1424  			Name:      "matched-by-resource-name",
  1425  			Namespace: "default",
  1426  		},
  1427  	}
  1428  
  1429  	_, err = client.CoreV1().ConfigMaps(matchedConfigMap.Namespace).Create(context.TODO(), matchedConfigMap, metav1.CreateOptions{})
  1430  	if !strings.Contains(err.Error(), "matched by resource names!") {
  1431  		t.Errorf("unexpected error: %v", err)
  1432  	}
  1433  
  1434  	allowedConfigMap := &v1.ConfigMap{
  1435  		ObjectMeta: metav1.ObjectMeta{
  1436  			Name:      "not-matched-by-resource-name",
  1437  			Namespace: "default",
  1438  		},
  1439  	}
  1440  
  1441  	if _, err := client.CoreV1().ConfigMaps(allowedConfigMap.Namespace).Create(context.TODO(), allowedConfigMap, metav1.CreateOptions{}); err != nil {
  1442  		t.Errorf("unexpected error: %v", err)
  1443  	}
  1444  }
  1445  
  1446  func Test_ValidatingAdmissionPolicy_MatchWithExcludeResources(t *testing.T) {
  1447  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1448  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1449  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1450  	}, framework.SharedEtcd())
  1451  	if err != nil {
  1452  		t.Fatal(err)
  1453  	}
  1454  	defer server.TearDownFn()
  1455  
  1456  	config := server.ClientConfig
  1457  
  1458  	client, err := clientset.NewForConfig(config)
  1459  	if err != nil {
  1460  		t.Fatal(err)
  1461  	}
  1462  
  1463  	policy := withValidations([]admissionregistrationv1.Validation{
  1464  		{
  1465  			Expression: "false",
  1466  			Message:    "not matched by exclude resources!",
  1467  		},
  1468  	}, withPolicyMatch("*", withFailurePolicy(admissionregistrationv1.Fail, makePolicy("match-by-resource-names"))))
  1469  
  1470  	policy = withExcludePolicyMatch("configmaps", policy)
  1471  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1472  	if err != nil {
  1473  		t.Fatal(err)
  1474  	}
  1475  
  1476  	policyBinding := makeBinding("match-by-resource-names-binding", "match-by-resource-names", "")
  1477  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Create(context.TODO(), policyBinding, metav1.CreateOptions{})
  1478  	if err != nil {
  1479  		t.Fatal(err)
  1480  	}
  1481  
  1482  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1483  		secret := &v1.Secret{
  1484  			ObjectMeta: metav1.ObjectMeta{
  1485  				GenerateName: "not-matched-by-exclude-resources",
  1486  				Namespace:    "default",
  1487  			},
  1488  		}
  1489  
  1490  		_, err := client.CoreV1().Secrets(secret.Namespace).Create(context.TODO(), secret, metav1.CreateOptions{})
  1491  		// policy not enforced yet, try again
  1492  		if err == nil {
  1493  			return false, nil
  1494  		}
  1495  
  1496  		if !strings.Contains(err.Error(), "not matched by exclude resources!") {
  1497  			return false, err
  1498  		}
  1499  
  1500  		return true, nil
  1501  
  1502  	}); waitErr != nil {
  1503  		t.Errorf("timed out waiting: %v", waitErr)
  1504  	}
  1505  
  1506  	allowedConfigMap := &v1.ConfigMap{
  1507  		ObjectMeta: metav1.ObjectMeta{
  1508  			Name:      "matched-by-exclude-resources",
  1509  			Namespace: "default",
  1510  		},
  1511  	}
  1512  
  1513  	if _, err := client.CoreV1().ConfigMaps(allowedConfigMap.Namespace).Create(context.TODO(), allowedConfigMap, metav1.CreateOptions{}); err != nil {
  1514  		t.Errorf("unexpected error: %v", err)
  1515  	}
  1516  }
  1517  
  1518  func Test_ValidatingAdmissionPolicy_MatchWithMatchPolicyEquivalent(t *testing.T) {
  1519  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1520  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1521  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1522  	}, framework.SharedEtcd())
  1523  	etcd.CreateTestCRDs(t, apiextensionsclientset.NewForConfigOrDie(server.ClientConfig), false, versionedCustomResourceDefinition())
  1524  	if err != nil {
  1525  		t.Fatal(err)
  1526  	}
  1527  	defer server.TearDownFn()
  1528  
  1529  	config := server.ClientConfig
  1530  
  1531  	client, err := clientset.NewForConfig(config)
  1532  	if err != nil {
  1533  		t.Fatal(err)
  1534  	}
  1535  
  1536  	policy := withValidations([]admissionregistrationv1.Validation{
  1537  		{
  1538  			Expression: "false",
  1539  			Message:    "matched by equivalent match policy!",
  1540  		},
  1541  	}, withFailurePolicy(admissionregistrationv1.Fail, makePolicy("match-by-match-policy-equivalent")))
  1542  	policy.Spec.MatchConstraints = &admissionregistrationv1.MatchResources{
  1543  		ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{
  1544  			{
  1545  				RuleWithOperations: admissionregistrationv1.RuleWithOperations{
  1546  					Operations: []admissionregistrationv1.OperationType{
  1547  						"*",
  1548  					},
  1549  					Rule: admissionregistrationv1.Rule{
  1550  						APIGroups: []string{
  1551  							"awesome.bears.com",
  1552  						},
  1553  						APIVersions: []string{
  1554  							"v1",
  1555  						},
  1556  						Resources: []string{
  1557  							"pandas",
  1558  						},
  1559  					},
  1560  				},
  1561  			},
  1562  		},
  1563  	}
  1564  	policy = withWaitReadyConstraintAndExpression(policy)
  1565  	if _, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
  1566  		t.Fatal(err)
  1567  	}
  1568  
  1569  	policyBinding := makeBinding("match-by-match-policy-equivalent-binding", "match-by-match-policy-equivalent", "")
  1570  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  1571  		t.Fatal(err)
  1572  	}
  1573  
  1574  	v1Resource := &unstructured.Unstructured{
  1575  		Object: map[string]interface{}{
  1576  			"apiVersion": "awesome.bears.com" + "/" + "v1",
  1577  			"kind":       "Panda",
  1578  			"metadata": map[string]interface{}{
  1579  				"name": "v1-bears",
  1580  			},
  1581  		},
  1582  	}
  1583  
  1584  	v2Resource := &unstructured.Unstructured{
  1585  		Object: map[string]interface{}{
  1586  			"apiVersion": "awesome.bears.com" + "/" + "v2",
  1587  			"kind":       "Panda",
  1588  			"metadata": map[string]interface{}{
  1589  				"name": "v2-bears",
  1590  			},
  1591  		},
  1592  	}
  1593  
  1594  	dynamicClient, err := dynamic.NewForConfig(config)
  1595  	if err != nil {
  1596  		t.Fatal(err)
  1597  	}
  1598  
  1599  	_, err = dynamicClient.Resource(schema.GroupVersionResource{Group: "awesome.bears.com", Version: "v1", Resource: "pandas"}).Create(context.TODO(), v1Resource, metav1.CreateOptions{})
  1600  	if !strings.Contains(err.Error(), "matched by equivalent match policy!") {
  1601  		t.Errorf("v1 panadas did not match against policy, err: %v", err)
  1602  	}
  1603  
  1604  	_, err = dynamicClient.Resource(schema.GroupVersionResource{Group: "awesome.bears.com", Version: "v2", Resource: "pandas"}).Create(context.TODO(), v2Resource, metav1.CreateOptions{})
  1605  	if !strings.Contains(err.Error(), "matched by equivalent match policy!") {
  1606  		t.Errorf("v2 panadas did not match against policy, err: %v", err)
  1607  	}
  1608  }
  1609  
  1610  func Test_ValidatingAdmissionPolicy_MatchWithMatchPolicyExact(t *testing.T) {
  1611  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1612  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1613  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1614  	}, framework.SharedEtcd())
  1615  	etcd.CreateTestCRDs(t, apiextensionsclientset.NewForConfigOrDie(server.ClientConfig), false, versionedCustomResourceDefinition())
  1616  	if err != nil {
  1617  		t.Fatal(err)
  1618  	}
  1619  	defer server.TearDownFn()
  1620  
  1621  	config := server.ClientConfig
  1622  
  1623  	client, err := clientset.NewForConfig(config)
  1624  	if err != nil {
  1625  		t.Fatal(err)
  1626  	}
  1627  
  1628  	policy := withValidations([]admissionregistrationv1.Validation{
  1629  		{
  1630  			Expression: "false",
  1631  			Message:    "matched by exact match policy!",
  1632  		},
  1633  	}, withFailurePolicy(admissionregistrationv1.Fail, makePolicy("match-by-match-policy-exact")))
  1634  	matchPolicyExact := admissionregistrationv1.Exact
  1635  	policy.Spec.MatchConstraints = &admissionregistrationv1.MatchResources{
  1636  		MatchPolicy: &matchPolicyExact,
  1637  		ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{
  1638  			{
  1639  				RuleWithOperations: admissionregistrationv1.RuleWithOperations{
  1640  					Operations: []admissionregistrationv1.OperationType{
  1641  						"*",
  1642  					},
  1643  					Rule: admissionregistrationv1.Rule{
  1644  						APIGroups: []string{
  1645  							"awesome.bears.com",
  1646  						},
  1647  						APIVersions: []string{
  1648  							"v1",
  1649  						},
  1650  						Resources: []string{
  1651  							"pandas",
  1652  						},
  1653  					},
  1654  				},
  1655  			},
  1656  		},
  1657  	}
  1658  	policy = withWaitReadyConstraintAndExpression(policy)
  1659  	if _, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
  1660  		t.Fatal(err)
  1661  	}
  1662  
  1663  	policyBinding := makeBinding("match-by-match-policy-exact-binding", "match-by-match-policy-exact", "")
  1664  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  1665  		t.Fatal(err)
  1666  	}
  1667  
  1668  	v1Resource := &unstructured.Unstructured{
  1669  		Object: map[string]interface{}{
  1670  			"apiVersion": "awesome.bears.com" + "/" + "v1",
  1671  			"kind":       "Panda",
  1672  			"metadata": map[string]interface{}{
  1673  				"name": "v1-bears",
  1674  			},
  1675  		},
  1676  	}
  1677  
  1678  	v2Resource := &unstructured.Unstructured{
  1679  		Object: map[string]interface{}{
  1680  			"apiVersion": "awesome.bears.com" + "/" + "v2",
  1681  			"kind":       "Panda",
  1682  			"metadata": map[string]interface{}{
  1683  				"name": "v2-bears",
  1684  			},
  1685  		},
  1686  	}
  1687  
  1688  	dynamicClient, err := dynamic.NewForConfig(config)
  1689  	if err != nil {
  1690  		t.Fatal(err)
  1691  	}
  1692  
  1693  	_, err = dynamicClient.Resource(schema.GroupVersionResource{Group: "awesome.bears.com", Version: "v1", Resource: "pandas"}).Create(context.TODO(), v1Resource, metav1.CreateOptions{})
  1694  	if !strings.Contains(err.Error(), "matched by exact match policy!") {
  1695  		t.Errorf("v1 panadas did not match against policy, err: %v", err)
  1696  	}
  1697  
  1698  	// v2 panadas is allowed since policy specificed match policy Exact and only matched against v1
  1699  	_, err = dynamicClient.Resource(schema.GroupVersionResource{Group: "awesome.bears.com", Version: "v2", Resource: "pandas"}).Create(context.TODO(), v2Resource, metav1.CreateOptions{})
  1700  	if err != nil {
  1701  		t.Error(err)
  1702  	}
  1703  }
  1704  
  1705  func Test_ValidatingAdmissionPolicy_MatchExcludedResource(t *testing.T) {
  1706  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1707  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1708  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1709  	}, framework.SharedEtcd())
  1710  	if err != nil {
  1711  		t.Fatal(err)
  1712  	}
  1713  	defer server.TearDownFn()
  1714  
  1715  	config := server.ClientConfig
  1716  
  1717  	client, err := clientset.NewForConfig(config)
  1718  	if err != nil {
  1719  		t.Fatal(err)
  1720  	}
  1721  
  1722  	policy := withValidations([]admissionregistrationv1.Validation{
  1723  		{
  1724  			Expression: "false",
  1725  			Message:    "try to deny SelfSubjectReview",
  1726  		},
  1727  	}, withFailurePolicy(admissionregistrationv1.Fail, makePolicy("match-excluded-resources")))
  1728  	policy.Spec.MatchConstraints = &admissionregistrationv1.MatchResources{
  1729  		MatchPolicy: ptr.To(admissionregistrationv1.Exact),
  1730  		ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{
  1731  			{
  1732  				RuleWithOperations: admissionregistrationv1.RuleWithOperations{
  1733  					Operations: []admissionregistrationv1.OperationType{
  1734  						"*",
  1735  					},
  1736  					Rule: admissionregistrationv1.Rule{
  1737  						APIGroups: []string{
  1738  							"authentication.k8s.io",
  1739  						},
  1740  						APIVersions: []string{
  1741  							"v1",
  1742  						},
  1743  						Resources: []string{
  1744  							"selfsubjectreviews",
  1745  						},
  1746  					},
  1747  				},
  1748  			},
  1749  		},
  1750  	}
  1751  	policy = withWaitReadyConstraintAndExpression(policy)
  1752  	if _, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.Background(), policy, metav1.CreateOptions{}); err != nil {
  1753  		t.Fatalf("fail to create policy: %v", err)
  1754  	}
  1755  
  1756  	policyBinding := makeBinding("match-by-match-policy-exact-binding", "match-excluded-resources", "")
  1757  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  1758  		t.Fatalf("fail to create and wait for binding: %v", err)
  1759  	}
  1760  	r, err := client.AuthenticationV1().SelfSubjectReviews().Create(context.Background(), &authenticationv1.SelfSubjectReview{}, metav1.CreateOptions{})
  1761  	if err != nil {
  1762  		t.Fatalf("unexpected denied SelfSubjectReview: %v", err)
  1763  	}
  1764  	// confidence check the returned user info.
  1765  	if len(r.Status.UserInfo.UID) == 0 {
  1766  		t.Errorf("unexpected invalid user info: %v", r.Status.UserInfo)
  1767  	}
  1768  }
  1769  
  1770  // Test_ValidatingAdmissionPolicy_PolicyDeletedThenRecreated validates that deleting a ValidatingAdmissionPolicy
  1771  // removes the policy from the apiserver admission chain and recreating it re-enables it.
  1772  func Test_ValidatingAdmissionPolicy_PolicyDeletedThenRecreated(t *testing.T) {
  1773  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1774  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1775  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1776  	}, framework.SharedEtcd())
  1777  	if err != nil {
  1778  		t.Fatal(err)
  1779  	}
  1780  	defer server.TearDownFn()
  1781  
  1782  	config := server.ClientConfig
  1783  
  1784  	client, err := clientset.NewForConfig(config)
  1785  	if err != nil {
  1786  		t.Fatal(err)
  1787  	}
  1788  
  1789  	policy := withValidations([]admissionregistrationv1.Validation{
  1790  		{
  1791  			Expression: "object.metadata.name.startsWith('test')",
  1792  			Message:    "wrong prefix",
  1793  		},
  1794  	}, withParams(configParamKind(), withNamespaceMatch(withFailurePolicy(admissionregistrationv1.Fail, makePolicy("allowed-prefixes")))))
  1795  	policy = withWaitReadyConstraintAndExpression(policy)
  1796  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1797  	if err != nil {
  1798  		t.Fatal(err)
  1799  	}
  1800  
  1801  	// validate that namespaces starting with "test" are allowed
  1802  	policyBinding := makeBinding("allowed-prefixes-binding", "allowed-prefixes", "")
  1803  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  1804  		t.Fatal(err)
  1805  	}
  1806  
  1807  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1808  		disallowedNamespace := &v1.Namespace{
  1809  			ObjectMeta: metav1.ObjectMeta{
  1810  				GenerateName: "not-test-",
  1811  			},
  1812  		}
  1813  
  1814  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1815  		if err == nil {
  1816  			return false, nil
  1817  		}
  1818  
  1819  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1820  			return false, nil
  1821  		}
  1822  
  1823  		if !strings.Contains(err.Error(), "wrong prefix") {
  1824  			return false, err
  1825  		}
  1826  
  1827  		return true, nil
  1828  	}); waitErr != nil {
  1829  		t.Errorf("timed out waiting: %v", err)
  1830  	}
  1831  
  1832  	// delete the binding object and validate that policy is not enforced
  1833  	if err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Delete(context.TODO(), "allowed-prefixes", metav1.DeleteOptions{}); err != nil {
  1834  		t.Fatal(err)
  1835  	}
  1836  
  1837  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1838  		allowedNamespace := &v1.Namespace{
  1839  			ObjectMeta: metav1.ObjectMeta{
  1840  				GenerateName: "not-test-",
  1841  			},
  1842  		}
  1843  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
  1844  		if err == nil {
  1845  			return true, nil
  1846  		}
  1847  
  1848  		// old policy is still enforced, try again
  1849  		if strings.Contains(err.Error(), "wrong prefix") {
  1850  			return false, nil
  1851  		}
  1852  
  1853  		return false, err
  1854  	}); waitErr != nil {
  1855  		t.Errorf("timed out waiting: %v", err)
  1856  	}
  1857  
  1858  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1859  	if err != nil {
  1860  		t.Fatal(err)
  1861  	}
  1862  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1863  		disallowedNamespace := &v1.Namespace{
  1864  			ObjectMeta: metav1.ObjectMeta{
  1865  				GenerateName: "not-test-",
  1866  			},
  1867  		}
  1868  
  1869  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1870  		if err == nil {
  1871  			return false, nil
  1872  		}
  1873  
  1874  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1875  			return false, nil
  1876  		}
  1877  
  1878  		if !strings.Contains(err.Error(), "wrong prefix") {
  1879  			return false, err
  1880  		}
  1881  
  1882  		return true, nil
  1883  	}); waitErr != nil {
  1884  		t.Errorf("timed out waiting: %v", err)
  1885  	}
  1886  }
  1887  
  1888  // Test_ValidatingAdmissionPolicy_BindingDeletedThenRecreated validates that deleting a ValidatingAdmissionPolicyBinding
  1889  // removes the policy from the apiserver admission chain and recreating it re-enables it.
  1890  func Test_ValidatingAdmissionPolicy_BindingDeletedThenRecreated(t *testing.T) {
  1891  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1892  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1893  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1894  	}, framework.SharedEtcd())
  1895  	if err != nil {
  1896  		t.Fatal(err)
  1897  	}
  1898  	defer server.TearDownFn()
  1899  
  1900  	config := server.ClientConfig
  1901  
  1902  	client, err := clientset.NewForConfig(config)
  1903  	if err != nil {
  1904  		t.Fatal(err)
  1905  	}
  1906  
  1907  	policy := withValidations([]admissionregistrationv1.Validation{
  1908  		{
  1909  			Expression: "object.metadata.name.startsWith('test')",
  1910  			Message:    "wrong prefix",
  1911  		},
  1912  	}, withParams(configParamKind(), withNamespaceMatch(withFailurePolicy(admissionregistrationv1.Fail, makePolicy("allowed-prefixes")))))
  1913  	policy = withWaitReadyConstraintAndExpression(policy)
  1914  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1915  	if err != nil {
  1916  		t.Fatal(err)
  1917  	}
  1918  
  1919  	// validate that namespaces starting with "test" are allowed
  1920  	policyBinding := makeBinding("allowed-prefixes-binding", "allowed-prefixes", "")
  1921  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  1922  		t.Fatal(err)
  1923  	}
  1924  
  1925  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1926  		disallowedNamespace := &v1.Namespace{
  1927  			ObjectMeta: metav1.ObjectMeta{
  1928  				GenerateName: "not-test-",
  1929  			},
  1930  		}
  1931  
  1932  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1933  		if err == nil {
  1934  			return false, nil
  1935  		}
  1936  
  1937  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1938  			return false, nil
  1939  		}
  1940  
  1941  		if !strings.Contains(err.Error(), "wrong prefix") {
  1942  			return false, err
  1943  		}
  1944  
  1945  		return true, nil
  1946  	}); waitErr != nil {
  1947  		t.Errorf("timed out waiting: %v", err)
  1948  	}
  1949  
  1950  	// delete the binding object and validate that policy is not enforced
  1951  	if err := client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Delete(context.TODO(), "allowed-prefixes-binding", metav1.DeleteOptions{}); err != nil {
  1952  		t.Fatal(err)
  1953  	}
  1954  
  1955  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1956  		allowedNamespace := &v1.Namespace{
  1957  			ObjectMeta: metav1.ObjectMeta{
  1958  				GenerateName: "not-test-",
  1959  			},
  1960  		}
  1961  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
  1962  		if err == nil {
  1963  			return true, nil
  1964  		}
  1965  
  1966  		// old policy is still enforced, try again
  1967  		if strings.Contains(err.Error(), "wrong prefix") {
  1968  			return false, nil
  1969  		}
  1970  
  1971  		return false, err
  1972  	}); waitErr != nil {
  1973  		t.Errorf("timed out waiting: %v", err)
  1974  	}
  1975  
  1976  	// recreate the policy binding and test that policy is enforced again
  1977  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Create(context.TODO(), policyBinding, metav1.CreateOptions{})
  1978  	if err != nil {
  1979  		t.Fatal(err)
  1980  	}
  1981  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1982  		disallowedNamespace := &v1.Namespace{
  1983  			ObjectMeta: metav1.ObjectMeta{
  1984  				GenerateName: "not-test-",
  1985  			},
  1986  		}
  1987  
  1988  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1989  		if err == nil {
  1990  			return false, nil
  1991  		}
  1992  
  1993  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1994  			return false, nil
  1995  		}
  1996  
  1997  		if !strings.Contains(err.Error(), "wrong prefix") {
  1998  			return false, err
  1999  		}
  2000  
  2001  		return true, nil
  2002  	}); waitErr != nil {
  2003  		t.Errorf("timed out waiting: %v", err)
  2004  	}
  2005  }
  2006  
  2007  // Test_ValidatingAdmissionPolicy_ParamResourceDeletedThenRecreated validates that deleting a param resource referenced
  2008  // by a binding renders the policy as invalid. Recreating the param resource re-enables the policy.
  2009  func Test_ValidatingAdmissionPolicy_ParamResourceDeletedThenRecreated(t *testing.T) {
  2010  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  2011  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  2012  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2013  	}, framework.SharedEtcd())
  2014  	if err != nil {
  2015  		t.Fatal(err)
  2016  	}
  2017  	defer server.TearDownFn()
  2018  
  2019  	config := server.ClientConfig
  2020  
  2021  	client, err := clientset.NewForConfig(config)
  2022  	if err != nil {
  2023  		t.Fatal(err)
  2024  	}
  2025  
  2026  	param := &v1.ConfigMap{
  2027  		ObjectMeta: metav1.ObjectMeta{
  2028  			Name:      "test",
  2029  			Namespace: "default",
  2030  		},
  2031  	}
  2032  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), param, metav1.CreateOptions{}); err != nil {
  2033  		t.Fatal(err)
  2034  	}
  2035  
  2036  	policy := withValidations([]admissionregistrationv1.Validation{
  2037  		{
  2038  			Expression: "object.metadata.name.startsWith(params.metadata.name)",
  2039  			Message:    "wrong prefix",
  2040  		},
  2041  	}, withParams(configParamKind(), withNamespaceMatch(withFailurePolicy(admissionregistrationv1.Fail, makePolicy("allowed-prefixes")))))
  2042  	policy = withWaitReadyConstraintAndExpression(policy)
  2043  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  2044  	if err != nil {
  2045  		t.Fatal(err)
  2046  	}
  2047  
  2048  	// validate that namespaces starting with "test" are allowed
  2049  	policyBinding := makeBinding("allowed-prefixes-binding", "allowed-prefixes", "test")
  2050  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  2051  		t.Fatal(err)
  2052  	}
  2053  
  2054  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  2055  		disallowedNamespace := &v1.Namespace{
  2056  			ObjectMeta: metav1.ObjectMeta{
  2057  				GenerateName: "not-test-",
  2058  			},
  2059  		}
  2060  
  2061  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  2062  		if err == nil {
  2063  			return false, nil
  2064  		}
  2065  
  2066  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  2067  			return false, nil
  2068  		}
  2069  
  2070  		if !strings.Contains(err.Error(), "wrong prefix") {
  2071  			return false, err
  2072  		}
  2073  
  2074  		return true, nil
  2075  	}); waitErr != nil {
  2076  		t.Errorf("timed out waiting: %v", err)
  2077  	}
  2078  
  2079  	// delete param object and validate that policy is invalid
  2080  	if err := client.CoreV1().ConfigMaps("default").Delete(context.TODO(), "test", metav1.DeleteOptions{}); err != nil {
  2081  		t.Fatal(err)
  2082  	}
  2083  
  2084  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  2085  		allowedNamespace := &v1.Namespace{
  2086  			ObjectMeta: metav1.ObjectMeta{
  2087  				GenerateName: "not-test-",
  2088  			},
  2089  		}
  2090  
  2091  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
  2092  		// old policy is still enforced, try again
  2093  		if strings.Contains(err.Error(), "wrong prefix") {
  2094  			return false, nil
  2095  		}
  2096  
  2097  		if !strings.Contains(err.Error(), "failed to configure binding: no params found for policy binding with `Deny` parameterNotFoundAction") {
  2098  			return false, err
  2099  		}
  2100  
  2101  		return true, nil
  2102  	}); waitErr != nil {
  2103  		t.Errorf("timed out waiting: %v", err)
  2104  	}
  2105  
  2106  	// recreate the param resource and validate namespace is disallowed again
  2107  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), param, metav1.CreateOptions{}); err != nil {
  2108  		t.Fatal(err)
  2109  	}
  2110  
  2111  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  2112  		disallowedNamespace := &v1.Namespace{
  2113  			ObjectMeta: metav1.ObjectMeta{
  2114  				GenerateName: "not-test-",
  2115  			},
  2116  		}
  2117  
  2118  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  2119  		// cache not synced with new object yet, try again
  2120  		if strings.Contains(err.Error(), "failed to configure binding: no params found for policy binding with `Deny` parameterNotFoundAction") {
  2121  			return false, nil
  2122  		}
  2123  
  2124  		if !strings.Contains(err.Error(), "wrong prefix") {
  2125  			return false, err
  2126  		}
  2127  
  2128  		return true, nil
  2129  	}); waitErr != nil {
  2130  		t.Errorf("timed out waiting: %v", err)
  2131  	}
  2132  }
  2133  
  2134  // TestCRDParams tests that a CustomResource can be used as a param resource for a ValidatingAdmissionPolicy.
  2135  func TestCRDParams(t *testing.T) {
  2136  	testcases := []struct {
  2137  		name          string
  2138  		resource      *unstructured.Unstructured
  2139  		policy        *admissionregistrationv1.ValidatingAdmissionPolicy
  2140  		policyBinding *admissionregistrationv1.ValidatingAdmissionPolicyBinding
  2141  		namespace     *v1.Namespace
  2142  		err           string
  2143  		failureReason metav1.StatusReason
  2144  	}{
  2145  		{
  2146  			name: "a rule that uses data from a CRD param resource does NOT pass",
  2147  			resource: &unstructured.Unstructured{Object: map[string]interface{}{
  2148  				"apiVersion": "awesome.bears.com/v1",
  2149  				"kind":       "Panda",
  2150  				"metadata": map[string]interface{}{
  2151  					"name": "config-obj",
  2152  				},
  2153  				"spec": map[string]interface{}{
  2154  					"nameCheck": "crd-test-k8s",
  2155  				},
  2156  			}},
  2157  			policy: withValidations([]admissionregistrationv1.Validation{
  2158  				{
  2159  					Expression: "params.spec.nameCheck == object.metadata.name",
  2160  				},
  2161  			}, withNamespaceMatch(withParams(withCRDParamKind("Panda", "awesome.bears.com", "v1"), withFailurePolicy(admissionregistrationv1.Fail, makePolicy("test-policy"))))),
  2162  			policyBinding: makeBinding("crd-policy-binding", "test-policy", "config-obj"),
  2163  			namespace: &v1.Namespace{
  2164  				ObjectMeta: metav1.ObjectMeta{
  2165  					Name: "incorrect-name",
  2166  				},
  2167  			},
  2168  			err:           `namespaces "incorrect-name" is forbidden: ValidatingAdmissionPolicy 'test-policy' with binding 'crd-policy-binding' denied request: failed expression: params.spec.nameCheck == object.metadata.name`,
  2169  			failureReason: metav1.StatusReasonInvalid,
  2170  		},
  2171  		{
  2172  			name: "a rule that uses data from a CRD param resource that does pass",
  2173  			resource: &unstructured.Unstructured{Object: map[string]interface{}{
  2174  				"apiVersion": "awesome.bears.com/v1",
  2175  				"kind":       "Panda",
  2176  				"metadata": map[string]interface{}{
  2177  					"name": "config-obj",
  2178  				},
  2179  				"spec": map[string]interface{}{
  2180  					"nameCheck": "crd-test-k8s",
  2181  				},
  2182  			}},
  2183  			policy: withValidations([]admissionregistrationv1.Validation{
  2184  				{
  2185  					Expression: "params.spec.nameCheck == object.metadata.name",
  2186  				},
  2187  			}, withNamespaceMatch(withParams(withCRDParamKind("Panda", "awesome.bears.com", "v1"), withFailurePolicy(admissionregistrationv1.Fail, makePolicy("test-policy"))))),
  2188  			policyBinding: makeBinding("crd-policy-binding", "test-policy", "config-obj"),
  2189  			namespace: &v1.Namespace{
  2190  				ObjectMeta: metav1.ObjectMeta{
  2191  					Name: "crd-test-k8s",
  2192  				},
  2193  			},
  2194  			err: ``,
  2195  		},
  2196  	}
  2197  
  2198  	for _, testcase := range testcases {
  2199  		t.Run(testcase.name, func(t *testing.T) {
  2200  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  2201  			server, err := apiservertesting.StartTestServer(t, nil, []string{
  2202  				"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2203  			}, framework.SharedEtcd())
  2204  			if err != nil {
  2205  				t.Fatal(err)
  2206  			}
  2207  			defer server.TearDownFn()
  2208  
  2209  			config := server.ClientConfig
  2210  
  2211  			client, err := clientset.NewForConfig(config)
  2212  			if err != nil {
  2213  				t.Fatal(err)
  2214  			}
  2215  
  2216  			crd := versionedCustomResourceDefinition()
  2217  			etcd.CreateTestCRDs(t, apiextensionsclientset.NewForConfigOrDie(server.ClientConfig), false, crd)
  2218  			dynamicClient, err := dynamic.NewForConfig(config)
  2219  			if err != nil {
  2220  				t.Fatal(err)
  2221  			}
  2222  			gvr := schema.GroupVersionResource{
  2223  				Group:    crd.Spec.Group,
  2224  				Version:  crd.Spec.Versions[0].Name,
  2225  				Resource: crd.Spec.Names.Plural,
  2226  			}
  2227  			crClient := dynamicClient.Resource(gvr)
  2228  			_, err = crClient.Create(context.TODO(), testcase.resource, metav1.CreateOptions{})
  2229  			if err != nil {
  2230  				t.Fatalf("error creating %s: %s", gvr, err)
  2231  			}
  2232  
  2233  			policy := withWaitReadyConstraintAndExpression(testcase.policy)
  2234  			if _, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
  2235  				t.Fatal(err)
  2236  			}
  2237  			// remove default namespace since the CRD is cluster-scoped
  2238  			testcase.policyBinding.Spec.ParamRef.Namespace = ""
  2239  			if err := createAndWaitReady(t, client, testcase.policyBinding, nil); err != nil {
  2240  				t.Fatal(err)
  2241  			}
  2242  
  2243  			_, err = client.CoreV1().Namespaces().Create(context.TODO(), testcase.namespace, metav1.CreateOptions{})
  2244  
  2245  			checkExpectedError(t, err, testcase.err)
  2246  			checkFailureReason(t, err, testcase.failureReason)
  2247  		})
  2248  	}
  2249  }
  2250  
  2251  func TestBindingRemoval(t *testing.T) {
  2252  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  2253  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  2254  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2255  	}, framework.SharedEtcd())
  2256  	if err != nil {
  2257  		t.Fatal(err)
  2258  	}
  2259  	defer server.TearDownFn()
  2260  
  2261  	config := server.ClientConfig
  2262  
  2263  	client, err := clientset.NewForConfig(config)
  2264  	if err != nil {
  2265  		t.Fatal(err)
  2266  	}
  2267  
  2268  	policy := withValidations([]admissionregistrationv1.Validation{
  2269  		{
  2270  			Expression: "false",
  2271  			Message:    "policy still in effect",
  2272  		},
  2273  	}, withNamespaceMatch(withFailurePolicy(admissionregistrationv1.Fail, makePolicy("test-policy"))))
  2274  	policy = withWaitReadyConstraintAndExpression(policy)
  2275  	if _, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
  2276  		t.Fatal(err)
  2277  	}
  2278  
  2279  	binding := makeBinding("test-binding", "test-policy", "test-params")
  2280  	if err := createAndWaitReady(t, client, binding, nil); err != nil {
  2281  		t.Fatal(err)
  2282  	}
  2283  	// check that the policy is active
  2284  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  2285  		namespace := &v1.Namespace{
  2286  			ObjectMeta: metav1.ObjectMeta{
  2287  				GenerateName: "check-namespace",
  2288  			},
  2289  		}
  2290  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{})
  2291  		if err != nil {
  2292  			if strings.Contains(err.Error(), "policy still in effect") {
  2293  				return true, nil
  2294  			} else {
  2295  				// unexpected error while attempting namespace creation
  2296  				return true, err
  2297  			}
  2298  		}
  2299  		return false, nil
  2300  	}); waitErr != nil {
  2301  		t.Errorf("timed out waiting: %v", waitErr)
  2302  	}
  2303  	if err = client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Delete(context.TODO(), "test-binding", metav1.DeleteOptions{}); err != nil {
  2304  		t.Fatal(err)
  2305  	}
  2306  
  2307  	// wait for binding to be deleted
  2308  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  2309  
  2310  		_, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Get(context.TODO(), "test-binding", metav1.GetOptions{})
  2311  		if err != nil {
  2312  			if apierrors.IsNotFound(err) {
  2313  				return true, nil
  2314  			} else {
  2315  				return true, err
  2316  			}
  2317  		}
  2318  
  2319  		return false, nil
  2320  	}); waitErr != nil {
  2321  		t.Errorf("timed out waiting: %v", waitErr)
  2322  	}
  2323  
  2324  	// policy should be considered in an invalid state and namespace creation should be allowed
  2325  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  2326  		namespace := &v1.Namespace{
  2327  			ObjectMeta: metav1.ObjectMeta{
  2328  				GenerateName: "test-namespace",
  2329  			},
  2330  		}
  2331  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{})
  2332  		if err != nil {
  2333  			t.Logf("namespace creation failed: %s", err)
  2334  			return false, nil
  2335  		}
  2336  
  2337  		return true, nil
  2338  	}); waitErr != nil {
  2339  		t.Errorf("expected namespace creation to succeed but timed out waiting: %v", waitErr)
  2340  	}
  2341  }
  2342  
  2343  // Test_ValidateSecondaryAuthorization tests a ValidatingAdmissionPolicy that performs secondary authorization checks
  2344  // for both users and service accounts.
  2345  func Test_ValidateSecondaryAuthorization(t *testing.T) {
  2346  	testcases := []struct {
  2347  		name             string
  2348  		rbac             *rbacv1.PolicyRule
  2349  		expression       string
  2350  		allowed          bool
  2351  		extraAccountFn   func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset
  2352  		extraAccountRbac *rbacv1.PolicyRule
  2353  	}{
  2354  		{
  2355  			name: "principal is allowed to create a specific deployment",
  2356  			rbac: &rbacv1.PolicyRule{
  2357  				Verbs:         []string{"create"},
  2358  				APIGroups:     []string{"apps"},
  2359  				Resources:     []string{"deployments/status"},
  2360  				ResourceNames: []string{"charmander"},
  2361  			},
  2362  			expression: "authorizer.group('apps').resource('deployments').subresource('status').namespace('default').namespace('default').name('charmander').check('create').allowed()",
  2363  			allowed:    true,
  2364  		},
  2365  		{
  2366  			name:       "principal is not allowed to create a specific deployment",
  2367  			expression: "authorizer.group('apps').resource('deployments').subresource('status').namespace('default').name('charmander').check('create').allowed()",
  2368  			allowed:    false,
  2369  		},
  2370  		{
  2371  			name: "principal is authorized for custom verb on current resource",
  2372  			rbac: &rbacv1.PolicyRule{
  2373  				Verbs:     []string{"anthropomorphize"},
  2374  				APIGroups: []string{""},
  2375  				Resources: []string{"namespaces"},
  2376  			},
  2377  			expression: "authorizer.requestResource.check('anthropomorphize').allowed()",
  2378  			allowed:    true,
  2379  		},
  2380  		{
  2381  			name:       "principal is not authorized for custom verb on current resource",
  2382  			expression: "authorizer.requestResource.check('anthropomorphize').allowed()",
  2383  			allowed:    false,
  2384  		},
  2385  		{
  2386  			name:           "serviceaccount is authorized for custom verb on current resource",
  2387  			extraAccountFn: serviceAccountClient("default", "extra-acct"),
  2388  			extraAccountRbac: &rbacv1.PolicyRule{
  2389  				Verbs:     []string{"anthropomorphize"},
  2390  				APIGroups: []string{""},
  2391  				Resources: []string{"pods"},
  2392  			},
  2393  			expression: "authorizer.serviceAccount('default', 'extra-acct').group('').resource('pods').check('anthropomorphize').allowed()",
  2394  			allowed:    true,
  2395  		},
  2396  	}
  2397  
  2398  	for _, testcase := range testcases {
  2399  		t.Run(testcase.name, func(t *testing.T) {
  2400  			clients := map[string]func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset{
  2401  				"user":           secondaryAuthorizationUserClient,
  2402  				"serviceaccount": secondaryAuthorizationServiceAccountClient,
  2403  			}
  2404  
  2405  			for clientName, clientFn := range clients {
  2406  				t.Run(clientName, func(t *testing.T) {
  2407  					defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  2408  					server, err := apiservertesting.StartTestServer(t, nil, []string{
  2409  						"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2410  						"--authorization-mode=RBAC",
  2411  						"--anonymous-auth",
  2412  					}, framework.SharedEtcd())
  2413  					if err != nil {
  2414  						t.Fatal(err)
  2415  					}
  2416  					defer server.TearDownFn()
  2417  
  2418  					// For test set up such as creating policies, bindings and RBAC rules.
  2419  					adminClient := clientset.NewForConfigOrDie(server.ClientConfig)
  2420  
  2421  					// Principal is always allowed to create and update namespaces so that the admission requests to test
  2422  					// authorization expressions can be sent by the principal.
  2423  					rules := []rbacv1.PolicyRule{{
  2424  						Verbs:     []string{"create", "update"},
  2425  						APIGroups: []string{""},
  2426  						Resources: []string{"namespaces"},
  2427  					}}
  2428  					if testcase.rbac != nil {
  2429  						rules = append(rules, *testcase.rbac)
  2430  					}
  2431  
  2432  					client := clientFn(t, adminClient, server.ClientConfig, rules)
  2433  
  2434  					if testcase.extraAccountFn != nil {
  2435  						var extraRules []rbacv1.PolicyRule
  2436  						if testcase.extraAccountRbac != nil {
  2437  							extraRules = append(rules, *testcase.extraAccountRbac)
  2438  						}
  2439  						testcase.extraAccountFn(t, adminClient, server.ClientConfig, extraRules)
  2440  					}
  2441  
  2442  					policy := withWaitReadyConstraintAndExpression(withValidations([]admissionregistrationv1.Validation{
  2443  						{
  2444  							Expression: testcase.expression,
  2445  						},
  2446  					}, withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-authz")))))
  2447  					if _, err := adminClient.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
  2448  						t.Fatal(err)
  2449  					}
  2450  					if err := createAndWaitReady(t, adminClient, makeBinding("validate-authz-binding", "validate-authz", ""), nil); err != nil {
  2451  						t.Fatal(err)
  2452  					}
  2453  
  2454  					ns := &v1.Namespace{
  2455  						ObjectMeta: metav1.ObjectMeta{
  2456  							Name: "test-authz",
  2457  						},
  2458  					}
  2459  					_, err = client.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{})
  2460  
  2461  					var expected metav1.StatusReason = ""
  2462  					if !testcase.allowed {
  2463  						expected = metav1.StatusReasonInvalid
  2464  					}
  2465  					checkFailureReason(t, err, expected)
  2466  				})
  2467  			}
  2468  		})
  2469  	}
  2470  }
  2471  
  2472  func TestCRDsOnStartup(t *testing.T) {
  2473  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  2474  
  2475  	testContext, testCancel := context.WithCancel(context.Background())
  2476  	defer testCancel()
  2477  
  2478  	// Start server and create CRD, and validatingadmission policy and binding
  2479  	etcdConfig := framework.SharedEtcd()
  2480  	server := apiservertesting.StartTestServerOrDie(t, nil, []string{
  2481  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2482  		"--authorization-mode=RBAC",
  2483  		"--anonymous-auth",
  2484  	}, etcdConfig)
  2485  	client := clientset.NewForConfigOrDie(server.ClientConfig)
  2486  	dynamicClient := dynamic.NewForConfigOrDie(server.ClientConfig)
  2487  	apiextclient := apiextensionsclientset.NewForConfigOrDie(server.ClientConfig)
  2488  	myCRD := &apiextensionsv1.CustomResourceDefinition{
  2489  		ObjectMeta: metav1.ObjectMeta{
  2490  			Name: "foos.cr.bar.com",
  2491  		},
  2492  		Spec: apiextensionsv1.CustomResourceDefinitionSpec{
  2493  			Group: "cr.bar.com",
  2494  			Scope: apiextensionsv1.NamespaceScoped,
  2495  			Names: apiextensionsv1.CustomResourceDefinitionNames{
  2496  				Plural: "foos",
  2497  				Kind:   "Foo",
  2498  			},
  2499  			Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
  2500  				{
  2501  					Name:    "v1",
  2502  					Served:  true,
  2503  					Storage: true,
  2504  					Schema:  fixtures.AllowAllSchema(),
  2505  				},
  2506  			},
  2507  		},
  2508  	}
  2509  
  2510  	// Create a bunch of fake CRDs to make the initial startup sync take a long time
  2511  	for i := 0; i < 100; i++ {
  2512  		crd := myCRD.DeepCopy()
  2513  		crd.Name = fmt.Sprintf("foos%d.cr.bar.com", i)
  2514  		crd.Spec.Names.Plural = fmt.Sprintf("foos%d", i)
  2515  		crd.Spec.Names.Kind = fmt.Sprintf("Foo%d", i)
  2516  
  2517  		if _, err := apiextclient.ApiextensionsV1().CustomResourceDefinitions().Create(context.Background(), crd, metav1.CreateOptions{}); err != nil {
  2518  			t.Fatal(err)
  2519  		}
  2520  	}
  2521  
  2522  	etcd.CreateTestCRDs(t, apiextclient, false, myCRD)
  2523  	crdGVK := schema.GroupVersionKind{
  2524  		Group:   "cr.bar.com",
  2525  		Version: "v1",
  2526  		Kind:    "Foo",
  2527  	}
  2528  	crdGVR := crdGVK.GroupVersion().WithResource("foos")
  2529  
  2530  	param := &unstructured.Unstructured{
  2531  		Object: map[string]interface{}{
  2532  			"metadata": map[string]interface{}{
  2533  				"name":      "test",
  2534  				"namespace": "default",
  2535  			},
  2536  			"foo": "bar",
  2537  		},
  2538  	}
  2539  	param.GetObjectKind().SetGroupVersionKind(crdGVK)
  2540  
  2541  	if _, err := dynamicClient.Resource(crdGVR).Namespace("default").Create(context.TODO(), param, metav1.CreateOptions{}); err != nil {
  2542  		t.Fatal(err)
  2543  	}
  2544  
  2545  	policy := withValidations([]admissionregistrationv1.Validation{
  2546  		{
  2547  			Expression: "object.metadata.name.startsWith(params.metadata.name)",
  2548  			Message:    "wrong prefix",
  2549  		},
  2550  	}, withParams(withCRDParamKind(crdGVK.Kind, crdGVK.Group, crdGVK.Version), withNamespaceMatch(withFailurePolicy(admissionregistrationv1.Fail, makePolicy("allowed-prefixes")))))
  2551  	policy = withWaitReadyConstraintAndExpression(policy)
  2552  	_, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  2553  	if err != nil {
  2554  		t.Fatal(err)
  2555  	}
  2556  
  2557  	// validate that namespaces starting with "test" are allowed
  2558  	policyBinding := makeBinding("allowed-prefixes-binding", "allowed-prefixes", "test")
  2559  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  2560  		t.Fatal(err)
  2561  	}
  2562  
  2563  	doCheck := func(client clientset.Interface) {
  2564  		if waitErr := wait.PollUntilContextTimeout(testContext, time.Millisecond*100, 3*time.Minute, true, func(ctx context.Context) (bool, error) {
  2565  			disallowedNamespace := &v1.Namespace{
  2566  				ObjectMeta: metav1.ObjectMeta{
  2567  					GenerateName: "not-test-",
  2568  				},
  2569  			}
  2570  
  2571  			_, err = client.CoreV1().Namespaces().Create(testContext, disallowedNamespace, metav1.CreateOptions{})
  2572  			if err == nil {
  2573  				return false, nil
  2574  			}
  2575  
  2576  			if strings.Contains(err.Error(), "not yet synced to use for admission") {
  2577  				return false, nil
  2578  			}
  2579  
  2580  			if strings.Contains(err.Error(), "failed to find resource referenced by paramKind") {
  2581  				return false, nil
  2582  			}
  2583  
  2584  			if !strings.Contains(err.Error(), "wrong prefix") {
  2585  				return false, err
  2586  			}
  2587  
  2588  			return true, nil
  2589  		}); waitErr != nil {
  2590  			t.Errorf("timed out waiting: %v", err)
  2591  		}
  2592  	}
  2593  
  2594  	// Show that the policy & binding are correctly working before restarting
  2595  	// to use the paramKind and deliver an error
  2596  	doCheck(client)
  2597  	server.TearDownFn()
  2598  
  2599  	// Start the server.
  2600  	server = apiservertesting.StartTestServerOrDie(
  2601  		t,
  2602  		&apiservertesting.TestServerInstanceOptions{},
  2603  		[]string{
  2604  			"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2605  			"--authorization-mode=RBAC",
  2606  			"--anonymous-auth",
  2607  		},
  2608  		etcdConfig)
  2609  	defer server.TearDownFn()
  2610  
  2611  	// Now that the server is restarted, show again that the policy & binding are correctly working
  2612  	client = clientset.NewForConfigOrDie(server.ClientConfig)
  2613  
  2614  	doCheck(client)
  2615  
  2616  }
  2617  
  2618  type clientFn func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset
  2619  
  2620  func secondaryAuthorizationUserClient(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset {
  2621  	clientConfig = rest.CopyConfig(clientConfig)
  2622  	clientConfig.Impersonate = rest.ImpersonationConfig{
  2623  		UserName: "alice",
  2624  		UID:      "1234",
  2625  	}
  2626  	client := clientset.NewForConfigOrDie(clientConfig)
  2627  
  2628  	for _, rule := range rules {
  2629  		authutil.GrantUserAuthorization(t, context.TODO(), adminClient, "alice", rule)
  2630  	}
  2631  	return client
  2632  }
  2633  
  2634  func secondaryAuthorizationServiceAccountClient(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset {
  2635  	return serviceAccountClient("default", "test-service-acct")(t, adminClient, clientConfig, rules)
  2636  }
  2637  
  2638  func serviceAccountClient(namespace, name string) clientFn {
  2639  	return func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset {
  2640  		clientConfig = rest.CopyConfig(clientConfig)
  2641  		sa, err := adminClient.CoreV1().ServiceAccounts(namespace).Create(context.TODO(), &v1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: name}}, metav1.CreateOptions{})
  2642  		if err != nil {
  2643  			t.Fatal(err)
  2644  		}
  2645  		uid := sa.UID
  2646  
  2647  		clientConfig.Impersonate = rest.ImpersonationConfig{
  2648  			UserName: "system:serviceaccount:" + namespace + ":" + name,
  2649  			UID:      string(uid),
  2650  		}
  2651  		client := clientset.NewForConfigOrDie(clientConfig)
  2652  
  2653  		for _, rule := range rules {
  2654  			authutil.GrantServiceAccountAuthorization(t, context.TODO(), adminClient, name, namespace, rule)
  2655  		}
  2656  		return client
  2657  	}
  2658  }
  2659  
  2660  func withWaitReadyConstraintAndExpression(policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2661  	policy = policy.DeepCopy()
  2662  	policy.Spec.MatchConstraints.ResourceRules = append(policy.Spec.MatchConstraints.ResourceRules, admissionregistrationv1.NamedRuleWithOperations{
  2663  		ResourceNames: []string{"test-marker"},
  2664  		RuleWithOperations: admissionregistrationv1.RuleWithOperations{
  2665  			Operations: []admissionregistrationv1.OperationType{
  2666  				"UPDATE",
  2667  			},
  2668  			Rule: admissionregistrationv1.Rule{
  2669  				APIGroups: []string{
  2670  					"",
  2671  				},
  2672  				APIVersions: []string{
  2673  					"v1",
  2674  				},
  2675  				Resources: []string{
  2676  					"endpoints",
  2677  				},
  2678  			},
  2679  		},
  2680  	})
  2681  	policy.Spec.Validations = append([]admissionregistrationv1.Validation{{
  2682  		Expression: "object.metadata.name != 'test-marker'",
  2683  		Message:    "marker denied; policy is ready",
  2684  	}}, policy.Spec.Validations...)
  2685  	return policy
  2686  }
  2687  
  2688  func createAndWaitReady(t *testing.T, client clientset.Interface, binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding, matchLabels map[string]string) error {
  2689  	return createAndWaitReadyNamespaced(t, client, binding, matchLabels, "default")
  2690  }
  2691  
  2692  func createAndWaitReadyNamespaced(t *testing.T, client clientset.Interface, binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding, matchLabels map[string]string, ns string) error {
  2693  	return createAndWaitReadyNamespacedWithWarnHandler(t, client, binding, matchLabels, ns, newWarningHandler())
  2694  }
  2695  
  2696  func createAndWaitReadyNamespacedWithWarnHandler(t *testing.T, client clientset.Interface, binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding, matchLabels map[string]string, ns string, handler *warningHandler) error {
  2697  	marker := &v1.Endpoints{ObjectMeta: metav1.ObjectMeta{Name: "test-marker", Namespace: ns, Labels: matchLabels}}
  2698  	defer func() {
  2699  		err := client.CoreV1().Endpoints(ns).Delete(context.TODO(), marker.Name, metav1.DeleteOptions{})
  2700  		if err != nil {
  2701  			t.Logf("error deleting marker: %v", err)
  2702  		}
  2703  	}()
  2704  	marker, err := client.CoreV1().Endpoints(ns).Create(context.TODO(), marker, metav1.CreateOptions{})
  2705  	if err != nil {
  2706  		return err
  2707  	}
  2708  
  2709  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Create(context.TODO(), binding, metav1.CreateOptions{})
  2710  	if err != nil {
  2711  		return err
  2712  	}
  2713  
  2714  	if waitErr := wait.PollImmediate(time.Millisecond*5, wait.ForeverTestTimeout, func() (bool, error) {
  2715  		handler.reset()
  2716  		_, err := client.CoreV1().Endpoints(ns).Patch(context.TODO(), marker.Name, types.JSONPatchType, []byte("[]"), metav1.PatchOptions{})
  2717  		if handler.hasObservedMarker() {
  2718  			return true, nil
  2719  		}
  2720  		if err != nil && strings.Contains(err.Error(), "marker denied; policy is ready") {
  2721  			return true, nil
  2722  		} else if err != nil && strings.Contains(err.Error(), "not yet synced to use for admission") {
  2723  			t.Logf("waiting for policy to be ready. Marker: %v. Admission not synced yet: %v", marker, err)
  2724  			return false, nil
  2725  		} else {
  2726  			t.Logf("waiting for policy to be ready. Marker: %v, Last marker patch response: %v", marker, err)
  2727  			return false, err
  2728  		}
  2729  	}); waitErr != nil {
  2730  		return waitErr
  2731  	}
  2732  	t.Logf("Marker ready: %v", marker)
  2733  	handler.reset()
  2734  	return nil
  2735  }
  2736  
  2737  func withMatchNamespace(binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding, ns string) *admissionregistrationv1.ValidatingAdmissionPolicyBinding {
  2738  	binding.Spec.MatchResources = &admissionregistrationv1.MatchResources{
  2739  		NamespaceSelector: &metav1.LabelSelector{
  2740  			MatchExpressions: []metav1.LabelSelectorRequirement{
  2741  				{
  2742  					Key:      "kubernetes.io/metadata.name",
  2743  					Operator: metav1.LabelSelectorOpIn,
  2744  					Values:   []string{ns},
  2745  				},
  2746  			},
  2747  		},
  2748  	}
  2749  	return binding
  2750  }
  2751  
  2752  func makePolicy(name string) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2753  	return &admissionregistrationv1.ValidatingAdmissionPolicy{
  2754  		ObjectMeta: metav1.ObjectMeta{Name: name},
  2755  	}
  2756  }
  2757  
  2758  func withParams(params *admissionregistrationv1.ParamKind, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2759  	policy.Spec.ParamKind = params
  2760  	return policy
  2761  }
  2762  
  2763  func configParamKind() *admissionregistrationv1.ParamKind {
  2764  	return &admissionregistrationv1.ParamKind{
  2765  		APIVersion: "v1",
  2766  		Kind:       "ConfigMap",
  2767  	}
  2768  }
  2769  
  2770  func withFailurePolicy(failure admissionregistrationv1.FailurePolicyType, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2771  	policy.Spec.FailurePolicy = &failure
  2772  	return policy
  2773  }
  2774  
  2775  func withNamespaceMatch(policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2776  	return withPolicyMatch("namespaces", policy)
  2777  }
  2778  
  2779  func withConfigMapMatch(policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2780  	return withPolicyMatch("configmaps", policy)
  2781  }
  2782  
  2783  func withObjectSelector(labelSelector *metav1.LabelSelector, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2784  	policy.Spec.MatchConstraints.ObjectSelector = labelSelector
  2785  	return policy
  2786  }
  2787  
  2788  func withNamespaceSelector(labelSelector *metav1.LabelSelector, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2789  	policy.Spec.MatchConstraints.NamespaceSelector = labelSelector
  2790  	return policy
  2791  }
  2792  
  2793  func withPolicyMatch(resource string, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2794  	policy.Spec.MatchConstraints = &admissionregistrationv1.MatchResources{
  2795  		ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{
  2796  			{
  2797  				RuleWithOperations: admissionregistrationv1.RuleWithOperations{
  2798  					Operations: []admissionregistrationv1.OperationType{
  2799  						"*",
  2800  					},
  2801  					Rule: admissionregistrationv1.Rule{
  2802  						APIGroups: []string{
  2803  							"",
  2804  						},
  2805  						APIVersions: []string{
  2806  							"*",
  2807  						},
  2808  						Resources: []string{
  2809  							resource,
  2810  						},
  2811  					},
  2812  				},
  2813  			},
  2814  		},
  2815  	}
  2816  	return policy
  2817  }
  2818  
  2819  func withExcludePolicyMatch(resource string, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2820  	policy.Spec.MatchConstraints.ExcludeResourceRules = []admissionregistrationv1.NamedRuleWithOperations{
  2821  		{
  2822  			RuleWithOperations: admissionregistrationv1.RuleWithOperations{
  2823  				Operations: []admissionregistrationv1.OperationType{
  2824  					"*",
  2825  				},
  2826  				Rule: admissionregistrationv1.Rule{
  2827  					APIGroups: []string{
  2828  						"",
  2829  					},
  2830  					APIVersions: []string{
  2831  						"*",
  2832  					},
  2833  					Resources: []string{
  2834  						resource,
  2835  					},
  2836  				},
  2837  			},
  2838  		},
  2839  	}
  2840  	return policy
  2841  }
  2842  
  2843  func withPolicyExistsLabels(labels []string, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2844  	if policy.Spec.MatchConstraints == nil {
  2845  		policy.Spec.MatchConstraints = &admissionregistrationv1.MatchResources{}
  2846  	}
  2847  	matchExprs := buildExistsSelector(labels)
  2848  	policy.Spec.MatchConstraints.ObjectSelector = &metav1.LabelSelector{
  2849  		MatchExpressions: matchExprs,
  2850  	}
  2851  	return policy
  2852  }
  2853  
  2854  func withValidations(validations []admissionregistrationv1.Validation, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2855  	policy.Spec.Validations = validations
  2856  	return policy
  2857  }
  2858  
  2859  func withAuditAnnotations(auditAnnotations []admissionregistrationv1.AuditAnnotation, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2860  	policy.Spec.AuditAnnotations = auditAnnotations
  2861  	return policy
  2862  }
  2863  
  2864  func makeBinding(name, policyName, paramName string) *admissionregistrationv1.ValidatingAdmissionPolicyBinding {
  2865  	var paramRef *admissionregistrationv1.ParamRef
  2866  	if paramName != "" {
  2867  		denyAction := admissionregistrationv1.DenyAction
  2868  		paramRef = &admissionregistrationv1.ParamRef{
  2869  			Name:                    paramName,
  2870  			Namespace:               "default",
  2871  			ParameterNotFoundAction: &denyAction,
  2872  		}
  2873  	}
  2874  	return &admissionregistrationv1.ValidatingAdmissionPolicyBinding{
  2875  		ObjectMeta: metav1.ObjectMeta{Name: name},
  2876  		Spec: admissionregistrationv1.ValidatingAdmissionPolicyBindingSpec{
  2877  			PolicyName:        policyName,
  2878  			ParamRef:          paramRef,
  2879  			ValidationActions: []admissionregistrationv1.ValidationAction{admissionregistrationv1.Deny},
  2880  		},
  2881  	}
  2882  }
  2883  
  2884  func withValidationActions(validationActions []admissionregistrationv1.ValidationAction, binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding) *admissionregistrationv1.ValidatingAdmissionPolicyBinding {
  2885  	binding.Spec.ValidationActions = validationActions
  2886  	return binding
  2887  }
  2888  
  2889  func withBindingExistsLabels(labels []string, policy *admissionregistrationv1.ValidatingAdmissionPolicy, binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding) *admissionregistrationv1.ValidatingAdmissionPolicyBinding {
  2890  	if policy != nil {
  2891  		// shallow copy
  2892  		constraintsCopy := *policy.Spec.MatchConstraints
  2893  		binding.Spec.MatchResources = &constraintsCopy
  2894  	}
  2895  	matchExprs := buildExistsSelector(labels)
  2896  	binding.Spec.MatchResources.ObjectSelector = &metav1.LabelSelector{
  2897  		MatchExpressions: matchExprs,
  2898  	}
  2899  	return binding
  2900  }
  2901  
  2902  func buildExistsSelector(labels []string) []metav1.LabelSelectorRequirement {
  2903  	matchExprs := make([]metav1.LabelSelectorRequirement, len(labels))
  2904  	for i := 0; i < len(labels); i++ {
  2905  		matchExprs[i].Key = labels[i]
  2906  		matchExprs[i].Operator = metav1.LabelSelectorOpExists
  2907  	}
  2908  	return matchExprs
  2909  }
  2910  
  2911  func makeConfigParams(name string, data map[string]string) *v1.ConfigMap {
  2912  	return &v1.ConfigMap{
  2913  		ObjectMeta: metav1.ObjectMeta{Name: name},
  2914  		Data:       data,
  2915  	}
  2916  }
  2917  
  2918  func checkForFailedRule(t *testing.T, err error) {
  2919  	if !strings.Contains(err.Error(), "failed expression") {
  2920  		t.Fatalf("unexpected error (expected to find \"failed expression\"): %s", err)
  2921  	}
  2922  	if strings.Contains(err.Error(), "evaluation error") {
  2923  		t.Fatalf("CEL rule evaluation failed: %s", err)
  2924  	}
  2925  }
  2926  
  2927  func checkFailureReason(t *testing.T, err error, expectedReason metav1.StatusReason) {
  2928  	if err == nil && expectedReason == "" {
  2929  		// no reason was given, no error was passed - early exit
  2930  		return
  2931  	}
  2932  	switch e := err.(type) {
  2933  	case apierrors.APIStatus:
  2934  		reason := e.Status().Reason
  2935  		if reason != expectedReason {
  2936  			t.Logf("actual error reason: %v", reason)
  2937  			t.Logf("expected failure reason: %v", expectedReason)
  2938  			t.Error("Unexpected error reason")
  2939  		}
  2940  	default:
  2941  		t.Errorf("Unexpected error: %v", err)
  2942  	}
  2943  }
  2944  
  2945  func checkExpectedWarnings(t *testing.T, recordedWarnings *warningHandler, expectedWarnings sets.Set[string]) {
  2946  	if !recordedWarnings.equals(expectedWarnings) {
  2947  		t.Errorf("Expected warnings '%v' but got '%v", expectedWarnings, recordedWarnings)
  2948  	}
  2949  }
  2950  
  2951  func checkAuditEvents(t *testing.T, logFile *os.File, auditEvents []utils.AuditEvent, filter utils.AuditAnnotationsFilter) {
  2952  	stream, err := os.OpenFile(logFile.Name(), os.O_RDWR, 0600)
  2953  	if err != nil {
  2954  		t.Errorf("unexpected error: %v", err)
  2955  	}
  2956  	defer stream.Close()
  2957  
  2958  	if auditEvents != nil {
  2959  		missing, err := utils.CheckAuditLinesFiltered(stream, auditEvents, auditv1.SchemeGroupVersion, filter)
  2960  		if err != nil {
  2961  			t.Errorf("unexpected error checking audit lines: %v", err)
  2962  		}
  2963  		if len(missing.MissingEvents) > 0 {
  2964  			t.Errorf("failed to get expected events -- missing: %s", missing)
  2965  		}
  2966  	}
  2967  	if err := stream.Truncate(0); err != nil {
  2968  		t.Errorf("unexpected error truncate file: %v", err)
  2969  	}
  2970  	if _, err := stream.Seek(0, 0); err != nil {
  2971  		t.Errorf("unexpected error reset offset: %v", err)
  2972  	}
  2973  }
  2974  
  2975  func withCRDParamKind(kind, crdGroup, crdVersion string) *admissionregistrationv1.ParamKind {
  2976  	return &admissionregistrationv1.ParamKind{
  2977  		APIVersion: crdGroup + "/" + crdVersion,
  2978  		Kind:       kind,
  2979  	}
  2980  }
  2981  
  2982  func checkExpectedError(t *testing.T, err error, expectedErr string) {
  2983  	if err == nil && expectedErr == "" {
  2984  		return
  2985  	}
  2986  	if err == nil && expectedErr != "" {
  2987  		t.Logf("actual error: %v", err)
  2988  		t.Logf("expected error: %v", expectedErr)
  2989  		t.Fatal("got nil error but expected an error")
  2990  	}
  2991  
  2992  	if err != nil && expectedErr == "" {
  2993  		t.Logf("actual error: %v", err)
  2994  		t.Logf("expected error: %v", expectedErr)
  2995  		t.Fatal("got error but expected none")
  2996  	}
  2997  
  2998  	if err.Error() != expectedErr {
  2999  		t.Logf("actual validation error: %v", err)
  3000  		t.Logf("expected validation error: %v", expectedErr)
  3001  		t.Error("unexpected validation error")
  3002  	}
  3003  }
  3004  
  3005  // Copied from etcd.GetCustomResourceDefinitionData
  3006  func versionedCustomResourceDefinition() *apiextensionsv1.CustomResourceDefinition {
  3007  	return &apiextensionsv1.CustomResourceDefinition{
  3008  		ObjectMeta: metav1.ObjectMeta{
  3009  			Name: "pandas.awesome.bears.com",
  3010  		},
  3011  		Spec: apiextensionsv1.CustomResourceDefinitionSpec{
  3012  			Group: "awesome.bears.com",
  3013  			Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
  3014  				{
  3015  					Name:    "v1",
  3016  					Served:  true,
  3017  					Storage: true,
  3018  					Schema:  fixtures.AllowAllSchema(),
  3019  					Subresources: &apiextensionsv1.CustomResourceSubresources{
  3020  						Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
  3021  						Scale: &apiextensionsv1.CustomResourceSubresourceScale{
  3022  							SpecReplicasPath:   ".spec.replicas",
  3023  							StatusReplicasPath: ".status.replicas",
  3024  							LabelSelectorPath:  func() *string { path := ".status.selector"; return &path }(),
  3025  						},
  3026  					},
  3027  				},
  3028  				{
  3029  					Name:    "v2",
  3030  					Served:  true,
  3031  					Storage: false,
  3032  					Schema:  fixtures.AllowAllSchema(),
  3033  					Subresources: &apiextensionsv1.CustomResourceSubresources{
  3034  						Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
  3035  						Scale: &apiextensionsv1.CustomResourceSubresourceScale{
  3036  							SpecReplicasPath:   ".spec.replicas",
  3037  							StatusReplicasPath: ".status.replicas",
  3038  							LabelSelectorPath:  func() *string { path := ".status.selector"; return &path }(),
  3039  						},
  3040  					},
  3041  				},
  3042  			},
  3043  			Scope: apiextensionsv1.ClusterScoped,
  3044  			Names: apiextensionsv1.CustomResourceDefinitionNames{
  3045  				Plural: "pandas",
  3046  				Kind:   "Panda",
  3047  			},
  3048  		},
  3049  	}
  3050  }
  3051  
  3052  type warningHandler struct {
  3053  	lock           sync.Mutex
  3054  	warnings       sets.Set[string]
  3055  	observedMarker bool
  3056  }
  3057  
  3058  func newWarningHandler() *warningHandler {
  3059  	return &warningHandler{warnings: sets.New[string]()}
  3060  }
  3061  
  3062  func (w *warningHandler) reset() {
  3063  	w.lock.Lock()
  3064  	defer w.lock.Unlock()
  3065  	w.warnings = sets.New[string]()
  3066  	w.observedMarker = false
  3067  }
  3068  
  3069  func (w *warningHandler) equals(s sets.Set[string]) bool {
  3070  	w.lock.Lock()
  3071  	defer w.lock.Unlock()
  3072  	return w.warnings.Equal(s)
  3073  }
  3074  
  3075  func (w *warningHandler) hasObservedMarker() bool {
  3076  	w.lock.Lock()
  3077  	defer w.lock.Unlock()
  3078  	return w.observedMarker
  3079  }
  3080  
  3081  func (w *warningHandler) HandleWarningHeader(code int, _ string, message string) {
  3082  	if strings.HasSuffix(message, "marker denied; policy is ready") {
  3083  		func() {
  3084  			w.lock.Lock()
  3085  			defer w.lock.Unlock()
  3086  			w.observedMarker = true
  3087  		}()
  3088  	}
  3089  	if code != 299 || len(message) == 0 {
  3090  		return
  3091  	}
  3092  	w.lock.Lock()
  3093  	defer w.lock.Unlock()
  3094  	w.warnings.Insert(message)
  3095  }
  3096  
  3097  func expectedAuditEvents(auditAnnotations map[string]string, ns string, code int32) []utils.AuditEvent {
  3098  	return []utils.AuditEvent{
  3099  		{
  3100  			Level:                  auditinternal.LevelRequest,
  3101  			Stage:                  auditinternal.StageResponseComplete,
  3102  			RequestURI:             fmt.Sprintf("/api/v1/namespaces/%s/configmaps", ns),
  3103  			Verb:                   "create",
  3104  			Code:                   code,
  3105  			User:                   "system:apiserver",
  3106  			ImpersonatedUser:       testReinvocationClientUsername,
  3107  			ImpersonatedGroups:     "system:authenticated",
  3108  			Resource:               "configmaps",
  3109  			Namespace:              ns,
  3110  			AuthorizeDecision:      "allow",
  3111  			RequestObject:          true,
  3112  			ResponseObject:         false,
  3113  			CustomAuditAnnotations: auditAnnotations,
  3114  		},
  3115  	}
  3116  }
  3117  
  3118  const (
  3119  	testReinvocationClientUsername = "webhook-reinvocation-integration-client"
  3120  	auditPolicy                    = `
  3121  apiVersion: audit.k8s.io/v1
  3122  kind: Policy
  3123  rules:
  3124    - level: Request
  3125      resources:
  3126        - group: "" # core
  3127          resources: ["configmaps"]
  3128  `
  3129  )
  3130  
  3131  func TestAuthorizationDecisionCaching(t *testing.T) {
  3132  	for _, tc := range []struct {
  3133  		name        string
  3134  		validations []admissionregistrationv1.Validation
  3135  	}{
  3136  		{
  3137  			name: "hit",
  3138  			validations: []admissionregistrationv1.Validation{
  3139  				{
  3140  					Expression: "authorizer.requestResource.check('test').reason() == authorizer.requestResource.check('test').reason()",
  3141  				},
  3142  			},
  3143  		},
  3144  		{
  3145  			name: "miss",
  3146  			validations: []admissionregistrationv1.Validation{
  3147  				{
  3148  					Expression: "authorizer.requestResource.subresource('a').check('test').reason() == '1'",
  3149  				},
  3150  				{
  3151  					Expression: "authorizer.requestResource.subresource('b').check('test').reason() == '2'",
  3152  				},
  3153  				{
  3154  					Expression: "authorizer.requestResource.subresource('c').check('test').reason() == '3'",
  3155  				},
  3156  			},
  3157  		},
  3158  	} {
  3159  		t.Run(tc.name, func(t *testing.T) {
  3160  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  3161  
  3162  			ctx, cancel := context.WithCancel(context.TODO())
  3163  			defer cancel()
  3164  
  3165  			var nChecks int
  3166  			webhook := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3167  				var review authorizationv1.SubjectAccessReview
  3168  				if err := json.NewDecoder(r.Body).Decode(&review); err != nil {
  3169  					http.Error(w, err.Error(), http.StatusBadRequest)
  3170  				}
  3171  
  3172  				review.Status.Allowed = true
  3173  				if review.Spec.ResourceAttributes.Verb == "test" {
  3174  					nChecks++
  3175  					review.Status.Reason = fmt.Sprintf("%d", nChecks)
  3176  				}
  3177  
  3178  				w.Header().Set("Content-Type", "application/json")
  3179  				if err := json.NewEncoder(w).Encode(review); err != nil {
  3180  					http.Error(w, err.Error(), http.StatusInternalServerError)
  3181  				}
  3182  			}))
  3183  			defer webhook.Close()
  3184  
  3185  			kcfd, err := os.CreateTemp("", "kubeconfig-")
  3186  			if err != nil {
  3187  				t.Fatal(err)
  3188  			}
  3189  			func() {
  3190  				defer kcfd.Close()
  3191  				tmpl, err := template.New("kubeconfig").Parse(`
  3192  apiVersion: v1
  3193  kind: Config
  3194  clusters:
  3195    - name: test-authz-service
  3196      cluster:
  3197        server: {{ .Server }}
  3198  users:
  3199    - name: test-api-server
  3200  current-context: webhook
  3201  contexts:
  3202  - context:
  3203      cluster: test-authz-service
  3204      user: test-api-server
  3205    name: webhook
  3206  `)
  3207  				if err != nil {
  3208  					t.Fatal(err)
  3209  				}
  3210  				err = tmpl.Execute(kcfd, struct {
  3211  					Server string
  3212  				}{
  3213  					Server: webhook.URL,
  3214  				})
  3215  				if err != nil {
  3216  					t.Fatal(err)
  3217  				}
  3218  			}()
  3219  
  3220  			client, config, teardown := framework.StartTestServer(ctx, t, framework.TestServerSetup{
  3221  				ModifyServerRunOptions: func(options *options.ServerRunOptions) {
  3222  					options.Admission.GenericAdmission.EnablePlugins = append(options.Admission.GenericAdmission.EnablePlugins, "ValidatingAdmissionPolicy")
  3223  					options.APIEnablement.RuntimeConfig.Set("api/all=true")
  3224  
  3225  					options.Authorization.Modes = []string{authzmodes.ModeWebhook}
  3226  					options.Authorization.WebhookConfigFile = kcfd.Name()
  3227  					options.Authorization.WebhookVersion = "v1"
  3228  					// Bypass webhook cache to observe the policy plugin's cache behavior.
  3229  					options.Authorization.WebhookCacheAuthorizedTTL = 0
  3230  					options.Authorization.WebhookCacheUnauthorizedTTL = 0
  3231  				},
  3232  			})
  3233  			defer teardown()
  3234  
  3235  			policy := &admissionregistrationv1.ValidatingAdmissionPolicy{
  3236  				ObjectMeta: metav1.ObjectMeta{
  3237  					Name: "test-authorization-decision-caching-policy",
  3238  				},
  3239  				Spec: admissionregistrationv1.ValidatingAdmissionPolicySpec{
  3240  					MatchConstraints: &admissionregistrationv1.MatchResources{
  3241  						ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{
  3242  							{
  3243  								ResourceNames: []string{"test-authorization-decision-caching-namespace"},
  3244  								RuleWithOperations: admissionregistrationv1.RuleWithOperations{
  3245  									Operations: []admissionregistrationv1.OperationType{
  3246  										admissionregistrationv1.Create,
  3247  									},
  3248  									Rule: admissionregistrationv1.Rule{
  3249  										APIGroups:   []string{""},
  3250  										APIVersions: []string{"v1"},
  3251  										Resources:   []string{"namespaces"},
  3252  									},
  3253  								},
  3254  							},
  3255  						},
  3256  					},
  3257  					Validations: tc.validations,
  3258  				},
  3259  			}
  3260  
  3261  			policy, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(ctx, withWaitReadyConstraintAndExpression(policy), metav1.CreateOptions{})
  3262  			if err != nil {
  3263  				t.Fatal(err)
  3264  			}
  3265  
  3266  			if err := createAndWaitReady(t, client, makeBinding(policy.Name+"-binding", policy.Name, ""), nil); err != nil {
  3267  				t.Fatal(err)
  3268  			}
  3269  
  3270  			config = rest.CopyConfig(config)
  3271  			config.Impersonate = rest.ImpersonationConfig{
  3272  				UserName: "alice",
  3273  				UID:      "1234",
  3274  			}
  3275  			client, err = clientset.NewForConfig(config)
  3276  			if err != nil {
  3277  				t.Fatal(err)
  3278  			}
  3279  
  3280  			if _, err := client.CoreV1().Namespaces().Create(
  3281  				ctx,
  3282  				&v1.Namespace{
  3283  					ObjectMeta: metav1.ObjectMeta{
  3284  						Name: "test-authorization-decision-caching-namespace",
  3285  					},
  3286  				},
  3287  				metav1.CreateOptions{},
  3288  			); err != nil {
  3289  				t.Fatal(err)
  3290  			}
  3291  		})
  3292  	}
  3293  }
  3294  

View as plain text