k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/integration/apiserver/cel/validatingadmissionpolicy_test.go (about)

     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  			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  	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  			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  	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  	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  	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  	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  	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  	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  	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  	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  	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  	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  	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  	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  	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  	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  	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  // Test_CostLimitForValidation tests the cost limit set for a ValidatingAdmissionPolicy.
  2135  func Test_CostLimitForValidation(t *testing.T) {
  2136  	testcases := []struct {
  2137  		name                  string
  2138  		policy                *admissionregistrationv1.ValidatingAdmissionPolicy
  2139  		policyBinding         *admissionregistrationv1.ValidatingAdmissionPolicyBinding
  2140  		namespace             *v1.Namespace
  2141  		err                   string
  2142  		failureReason         metav1.StatusReason
  2143  		strictCostEnforcement bool
  2144  	}{
  2145  		{
  2146  			name: "With StrictCostEnforcementForVAP: Single expression exceeds per call cost limit for native library",
  2147  			policy: withValidations([]admissionregistrationv1.Validation{
  2148  				{
  2149  					Expression: "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(x, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(y, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z5, int('1'.find('[0-9]*')) < 100)))))))",
  2150  				},
  2151  			}, withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
  2152  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
  2153  			namespace: &v1.Namespace{
  2154  				ObjectMeta: metav1.ObjectMeta{
  2155  					Name: "test-k8s",
  2156  				},
  2157  			},
  2158  			err:                   "operation cancelled: actual cost limit exceeded",
  2159  			failureReason:         metav1.StatusReasonInvalid,
  2160  			strictCostEnforcement: true,
  2161  		},
  2162  		{
  2163  			name: "With StrictCostEnforcementForVAP: Expression exceeds per call cost limit for extended library",
  2164  			policy: withValidations([]admissionregistrationv1.Validation{
  2165  				{
  2166  
  2167  					Expression: "authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed() && authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed() && authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed()",
  2168  				},
  2169  			}, withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
  2170  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
  2171  			namespace: &v1.Namespace{
  2172  				ObjectMeta: metav1.ObjectMeta{
  2173  					Name: "test-k8s",
  2174  				},
  2175  			},
  2176  			err:                   "operation cancelled: actual cost limit exceeded",
  2177  			failureReason:         metav1.StatusReasonInvalid,
  2178  			strictCostEnforcement: true,
  2179  		},
  2180  		{
  2181  			name: "With StrictCostEnforcementForVAP: Expression exceeds per call cost limit for extended library in variables",
  2182  			policy: withVariables([]admissionregistrationv1.Variable{
  2183  				{
  2184  					Name:       "authzCheck",
  2185  					Expression: "authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed() && authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed() && authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed()",
  2186  				},
  2187  			}, withValidations([]admissionregistrationv1.Validation{
  2188  				{
  2189  					Expression: "variables.authzCheck",
  2190  				},
  2191  			}, withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
  2192  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
  2193  			namespace: &v1.Namespace{
  2194  				ObjectMeta: metav1.ObjectMeta{
  2195  					Name: "test-k8s",
  2196  				},
  2197  			},
  2198  			err:                   "operation cancelled: actual cost limit exceeded",
  2199  			failureReason:         metav1.StatusReasonInvalid,
  2200  			strictCostEnforcement: true,
  2201  		},
  2202  		{
  2203  			name: "With StrictCostEnforcementForVAP: Expression exceeds per call cost limit for extended library in matchConditions",
  2204  			policy: withMatchConditions([]admissionregistrationv1.MatchCondition{
  2205  				{
  2206  					Name:       "test",
  2207  					Expression: "authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed() && authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed() && authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed()",
  2208  				},
  2209  			}, withValidations([]admissionregistrationv1.Validation{
  2210  				{
  2211  					Expression: "true",
  2212  				},
  2213  			}, withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
  2214  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
  2215  			namespace: &v1.Namespace{
  2216  				ObjectMeta: metav1.ObjectMeta{
  2217  					Name: "test-k8s",
  2218  				},
  2219  			},
  2220  			err:                   "operation cancelled: actual cost limit exceeded",
  2221  			failureReason:         metav1.StatusReasonInvalid,
  2222  			strictCostEnforcement: true,
  2223  		},
  2224  		{
  2225  			name:          "With StrictCostEnforcementForVAP: Expression exceeds per policy cost limit for extended library",
  2226  			policy:        withValidations(generateValidationsWithAuthzCheck(29, "authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed()"), withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
  2227  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
  2228  			namespace: &v1.Namespace{
  2229  				ObjectMeta: metav1.ObjectMeta{
  2230  					Name: "test-k8s",
  2231  				},
  2232  			},
  2233  			err:                   "validation failed due to running out of cost budget, no further validation rules will be run",
  2234  			failureReason:         metav1.StatusReasonInvalid,
  2235  			strictCostEnforcement: true,
  2236  		},
  2237  		{
  2238  			name: "Without StrictCostEnforcementForVAP: Single expression exceeds per call cost limit for native library",
  2239  			policy: withValidations([]admissionregistrationv1.Validation{
  2240  				{
  2241  					Expression: "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(x, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(y, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z5, int('1'.find('[0-9]*')) < 100)))))))",
  2242  				},
  2243  			}, withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
  2244  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
  2245  			namespace: &v1.Namespace{
  2246  				ObjectMeta: metav1.ObjectMeta{
  2247  					Name: "test-k8s",
  2248  				},
  2249  			},
  2250  			err:                   "operation cancelled: actual cost limit exceeded",
  2251  			failureReason:         metav1.StatusReasonInvalid,
  2252  			strictCostEnforcement: false,
  2253  		},
  2254  		{
  2255  			name: "Without StrictCostEnforcementForVAP: Expression does not exceed per call cost limit for extended library",
  2256  			policy: withValidations([]admissionregistrationv1.Validation{
  2257  				{
  2258  
  2259  					Expression: "authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed() && authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed() && authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed()",
  2260  				},
  2261  			}, withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
  2262  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
  2263  			namespace: &v1.Namespace{
  2264  				ObjectMeta: metav1.ObjectMeta{
  2265  					Name: "test-k8s",
  2266  				},
  2267  			},
  2268  			strictCostEnforcement: false,
  2269  		},
  2270  		{
  2271  			name: "Without StrictCostEnforcementForVAP: Expression does not exceed per call cost limit for extended library in variables",
  2272  			policy: withVariables([]admissionregistrationv1.Variable{
  2273  				{
  2274  					Name:       "authzCheck",
  2275  					Expression: "authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed() && authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed() && authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed()",
  2276  				},
  2277  			}, withValidations([]admissionregistrationv1.Validation{
  2278  				{
  2279  					Expression: "variables.authzCheck",
  2280  				},
  2281  			}, withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
  2282  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
  2283  			namespace: &v1.Namespace{
  2284  				ObjectMeta: metav1.ObjectMeta{
  2285  					Name: "test-k8s",
  2286  				},
  2287  			},
  2288  			strictCostEnforcement: false,
  2289  		},
  2290  		{
  2291  			name:          "Without StrictCostEnforcementForVAP: Expression does not exceed per policy cost limit for extended library",
  2292  			policy:        withValidations(generateValidationsWithAuthzCheck(29, "authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed()"), withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
  2293  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
  2294  			namespace: &v1.Namespace{
  2295  				ObjectMeta: metav1.ObjectMeta{
  2296  					Name: "test-k8s",
  2297  				},
  2298  			},
  2299  			strictCostEnforcement: false,
  2300  		},
  2301  	}
  2302  	for _, testcase := range testcases {
  2303  		t.Run(testcase.name, func(t *testing.T) {
  2304  			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.StrictCostEnforcementForVAP, testcase.strictCostEnforcement)
  2305  
  2306  			server, err := apiservertesting.StartTestServer(t, nil, []string{
  2307  				"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2308  			}, framework.SharedEtcd())
  2309  			if err != nil {
  2310  				t.Fatal(err)
  2311  			}
  2312  			defer server.TearDownFn()
  2313  
  2314  			config := server.ClientConfig
  2315  
  2316  			client, err := clientset.NewForConfig(config)
  2317  			if err != nil {
  2318  				t.Fatal(err)
  2319  			}
  2320  			policy := withWaitReadyConstraintAndExpression(testcase.policy)
  2321  			if _, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
  2322  				t.Fatal(err)
  2323  			}
  2324  			if err := createAndWaitReady(t, client, testcase.policyBinding, nil); err != nil {
  2325  				t.Fatal(err)
  2326  			}
  2327  
  2328  			_, err = client.CoreV1().Namespaces().Create(context.TODO(), testcase.namespace, metav1.CreateOptions{})
  2329  
  2330  			checkExpectedError(t, err, testcase.err)
  2331  			checkFailureReason(t, err, testcase.failureReason)
  2332  		})
  2333  	}
  2334  }
  2335  
  2336  // generate n validation rules with provided expression
  2337  func generateValidationsWithAuthzCheck(num int, exp string) []admissionregistrationv1.Validation {
  2338  	var validations = make([]admissionregistrationv1.Validation, num)
  2339  	for i := 0; i < num; i++ {
  2340  		validations[i].Expression = exp
  2341  	}
  2342  	return validations
  2343  }
  2344  
  2345  // TestCRDParams tests that a CustomResource can be used as a param resource for a ValidatingAdmissionPolicy.
  2346  func TestCRDParams(t *testing.T) {
  2347  	testcases := []struct {
  2348  		name          string
  2349  		resource      *unstructured.Unstructured
  2350  		policy        *admissionregistrationv1.ValidatingAdmissionPolicy
  2351  		policyBinding *admissionregistrationv1.ValidatingAdmissionPolicyBinding
  2352  		namespace     *v1.Namespace
  2353  		err           string
  2354  		failureReason metav1.StatusReason
  2355  	}{
  2356  		{
  2357  			name: "a rule that uses data from a CRD param resource does NOT pass",
  2358  			resource: &unstructured.Unstructured{Object: map[string]interface{}{
  2359  				"apiVersion": "awesome.bears.com/v1",
  2360  				"kind":       "Panda",
  2361  				"metadata": map[string]interface{}{
  2362  					"name": "config-obj",
  2363  				},
  2364  				"spec": map[string]interface{}{
  2365  					"nameCheck": "crd-test-k8s",
  2366  				},
  2367  			}},
  2368  			policy: withValidations([]admissionregistrationv1.Validation{
  2369  				{
  2370  					Expression: "params.spec.nameCheck == object.metadata.name",
  2371  				},
  2372  			}, withNamespaceMatch(withParams(withCRDParamKind("Panda", "awesome.bears.com", "v1"), withFailurePolicy(admissionregistrationv1.Fail, makePolicy("test-policy"))))),
  2373  			policyBinding: makeBinding("crd-policy-binding", "test-policy", "config-obj"),
  2374  			namespace: &v1.Namespace{
  2375  				ObjectMeta: metav1.ObjectMeta{
  2376  					Name: "incorrect-name",
  2377  				},
  2378  			},
  2379  			err:           `namespaces "incorrect-name" is forbidden: ValidatingAdmissionPolicy 'test-policy' with binding 'crd-policy-binding' denied request: failed expression: params.spec.nameCheck == object.metadata.name`,
  2380  			failureReason: metav1.StatusReasonInvalid,
  2381  		},
  2382  		{
  2383  			name: "a rule that uses data from a CRD param resource that does pass",
  2384  			resource: &unstructured.Unstructured{Object: map[string]interface{}{
  2385  				"apiVersion": "awesome.bears.com/v1",
  2386  				"kind":       "Panda",
  2387  				"metadata": map[string]interface{}{
  2388  					"name": "config-obj",
  2389  				},
  2390  				"spec": map[string]interface{}{
  2391  					"nameCheck": "crd-test-k8s",
  2392  				},
  2393  			}},
  2394  			policy: withValidations([]admissionregistrationv1.Validation{
  2395  				{
  2396  					Expression: "params.spec.nameCheck == object.metadata.name",
  2397  				},
  2398  			}, withNamespaceMatch(withParams(withCRDParamKind("Panda", "awesome.bears.com", "v1"), withFailurePolicy(admissionregistrationv1.Fail, makePolicy("test-policy"))))),
  2399  			policyBinding: makeBinding("crd-policy-binding", "test-policy", "config-obj"),
  2400  			namespace: &v1.Namespace{
  2401  				ObjectMeta: metav1.ObjectMeta{
  2402  					Name: "crd-test-k8s",
  2403  				},
  2404  			},
  2405  			err: ``,
  2406  		},
  2407  	}
  2408  
  2409  	for _, testcase := range testcases {
  2410  		t.Run(testcase.name, func(t *testing.T) {
  2411  			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)
  2412  			server, err := apiservertesting.StartTestServer(t, nil, []string{
  2413  				"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2414  			}, framework.SharedEtcd())
  2415  			if err != nil {
  2416  				t.Fatal(err)
  2417  			}
  2418  			defer server.TearDownFn()
  2419  
  2420  			config := server.ClientConfig
  2421  
  2422  			client, err := clientset.NewForConfig(config)
  2423  			if err != nil {
  2424  				t.Fatal(err)
  2425  			}
  2426  
  2427  			crd := versionedCustomResourceDefinition()
  2428  			etcd.CreateTestCRDs(t, apiextensionsclientset.NewForConfigOrDie(server.ClientConfig), false, crd)
  2429  			dynamicClient, err := dynamic.NewForConfig(config)
  2430  			if err != nil {
  2431  				t.Fatal(err)
  2432  			}
  2433  			gvr := schema.GroupVersionResource{
  2434  				Group:    crd.Spec.Group,
  2435  				Version:  crd.Spec.Versions[0].Name,
  2436  				Resource: crd.Spec.Names.Plural,
  2437  			}
  2438  			crClient := dynamicClient.Resource(gvr)
  2439  			_, err = crClient.Create(context.TODO(), testcase.resource, metav1.CreateOptions{})
  2440  			if err != nil {
  2441  				t.Fatalf("error creating %s: %s", gvr, err)
  2442  			}
  2443  
  2444  			policy := withWaitReadyConstraintAndExpression(testcase.policy)
  2445  			if _, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
  2446  				t.Fatal(err)
  2447  			}
  2448  			// remove default namespace since the CRD is cluster-scoped
  2449  			testcase.policyBinding.Spec.ParamRef.Namespace = ""
  2450  			if err := createAndWaitReady(t, client, testcase.policyBinding, nil); err != nil {
  2451  				t.Fatal(err)
  2452  			}
  2453  
  2454  			_, err = client.CoreV1().Namespaces().Create(context.TODO(), testcase.namespace, metav1.CreateOptions{})
  2455  
  2456  			checkExpectedError(t, err, testcase.err)
  2457  			checkFailureReason(t, err, testcase.failureReason)
  2458  		})
  2459  	}
  2460  }
  2461  
  2462  func TestBindingRemoval(t *testing.T) {
  2463  	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)
  2464  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  2465  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2466  	}, framework.SharedEtcd())
  2467  	if err != nil {
  2468  		t.Fatal(err)
  2469  	}
  2470  	defer server.TearDownFn()
  2471  
  2472  	config := server.ClientConfig
  2473  
  2474  	client, err := clientset.NewForConfig(config)
  2475  	if err != nil {
  2476  		t.Fatal(err)
  2477  	}
  2478  
  2479  	policy := withValidations([]admissionregistrationv1.Validation{
  2480  		{
  2481  			Expression: "false",
  2482  			Message:    "policy still in effect",
  2483  		},
  2484  	}, withNamespaceMatch(withFailurePolicy(admissionregistrationv1.Fail, makePolicy("test-policy"))))
  2485  	policy = withWaitReadyConstraintAndExpression(policy)
  2486  	if _, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
  2487  		t.Fatal(err)
  2488  	}
  2489  
  2490  	binding := makeBinding("test-binding", "test-policy", "test-params")
  2491  	if err := createAndWaitReady(t, client, binding, nil); err != nil {
  2492  		t.Fatal(err)
  2493  	}
  2494  	// check that the policy is active
  2495  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  2496  		namespace := &v1.Namespace{
  2497  			ObjectMeta: metav1.ObjectMeta{
  2498  				GenerateName: "check-namespace",
  2499  			},
  2500  		}
  2501  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{})
  2502  		if err != nil {
  2503  			if strings.Contains(err.Error(), "policy still in effect") {
  2504  				return true, nil
  2505  			} else {
  2506  				// unexpected error while attempting namespace creation
  2507  				return true, err
  2508  			}
  2509  		}
  2510  		return false, nil
  2511  	}); waitErr != nil {
  2512  		t.Errorf("timed out waiting: %v", waitErr)
  2513  	}
  2514  	if err = client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Delete(context.TODO(), "test-binding", metav1.DeleteOptions{}); err != nil {
  2515  		t.Fatal(err)
  2516  	}
  2517  
  2518  	// wait for binding to be deleted
  2519  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  2520  
  2521  		_, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Get(context.TODO(), "test-binding", metav1.GetOptions{})
  2522  		if err != nil {
  2523  			if apierrors.IsNotFound(err) {
  2524  				return true, nil
  2525  			} else {
  2526  				return true, err
  2527  			}
  2528  		}
  2529  
  2530  		return false, nil
  2531  	}); waitErr != nil {
  2532  		t.Errorf("timed out waiting: %v", waitErr)
  2533  	}
  2534  
  2535  	// policy should be considered in an invalid state and namespace creation should be allowed
  2536  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  2537  		namespace := &v1.Namespace{
  2538  			ObjectMeta: metav1.ObjectMeta{
  2539  				GenerateName: "test-namespace",
  2540  			},
  2541  		}
  2542  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{})
  2543  		if err != nil {
  2544  			t.Logf("namespace creation failed: %s", err)
  2545  			return false, nil
  2546  		}
  2547  
  2548  		return true, nil
  2549  	}); waitErr != nil {
  2550  		t.Errorf("expected namespace creation to succeed but timed out waiting: %v", waitErr)
  2551  	}
  2552  }
  2553  
  2554  // Test_ValidateSecondaryAuthorization tests a ValidatingAdmissionPolicy that performs secondary authorization checks
  2555  // for both users and service accounts.
  2556  func Test_ValidateSecondaryAuthorization(t *testing.T) {
  2557  	testcases := []struct {
  2558  		name             string
  2559  		rbac             *rbacv1.PolicyRule
  2560  		expression       string
  2561  		allowed          bool
  2562  		extraAccountFn   func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset
  2563  		extraAccountRbac *rbacv1.PolicyRule
  2564  	}{
  2565  		{
  2566  			name: "principal is allowed to create a specific deployment",
  2567  			rbac: &rbacv1.PolicyRule{
  2568  				Verbs:         []string{"create"},
  2569  				APIGroups:     []string{"apps"},
  2570  				Resources:     []string{"deployments/status"},
  2571  				ResourceNames: []string{"charmander"},
  2572  			},
  2573  			expression: "authorizer.group('apps').resource('deployments').subresource('status').namespace('default').namespace('default').name('charmander').check('create').allowed()",
  2574  			allowed:    true,
  2575  		},
  2576  		{
  2577  			name:       "principal is not allowed to create a specific deployment",
  2578  			expression: "authorizer.group('apps').resource('deployments').subresource('status').namespace('default').name('charmander').check('create').allowed()",
  2579  			allowed:    false,
  2580  		},
  2581  		{
  2582  			name: "principal is authorized for custom verb on current resource",
  2583  			rbac: &rbacv1.PolicyRule{
  2584  				Verbs:     []string{"anthropomorphize"},
  2585  				APIGroups: []string{""},
  2586  				Resources: []string{"namespaces"},
  2587  			},
  2588  			expression: "authorizer.requestResource.check('anthropomorphize').allowed()",
  2589  			allowed:    true,
  2590  		},
  2591  		{
  2592  			name:       "principal is not authorized for custom verb on current resource",
  2593  			expression: "authorizer.requestResource.check('anthropomorphize').allowed()",
  2594  			allowed:    false,
  2595  		},
  2596  		{
  2597  			name:           "serviceaccount is authorized for custom verb on current resource",
  2598  			extraAccountFn: serviceAccountClient("default", "extra-acct"),
  2599  			extraAccountRbac: &rbacv1.PolicyRule{
  2600  				Verbs:     []string{"anthropomorphize"},
  2601  				APIGroups: []string{""},
  2602  				Resources: []string{"pods"},
  2603  			},
  2604  			expression: "authorizer.serviceAccount('default', 'extra-acct').group('').resource('pods').check('anthropomorphize').allowed()",
  2605  			allowed:    true,
  2606  		},
  2607  	}
  2608  
  2609  	for _, testcase := range testcases {
  2610  		t.Run(testcase.name, func(t *testing.T) {
  2611  			clients := map[string]func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset{
  2612  				"user":           secondaryAuthorizationUserClient,
  2613  				"serviceaccount": secondaryAuthorizationServiceAccountClient,
  2614  			}
  2615  
  2616  			for clientName, clientFn := range clients {
  2617  				t.Run(clientName, func(t *testing.T) {
  2618  					featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)
  2619  					server, err := apiservertesting.StartTestServer(t, nil, []string{
  2620  						"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2621  						"--authorization-mode=RBAC",
  2622  						"--anonymous-auth",
  2623  					}, framework.SharedEtcd())
  2624  					if err != nil {
  2625  						t.Fatal(err)
  2626  					}
  2627  					defer server.TearDownFn()
  2628  
  2629  					// For test set up such as creating policies, bindings and RBAC rules.
  2630  					adminClient := clientset.NewForConfigOrDie(server.ClientConfig)
  2631  
  2632  					// Principal is always allowed to create and update namespaces so that the admission requests to test
  2633  					// authorization expressions can be sent by the principal.
  2634  					rules := []rbacv1.PolicyRule{{
  2635  						Verbs:     []string{"create", "update"},
  2636  						APIGroups: []string{""},
  2637  						Resources: []string{"namespaces"},
  2638  					}}
  2639  					if testcase.rbac != nil {
  2640  						rules = append(rules, *testcase.rbac)
  2641  					}
  2642  
  2643  					client := clientFn(t, adminClient, server.ClientConfig, rules)
  2644  
  2645  					if testcase.extraAccountFn != nil {
  2646  						var extraRules []rbacv1.PolicyRule
  2647  						if testcase.extraAccountRbac != nil {
  2648  							extraRules = append(rules, *testcase.extraAccountRbac)
  2649  						}
  2650  						testcase.extraAccountFn(t, adminClient, server.ClientConfig, extraRules)
  2651  					}
  2652  
  2653  					policy := withWaitReadyConstraintAndExpression(withValidations([]admissionregistrationv1.Validation{
  2654  						{
  2655  							Expression: testcase.expression,
  2656  						},
  2657  					}, withFailurePolicy(admissionregistrationv1.Fail, withNamespaceMatch(makePolicy("validate-authz")))))
  2658  					if _, err := adminClient.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
  2659  						t.Fatal(err)
  2660  					}
  2661  					if err := createAndWaitReady(t, adminClient, makeBinding("validate-authz-binding", "validate-authz", ""), nil); err != nil {
  2662  						t.Fatal(err)
  2663  					}
  2664  
  2665  					ns := &v1.Namespace{
  2666  						ObjectMeta: metav1.ObjectMeta{
  2667  							Name: "test-authz",
  2668  						},
  2669  					}
  2670  					_, err = client.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{})
  2671  
  2672  					var expected metav1.StatusReason = ""
  2673  					if !testcase.allowed {
  2674  						expected = metav1.StatusReasonInvalid
  2675  					}
  2676  					checkFailureReason(t, err, expected)
  2677  				})
  2678  			}
  2679  		})
  2680  	}
  2681  }
  2682  
  2683  func TestCRDsOnStartup(t *testing.T) {
  2684  	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)
  2685  
  2686  	testContext, testCancel := context.WithCancel(context.Background())
  2687  	defer testCancel()
  2688  
  2689  	// Start server and create CRD, and validatingadmission policy and binding
  2690  	etcdConfig := framework.SharedEtcd()
  2691  	server := apiservertesting.StartTestServerOrDie(t, nil, []string{
  2692  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2693  		"--authorization-mode=RBAC",
  2694  		"--anonymous-auth",
  2695  	}, etcdConfig)
  2696  	client := clientset.NewForConfigOrDie(server.ClientConfig)
  2697  	dynamicClient := dynamic.NewForConfigOrDie(server.ClientConfig)
  2698  	apiextclient := apiextensionsclientset.NewForConfigOrDie(server.ClientConfig)
  2699  	myCRD := &apiextensionsv1.CustomResourceDefinition{
  2700  		ObjectMeta: metav1.ObjectMeta{
  2701  			Name: "foos.cr.bar.com",
  2702  		},
  2703  		Spec: apiextensionsv1.CustomResourceDefinitionSpec{
  2704  			Group: "cr.bar.com",
  2705  			Scope: apiextensionsv1.NamespaceScoped,
  2706  			Names: apiextensionsv1.CustomResourceDefinitionNames{
  2707  				Plural: "foos",
  2708  				Kind:   "Foo",
  2709  			},
  2710  			Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
  2711  				{
  2712  					Name:    "v1",
  2713  					Served:  true,
  2714  					Storage: true,
  2715  					Schema:  fixtures.AllowAllSchema(),
  2716  				},
  2717  			},
  2718  		},
  2719  	}
  2720  
  2721  	// Create a bunch of fake CRDs to make the initial startup sync take a long time
  2722  	for i := 0; i < 100; i++ {
  2723  		crd := myCRD.DeepCopy()
  2724  		crd.Name = fmt.Sprintf("foos%d.cr.bar.com", i)
  2725  		crd.Spec.Names.Plural = fmt.Sprintf("foos%d", i)
  2726  		crd.Spec.Names.Kind = fmt.Sprintf("Foo%d", i)
  2727  
  2728  		if _, err := apiextclient.ApiextensionsV1().CustomResourceDefinitions().Create(context.Background(), crd, metav1.CreateOptions{}); err != nil {
  2729  			t.Fatal(err)
  2730  		}
  2731  	}
  2732  
  2733  	etcd.CreateTestCRDs(t, apiextclient, false, myCRD)
  2734  	crdGVK := schema.GroupVersionKind{
  2735  		Group:   "cr.bar.com",
  2736  		Version: "v1",
  2737  		Kind:    "Foo",
  2738  	}
  2739  	crdGVR := crdGVK.GroupVersion().WithResource("foos")
  2740  
  2741  	param := &unstructured.Unstructured{
  2742  		Object: map[string]interface{}{
  2743  			"metadata": map[string]interface{}{
  2744  				"name":      "test",
  2745  				"namespace": "default",
  2746  			},
  2747  			"foo": "bar",
  2748  		},
  2749  	}
  2750  	param.GetObjectKind().SetGroupVersionKind(crdGVK)
  2751  
  2752  	if _, err := dynamicClient.Resource(crdGVR).Namespace("default").Create(context.TODO(), param, metav1.CreateOptions{}); err != nil {
  2753  		t.Fatal(err)
  2754  	}
  2755  
  2756  	policy := withValidations([]admissionregistrationv1.Validation{
  2757  		{
  2758  			Expression: "object.metadata.name.startsWith(params.metadata.name)",
  2759  			Message:    "wrong prefix",
  2760  		},
  2761  	}, withParams(withCRDParamKind(crdGVK.Kind, crdGVK.Group, crdGVK.Version), withNamespaceMatch(withFailurePolicy(admissionregistrationv1.Fail, makePolicy("allowed-prefixes")))))
  2762  	policy = withWaitReadyConstraintAndExpression(policy)
  2763  	_, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  2764  	if err != nil {
  2765  		t.Fatal(err)
  2766  	}
  2767  
  2768  	// validate that namespaces starting with "test" are allowed
  2769  	policyBinding := makeBinding("allowed-prefixes-binding", "allowed-prefixes", "test")
  2770  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  2771  		t.Fatal(err)
  2772  	}
  2773  
  2774  	doCheck := func(client clientset.Interface) {
  2775  		if waitErr := wait.PollUntilContextTimeout(testContext, time.Millisecond*100, 3*time.Minute, true, func(ctx context.Context) (bool, error) {
  2776  			disallowedNamespace := &v1.Namespace{
  2777  				ObjectMeta: metav1.ObjectMeta{
  2778  					GenerateName: "not-test-",
  2779  				},
  2780  			}
  2781  
  2782  			_, err = client.CoreV1().Namespaces().Create(testContext, disallowedNamespace, metav1.CreateOptions{})
  2783  			if err == nil {
  2784  				return false, nil
  2785  			}
  2786  
  2787  			if strings.Contains(err.Error(), "not yet synced to use for admission") {
  2788  				return false, nil
  2789  			}
  2790  
  2791  			if strings.Contains(err.Error(), "failed to find resource referenced by paramKind") {
  2792  				return false, nil
  2793  			}
  2794  
  2795  			if !strings.Contains(err.Error(), "wrong prefix") {
  2796  				return false, err
  2797  			}
  2798  
  2799  			return true, nil
  2800  		}); waitErr != nil {
  2801  			t.Errorf("timed out waiting: %v", err)
  2802  		}
  2803  	}
  2804  
  2805  	// Show that the policy & binding are correctly working before restarting
  2806  	// to use the paramKind and deliver an error
  2807  	doCheck(client)
  2808  	server.TearDownFn()
  2809  
  2810  	// Start the server.
  2811  	server = apiservertesting.StartTestServerOrDie(
  2812  		t,
  2813  		&apiservertesting.TestServerInstanceOptions{},
  2814  		[]string{
  2815  			"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2816  			"--authorization-mode=RBAC",
  2817  			"--anonymous-auth",
  2818  		},
  2819  		etcdConfig)
  2820  	defer server.TearDownFn()
  2821  
  2822  	// Now that the server is restarted, show again that the policy & binding are correctly working
  2823  	client = clientset.NewForConfigOrDie(server.ClientConfig)
  2824  
  2825  	doCheck(client)
  2826  
  2827  }
  2828  
  2829  type clientFn func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset
  2830  
  2831  func secondaryAuthorizationUserClient(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset {
  2832  	clientConfig = rest.CopyConfig(clientConfig)
  2833  	clientConfig.Impersonate = rest.ImpersonationConfig{
  2834  		UserName: "alice",
  2835  		UID:      "1234",
  2836  	}
  2837  	client := clientset.NewForConfigOrDie(clientConfig)
  2838  
  2839  	for _, rule := range rules {
  2840  		authutil.GrantUserAuthorization(t, context.TODO(), adminClient, "alice", rule)
  2841  	}
  2842  	return client
  2843  }
  2844  
  2845  func secondaryAuthorizationServiceAccountClient(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset {
  2846  	return serviceAccountClient("default", "test-service-acct")(t, adminClient, clientConfig, rules)
  2847  }
  2848  
  2849  func serviceAccountClient(namespace, name string) clientFn {
  2850  	return func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset {
  2851  		clientConfig = rest.CopyConfig(clientConfig)
  2852  		sa, err := adminClient.CoreV1().ServiceAccounts(namespace).Create(context.TODO(), &v1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: name}}, metav1.CreateOptions{})
  2853  		if err != nil {
  2854  			t.Fatal(err)
  2855  		}
  2856  		uid := sa.UID
  2857  
  2858  		clientConfig.Impersonate = rest.ImpersonationConfig{
  2859  			UserName: "system:serviceaccount:" + namespace + ":" + name,
  2860  			UID:      string(uid),
  2861  		}
  2862  		client := clientset.NewForConfigOrDie(clientConfig)
  2863  
  2864  		for _, rule := range rules {
  2865  			authutil.GrantServiceAccountAuthorization(t, context.TODO(), adminClient, name, namespace, rule)
  2866  		}
  2867  		return client
  2868  	}
  2869  }
  2870  
  2871  func withWaitReadyConstraintAndExpression(policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2872  	policy = policy.DeepCopy()
  2873  	policy.Spec.MatchConstraints.ResourceRules = append(policy.Spec.MatchConstraints.ResourceRules, admissionregistrationv1.NamedRuleWithOperations{
  2874  		ResourceNames: []string{"test-marker"},
  2875  		RuleWithOperations: admissionregistrationv1.RuleWithOperations{
  2876  			Operations: []admissionregistrationv1.OperationType{
  2877  				"UPDATE",
  2878  			},
  2879  			Rule: admissionregistrationv1.Rule{
  2880  				APIGroups: []string{
  2881  					"",
  2882  				},
  2883  				APIVersions: []string{
  2884  					"v1",
  2885  				},
  2886  				Resources: []string{
  2887  					"endpoints",
  2888  				},
  2889  			},
  2890  		},
  2891  	})
  2892  	policy.Spec.Validations = append([]admissionregistrationv1.Validation{{
  2893  		Expression: "object.metadata.name != 'test-marker'",
  2894  		Message:    "marker denied; policy is ready",
  2895  	}}, policy.Spec.Validations...)
  2896  	return policy
  2897  }
  2898  
  2899  func createAndWaitReady(t *testing.T, client clientset.Interface, binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding, matchLabels map[string]string) error {
  2900  	return createAndWaitReadyNamespaced(t, client, binding, matchLabels, "default")
  2901  }
  2902  
  2903  func createAndWaitReadyNamespaced(t *testing.T, client clientset.Interface, binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding, matchLabels map[string]string, ns string) error {
  2904  	return createAndWaitReadyNamespacedWithWarnHandler(t, client, binding, matchLabels, ns, newWarningHandler())
  2905  }
  2906  
  2907  func createAndWaitReadyNamespacedWithWarnHandler(t *testing.T, client clientset.Interface, binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding, matchLabels map[string]string, ns string, handler *warningHandler) error {
  2908  	marker := &v1.Endpoints{ObjectMeta: metav1.ObjectMeta{Name: "test-marker", Namespace: ns, Labels: matchLabels}}
  2909  	defer func() {
  2910  		err := client.CoreV1().Endpoints(ns).Delete(context.TODO(), marker.Name, metav1.DeleteOptions{})
  2911  		if err != nil {
  2912  			t.Logf("error deleting marker: %v", err)
  2913  		}
  2914  	}()
  2915  	marker, err := client.CoreV1().Endpoints(ns).Create(context.TODO(), marker, metav1.CreateOptions{})
  2916  	if err != nil {
  2917  		return err
  2918  	}
  2919  
  2920  	_, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Create(context.TODO(), binding, metav1.CreateOptions{})
  2921  	if err != nil {
  2922  		return err
  2923  	}
  2924  
  2925  	if waitErr := wait.PollImmediate(time.Millisecond*5, wait.ForeverTestTimeout, func() (bool, error) {
  2926  		handler.reset()
  2927  		_, err := client.CoreV1().Endpoints(ns).Patch(context.TODO(), marker.Name, types.JSONPatchType, []byte("[]"), metav1.PatchOptions{})
  2928  		if handler.hasObservedMarker() {
  2929  			return true, nil
  2930  		}
  2931  		if err != nil && strings.Contains(err.Error(), "marker denied; policy is ready") {
  2932  			return true, nil
  2933  		} else if err != nil && strings.Contains(err.Error(), "not yet synced to use for admission") {
  2934  			t.Logf("waiting for policy to be ready. Marker: %v. Admission not synced yet: %v", marker, err)
  2935  			return false, nil
  2936  		} else if err != nil && strings.Contains(err.Error(), "validation failed due to running out of cost budget, no further validation rules will be run") {
  2937  			t.Logf("policy is ready and exceeds the budget: %v", err)
  2938  			return true, nil
  2939  		} else if err != nil && strings.Contains(err.Error(), "operation cancelled: actual cost limit exceeded") {
  2940  			t.Logf("policy is ready and exceeds the budget: %v", err)
  2941  			return true, nil
  2942  		} else {
  2943  			t.Logf("waiting for policy to be ready. Marker: %v, Last marker patch response: %v", marker, err)
  2944  			return false, err
  2945  		}
  2946  	}); waitErr != nil {
  2947  		return waitErr
  2948  	}
  2949  	t.Logf("Marker ready: %v", marker)
  2950  	handler.reset()
  2951  	return nil
  2952  }
  2953  
  2954  func withMatchNamespace(binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding, ns string) *admissionregistrationv1.ValidatingAdmissionPolicyBinding {
  2955  	binding.Spec.MatchResources = &admissionregistrationv1.MatchResources{
  2956  		NamespaceSelector: &metav1.LabelSelector{
  2957  			MatchExpressions: []metav1.LabelSelectorRequirement{
  2958  				{
  2959  					Key:      "kubernetes.io/metadata.name",
  2960  					Operator: metav1.LabelSelectorOpIn,
  2961  					Values:   []string{ns},
  2962  				},
  2963  			},
  2964  		},
  2965  	}
  2966  	return binding
  2967  }
  2968  
  2969  func makePolicy(name string) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2970  	return &admissionregistrationv1.ValidatingAdmissionPolicy{
  2971  		ObjectMeta: metav1.ObjectMeta{Name: name},
  2972  	}
  2973  }
  2974  
  2975  func withParams(params *admissionregistrationv1.ParamKind, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2976  	policy.Spec.ParamKind = params
  2977  	return policy
  2978  }
  2979  
  2980  func configParamKind() *admissionregistrationv1.ParamKind {
  2981  	return &admissionregistrationv1.ParamKind{
  2982  		APIVersion: "v1",
  2983  		Kind:       "ConfigMap",
  2984  	}
  2985  }
  2986  
  2987  func withFailurePolicy(failure admissionregistrationv1.FailurePolicyType, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2988  	policy.Spec.FailurePolicy = &failure
  2989  	return policy
  2990  }
  2991  
  2992  func withNamespaceMatch(policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2993  	return withPolicyMatch("namespaces", policy)
  2994  }
  2995  
  2996  func withConfigMapMatch(policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  2997  	return withPolicyMatch("configmaps", policy)
  2998  }
  2999  
  3000  func withObjectSelector(labelSelector *metav1.LabelSelector, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  3001  	policy.Spec.MatchConstraints.ObjectSelector = labelSelector
  3002  	return policy
  3003  }
  3004  
  3005  func withNamespaceSelector(labelSelector *metav1.LabelSelector, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  3006  	policy.Spec.MatchConstraints.NamespaceSelector = labelSelector
  3007  	return policy
  3008  }
  3009  
  3010  func withPolicyMatch(resource string, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  3011  	policy.Spec.MatchConstraints = &admissionregistrationv1.MatchResources{
  3012  		ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{
  3013  			{
  3014  				RuleWithOperations: admissionregistrationv1.RuleWithOperations{
  3015  					Operations: []admissionregistrationv1.OperationType{
  3016  						"*",
  3017  					},
  3018  					Rule: admissionregistrationv1.Rule{
  3019  						APIGroups: []string{
  3020  							"",
  3021  						},
  3022  						APIVersions: []string{
  3023  							"*",
  3024  						},
  3025  						Resources: []string{
  3026  							resource,
  3027  						},
  3028  					},
  3029  				},
  3030  			},
  3031  		},
  3032  	}
  3033  	return policy
  3034  }
  3035  
  3036  func withExcludePolicyMatch(resource string, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  3037  	policy.Spec.MatchConstraints.ExcludeResourceRules = []admissionregistrationv1.NamedRuleWithOperations{
  3038  		{
  3039  			RuleWithOperations: admissionregistrationv1.RuleWithOperations{
  3040  				Operations: []admissionregistrationv1.OperationType{
  3041  					"*",
  3042  				},
  3043  				Rule: admissionregistrationv1.Rule{
  3044  					APIGroups: []string{
  3045  						"",
  3046  					},
  3047  					APIVersions: []string{
  3048  						"*",
  3049  					},
  3050  					Resources: []string{
  3051  						resource,
  3052  					},
  3053  				},
  3054  			},
  3055  		},
  3056  	}
  3057  	return policy
  3058  }
  3059  
  3060  func withPolicyExistsLabels(labels []string, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  3061  	if policy.Spec.MatchConstraints == nil {
  3062  		policy.Spec.MatchConstraints = &admissionregistrationv1.MatchResources{}
  3063  	}
  3064  	matchExprs := buildExistsSelector(labels)
  3065  	policy.Spec.MatchConstraints.ObjectSelector = &metav1.LabelSelector{
  3066  		MatchExpressions: matchExprs,
  3067  	}
  3068  	return policy
  3069  }
  3070  
  3071  func withValidations(validations []admissionregistrationv1.Validation, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  3072  	policy.Spec.Validations = validations
  3073  	return policy
  3074  }
  3075  
  3076  func withVariables(variables []admissionregistrationv1.Variable, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  3077  	policy.Spec.Variables = variables
  3078  	return policy
  3079  }
  3080  
  3081  func withMatchConditions(matchConditions []admissionregistrationv1.MatchCondition, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  3082  	policy.Spec.MatchConditions = matchConditions
  3083  	return policy
  3084  }
  3085  
  3086  func withAuditAnnotations(auditAnnotations []admissionregistrationv1.AuditAnnotation, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
  3087  	policy.Spec.AuditAnnotations = auditAnnotations
  3088  	return policy
  3089  }
  3090  
  3091  func makeBinding(name, policyName, paramName string) *admissionregistrationv1.ValidatingAdmissionPolicyBinding {
  3092  	var paramRef *admissionregistrationv1.ParamRef
  3093  	if paramName != "" {
  3094  		denyAction := admissionregistrationv1.DenyAction
  3095  		paramRef = &admissionregistrationv1.ParamRef{
  3096  			Name:                    paramName,
  3097  			Namespace:               "default",
  3098  			ParameterNotFoundAction: &denyAction,
  3099  		}
  3100  	}
  3101  	return &admissionregistrationv1.ValidatingAdmissionPolicyBinding{
  3102  		ObjectMeta: metav1.ObjectMeta{Name: name},
  3103  		Spec: admissionregistrationv1.ValidatingAdmissionPolicyBindingSpec{
  3104  			PolicyName:        policyName,
  3105  			ParamRef:          paramRef,
  3106  			ValidationActions: []admissionregistrationv1.ValidationAction{admissionregistrationv1.Deny},
  3107  		},
  3108  	}
  3109  }
  3110  
  3111  func withValidationActions(validationActions []admissionregistrationv1.ValidationAction, binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding) *admissionregistrationv1.ValidatingAdmissionPolicyBinding {
  3112  	binding.Spec.ValidationActions = validationActions
  3113  	return binding
  3114  }
  3115  
  3116  func withBindingExistsLabels(labels []string, policy *admissionregistrationv1.ValidatingAdmissionPolicy, binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding) *admissionregistrationv1.ValidatingAdmissionPolicyBinding {
  3117  	if policy != nil {
  3118  		// shallow copy
  3119  		constraintsCopy := *policy.Spec.MatchConstraints
  3120  		binding.Spec.MatchResources = &constraintsCopy
  3121  	}
  3122  	matchExprs := buildExistsSelector(labels)
  3123  	binding.Spec.MatchResources.ObjectSelector = &metav1.LabelSelector{
  3124  		MatchExpressions: matchExprs,
  3125  	}
  3126  	return binding
  3127  }
  3128  
  3129  func buildExistsSelector(labels []string) []metav1.LabelSelectorRequirement {
  3130  	matchExprs := make([]metav1.LabelSelectorRequirement, len(labels))
  3131  	for i := 0; i < len(labels); i++ {
  3132  		matchExprs[i].Key = labels[i]
  3133  		matchExprs[i].Operator = metav1.LabelSelectorOpExists
  3134  	}
  3135  	return matchExprs
  3136  }
  3137  
  3138  func makeConfigParams(name string, data map[string]string) *v1.ConfigMap {
  3139  	return &v1.ConfigMap{
  3140  		ObjectMeta: metav1.ObjectMeta{Name: name},
  3141  		Data:       data,
  3142  	}
  3143  }
  3144  
  3145  func checkForFailedRule(t *testing.T, err error) {
  3146  	if !strings.Contains(err.Error(), "failed expression") {
  3147  		t.Fatalf("unexpected error (expected to find \"failed expression\"): %s", err)
  3148  	}
  3149  	if strings.Contains(err.Error(), "evaluation error") {
  3150  		t.Fatalf("CEL rule evaluation failed: %s", err)
  3151  	}
  3152  }
  3153  
  3154  func checkFailureReason(t *testing.T, err error, expectedReason metav1.StatusReason) {
  3155  	if err == nil && expectedReason == "" {
  3156  		// no reason was given, no error was passed - early exit
  3157  		return
  3158  	}
  3159  	switch e := err.(type) {
  3160  	case apierrors.APIStatus:
  3161  		reason := e.Status().Reason
  3162  		if reason != expectedReason {
  3163  			t.Logf("actual error reason: %v", reason)
  3164  			t.Logf("expected failure reason: %v", expectedReason)
  3165  			t.Error("Unexpected error reason")
  3166  		}
  3167  	default:
  3168  		t.Errorf("Unexpected error: %v", err)
  3169  	}
  3170  }
  3171  
  3172  func checkExpectedWarnings(t *testing.T, recordedWarnings *warningHandler, expectedWarnings sets.Set[string]) {
  3173  	if !recordedWarnings.equals(expectedWarnings) {
  3174  		t.Errorf("Expected warnings '%v' but got '%v", expectedWarnings, recordedWarnings)
  3175  	}
  3176  }
  3177  
  3178  func checkAuditEvents(t *testing.T, logFile *os.File, auditEvents []utils.AuditEvent, filter utils.AuditAnnotationsFilter) {
  3179  	stream, err := os.OpenFile(logFile.Name(), os.O_RDWR, 0600)
  3180  	if err != nil {
  3181  		t.Errorf("unexpected error: %v", err)
  3182  	}
  3183  	defer stream.Close()
  3184  
  3185  	if auditEvents != nil {
  3186  		missing, err := utils.CheckAuditLinesFiltered(stream, auditEvents, auditv1.SchemeGroupVersion, filter)
  3187  		if err != nil {
  3188  			t.Errorf("unexpected error checking audit lines: %v", err)
  3189  		}
  3190  		if len(missing.MissingEvents) > 0 {
  3191  			t.Errorf("failed to get expected events -- missing: %s", missing)
  3192  		}
  3193  	}
  3194  	if err := stream.Truncate(0); err != nil {
  3195  		t.Errorf("unexpected error truncate file: %v", err)
  3196  	}
  3197  	if _, err := stream.Seek(0, 0); err != nil {
  3198  		t.Errorf("unexpected error reset offset: %v", err)
  3199  	}
  3200  }
  3201  
  3202  func withCRDParamKind(kind, crdGroup, crdVersion string) *admissionregistrationv1.ParamKind {
  3203  	return &admissionregistrationv1.ParamKind{
  3204  		APIVersion: crdGroup + "/" + crdVersion,
  3205  		Kind:       kind,
  3206  	}
  3207  }
  3208  
  3209  func checkExpectedError(t *testing.T, err error, expectedErr string) {
  3210  	if err == nil && expectedErr == "" {
  3211  		return
  3212  	}
  3213  	if err == nil && expectedErr != "" {
  3214  		t.Logf("actual error: %v", err)
  3215  		t.Logf("expected error: %v", expectedErr)
  3216  		t.Fatal("got nil error but expected an error")
  3217  	}
  3218  
  3219  	if err != nil && expectedErr == "" {
  3220  		t.Logf("actual error: %v", err)
  3221  		t.Logf("expected error: %v", expectedErr)
  3222  		t.Fatal("got error but expected none")
  3223  	}
  3224  
  3225  	if !strings.Contains(err.Error(), expectedErr) {
  3226  		t.Logf("actual validation error: %v", err)
  3227  		t.Logf("expected validation error: %v", expectedErr)
  3228  		t.Error("unexpected validation error")
  3229  	}
  3230  }
  3231  
  3232  // Copied from etcd.GetCustomResourceDefinitionData
  3233  func versionedCustomResourceDefinition() *apiextensionsv1.CustomResourceDefinition {
  3234  	return &apiextensionsv1.CustomResourceDefinition{
  3235  		ObjectMeta: metav1.ObjectMeta{
  3236  			Name: "pandas.awesome.bears.com",
  3237  		},
  3238  		Spec: apiextensionsv1.CustomResourceDefinitionSpec{
  3239  			Group: "awesome.bears.com",
  3240  			Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
  3241  				{
  3242  					Name:    "v1",
  3243  					Served:  true,
  3244  					Storage: true,
  3245  					Schema:  fixtures.AllowAllSchema(),
  3246  					Subresources: &apiextensionsv1.CustomResourceSubresources{
  3247  						Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
  3248  						Scale: &apiextensionsv1.CustomResourceSubresourceScale{
  3249  							SpecReplicasPath:   ".spec.replicas",
  3250  							StatusReplicasPath: ".status.replicas",
  3251  							LabelSelectorPath:  func() *string { path := ".status.selector"; return &path }(),
  3252  						},
  3253  					},
  3254  				},
  3255  				{
  3256  					Name:    "v2",
  3257  					Served:  true,
  3258  					Storage: false,
  3259  					Schema:  fixtures.AllowAllSchema(),
  3260  					Subresources: &apiextensionsv1.CustomResourceSubresources{
  3261  						Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
  3262  						Scale: &apiextensionsv1.CustomResourceSubresourceScale{
  3263  							SpecReplicasPath:   ".spec.replicas",
  3264  							StatusReplicasPath: ".status.replicas",
  3265  							LabelSelectorPath:  func() *string { path := ".status.selector"; return &path }(),
  3266  						},
  3267  					},
  3268  				},
  3269  			},
  3270  			Scope: apiextensionsv1.ClusterScoped,
  3271  			Names: apiextensionsv1.CustomResourceDefinitionNames{
  3272  				Plural: "pandas",
  3273  				Kind:   "Panda",
  3274  			},
  3275  		},
  3276  	}
  3277  }
  3278  
  3279  type warningHandler struct {
  3280  	lock           sync.Mutex
  3281  	warnings       sets.Set[string]
  3282  	observedMarker bool
  3283  }
  3284  
  3285  func newWarningHandler() *warningHandler {
  3286  	return &warningHandler{warnings: sets.New[string]()}
  3287  }
  3288  
  3289  func (w *warningHandler) reset() {
  3290  	w.lock.Lock()
  3291  	defer w.lock.Unlock()
  3292  	w.warnings = sets.New[string]()
  3293  	w.observedMarker = false
  3294  }
  3295  
  3296  func (w *warningHandler) equals(s sets.Set[string]) bool {
  3297  	w.lock.Lock()
  3298  	defer w.lock.Unlock()
  3299  	return w.warnings.Equal(s)
  3300  }
  3301  
  3302  func (w *warningHandler) hasObservedMarker() bool {
  3303  	w.lock.Lock()
  3304  	defer w.lock.Unlock()
  3305  	return w.observedMarker
  3306  }
  3307  
  3308  func (w *warningHandler) HandleWarningHeader(code int, _ string, message string) {
  3309  	if strings.HasSuffix(message, "marker denied; policy is ready") {
  3310  		func() {
  3311  			w.lock.Lock()
  3312  			defer w.lock.Unlock()
  3313  			w.observedMarker = true
  3314  		}()
  3315  	}
  3316  	if code != 299 || len(message) == 0 {
  3317  		return
  3318  	}
  3319  	w.lock.Lock()
  3320  	defer w.lock.Unlock()
  3321  	w.warnings.Insert(message)
  3322  }
  3323  
  3324  func expectedAuditEvents(auditAnnotations map[string]string, ns string, code int32) []utils.AuditEvent {
  3325  	return []utils.AuditEvent{
  3326  		{
  3327  			Level:                  auditinternal.LevelRequest,
  3328  			Stage:                  auditinternal.StageResponseComplete,
  3329  			RequestURI:             fmt.Sprintf("/api/v1/namespaces/%s/configmaps", ns),
  3330  			Verb:                   "create",
  3331  			Code:                   code,
  3332  			User:                   "system:apiserver",
  3333  			ImpersonatedUser:       testReinvocationClientUsername,
  3334  			ImpersonatedGroups:     "system:authenticated",
  3335  			Resource:               "configmaps",
  3336  			Namespace:              ns,
  3337  			AuthorizeDecision:      "allow",
  3338  			RequestObject:          true,
  3339  			ResponseObject:         false,
  3340  			CustomAuditAnnotations: auditAnnotations,
  3341  		},
  3342  	}
  3343  }
  3344  
  3345  const (
  3346  	testReinvocationClientUsername = "webhook-reinvocation-integration-client"
  3347  	auditPolicy                    = `
  3348  apiVersion: audit.k8s.io/v1
  3349  kind: Policy
  3350  rules:
  3351    - level: Request
  3352      resources:
  3353        - group: "" # core
  3354          resources: ["configmaps"]
  3355  `
  3356  )
  3357  
  3358  func TestAuthorizationDecisionCaching(t *testing.T) {
  3359  	for _, tc := range []struct {
  3360  		name        string
  3361  		validations []admissionregistrationv1.Validation
  3362  	}{
  3363  		{
  3364  			name: "hit",
  3365  			validations: []admissionregistrationv1.Validation{
  3366  				{
  3367  					Expression: "authorizer.requestResource.check('test').reason() == authorizer.requestResource.check('test').reason()",
  3368  				},
  3369  			},
  3370  		},
  3371  		{
  3372  			name: "miss",
  3373  			validations: []admissionregistrationv1.Validation{
  3374  				{
  3375  					Expression: "authorizer.requestResource.subresource('a').check('test').reason() == '1'",
  3376  				},
  3377  				{
  3378  					Expression: "authorizer.requestResource.subresource('b').check('test').reason() == '2'",
  3379  				},
  3380  				{
  3381  					Expression: "authorizer.requestResource.subresource('c').check('test').reason() == '3'",
  3382  				},
  3383  			},
  3384  		},
  3385  	} {
  3386  		t.Run(tc.name, func(t *testing.T) {
  3387  			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)
  3388  
  3389  			ctx, cancel := context.WithCancel(context.TODO())
  3390  			defer cancel()
  3391  
  3392  			var nChecks int
  3393  			webhook := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3394  				var review authorizationv1.SubjectAccessReview
  3395  				if err := json.NewDecoder(r.Body).Decode(&review); err != nil {
  3396  					http.Error(w, err.Error(), http.StatusBadRequest)
  3397  				}
  3398  
  3399  				review.Status.Allowed = true
  3400  				if review.Spec.ResourceAttributes.Verb == "test" {
  3401  					nChecks++
  3402  					review.Status.Reason = fmt.Sprintf("%d", nChecks)
  3403  				}
  3404  
  3405  				w.Header().Set("Content-Type", "application/json")
  3406  				if err := json.NewEncoder(w).Encode(review); err != nil {
  3407  					http.Error(w, err.Error(), http.StatusInternalServerError)
  3408  				}
  3409  			}))
  3410  			defer webhook.Close()
  3411  
  3412  			kcfd, err := os.CreateTemp("", "kubeconfig-")
  3413  			if err != nil {
  3414  				t.Fatal(err)
  3415  			}
  3416  			func() {
  3417  				defer kcfd.Close()
  3418  				tmpl, err := template.New("kubeconfig").Parse(`
  3419  apiVersion: v1
  3420  kind: Config
  3421  clusters:
  3422    - name: test-authz-service
  3423      cluster:
  3424        server: {{ .Server }}
  3425  users:
  3426    - name: test-api-server
  3427  current-context: webhook
  3428  contexts:
  3429  - context:
  3430      cluster: test-authz-service
  3431      user: test-api-server
  3432    name: webhook
  3433  `)
  3434  				if err != nil {
  3435  					t.Fatal(err)
  3436  				}
  3437  				err = tmpl.Execute(kcfd, struct {
  3438  					Server string
  3439  				}{
  3440  					Server: webhook.URL,
  3441  				})
  3442  				if err != nil {
  3443  					t.Fatal(err)
  3444  				}
  3445  			}()
  3446  
  3447  			client, config, teardown := framework.StartTestServer(ctx, t, framework.TestServerSetup{
  3448  				ModifyServerRunOptions: func(options *options.ServerRunOptions) {
  3449  					options.Admission.GenericAdmission.EnablePlugins = append(options.Admission.GenericAdmission.EnablePlugins, "ValidatingAdmissionPolicy")
  3450  					options.APIEnablement.RuntimeConfig.Set("api/all=true")
  3451  
  3452  					options.Authorization.Modes = []string{authzmodes.ModeWebhook}
  3453  					options.Authorization.WebhookConfigFile = kcfd.Name()
  3454  					options.Authorization.WebhookVersion = "v1"
  3455  					// Bypass webhook cache to observe the policy plugin's cache behavior.
  3456  					options.Authorization.WebhookCacheAuthorizedTTL = 0
  3457  					options.Authorization.WebhookCacheUnauthorizedTTL = 0
  3458  				},
  3459  			})
  3460  			defer teardown()
  3461  
  3462  			policy := &admissionregistrationv1.ValidatingAdmissionPolicy{
  3463  				ObjectMeta: metav1.ObjectMeta{
  3464  					Name: "test-authorization-decision-caching-policy",
  3465  				},
  3466  				Spec: admissionregistrationv1.ValidatingAdmissionPolicySpec{
  3467  					MatchConstraints: &admissionregistrationv1.MatchResources{
  3468  						ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{
  3469  							{
  3470  								ResourceNames: []string{"test-authorization-decision-caching-namespace"},
  3471  								RuleWithOperations: admissionregistrationv1.RuleWithOperations{
  3472  									Operations: []admissionregistrationv1.OperationType{
  3473  										admissionregistrationv1.Create,
  3474  									},
  3475  									Rule: admissionregistrationv1.Rule{
  3476  										APIGroups:   []string{""},
  3477  										APIVersions: []string{"v1"},
  3478  										Resources:   []string{"namespaces"},
  3479  									},
  3480  								},
  3481  							},
  3482  						},
  3483  					},
  3484  					Validations: tc.validations,
  3485  				},
  3486  			}
  3487  
  3488  			policy, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(ctx, withWaitReadyConstraintAndExpression(policy), metav1.CreateOptions{})
  3489  			if err != nil {
  3490  				t.Fatal(err)
  3491  			}
  3492  
  3493  			if err := createAndWaitReady(t, client, makeBinding(policy.Name+"-binding", policy.Name, ""), nil); err != nil {
  3494  				t.Fatal(err)
  3495  			}
  3496  
  3497  			config = rest.CopyConfig(config)
  3498  			config.Impersonate = rest.ImpersonationConfig{
  3499  				UserName: "alice",
  3500  				UID:      "1234",
  3501  			}
  3502  			client, err = clientset.NewForConfig(config)
  3503  			if err != nil {
  3504  				t.Fatal(err)
  3505  			}
  3506  
  3507  			if _, err := client.CoreV1().Namespaces().Create(
  3508  				ctx,
  3509  				&v1.Namespace{
  3510  					ObjectMeta: metav1.ObjectMeta{
  3511  						Name: "test-authorization-decision-caching-namespace",
  3512  					},
  3513  				},
  3514  				metav1.CreateOptions{},
  3515  			); err != nil {
  3516  				t.Fatal(err)
  3517  			}
  3518  		})
  3519  	}
  3520  }