From 13e0887c4cd7e290b0b67d1073b6e18d1bae16a5 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Wed, 25 Aug 2021 14:36:15 -0400 Subject: [PATCH 1/4] PodSecurity: add admission benchmark go test ./plugin/pkg/admission/security/podsecurity -bench /pod -benchmem goos: darwin goarch: amd64 pkg: k8s.io/kubernetes/plugin/pkg/admission/security/podsecurity cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz BenchmarkVerifyPod/enforce-implicit_pod-12 702789 1585 ns/op 2120 B/op 12 allocs/op BenchmarkVerifyPod/enforce-privileged_pod-12 737588 1607 ns/op 2120 B/op 12 allocs/op BenchmarkVerifyPod/enforce-baseline_pod-12 409818 2974 ns/op 3368 B/op 17 allocs/op BenchmarkVerifyPod/enforce-restricted_pod-12 370262 3385 ns/op 3368 B/op 17 allocs/op BenchmarkVerifyPod/warn-baseline_pod-12 391808 3101 ns/op 3368 B/op 17 allocs/op BenchmarkVerifyPod/warn-restricted_pod-12 349411 3452 ns/op 3368 B/op 17 allocs/op BenchmarkVerifyPod/enforce-warn-audit-baseline_pod-12 208221 5735 ns/op 5864 B/op 27 allocs/op BenchmarkVerifyPod/warn-baseline-audit-restricted_pod-12 249662 4849 ns/op 4616 B/op 22 allocs/op PASS ok k8s.io/kubernetes/plugin/pkg/admission/security/podsecurity 10.707s --- .../security/podsecurity/admission_test.go | 123 ++++ .../security/podsecurity/testdata/pod.yaml | 676 ++++++++++++++++++ 2 files changed, 799 insertions(+) create mode 100644 plugin/pkg/admission/security/podsecurity/testdata/pod.yaml diff --git a/plugin/pkg/admission/security/podsecurity/admission_test.go b/plugin/pkg/admission/security/podsecurity/admission_test.go index c28f09c2bfe..8846ce407ca 100644 --- a/plugin/pkg/admission/security/podsecurity/admission_test.go +++ b/plugin/pkg/admission/security/podsecurity/admission_test.go @@ -17,14 +17,27 @@ limitations under the License. package podsecurity import ( + "context" + "io/ioutil" "testing" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apiserver/pkg/admission" + "k8s.io/apiserver/pkg/authentication/user" + utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/core" + v1 "k8s.io/kubernetes/pkg/apis/core/v1" + "k8s.io/kubernetes/pkg/features" podsecurityadmission "k8s.io/pod-security-admission/admission" + "sigs.k8s.io/yaml" ) func TestConvert(t *testing.T) { @@ -58,3 +71,113 @@ func TestConvert(t *testing.T) { } } } + +func BenchmarkVerifyPod(b *testing.B) { + defer featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, features.PodSecurity, true)() + + p, err := newPlugin(nil) + if err != nil { + b.Fatal(err) + } + + p.InspectFeatureGates(utilfeature.DefaultFeatureGate) + + enforceImplicitPrivilegedNamespace := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "enforce-implicit", Labels: map[string]string{}}} + enforcePrivilegedNamespace := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "enforce-privileged", Labels: map[string]string{"pod-security.kubernetes.io/enforce": "privileged"}}} + enforceBaselineNamespace := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "enforce-baseline", Labels: map[string]string{"pod-security.kubernetes.io/enforce": "baseline"}}} + enforceRestrictedNamespace := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "enforce-restricted", Labels: map[string]string{"pod-security.kubernetes.io/enforce": "restricted"}}} + warnBaselineNamespace := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "warn-baseline", Labels: map[string]string{"pod-security.kubernetes.io/warn": "baseline"}}} + warnRestrictedNamespace := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "warn-restricted", Labels: map[string]string{"pod-security.kubernetes.io/warn": "restricted"}}} + enforceWarnAuditBaseline := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "enforce-warn-audit-baseline", Labels: map[string]string{"pod-security.kubernetes.io/enforce": "baseline", "pod-security.kubernetes.io/warn": "baseline", "pod-security.kubernetes.io/audit": "baseline"}}} + warnBaselineAuditRestrictedNamespace := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "warn-baseline-audit-restricted", Labels: map[string]string{"pod-security.kubernetes.io/warn": "baseline", "pod-security.kubernetes.io/audit": "restricted"}}} + c := fake.NewSimpleClientset( + enforceImplicitPrivilegedNamespace, + enforcePrivilegedNamespace, + enforceBaselineNamespace, + enforceRestrictedNamespace, + warnBaselineNamespace, + warnRestrictedNamespace, + enforceWarnAuditBaseline, + warnBaselineAuditRestrictedNamespace, + ) + p.SetExternalKubeClientSet(c) + + informerFactory := informers.NewSharedInformerFactory(c, 0) + p.SetExternalKubeInformerFactory(informerFactory) + stopCh := make(chan struct{}) + defer close(stopCh) + informerFactory.Start(stopCh) + informerFactory.WaitForCacheSync(stopCh) + + if err := p.ValidateInitialization(); err != nil { + b.Fatal(err) + } + + corePod := &core.Pod{} + v1Pod := &corev1.Pod{} + data, err := ioutil.ReadFile("testdata/pod.yaml") + if err != nil { + b.Fatal(err) + } + if err := yaml.Unmarshal(data, v1Pod); err != nil { + b.Fatal(err) + } + if err := v1.Convert_v1_Pod_To_core_Pod(v1Pod, corePod, nil); err != nil { + b.Fatal(err) + } + + appsDeployment := &apps.Deployment{ + ObjectMeta: metav1.ObjectMeta{Name: "mydeployment"}, + Spec: apps.DeploymentSpec{ + Template: core.PodTemplateSpec{ + ObjectMeta: corePod.ObjectMeta, + Spec: corePod.Spec, + }, + }, + } + + namespaces := []string{ + "enforce-implicit", "enforce-privileged", "enforce-baseline", "enforce-restricted", + "warn-baseline", "warn-restricted", + "enforce-warn-audit-baseline", "warn-baseline-audit-restricted", + } + for _, namespace := range namespaces { + b.Run(namespace+"_pod", func(b *testing.B) { + ctx := context.Background() + attrs := admission.NewAttributesRecord( + corePod.DeepCopy(), nil, + schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}, + namespace, "mypod", + schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}, + "", + admission.Create, &metav1.CreateOptions{}, false, + &user.DefaultInfo{Name: "myuser"}, + ) + b.ResetTimer() + for i := 0; i < b.N; i++ { + if err := p.Validate(ctx, attrs, nil); err != nil { + b.Fatal(err) + } + } + }) + + b.Run(namespace+"_deployment", func(b *testing.B) { + ctx := context.Background() + attrs := admission.NewAttributesRecord( + appsDeployment.DeepCopy(), nil, + schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}, + namespace, "mydeployment", + schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}, + "", + admission.Create, &metav1.CreateOptions{}, false, + &user.DefaultInfo{Name: "myuser"}, + ) + b.ResetTimer() + for i := 0; i < b.N; i++ { + if err := p.Validate(ctx, attrs, nil); err != nil { + b.Fatal(err) + } + } + }) + } +} diff --git a/plugin/pkg/admission/security/podsecurity/testdata/pod.yaml b/plugin/pkg/admission/security/podsecurity/testdata/pod.yaml new file mode 100644 index 00000000000..a7754aab5d3 --- /dev/null +++ b/plugin/pkg/admission/security/podsecurity/testdata/pod.yaml @@ -0,0 +1,676 @@ +# this pod fixture is used for benchmarks and should be kept updated to pass the latest restricted policy +apiVersion: v1 +kind: Pod +metadata: + annotations: + scheduler.alpha.kubernetes.io/critical-pod: "" + seccomp.security.alpha.kubernetes.io/pod: runtime/default + creationTimestamp: "2021-08-20T14:35:04Z" + generateName: kube-dns-76dbc85bd5- + labels: + k8s-app: kube-dns + pod-template-hash: 76dbc85bd5 + managedFields: + - apiVersion: v1 + fieldsType: FieldsV1 + fieldsV1: + f:metadata: + f:annotations: + .: {} + f:scheduler.alpha.kubernetes.io/critical-pod: {} + f:seccomp.security.alpha.kubernetes.io/pod: {} + f:generateName: {} + f:labels: + .: {} + f:k8s-app: {} + f:pod-template-hash: {} + f:ownerReferences: + .: {} + k:{"uid":"901a2f14-52d5-468b-af25-6587b60f2887"}: + .: {} + f:apiVersion: {} + f:blockOwnerDeletion: {} + f:controller: {} + f:kind: {} + f:name: {} + f:uid: {} + f:spec: + f:affinity: + .: {} + f:podAntiAffinity: + .: {} + f:preferredDuringSchedulingIgnoredDuringExecution: {} + f:containers: + k:{"name":"dnsmasq"}: + .: {} + f:args: {} + f:image: image-name:tag-name + f:imagePullPolicy: {} + f:livenessProbe: + .: {} + f:failureThreshold: {} + f:httpGet: + .: {} + f:path: {} + f:port: {} + f:scheme: {} + f:initialDelaySeconds: {} + f:periodSeconds: {} + f:successThreshold: {} + f:timeoutSeconds: {} + f:name: {} + f:ports: + .: {} + k:{"containerPort":53,"protocol":"TCP"}: + .: {} + f:containerPort: {} + f:name: {} + f:protocol: {} + k:{"containerPort":53,"protocol":"UDP"}: + .: {} + f:containerPort: {} + f:name: {} + f:protocol: {} + f:resources: + .: {} + f:requests: + .: {} + f:cpu: {} + f:memory: {} + f:securityContext: + .: {} + f:capabilities: + .: {} + f:add: {} + f:drop: {} + f:terminationMessagePath: {} + f:terminationMessagePolicy: {} + f:volumeMounts: + .: {} + k:{"mountPath":"/etc/k8s/dns/dnsmasq-nanny"}: + .: {} + f:mountPath: {} + f:name: {} + k:{"name":"kubedns"}: + .: {} + f:args: {} + f:env: + .: {} + k:{"name":"PROMETHEUS_PORT"}: + .: {} + f:name: {} + f:value: {} + f:image: image-name:tag-name + f:imagePullPolicy: {} + f:livenessProbe: + .: {} + f:failureThreshold: {} + f:httpGet: + .: {} + f:path: {} + f:port: {} + f:scheme: {} + f:initialDelaySeconds: {} + f:periodSeconds: {} + f:successThreshold: {} + f:timeoutSeconds: {} + f:name: {} + f:ports: + .: {} + k:{"containerPort":10053,"protocol":"TCP"}: + .: {} + f:containerPort: {} + f:name: {} + f:protocol: {} + k:{"containerPort":10053,"protocol":"UDP"}: + .: {} + f:containerPort: {} + f:name: {} + f:protocol: {} + k:{"containerPort":10055,"protocol":"TCP"}: + .: {} + f:containerPort: {} + f:name: {} + f:protocol: {} + f:readinessProbe: + .: {} + f:failureThreshold: {} + f:httpGet: + .: {} + f:path: {} + f:port: {} + f:scheme: {} + f:initialDelaySeconds: {} + f:periodSeconds: {} + f:successThreshold: {} + f:timeoutSeconds: {} + f:resources: + .: {} + f:limits: + .: {} + f:memory: {} + f:requests: + .: {} + f:cpu: {} + f:memory: {} + f:securityContext: + .: {} + f:allowPrivilegeEscalation: {} + f:readOnlyRootFilesystem: {} + f:runAsGroup: {} + f:runAsUser: {} + f:terminationMessagePath: {} + f:terminationMessagePolicy: {} + f:volumeMounts: + .: {} + k:{"mountPath":"/kube-dns-config"}: + .: {} + f:mountPath: {} + f:name: {} + k:{"name":"prometheus-to-sd"}: + .: {} + f:command: {} + f:env: + .: {} + k:{"name":"POD_NAME"}: + .: {} + f:name: {} + f:valueFrom: + .: {} + f:fieldRef: + .: {} + f:apiVersion: {} + f:fieldPath: {} + k:{"name":"POD_NAMESPACE"}: + .: {} + f:name: {} + f:valueFrom: + .: {} + f:fieldRef: + .: {} + f:apiVersion: {} + f:fieldPath: {} + f:image: image-name:tag-name + f:imagePullPolicy: {} + f:name: {} + f:resources: {} + f:securityContext: + .: {} + f:allowPrivilegeEscalation: {} + f:readOnlyRootFilesystem: {} + f:runAsGroup: {} + f:runAsUser: {} + f:terminationMessagePath: {} + f:terminationMessagePolicy: {} + k:{"name":"sidecar"}: + .: {} + f:args: {} + f:image: image-name:tag-name + f:imagePullPolicy: {} + f:livenessProbe: + .: {} + f:failureThreshold: {} + f:httpGet: + .: {} + f:path: {} + f:port: {} + f:scheme: {} + f:initialDelaySeconds: {} + f:periodSeconds: {} + f:successThreshold: {} + f:timeoutSeconds: {} + f:name: {} + f:ports: + .: {} + k:{"containerPort":10054,"protocol":"TCP"}: + .: {} + f:containerPort: {} + f:name: {} + f:protocol: {} + f:resources: + .: {} + f:requests: + .: {} + f:cpu: {} + f:memory: {} + f:securityContext: + .: {} + f:allowPrivilegeEscalation: {} + f:readOnlyRootFilesystem: {} + f:runAsGroup: {} + f:runAsUser: {} + f:terminationMessagePath: {} + f:terminationMessagePolicy: {} + f:dnsPolicy: {} + f:enableServiceLinks: {} + f:nodeSelector: + .: {} + f:kubernetes.io/os: {} + f:priorityClassName: {} + f:restartPolicy: {} + f:schedulerName: {} + f:securityContext: + .: {} + f:fsGroup: {} + f:supplementalGroups: {} + f:serviceAccount: {} + f:serviceAccountName: {} + f:terminationGracePeriodSeconds: {} + f:tolerations: {} + f:volumes: + .: {} + k:{"name":"kube-dns-config"}: + .: {} + f:configMap: + .: {} + f:defaultMode: {} + f:name: {} + f:optional: {} + f:name: {} + manager: kube-controller-manager + operation: Update + time: "2021-08-20T14:35:04Z" + - apiVersion: v1 + fieldsType: FieldsV1 + fieldsV1: + f:status: + f:conditions: + .: {} + k:{"type":"PodScheduled"}: + .: {} + f:lastProbeTime: {} + f:lastTransitionTime: {} + f:message: {} + f:reason: {} + f:status: {} + f:type: {} + manager: kube-scheduler + operation: Update + time: "2021-08-20T14:35:04Z" + - apiVersion: v1 + fieldsType: FieldsV1 + fieldsV1: + f:status: + f:conditions: + k:{"type":"ContainersReady"}: + .: {} + f:lastProbeTime: {} + f:lastTransitionTime: {} + f:status: {} + f:type: {} + k:{"type":"Initialized"}: + .: {} + f:lastProbeTime: {} + f:lastTransitionTime: {} + f:status: {} + f:type: {} + k:{"type":"Ready"}: + .: {} + f:lastProbeTime: {} + f:lastTransitionTime: {} + f:status: {} + f:type: {} + f:containerStatuses: {} + f:hostIP: {} + f:phase: {} + f:podIP: {} + f:podIPs: + .: {} + k:{"ip":"10..10.10"}: + .: {} + f:ip: {} + f:startTime: {} + manager: kubelet + operation: Update + time: "2021-08-20T14:36:10Z" + name: kube-dns-76dbc85bd5-zl5tr + namespace: kube-system + ownerReferences: + - apiVersion: apps/v1 + blockOwnerDeletion: true + controller: true + kind: ReplicaSet + name: kube-dns-76dbc85bd5 + uid: 901a2f14-52d5-468b-af25-6587b60f2887 + resourceVersion: "1391" + uid: e98f0f22-0937-4495-8211-d5633e50fb8d +spec: + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchExpressions: + - key: k8s-app + operator: In + values: + - kube-dns + topologyKey: kubernetes.io/hostname + weight: 100 + containers: + - args: + - --domain=cluster.local. + - --dns-port=10053 + - --config-dir=/kube-dns-config + - --v=2 + env: + - name: PROMETHEUS_PORT + value: "10055" + image: image-name:tag-name + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 5 + httpGet: + path: /healthcheck/kubedns + port: 10054 + scheme: HTTP + initialDelaySeconds: 60 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + name: kubedns + ports: + - containerPort: 10053 + name: dns-local + protocol: UDP + - containerPort: 10053 + name: dns-tcp-local + protocol: TCP + - containerPort: 10055 + name: metrics + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /readiness + port: 8081 + scheme: HTTP + initialDelaySeconds: 3 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + resources: + limits: + memory: 210Mi + requests: + cpu: 100m + memory: 70Mi + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsGroup: 1001 + runAsUser: 1001 + runAsNonRoot: true + capabilities: + add: + - NET_BIND_SERVICE + drop: + - ALL + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /kube-dns-config + name: kube-dns-config + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access-s8rz5 + readOnly: true + - args: + - -v=2 + - -logtostderr + - -configDir=/etc/k8s/dns/dnsmasq-nanny + - -restartDnsmasq=true + - -- + - -k + - --cache-size=1000 + - --no-negcache + - --dns-forward-max=1500 + - --log-facility=- + - --server=/cluster.local/127.0.0.1#10053 + - --server=/in-addr.arpa/127.0.0.1#10053 + - --server=/ip6.arpa/127.0.0.1#10053 + image: image-name:tag-name + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 5 + httpGet: + path: /healthcheck/dnsmasq + port: 10054 + scheme: HTTP + initialDelaySeconds: 60 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + name: dnsmasq + ports: + - containerPort: 53 + name: dns + protocol: UDP + - containerPort: 53 + name: dns-tcp + protocol: TCP + resources: + requests: + cpu: 150m + memory: 20Mi + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + capabilities: + add: + - NET_BIND_SERVICE + drop: + - ALL + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /etc/k8s/dns/dnsmasq-nanny + name: kube-dns-config + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access-s8rz5 + readOnly: true + - args: + - --v=2 + - --logtostderr + - --probe=kubedns,127.0.0.1:10053,kubernetes.default.svc.cluster.local,5,SRV + - --probe=dnsmasq,127.0.0.1:53,kubernetes.default.svc.cluster.local,5,SRV + image: image-name:tag-name + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 5 + httpGet: + path: /metrics + port: 10054 + scheme: HTTP + initialDelaySeconds: 60 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + name: sidecar + ports: + - containerPort: 10054 + name: metrics + protocol: TCP + resources: + requests: + cpu: 10m + memory: 20Mi + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsGroup: 1001 + runAsUser: 1001 + runAsNonRoot: true + capabilities: + add: + - NET_BIND_SERVICE + drop: + - ALL + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access-s8rz5 + readOnly: true + - command: + - /monitor + - --stackdriver-prefix=container.googleapis.com/internal/addons + - --api-override=https://test-monitoring.sandbox.googleapis.com/ + - --pod-id=$(POD_NAME) + - --namespace-id=$(POD_NAMESPACE) + - --v=2 + env: + - name: POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + image: image-name:tag-name + imagePullPolicy: IfNotPresent + name: prometheus-to-sd + resources: {} + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsGroup: 1001 + runAsUser: 1001 + runAsNonRoot: true + capabilities: + add: + - NET_BIND_SERVICE + drop: + - ALL + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access-s8rz5 + readOnly: true + dnsPolicy: Default + enableServiceLinks: true + nodeName: mynode + nodeSelector: + kubernetes.io/os: linux + preemptionPolicy: PreemptLowerPriority + priority: 2000000000 + priorityClassName: system-cluster-critical + restartPolicy: Always + schedulerName: default-scheduler + securityContext: + fsGroup: 65534 + seccompProfile: + type: RuntimeDefault + supplementalGroups: + - 65534 + serviceAccount: kube-dns + serviceAccountName: kube-dns + terminationGracePeriodSeconds: 30 + tolerations: + - key: CriticalAddonsOnly + operator: Exists + - effect: NoExecute + key: node.kubernetes.io/not-ready + operator: Exists + tolerationSeconds: 300 + - effect: NoExecute + key: node.kubernetes.io/unreachable + operator: Exists + tolerationSeconds: 300 + volumes: + - configMap: + defaultMode: 420 + name: kube-dns + optional: true + name: kube-dns-config + - name: kube-api-access-s8rz5 + projected: + defaultMode: 420 + sources: + - serviceAccountToken: + expirationSeconds: 3607 + path: token + - configMap: + items: + - key: ca.crt + path: ca.crt + name: kube-root-ca.crt + - downwardAPI: + items: + - fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + path: namespace +status: + conditions: + - lastProbeTime: null + lastTransitionTime: "2021-08-20T14:35:31Z" + status: "True" + type: Initialized + - lastProbeTime: null + lastTransitionTime: "2021-08-20T14:36:10Z" + status: "True" + type: Ready + - lastProbeTime: null + lastTransitionTime: "2021-08-20T14:36:10Z" + status: "True" + type: ContainersReady + - lastProbeTime: null + lastTransitionTime: "2021-08-20T14:35:31Z" + status: "True" + type: PodScheduled + containerStatuses: + - containerID: containerd://f21ec303caca266fa4b81ebe6c210b5aa2b8ea6a262d8038db2c4f57db127187 + image: image-name:tag-name + imageID: imageid@sha256:8e2a7eaa7e6b1ede58d6361d0058a391260a46f0290b7f0368b709494e9e36bf + lastState: {} + name: dnsmasq + ready: true + restartCount: 0 + started: true + state: + running: + startedAt: "2021-08-20T14:36:03Z" + - containerID: containerd://bf3db3f330364ba2af3763a3c0b0bcd137f0556a73fffd0e0dbda61035b696a9 + image: image-name:tag-name + imageID: imageid@sha256:50a1d17afe48a4ae15c9321d8c16d8f1302358c92971884722514c4ed7315ca3 + lastState: {} + name: kubedns + ready: true + restartCount: 0 + started: true + state: + running: + startedAt: "2021-08-20T14:35:52Z" + - containerID: containerd://733304e5217f2c9827736e1226188b11488fd476d0b9f647bd098fe9db89460e + image: image-name:tag-name + imageID: imageid@sha256:aca8ef8aa7fae83e1f8583ed78dd4d11f655b9f22a0a76bda5edce6d8965bdf2 + lastState: {} + name: prometheus-to-sd + ready: true + restartCount: 0 + started: true + state: + running: + startedAt: "2021-08-20T14:36:09Z" + - containerID: containerd://4639ada29f769008d3b21eef48cd061534dfd7875b42d5103179d4f0258667e9 + image: image-name:tag-name + imageID: imageid@sha256:3bb5033aefb3e3dee259ab3d357d38d16eacf9cf2e1542ad577e3796410033ca + lastState: {} + name: sidecar + ready: true + restartCount: 0 + started: true + state: + running: + startedAt: "2021-08-20T14:36:06Z" + hostIP: 10.128.0.48 + phase: Running + podIP: 10..10.10 + podIPs: + - ip: 10..10.10 + qosClass: Burstable + startTime: "2021-08-20T14:35:31Z" \ No newline at end of file From d5589ba65fa2d057d16030a275fc609922390f53 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Wed, 25 Aug 2021 17:08:06 -0400 Subject: [PATCH 2/4] PodSecurity: optimize evaluation of fully-privileged namespaces benchmark old ns/op new ns/op delta BenchmarkVerifyPod/enforce-implicit_pod-12 2658 370 -86.07% BenchmarkVerifyPod/enforce-implicit_deployment-12 2462 408 -83.42% BenchmarkVerifyPod/enforce-privileged_pod-12 2346 420 -82.11% BenchmarkVerifyPod/enforce-privileged_deployment-12 2318 426 -81.64% BenchmarkVerifyPod/enforce-baseline_pod-12 3606 4259 +18.11% BenchmarkVerifyPod/enforce-baseline_deployment-12 2032 341 -83.22% BenchmarkVerifyPod/enforce-restricted_pod-12 3522 3322 -5.68% BenchmarkVerifyPod/enforce-restricted_deployment-12 1893 327 -82.70% BenchmarkVerifyPod/warn-baseline_pod-12 3076 2964 -3.64% BenchmarkVerifyPod/warn-baseline_deployment-12 3111 3069 -1.35% BenchmarkVerifyPod/warn-restricted_pod-12 3155 3223 +2.16% BenchmarkVerifyPod/warn-restricted_deployment-12 3235 3443 +6.43% BenchmarkVerifyPod/enforce-warn-audit-baseline_pod-12 5148 5193 +0.87% BenchmarkVerifyPod/enforce-warn-audit-baseline_deployment-12 4147 4295 +3.57% BenchmarkVerifyPod/warn-baseline-audit-restricted_pod-12 4286 4363 +1.80% BenchmarkVerifyPod/warn-baseline-audit-restricted_deployment-12 4447 4482 +0.79% benchmark old allocs new allocs delta BenchmarkVerifyPod/enforce-implicit_pod-12 12 2 -83.33% BenchmarkVerifyPod/enforce-implicit_deployment-12 14 2 -85.71% BenchmarkVerifyPod/enforce-privileged_pod-12 12 2 -83.33% BenchmarkVerifyPod/enforce-privileged_deployment-12 14 2 -85.71% BenchmarkVerifyPod/enforce-baseline_pod-12 17 17 +0.00% BenchmarkVerifyPod/enforce-baseline_deployment-12 14 2 -85.71% BenchmarkVerifyPod/enforce-restricted_pod-12 17 17 +0.00% BenchmarkVerifyPod/enforce-restricted_deployment-12 14 2 -85.71% BenchmarkVerifyPod/warn-baseline_pod-12 17 17 +0.00% BenchmarkVerifyPod/warn-baseline_deployment-12 19 19 +0.00% BenchmarkVerifyPod/warn-restricted_pod-12 17 17 +0.00% BenchmarkVerifyPod/warn-restricted_deployment-12 19 19 +0.00% BenchmarkVerifyPod/enforce-warn-audit-baseline_pod-12 27 27 +0.00% BenchmarkVerifyPod/enforce-warn-audit-baseline_deployment-12 24 24 +0.00% BenchmarkVerifyPod/warn-baseline-audit-restricted_pod-12 22 22 +0.00% BenchmarkVerifyPod/warn-baseline-audit-restricted_deployment-12 24 24 +0.00% benchmark old bytes new bytes delta BenchmarkVerifyPod/enforce-implicit_pod-12 2120 208 -90.19% BenchmarkVerifyPod/enforce-implicit_deployment-12 2304 208 -90.97% BenchmarkVerifyPod/enforce-privileged_pod-12 2120 208 -90.19% BenchmarkVerifyPod/enforce-privileged_deployment-12 2304 208 -90.97% BenchmarkVerifyPod/enforce-baseline_pod-12 3368 3368 +0.00% BenchmarkVerifyPod/enforce-baseline_deployment-12 2304 208 -90.97% BenchmarkVerifyPod/enforce-restricted_pod-12 3368 3368 +0.00% BenchmarkVerifyPod/enforce-restricted_deployment-12 2304 208 -90.97% BenchmarkVerifyPod/warn-baseline_pod-12 3368 3368 +0.00% BenchmarkVerifyPod/warn-baseline_deployment-12 3552 3552 +0.00% BenchmarkVerifyPod/warn-restricted_pod-12 3368 3368 +0.00% BenchmarkVerifyPod/warn-restricted_deployment-12 3552 3552 +0.00% BenchmarkVerifyPod/enforce-warn-audit-baseline_pod-12 5864 5864 +0.00% BenchmarkVerifyPod/enforce-warn-audit-baseline_deployment-12 4800 4800 +0.00% BenchmarkVerifyPod/warn-baseline-audit-restricted_pod-12 4616 4616 +0.00% BenchmarkVerifyPod/warn-baseline-audit-restricted_deployment-12 4800 4800 +0.00% --- .../admission/admission.go | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/staging/src/k8s.io/pod-security-admission/admission/admission.go b/staging/src/k8s.io/pod-security-admission/admission/admission.go index 33fdcc5ba85..4269b740383 100644 --- a/staging/src/k8s.io/pod-security-admission/admission/admission.go +++ b/staging/src/k8s.io/pod-security-admission/admission/admission.go @@ -302,6 +302,17 @@ func (a *Admission) ValidatePod(ctx context.Context, attrs Attributes) *admissio return allowedResponse() } + // short-circuit on privileged enforce+audit+warn namespaces + namespace, err := a.NamespaceGetter.GetNamespace(ctx, attrs.GetNamespace()) + if err != nil { + klog.ErrorS(err, "failed to fetch pod namespace", "namespace", attrs.GetNamespace()) + return internalErrorResponse(fmt.Sprintf("failed to lookup namespace %s", attrs.GetNamespace())) + } + nsPolicy, _ := a.PolicyToEvaluate(namespace.Labels) + if nsPolicy.Enforce.Level == api.LevelPrivileged && nsPolicy.Warn.Level == api.LevelPrivileged && nsPolicy.Audit.Level == api.LevelPrivileged { + return allowedResponse() + } + obj, err := attrs.GetObject() if err != nil { klog.ErrorS(err, "failed to decode object") @@ -341,6 +352,17 @@ func (a *Admission) ValidatePodController(ctx context.Context, attrs Attributes) return allowedResponse() } + // short-circuit on privileged audit+warn namespaces + namespace, err := a.NamespaceGetter.GetNamespace(ctx, attrs.GetNamespace()) + if err != nil { + klog.ErrorS(err, "failed to fetch pod namespace", "namespace", attrs.GetNamespace()) + return internalErrorResponse(fmt.Sprintf("failed to lookup namespace %s", attrs.GetNamespace())) + } + nsPolicy, _ := a.PolicyToEvaluate(namespace.Labels) + if nsPolicy.Warn.Level == api.LevelPrivileged && nsPolicy.Audit.Level == api.LevelPrivileged { + return allowedResponse() + } + obj, err := attrs.GetObject() if err != nil { klog.ErrorS(err, "failed to decode object") From 636c769fb8d715cdfbb0382761447b0652d86e7b Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Wed, 25 Aug 2021 14:38:45 -0400 Subject: [PATCH 3/4] PodSecurity: preconstruct reused values benchmark old ns/op new ns/op delta BenchmarkVerifyPod/enforce-implicit_pod-12 370 228 -38.49% BenchmarkVerifyPod/enforce-implicit_deployment-12 408 241 -40.86% BenchmarkVerifyPod/enforce-privileged_pod-12 420 242 -42.27% BenchmarkVerifyPod/enforce-privileged_deployment-12 426 256 -39.84% BenchmarkVerifyPod/enforce-baseline_pod-12 4259 3006 -29.42% BenchmarkVerifyPod/enforce-baseline_deployment-12 341 266 -22.12% BenchmarkVerifyPod/enforce-restricted_pod-12 3322 3282 -1.20% BenchmarkVerifyPod/enforce-restricted_deployment-12 327 260 -20.59% BenchmarkVerifyPod/warn-baseline_pod-12 2964 3020 +1.89% BenchmarkVerifyPod/warn-baseline_deployment-12 3069 3127 +1.89% BenchmarkVerifyPod/warn-restricted_pod-12 3223 3330 +3.32% BenchmarkVerifyPod/warn-restricted_deployment-12 3443 3533 +2.61% BenchmarkVerifyPod/enforce-warn-audit-baseline_pod-12 5193 5405 +4.08% BenchmarkVerifyPod/enforce-warn-audit-baseline_deployment-12 4295 4358 +1.47% BenchmarkVerifyPod/warn-baseline-audit-restricted_pod-12 4363 4513 +3.44% BenchmarkVerifyPod/warn-baseline-audit-restricted_deployment-12 4482 4588 +2.37% benchmark old allocs new allocs delta BenchmarkVerifyPod/enforce-implicit_pod-12 2 1 -50.00% BenchmarkVerifyPod/enforce-implicit_deployment-12 2 1 -50.00% BenchmarkVerifyPod/enforce-privileged_pod-12 2 1 -50.00% BenchmarkVerifyPod/enforce-privileged_deployment-12 2 1 -50.00% BenchmarkVerifyPod/enforce-baseline_pod-12 17 17 +0.00% BenchmarkVerifyPod/enforce-baseline_deployment-12 2 1 -50.00% BenchmarkVerifyPod/enforce-restricted_pod-12 17 17 +0.00% BenchmarkVerifyPod/enforce-restricted_deployment-12 2 1 -50.00% BenchmarkVerifyPod/warn-baseline_pod-12 17 17 +0.00% BenchmarkVerifyPod/warn-baseline_deployment-12 19 19 +0.00% BenchmarkVerifyPod/warn-restricted_pod-12 17 17 +0.00% BenchmarkVerifyPod/warn-restricted_deployment-12 19 19 +0.00% BenchmarkVerifyPod/enforce-warn-audit-baseline_pod-12 27 27 +0.00% BenchmarkVerifyPod/enforce-warn-audit-baseline_deployment-12 24 24 +0.00% BenchmarkVerifyPod/warn-baseline-audit-restricted_pod-12 22 22 +0.00% BenchmarkVerifyPod/warn-baseline-audit-restricted_deployment-12 24 24 +0.00% benchmark old bytes new bytes delta BenchmarkVerifyPod/enforce-implicit_pod-12 208 112 -46.15% BenchmarkVerifyPod/enforce-implicit_deployment-12 208 112 -46.15% BenchmarkVerifyPod/enforce-privileged_pod-12 208 112 -46.15% BenchmarkVerifyPod/enforce-privileged_deployment-12 208 112 -46.15% BenchmarkVerifyPod/enforce-baseline_pod-12 3368 3368 +0.00% BenchmarkVerifyPod/enforce-baseline_deployment-12 208 112 -46.15% BenchmarkVerifyPod/enforce-restricted_pod-12 3368 3368 +0.00% BenchmarkVerifyPod/enforce-restricted_deployment-12 208 112 -46.15% BenchmarkVerifyPod/warn-baseline_pod-12 3368 3368 +0.00% BenchmarkVerifyPod/warn-baseline_deployment-12 3552 3552 +0.00% BenchmarkVerifyPod/warn-restricted_pod-12 3368 3368 +0.00% BenchmarkVerifyPod/warn-restricted_deployment-12 3552 3552 +0.00% BenchmarkVerifyPod/enforce-warn-audit-baseline_pod-12 5864 5864 +0.00% BenchmarkVerifyPod/enforce-warn-audit-baseline_deployment-12 4800 4800 +0.00% BenchmarkVerifyPod/warn-baseline-audit-restricted_pod-12 4616 4616 +0.00% BenchmarkVerifyPod/warn-baseline-audit-restricted_deployment-12 4800 4800 +0.00% --- .../admission/admission.go | 56 +++++++++++++------ .../admission/main_test.go | 36 ++++++++++++ 2 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 staging/src/k8s.io/pod-security-admission/admission/main_test.go diff --git a/staging/src/k8s.io/pod-security-admission/admission/admission.go b/staging/src/k8s.io/pod-security-admission/admission/admission.go index 4269b740383..9f942e39c90 100644 --- a/staging/src/k8s.io/pod-security-admission/admission/admission.go +++ b/staging/src/k8s.io/pod-security-admission/admission/admission.go @@ -188,14 +188,20 @@ func (a *Admission) ValidateConfiguration() error { return nil } +var ( + namespacesResource = corev1.Resource("namespaces") + podsResource = corev1.Resource("pods") +) + // Validate admits an API request. // The objects in admission attributes are expected to be external v1 objects that we care about. +// The returned response may be shared and must not be mutated. func (a *Admission) Validate(ctx context.Context, attrs Attributes) *admissionv1.AdmissionResponse { var response *admissionv1.AdmissionResponse switch attrs.GetResource().GroupResource() { - case corev1.Resource("namespaces"): + case namespacesResource: response = a.ValidateNamespace(ctx, attrs) - case corev1.Resource("pods"): + case podsResource: response = a.ValidatePod(ctx, attrs) default: response = a.ValidatePodController(ctx, attrs) @@ -206,10 +212,13 @@ func (a *Admission) Validate(ctx context.Context, attrs Attributes) *admissionv1 return response } +// ValidateNamespace evaluates a namespace create or update request to ensure the pod security labels are valid, +// and checks existing pods in the namespace for violations of the new policy when updating the enforce level on a namespace. +// The returned response may be shared between evaluations and must not be mutated. func (a *Admission) ValidateNamespace(ctx context.Context, attrs Attributes) *admissionv1.AdmissionResponse { // short-circuit on subresources if attrs.GetSubresource() != "" { - return allowedResponse() + return sharedAllowedResponse() } obj, err := attrs.GetObject() if err != nil { @@ -230,7 +239,7 @@ func (a *Admission) ValidateNamespace(ctx context.Context, attrs Attributes) *ad if newErr != nil { return invalidResponse(newErr.Error()) } - return allowedResponse() + return sharedAllowedResponse() case admissionv1.Update: // if update, check if policy labels changed @@ -257,24 +266,24 @@ func (a *Admission) ValidateNamespace(ctx context.Context, attrs Attributes) *ad // * if the new enforce is the same version and level was relaxed // * for exempt namespaces if newPolicy.Enforce == oldPolicy.Enforce { - return allowedResponse() + return sharedAllowedResponse() } if newPolicy.Enforce.Level == api.LevelPrivileged { - return allowedResponse() + return sharedAllowedResponse() } if newPolicy.Enforce.Version == oldPolicy.Enforce.Version && api.CompareLevels(newPolicy.Enforce.Level, oldPolicy.Enforce.Level) < 1 { - return allowedResponse() + return sharedAllowedResponse() } if a.exemptNamespace(attrs.GetNamespace()) { - return allowedResponse() + return sharedAllowedResponse() } response := allowedResponse() response.Warnings = a.EvaluatePodsInNamespace(ctx, namespace.Name, newPolicy.Enforce) return response default: - return allowedResponse() + return sharedAllowedResponse() } } @@ -292,14 +301,16 @@ var ignoredPodSubresources = map[string]bool{ "status": true, } +// ValidatePod evaluates a pod create or update request against the effective policy for the namespace. +// The returned response may be shared between evaluations and must not be mutated. func (a *Admission) ValidatePod(ctx context.Context, attrs Attributes) *admissionv1.AdmissionResponse { // short-circuit on ignored subresources if ignoredPodSubresources[attrs.GetSubresource()] { - return allowedResponse() + return sharedAllowedResponse() } // short-circuit on exempt namespaces and users if a.exemptNamespace(attrs.GetNamespace()) || a.exemptUser(attrs.GetUserName()) { - return allowedResponse() + return sharedAllowedResponse() } // short-circuit on privileged enforce+audit+warn namespaces @@ -310,7 +321,7 @@ func (a *Admission) ValidatePod(ctx context.Context, attrs Attributes) *admissio } nsPolicy, _ := a.PolicyToEvaluate(namespace.Labels) if nsPolicy.Enforce.Level == api.LevelPrivileged && nsPolicy.Warn.Level == api.LevelPrivileged && nsPolicy.Audit.Level == api.LevelPrivileged { - return allowedResponse() + return sharedAllowedResponse() } obj, err := attrs.GetObject() @@ -336,20 +347,22 @@ func (a *Admission) ValidatePod(ctx context.Context, attrs Attributes) *admissio } if !isSignificantPodUpdate(pod, oldPod) { // Nothing we care about changed, so always allow the update. - return allowedResponse() + return sharedAllowedResponse() } } return a.EvaluatePod(ctx, attrs.GetNamespace(), &pod.ObjectMeta, &pod.Spec, true) } +// ValidatePodController evaluates a pod controller create or update request against the effective policy for the namespace. +// The returned response may be shared between evaluations and must not be mutated. func (a *Admission) ValidatePodController(ctx context.Context, attrs Attributes) *admissionv1.AdmissionResponse { // short-circuit on subresources if attrs.GetSubresource() != "" { - return allowedResponse() + return sharedAllowedResponse() } // short-circuit on exempt namespaces and users if a.exemptNamespace(attrs.GetNamespace()) || a.exemptUser(attrs.GetUserName()) { - return allowedResponse() + return sharedAllowedResponse() } // short-circuit on privileged audit+warn namespaces @@ -360,7 +373,7 @@ func (a *Admission) ValidatePodController(ctx context.Context, attrs Attributes) } nsPolicy, _ := a.PolicyToEvaluate(namespace.Labels) if nsPolicy.Warn.Level == api.LevelPrivileged && nsPolicy.Audit.Level == api.LevelPrivileged { - return allowedResponse() + return sharedAllowedResponse() } obj, err := attrs.GetObject() @@ -375,17 +388,18 @@ func (a *Admission) ValidatePodController(ctx context.Context, attrs Attributes) } if podMetadata == nil && podSpec == nil { // if a controller with an optional pod spec does not contain a pod spec, skip validation - return allowedResponse() + return sharedAllowedResponse() } return a.EvaluatePod(ctx, attrs.GetNamespace(), podMetadata, podSpec, false) } // EvaluatePod looks up the policy for the pods namespace, and checks it against the given pod(-like) object. // The enforce policy is only checked if enforce=true. +// The returned response may be shared between evaluations and must not be mutated. func (a *Admission) EvaluatePod(ctx context.Context, namespaceName string, podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec, enforce bool) *admissionv1.AdmissionResponse { // short-circuit on exempt runtimeclass if a.exemptRuntimeClass(podSpec.RuntimeClassName) { - return allowedResponse() + return sharedAllowedResponse() } namespace, err := a.NamespaceGetter.GetNamespace(ctx, namespaceName) @@ -478,6 +492,12 @@ func (a *Admission) PolicyToEvaluate(labels map[string]string) (api.Policy, erro return api.PolicyToEvaluate(labels, a.defaultPolicy) } +var _sharedAllowedResponse = allowedResponse() + +func sharedAllowedResponse() *admissionv1.AdmissionResponse { + return _sharedAllowedResponse +} + // allowedResponse is the response used when the admission decision is allow. func allowedResponse() *admissionv1.AdmissionResponse { return &admissionv1.AdmissionResponse{Allowed: true} diff --git a/staging/src/k8s.io/pod-security-admission/admission/main_test.go b/staging/src/k8s.io/pod-security-admission/admission/main_test.go new file mode 100644 index 00000000000..4d0e5851bf2 --- /dev/null +++ b/staging/src/k8s.io/pod-security-admission/admission/main_test.go @@ -0,0 +1,36 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package admission + +import ( + "fmt" + "os" + "reflect" + "testing" +) + +func TestMain(m *testing.M) { + sharedCopy := sharedAllowedResponse().DeepCopy() + rc := m.Run() + + if !reflect.DeepEqual(sharedCopy, sharedAllowedResponse()) { + fmt.Println("sharedAllowedReponse mutated") + rc = 1 + } + + os.Exit(rc) +} From 32a5f41ec4dadacfaccb06f379b868da1e266f64 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Tue, 21 Sep 2021 16:31:34 -0400 Subject: [PATCH 4/4] PodSecurity: avoid double parsing policy from namespace labels benchmark old ns/op new ns/op delta BenchmarkVerifyPod/enforce-implicit_pod-12 224 225 +0.40% BenchmarkVerifyPod/enforce-implicit_deployment-12 237 234 -1.31% BenchmarkVerifyPod/enforce-privileged_pod-12 259 245 -5.26% BenchmarkVerifyPod/enforce-privileged_deployment-12 261 254 -2.72% BenchmarkVerifyPod/enforce-baseline_pod-12 2967 2850 -3.94% BenchmarkVerifyPod/enforce-baseline_deployment-12 252 255 +0.87% BenchmarkVerifyPod/enforce-restricted_pod-12 3244 3125 -3.67% BenchmarkVerifyPod/enforce-restricted_deployment-12 258 261 +0.97% BenchmarkVerifyPod/warn-baseline_pod-12 2956 2841 -3.89% BenchmarkVerifyPod/warn-baseline_deployment-12 3034 2913 -3.99% BenchmarkVerifyPod/warn-restricted_pod-12 3276 3176 -3.05% BenchmarkVerifyPod/warn-restricted_deployment-12 3302 3157 -4.39% BenchmarkVerifyPod/enforce-warn-audit-baseline_pod-12 5159 5132 -0.52% BenchmarkVerifyPod/enforce-warn-audit-baseline_deployment-12 4208 4069 -3.30% BenchmarkVerifyPod/warn-baseline-audit-restricted_pod-12 4336 4252 -1.94% BenchmarkVerifyPod/warn-baseline-audit-restricted_deployment-12 4436 4316 -2.71% --- .../admission/admission.go | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/staging/src/k8s.io/pod-security-admission/admission/admission.go b/staging/src/k8s.io/pod-security-admission/admission/admission.go index 9f942e39c90..b48bd190567 100644 --- a/staging/src/k8s.io/pod-security-admission/admission/admission.go +++ b/staging/src/k8s.io/pod-security-admission/admission/admission.go @@ -319,8 +319,8 @@ func (a *Admission) ValidatePod(ctx context.Context, attrs Attributes) *admissio klog.ErrorS(err, "failed to fetch pod namespace", "namespace", attrs.GetNamespace()) return internalErrorResponse(fmt.Sprintf("failed to lookup namespace %s", attrs.GetNamespace())) } - nsPolicy, _ := a.PolicyToEvaluate(namespace.Labels) - if nsPolicy.Enforce.Level == api.LevelPrivileged && nsPolicy.Warn.Level == api.LevelPrivileged && nsPolicy.Audit.Level == api.LevelPrivileged { + nsPolicy, nsPolicyErr := a.PolicyToEvaluate(namespace.Labels) + if nsPolicyErr == nil && nsPolicy.Enforce.Level == api.LevelPrivileged && nsPolicy.Warn.Level == api.LevelPrivileged && nsPolicy.Audit.Level == api.LevelPrivileged { return sharedAllowedResponse() } @@ -350,7 +350,7 @@ func (a *Admission) ValidatePod(ctx context.Context, attrs Attributes) *admissio return sharedAllowedResponse() } } - return a.EvaluatePod(ctx, attrs.GetNamespace(), &pod.ObjectMeta, &pod.Spec, true) + return a.EvaluatePod(ctx, nsPolicy, nsPolicyErr, &pod.ObjectMeta, &pod.Spec, true) } // ValidatePodController evaluates a pod controller create or update request against the effective policy for the namespace. @@ -371,8 +371,8 @@ func (a *Admission) ValidatePodController(ctx context.Context, attrs Attributes) klog.ErrorS(err, "failed to fetch pod namespace", "namespace", attrs.GetNamespace()) return internalErrorResponse(fmt.Sprintf("failed to lookup namespace %s", attrs.GetNamespace())) } - nsPolicy, _ := a.PolicyToEvaluate(namespace.Labels) - if nsPolicy.Warn.Level == api.LevelPrivileged && nsPolicy.Audit.Level == api.LevelPrivileged { + nsPolicy, nsPolicyErr := a.PolicyToEvaluate(namespace.Labels) + if nsPolicyErr == nil && nsPolicy.Warn.Level == api.LevelPrivileged && nsPolicy.Audit.Level == api.LevelPrivileged { return sharedAllowedResponse() } @@ -390,29 +390,22 @@ func (a *Admission) ValidatePodController(ctx context.Context, attrs Attributes) // if a controller with an optional pod spec does not contain a pod spec, skip validation return sharedAllowedResponse() } - return a.EvaluatePod(ctx, attrs.GetNamespace(), podMetadata, podSpec, false) + return a.EvaluatePod(ctx, nsPolicy, nsPolicyErr, podMetadata, podSpec, false) } -// EvaluatePod looks up the policy for the pods namespace, and checks it against the given pod(-like) object. +// EvaluatePod evaluates the given policy against the given pod(-like) object. // The enforce policy is only checked if enforce=true. // The returned response may be shared between evaluations and must not be mutated. -func (a *Admission) EvaluatePod(ctx context.Context, namespaceName string, podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec, enforce bool) *admissionv1.AdmissionResponse { +func (a *Admission) EvaluatePod(ctx context.Context, nsPolicy api.Policy, nsPolicyErr error, podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec, enforce bool) *admissionv1.AdmissionResponse { // short-circuit on exempt runtimeclass if a.exemptRuntimeClass(podSpec.RuntimeClassName) { return sharedAllowedResponse() } - namespace, err := a.NamespaceGetter.GetNamespace(ctx, namespaceName) - if err != nil { - klog.ErrorS(err, "failed to fetch pod namespace", "namespace", namespaceName) - return internalErrorResponse(fmt.Sprintf("failed to lookup namespace %s", namespaceName)) - } - auditAnnotations := map[string]string{} - nsPolicy, err := a.PolicyToEvaluate(namespace.Labels) - if err != nil { - klog.V(2).InfoS("failed to parse PodSecurity namespace labels", "err", err) - auditAnnotations["error"] = fmt.Sprintf("Failed to parse policy: %v", err) + if nsPolicyErr != nil { + klog.V(2).InfoS("failed to parse PodSecurity namespace labels", "err", nsPolicyErr) + auditAnnotations["error"] = fmt.Sprintf("Failed to parse policy: %v", nsPolicyErr) } // TODO: log nsPolicy evaluation with context (op, resource, namespace, name) for the request.