mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Merge pull request #104588 from liggitt/podsecurity-benchmark
PodSecurity: benchmark and optimize privileged namespace evaluations
This commit is contained in:
commit
dce069ce22
@ -17,14 +17,27 @@ limitations under the License.
|
|||||||
package podsecurity
|
package podsecurity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"io/ioutil"
|
||||||
"testing"
|
"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"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"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/apps"
|
||||||
"k8s.io/kubernetes/pkg/apis/batch"
|
"k8s.io/kubernetes/pkg/apis/batch"
|
||||||
"k8s.io/kubernetes/pkg/apis/core"
|
"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"
|
podsecurityadmission "k8s.io/pod-security-admission/admission"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConvert(t *testing.T) {
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
676
plugin/pkg/admission/security/podsecurity/testdata/pod.yaml
vendored
Normal file
676
plugin/pkg/admission/security/podsecurity/testdata/pod.yaml
vendored
Normal file
@ -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"
|
@ -188,14 +188,20 @@ func (a *Admission) ValidateConfiguration() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
namespacesResource = corev1.Resource("namespaces")
|
||||||
|
podsResource = corev1.Resource("pods")
|
||||||
|
)
|
||||||
|
|
||||||
// Validate admits an API request.
|
// Validate admits an API request.
|
||||||
// The objects in admission attributes are expected to be external v1 objects that we care about.
|
// 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 {
|
func (a *Admission) Validate(ctx context.Context, attrs Attributes) *admissionv1.AdmissionResponse {
|
||||||
var response *admissionv1.AdmissionResponse
|
var response *admissionv1.AdmissionResponse
|
||||||
switch attrs.GetResource().GroupResource() {
|
switch attrs.GetResource().GroupResource() {
|
||||||
case corev1.Resource("namespaces"):
|
case namespacesResource:
|
||||||
response = a.ValidateNamespace(ctx, attrs)
|
response = a.ValidateNamespace(ctx, attrs)
|
||||||
case corev1.Resource("pods"):
|
case podsResource:
|
||||||
response = a.ValidatePod(ctx, attrs)
|
response = a.ValidatePod(ctx, attrs)
|
||||||
default:
|
default:
|
||||||
response = a.ValidatePodController(ctx, attrs)
|
response = a.ValidatePodController(ctx, attrs)
|
||||||
@ -206,10 +212,13 @@ func (a *Admission) Validate(ctx context.Context, attrs Attributes) *admissionv1
|
|||||||
return response
|
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 {
|
func (a *Admission) ValidateNamespace(ctx context.Context, attrs Attributes) *admissionv1.AdmissionResponse {
|
||||||
// short-circuit on subresources
|
// short-circuit on subresources
|
||||||
if attrs.GetSubresource() != "" {
|
if attrs.GetSubresource() != "" {
|
||||||
return allowedResponse()
|
return sharedAllowedResponse()
|
||||||
}
|
}
|
||||||
obj, err := attrs.GetObject()
|
obj, err := attrs.GetObject()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -230,7 +239,7 @@ func (a *Admission) ValidateNamespace(ctx context.Context, attrs Attributes) *ad
|
|||||||
if newErr != nil {
|
if newErr != nil {
|
||||||
return invalidResponse(newErr.Error())
|
return invalidResponse(newErr.Error())
|
||||||
}
|
}
|
||||||
return allowedResponse()
|
return sharedAllowedResponse()
|
||||||
|
|
||||||
case admissionv1.Update:
|
case admissionv1.Update:
|
||||||
// if update, check if policy labels changed
|
// 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
|
// * if the new enforce is the same version and level was relaxed
|
||||||
// * for exempt namespaces
|
// * for exempt namespaces
|
||||||
if newPolicy.Enforce == oldPolicy.Enforce {
|
if newPolicy.Enforce == oldPolicy.Enforce {
|
||||||
return allowedResponse()
|
return sharedAllowedResponse()
|
||||||
}
|
}
|
||||||
if newPolicy.Enforce.Level == api.LevelPrivileged {
|
if newPolicy.Enforce.Level == api.LevelPrivileged {
|
||||||
return allowedResponse()
|
return sharedAllowedResponse()
|
||||||
}
|
}
|
||||||
if newPolicy.Enforce.Version == oldPolicy.Enforce.Version &&
|
if newPolicy.Enforce.Version == oldPolicy.Enforce.Version &&
|
||||||
api.CompareLevels(newPolicy.Enforce.Level, oldPolicy.Enforce.Level) < 1 {
|
api.CompareLevels(newPolicy.Enforce.Level, oldPolicy.Enforce.Level) < 1 {
|
||||||
return allowedResponse()
|
return sharedAllowedResponse()
|
||||||
}
|
}
|
||||||
if a.exemptNamespace(attrs.GetNamespace()) {
|
if a.exemptNamespace(attrs.GetNamespace()) {
|
||||||
return allowedResponse()
|
return sharedAllowedResponse()
|
||||||
}
|
}
|
||||||
response := allowedResponse()
|
response := allowedResponse()
|
||||||
response.Warnings = a.EvaluatePodsInNamespace(ctx, namespace.Name, newPolicy.Enforce)
|
response.Warnings = a.EvaluatePodsInNamespace(ctx, namespace.Name, newPolicy.Enforce)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return allowedResponse()
|
return sharedAllowedResponse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,14 +301,27 @@ var ignoredPodSubresources = map[string]bool{
|
|||||||
"status": true,
|
"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 {
|
func (a *Admission) ValidatePod(ctx context.Context, attrs Attributes) *admissionv1.AdmissionResponse {
|
||||||
// short-circuit on ignored subresources
|
// short-circuit on ignored subresources
|
||||||
if ignoredPodSubresources[attrs.GetSubresource()] {
|
if ignoredPodSubresources[attrs.GetSubresource()] {
|
||||||
return allowedResponse()
|
return sharedAllowedResponse()
|
||||||
}
|
}
|
||||||
// short-circuit on exempt namespaces and users
|
// short-circuit on exempt namespaces and users
|
||||||
if a.exemptNamespace(attrs.GetNamespace()) || a.exemptUser(attrs.GetUserName()) {
|
if a.exemptNamespace(attrs.GetNamespace()) || a.exemptUser(attrs.GetUserName()) {
|
||||||
return allowedResponse()
|
return sharedAllowedResponse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, 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()
|
||||||
}
|
}
|
||||||
|
|
||||||
obj, err := attrs.GetObject()
|
obj, err := attrs.GetObject()
|
||||||
@ -325,20 +347,33 @@ func (a *Admission) ValidatePod(ctx context.Context, attrs Attributes) *admissio
|
|||||||
}
|
}
|
||||||
if !isSignificantPodUpdate(pod, oldPod) {
|
if !isSignificantPodUpdate(pod, oldPod) {
|
||||||
// Nothing we care about changed, so always allow the update.
|
// 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)
|
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.
|
||||||
|
// The returned response may be shared between evaluations and must not be mutated.
|
||||||
func (a *Admission) ValidatePodController(ctx context.Context, attrs Attributes) *admissionv1.AdmissionResponse {
|
func (a *Admission) ValidatePodController(ctx context.Context, attrs Attributes) *admissionv1.AdmissionResponse {
|
||||||
// short-circuit on subresources
|
// short-circuit on subresources
|
||||||
if attrs.GetSubresource() != "" {
|
if attrs.GetSubresource() != "" {
|
||||||
return allowedResponse()
|
return sharedAllowedResponse()
|
||||||
}
|
}
|
||||||
// short-circuit on exempt namespaces and users
|
// short-circuit on exempt namespaces and users
|
||||||
if a.exemptNamespace(attrs.GetNamespace()) || a.exemptUser(attrs.GetUserName()) {
|
if a.exemptNamespace(attrs.GetNamespace()) || a.exemptUser(attrs.GetUserName()) {
|
||||||
return allowedResponse()
|
return sharedAllowedResponse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, nsPolicyErr := a.PolicyToEvaluate(namespace.Labels)
|
||||||
|
if nsPolicyErr == nil && nsPolicy.Warn.Level == api.LevelPrivileged && nsPolicy.Audit.Level == api.LevelPrivileged {
|
||||||
|
return sharedAllowedResponse()
|
||||||
}
|
}
|
||||||
|
|
||||||
obj, err := attrs.GetObject()
|
obj, err := attrs.GetObject()
|
||||||
@ -353,30 +388,24 @@ func (a *Admission) ValidatePodController(ctx context.Context, attrs Attributes)
|
|||||||
}
|
}
|
||||||
if podMetadata == nil && podSpec == nil {
|
if podMetadata == nil && podSpec == nil {
|
||||||
// if a controller with an optional pod spec does not contain a pod spec, skip validation
|
// 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)
|
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 enforce policy is only checked if enforce=true.
|
||||||
func (a *Admission) EvaluatePod(ctx context.Context, namespaceName string, podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec, enforce bool) *admissionv1.AdmissionResponse {
|
// The returned response may be shared between evaluations and must not be mutated.
|
||||||
|
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
|
// short-circuit on exempt runtimeclass
|
||||||
if a.exemptRuntimeClass(podSpec.RuntimeClassName) {
|
if a.exemptRuntimeClass(podSpec.RuntimeClassName) {
|
||||||
return allowedResponse()
|
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{}
|
auditAnnotations := map[string]string{}
|
||||||
nsPolicy, err := a.PolicyToEvaluate(namespace.Labels)
|
if nsPolicyErr != nil {
|
||||||
if err != nil {
|
klog.V(2).InfoS("failed to parse PodSecurity namespace labels", "err", nsPolicyErr)
|
||||||
klog.V(2).InfoS("failed to parse PodSecurity namespace labels", "err", err)
|
auditAnnotations["error"] = fmt.Sprintf("Failed to parse policy: %v", nsPolicyErr)
|
||||||
auditAnnotations["error"] = fmt.Sprintf("Failed to parse policy: %v", err)
|
|
||||||
}
|
}
|
||||||
// TODO: log nsPolicy evaluation with context (op, resource, namespace, name) for the request.
|
// TODO: log nsPolicy evaluation with context (op, resource, namespace, name) for the request.
|
||||||
|
|
||||||
@ -456,6 +485,12 @@ func (a *Admission) PolicyToEvaluate(labels map[string]string) (api.Policy, erro
|
|||||||
return api.PolicyToEvaluate(labels, a.defaultPolicy)
|
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.
|
// allowedResponse is the response used when the admission decision is allow.
|
||||||
func allowedResponse() *admissionv1.AdmissionResponse {
|
func allowedResponse() *admissionv1.AdmissionResponse {
|
||||||
return &admissionv1.AdmissionResponse{Allowed: true}
|
return &admissionv1.AdmissionResponse{Allowed: true}
|
||||||
|
@ -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)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user