diff --git a/plugin/pkg/admission/security/podsecuritypolicy/admission.go b/plugin/pkg/admission/security/podsecuritypolicy/admission.go index 3a31f33f96..1c1b571f82 100644 --- a/plugin/pkg/admission/security/podsecuritypolicy/admission.go +++ b/plugin/pkg/admission/security/podsecuritypolicy/admission.go @@ -84,6 +84,7 @@ var _ admission.MutationInterface = &PodSecurityPolicyPlugin{} var _ admission.ValidationInterface = &PodSecurityPolicyPlugin{} var _ genericadmissioninit.WantsAuthorizer = &PodSecurityPolicyPlugin{} var _ kubeapiserveradmission.WantsInternalKubeInformerFactory = &PodSecurityPolicyPlugin{} +var auditKeyPrefix = strings.ToLower(PluginName) + "." + policy.GroupName + ".k8s.io" // newPlugin creates a new PSP admission plugin. func newPlugin(strategyFactory psp.StrategyFactory, failOnNoPolicies bool) *PodSecurityPolicyPlugin { @@ -136,6 +137,10 @@ func (c *PodSecurityPolicyPlugin) Admit(a admission.Attributes) error { pod.ObjectMeta.Annotations = map[string]string{} } pod.ObjectMeta.Annotations[psputil.ValidatedPSPAnnotation] = pspName + key := auditKeyPrefix + "/" + "admit-policy" + if err := a.AddAnnotation(key, pspName); err != nil { + glog.Warningf("failed to set admission audit annotation %s to %s: %v", key, pspName, err) + } return nil } @@ -154,11 +159,15 @@ func (c *PodSecurityPolicyPlugin) Validate(a admission.Attributes) error { pod := a.GetObject().(*api.Pod) // compute the context. Mutation is not allowed. ValidatedPSPAnnotation is used as a hint to gain same speed-up. - allowedPod, _, validationErrs, err := c.computeSecurityContext(a, pod, false, pod.ObjectMeta.Annotations[psputil.ValidatedPSPAnnotation]) + allowedPod, pspName, validationErrs, err := c.computeSecurityContext(a, pod, false, pod.ObjectMeta.Annotations[psputil.ValidatedPSPAnnotation]) if err != nil { return admission.NewForbidden(a, err) } if apiequality.Semantic.DeepEqual(pod, allowedPod) { + key := auditKeyPrefix + "/" + "validate-policy" + if err := a.AddAnnotation(key, pspName); err != nil { + glog.Warningf("failed to set admission audit annotation %s to %s: %v", key, pspName, err) + } return nil } diff --git a/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go b/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go index 6cdf1d3c93..e4644558d3 100644 --- a/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go +++ b/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go @@ -1798,11 +1798,24 @@ func testPSPAdmit(testCaseName string, psps []*policy.PodSecurityPolicy, pod *ka testPSPAdmitAdvanced(testCaseName, kadmission.Create, psps, nil, &user.DefaultInfo{}, pod, nil, shouldPassAdmit, shouldPassValidate, true, expectedPSP, t) } +// fakeAttributes decorate kadmission.Attributes. It's used to trace the added annotations. +type fakeAttributes struct { + kadmission.Attributes + annotations map[string]string +} + +func (f fakeAttributes) AddAnnotation(k, v string) error { + f.annotations[k] = v + return f.Attributes.AddAnnotation(k, v) +} + func testPSPAdmitAdvanced(testCaseName string, op kadmission.Operation, psps []*policy.PodSecurityPolicy, authz authorizer.Authorizer, userInfo user.Info, pod, oldPod *kapi.Pod, shouldPassAdmit, shouldPassValidate bool, canMutate bool, expectedPSP string, t *testing.T) { originalPod := pod.DeepCopy() plugin := NewTestAdmission(psps, authz) attrs := kadmission.NewAttributesRecord(pod, oldPod, kapi.Kind("Pod").WithVersion("version"), pod.Namespace, "", kapi.Resource("pods").WithVersion("version"), "", op, userInfo) + annotations := make(map[string]string) + attrs = &fakeAttributes{attrs, annotations} err := plugin.Admit(attrs) if shouldPassAdmit && err != nil { @@ -1832,11 +1845,27 @@ func testPSPAdmitAdvanced(testCaseName string, op kadmission.Operation, psps []* } err = plugin.Validate(attrs) + psp := "" + if shouldPassAdmit && op == kadmission.Create { + psp = expectedPSP + } + validateAuditAnnotation(t, testCaseName, annotations, "podsecuritypolicy.policy.k8s.io/admit-policy", psp) if shouldPassValidate && err != nil { t.Errorf("%s: expected no errors on Validate but received %v", testCaseName, err) } else if !shouldPassValidate && err == nil { t.Errorf("%s: expected errors on Validate but received none", testCaseName) } + if shouldPassValidate { + validateAuditAnnotation(t, testCaseName, annotations, "podsecuritypolicy.policy.k8s.io/validate-policy", expectedPSP) + } else { + validateAuditAnnotation(t, testCaseName, annotations, "podsecuritypolicy.policy.k8s.io/validate-policy", "") + } +} + +func validateAuditAnnotation(t *testing.T, testCaseName string, annotations map[string]string, key, value string) { + if annotations[key] != value { + t.Errorf("%s: expected to have annotations[%s] set to %q, got %q", testCaseName, key, value, annotations[key]) + } } func TestAssignSecurityContext(t *testing.T) { diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go index 8fe5a1cbb7..5427600782 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go @@ -97,6 +97,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, admit admission.Inte trace.Step("Conversion done") ae := request.AuditEventFrom(ctx) + admit = admission.WithAudit(admit, ae) audit.LogRequestObject(ae, obj, scope.Resource, scope.Subresource, scope.Serializer) userInfo, _ := request.UserFrom(ctx) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/delete.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/delete.go index 9da5d43e1b..03576d72a6 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/delete.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/delete.go @@ -56,6 +56,8 @@ func DeleteResource(r rest.GracefulDeleter, allowsOptions bool, scope RequestSco } ctx := req.Context() ctx = request.WithNamespace(ctx, namespace) + ae := request.AuditEventFrom(ctx) + admit = admission.WithAudit(admit, ae) options := &metav1.DeleteOptions{} if allowsOptions { @@ -188,6 +190,8 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco ctx := req.Context() ctx = request.WithNamespace(ctx, namespace) + ae := request.AuditEventFrom(ctx) + admit = admission.WithAudit(admit, ae) if admit != nil && admit.Handles(admission.Delete) { userInfo, _ := request.UserFrom(ctx) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go index e94d453204..0801dcef63 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go @@ -89,6 +89,8 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface } ae := request.AuditEventFrom(ctx) + admit = admission.WithAudit(admit, ae) + audit.LogRequestPatch(ae, patchJS) trace.Step("Recorded the audit event") diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go index 942e53483f..d42c0194dd 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go @@ -103,6 +103,9 @@ func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admissi } ctx := req.Context() ctx = request.WithNamespace(ctx, namespace) + ae := request.AuditEventFrom(ctx) + admit = admission.WithAudit(admit, ae) + opts, subpath, subpathKey := connecter.NewConnectOptions() if err := getRequestOptions(req, scope, opts, subpath, subpathKey, isSubresource); err != nil { err = errors.NewBadRequest(err.Error()) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/update.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/update.go index 4b10cf12bd..de242771db 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/update.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/update.go @@ -86,6 +86,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, admit admission.Interfac ae := request.AuditEventFrom(ctx) audit.LogRequestObject(ae, obj, scope.Resource, scope.Subresource, scope.Serializer) + admit = admission.WithAudit(admit, ae) if err := checkName(obj, name, namespace, scope.Namer); err != nil { scope.err(err, w, req)