123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751 |
- /*
- Copyright 2017 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 auth
- import (
- "encoding/json"
- "fmt"
- "strings"
- "time"
- apps "k8s.io/api/apps/v1"
- apiv1 "k8s.io/api/core/v1"
- apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
- apiextensionclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
- "k8s.io/apiextensions-apiserver/test/integration/fixtures"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/types"
- "k8s.io/apimachinery/pkg/util/wait"
- auditinternal "k8s.io/apiserver/pkg/apis/audit"
- auditv1 "k8s.io/apiserver/pkg/apis/audit/v1"
- clientset "k8s.io/client-go/kubernetes"
- restclient "k8s.io/client-go/rest"
- "k8s.io/kubernetes/test/e2e/framework"
- "k8s.io/kubernetes/test/e2e/framework/auth"
- e2edeploy "k8s.io/kubernetes/test/e2e/framework/deployment"
- e2elog "k8s.io/kubernetes/test/e2e/framework/log"
- "k8s.io/kubernetes/test/utils"
- imageutils "k8s.io/kubernetes/test/utils/image"
- jsonpatch "github.com/evanphx/json-patch"
- "github.com/onsi/ginkgo"
- )
- var (
- watchTestTimeout int64 = 1
- auditTestUser = "kubecfg"
- crd = fixtures.NewRandomNameCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped)
- crdName = strings.SplitN(crd.Name, ".", 2)[0]
- crdNamespace = strings.SplitN(crd.Name, ".", 2)[1]
- watchOptions = metav1.ListOptions{TimeoutSeconds: &watchTestTimeout}
- patch, _ = json.Marshal(jsonpatch.Patch{})
- )
- // TODO: Get rid of [DisabledForLargeClusters] when feature request #53455 is ready.
- // Marked as flaky until a reliable method for collecting server-side audit logs is available. See http://issue.k8s.io/74745#issuecomment-474052439
- var _ = SIGDescribe("Advanced Audit [DisabledForLargeClusters][Flaky]", func() {
- f := framework.NewDefaultFramework("audit")
- var namespace string
- ginkgo.BeforeEach(func() {
- framework.SkipUnlessProviderIs("gce")
- namespace = f.Namespace.Name
- })
- ginkgo.It("should audit API calls to create, get, update, patch, delete, list, watch pods.", func() {
- pod := &apiv1.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "audit-pod",
- },
- Spec: apiv1.PodSpec{
- Containers: []apiv1.Container{{
- Name: "pause",
- Image: imageutils.GetPauseImageName(),
- }},
- },
- }
- updatePod := func(pod *apiv1.Pod) {}
- f.PodClient().CreateSync(pod)
- _, err := f.PodClient().Get(pod.Name, metav1.GetOptions{})
- framework.ExpectNoError(err, "failed to get audit-pod")
- podChan, err := f.PodClient().Watch(watchOptions)
- framework.ExpectNoError(err, "failed to create watch for pods")
- podChan.Stop()
- f.PodClient().Update(pod.Name, updatePod)
- _, err = f.PodClient().List(metav1.ListOptions{})
- framework.ExpectNoError(err, "failed to list pods")
- _, err = f.PodClient().Patch(pod.Name, types.JSONPatchType, patch)
- framework.ExpectNoError(err, "failed to patch pod")
- f.PodClient().DeleteSync(pod.Name, &metav1.DeleteOptions{}, framework.DefaultPodDeletionTimeout)
- expectEvents(f, []utils.AuditEvent{
- {
- Level: auditinternal.LevelRequestResponse,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods", namespace),
- Verb: "create",
- Code: 201,
- User: auditTestUser,
- Resource: "pods",
- Namespace: namespace,
- RequestObject: true,
- ResponseObject: true,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelRequest,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
- Verb: "get",
- Code: 200,
- User: auditTestUser,
- Resource: "pods",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelRequest,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods", namespace),
- Verb: "list",
- Code: 200,
- User: auditTestUser,
- Resource: "pods",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelRequest,
- Stage: auditinternal.StageResponseStarted,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
- Verb: "watch",
- Code: 200,
- User: auditTestUser,
- Resource: "pods",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelRequest,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
- Verb: "watch",
- Code: 200,
- User: auditTestUser,
- Resource: "pods",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelRequestResponse,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
- Verb: "update",
- Code: 200,
- User: auditTestUser,
- Resource: "pods",
- Namespace: namespace,
- RequestObject: true,
- ResponseObject: true,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelRequestResponse,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
- Verb: "patch",
- Code: 200,
- User: auditTestUser,
- Resource: "pods",
- Namespace: namespace,
- RequestObject: true,
- ResponseObject: true,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelRequestResponse,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
- Verb: "delete",
- Code: 200,
- User: auditTestUser,
- Resource: "pods",
- Namespace: namespace,
- RequestObject: true,
- ResponseObject: true,
- AuthorizeDecision: "allow",
- },
- })
- })
- ginkgo.It("should audit API calls to create, get, update, patch, delete, list, watch deployments.", func() {
- podLabels := map[string]string{"name": "audit-deployment-pod"}
- d := e2edeploy.NewDeployment("audit-deployment", int32(1), podLabels, "redis", imageutils.GetE2EImage(imageutils.Redis), apps.RecreateDeploymentStrategyType)
- _, err := f.ClientSet.AppsV1().Deployments(namespace).Create(d)
- framework.ExpectNoError(err, "failed to create audit-deployment")
- _, err = f.ClientSet.AppsV1().Deployments(namespace).Get(d.Name, metav1.GetOptions{})
- framework.ExpectNoError(err, "failed to get audit-deployment")
- deploymentChan, err := f.ClientSet.AppsV1().Deployments(namespace).Watch(watchOptions)
- framework.ExpectNoError(err, "failed to create watch for deployments")
- deploymentChan.Stop()
- _, err = f.ClientSet.AppsV1().Deployments(namespace).Update(d)
- framework.ExpectNoError(err, "failed to update audit-deployment")
- _, err = f.ClientSet.AppsV1().Deployments(namespace).Patch(d.Name, types.JSONPatchType, patch)
- framework.ExpectNoError(err, "failed to patch deployment")
- _, err = f.ClientSet.AppsV1().Deployments(namespace).List(metav1.ListOptions{})
- framework.ExpectNoError(err, "failed to create list deployments")
- err = f.ClientSet.AppsV1().Deployments(namespace).Delete("audit-deployment", &metav1.DeleteOptions{})
- framework.ExpectNoError(err, "failed to delete deployments")
- expectEvents(f, []utils.AuditEvent{
- {
- Level: auditinternal.LevelRequestResponse,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments", namespace),
- Verb: "create",
- Code: 201,
- User: auditTestUser,
- Resource: "deployments",
- Namespace: namespace,
- RequestObject: true,
- ResponseObject: true,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelRequest,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments/audit-deployment", namespace),
- Verb: "get",
- Code: 200,
- User: auditTestUser,
- Resource: "deployments",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelRequest,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments", namespace),
- Verb: "list",
- Code: 200,
- User: auditTestUser,
- Resource: "deployments",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelRequest,
- Stage: auditinternal.StageResponseStarted,
- RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
- Verb: "watch",
- Code: 200,
- User: auditTestUser,
- Resource: "deployments",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelRequest,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
- Verb: "watch",
- Code: 200,
- User: auditTestUser,
- Resource: "deployments",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelRequestResponse,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments/audit-deployment", namespace),
- Verb: "update",
- Code: 200,
- User: auditTestUser,
- Resource: "deployments",
- Namespace: namespace,
- RequestObject: true,
- ResponseObject: true,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelRequestResponse,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments/audit-deployment", namespace),
- Verb: "patch",
- Code: 200,
- User: auditTestUser,
- Resource: "deployments",
- Namespace: namespace,
- RequestObject: true,
- ResponseObject: true,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelRequestResponse,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments/audit-deployment", namespace),
- Verb: "delete",
- Code: 200,
- User: auditTestUser,
- Resource: "deployments",
- Namespace: namespace,
- RequestObject: true,
- ResponseObject: true,
- AuthorizeDecision: "allow",
- },
- })
- })
- ginkgo.It("should audit API calls to create, get, update, patch, delete, list, watch configmaps.", func() {
- configMap := &apiv1.ConfigMap{
- ObjectMeta: metav1.ObjectMeta{
- Name: "audit-configmap",
- },
- Data: map[string]string{
- "map-key": "map-value",
- },
- }
- _, err := f.ClientSet.CoreV1().ConfigMaps(namespace).Create(configMap)
- framework.ExpectNoError(err, "failed to create audit-configmap")
- _, err = f.ClientSet.CoreV1().ConfigMaps(namespace).Get(configMap.Name, metav1.GetOptions{})
- framework.ExpectNoError(err, "failed to get audit-configmap")
- configMapChan, err := f.ClientSet.CoreV1().ConfigMaps(namespace).Watch(watchOptions)
- framework.ExpectNoError(err, "failed to create watch for config maps")
- configMapChan.Stop()
- _, err = f.ClientSet.CoreV1().ConfigMaps(namespace).Update(configMap)
- framework.ExpectNoError(err, "failed to update audit-configmap")
- _, err = f.ClientSet.CoreV1().ConfigMaps(namespace).Patch(configMap.Name, types.JSONPatchType, patch)
- framework.ExpectNoError(err, "failed to patch configmap")
- _, err = f.ClientSet.CoreV1().ConfigMaps(namespace).List(metav1.ListOptions{})
- framework.ExpectNoError(err, "failed to list config maps")
- err = f.ClientSet.CoreV1().ConfigMaps(namespace).Delete(configMap.Name, &metav1.DeleteOptions{})
- framework.ExpectNoError(err, "failed to delete audit-configmap")
- expectEvents(f, []utils.AuditEvent{
- {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps", namespace),
- Verb: "create",
- Code: 201,
- User: auditTestUser,
- Resource: "configmaps",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
- Verb: "get",
- Code: 200,
- User: auditTestUser,
- Resource: "configmaps",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps", namespace),
- Verb: "list",
- Code: 200,
- User: auditTestUser,
- Resource: "configmaps",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseStarted,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
- Verb: "watch",
- Code: 200,
- User: auditTestUser,
- Resource: "configmaps",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
- Verb: "watch",
- Code: 200,
- User: auditTestUser,
- Resource: "configmaps",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
- Verb: "update",
- Code: 200,
- User: auditTestUser,
- Resource: "configmaps",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
- Verb: "patch",
- Code: 200,
- User: auditTestUser,
- Resource: "configmaps",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
- Verb: "delete",
- Code: 200,
- User: auditTestUser,
- Resource: "configmaps",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- },
- })
- })
- ginkgo.It("should audit API calls to create, get, update, patch, delete, list, watch secrets.", func() {
- secret := &apiv1.Secret{
- ObjectMeta: metav1.ObjectMeta{
- Name: "audit-secret",
- },
- Data: map[string][]byte{
- "top-secret": []byte("foo-bar"),
- },
- }
- _, err := f.ClientSet.CoreV1().Secrets(namespace).Create(secret)
- framework.ExpectNoError(err, "failed to create audit-secret")
- _, err = f.ClientSet.CoreV1().Secrets(namespace).Get(secret.Name, metav1.GetOptions{})
- framework.ExpectNoError(err, "failed to get audit-secret")
- secretChan, err := f.ClientSet.CoreV1().Secrets(namespace).Watch(watchOptions)
- framework.ExpectNoError(err, "failed to create watch for secrets")
- secretChan.Stop()
- _, err = f.ClientSet.CoreV1().Secrets(namespace).Update(secret)
- framework.ExpectNoError(err, "failed to update audit-secret")
- _, err = f.ClientSet.CoreV1().Secrets(namespace).Patch(secret.Name, types.JSONPatchType, patch)
- framework.ExpectNoError(err, "failed to patch secret")
- _, err = f.ClientSet.CoreV1().Secrets(namespace).List(metav1.ListOptions{})
- framework.ExpectNoError(err, "failed to list secrets")
- err = f.ClientSet.CoreV1().Secrets(namespace).Delete(secret.Name, &metav1.DeleteOptions{})
- framework.ExpectNoError(err, "failed to delete audit-secret")
- expectEvents(f, []utils.AuditEvent{
- {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets", namespace),
- Verb: "create",
- Code: 201,
- User: auditTestUser,
- Resource: "secrets",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets/audit-secret", namespace),
- Verb: "get",
- Code: 200,
- User: auditTestUser,
- Resource: "secrets",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets", namespace),
- Verb: "list",
- Code: 200,
- User: auditTestUser,
- Resource: "secrets",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseStarted,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
- Verb: "watch",
- Code: 200,
- User: auditTestUser,
- Resource: "secrets",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
- Verb: "watch",
- Code: 200,
- User: auditTestUser,
- Resource: "secrets",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets/audit-secret", namespace),
- Verb: "update",
- Code: 200,
- User: auditTestUser,
- Resource: "secrets",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets/audit-secret", namespace),
- Verb: "patch",
- Code: 200,
- User: auditTestUser,
- Resource: "secrets",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/secrets/audit-secret", namespace),
- Verb: "delete",
- Code: 200,
- User: auditTestUser,
- Resource: "secrets",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- },
- })
- })
- ginkgo.It("should audit API calls to create and delete custom resource definition.", func() {
- config, err := framework.LoadConfig()
- framework.ExpectNoError(err, "failed to load config")
- apiExtensionClient, err := apiextensionclientset.NewForConfig(config)
- framework.ExpectNoError(err, "failed to initialize apiExtensionClient")
- crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, f.DynamicClient)
- framework.ExpectNoError(err, "failed to create custom resource definition")
- err = fixtures.DeleteCustomResourceDefinition(crd, apiExtensionClient)
- framework.ExpectNoError(err, "failed to delete custom resource definition")
- expectEvents(f, []utils.AuditEvent{
- {
- Level: auditinternal.LevelRequestResponse,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: "/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions",
- Verb: "create",
- Code: 201,
- User: auditTestUser,
- Resource: "customresourcedefinitions",
- RequestObject: true,
- ResponseObject: true,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/apis/%s/v1beta1/%s", crdNamespace, crdName),
- Verb: "create",
- Code: 201,
- User: auditTestUser,
- Resource: crdName,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelRequestResponse,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions/%s", crd.Name),
- Verb: "delete",
- Code: 200,
- User: auditTestUser,
- Resource: "customresourcedefinitions",
- RequestObject: false,
- ResponseObject: true,
- AuthorizeDecision: "allow",
- }, {
- Level: auditinternal.LevelMetadata,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/apis/%s/v1beta1/%s/setup-instance", crdNamespace, crdName),
- Verb: "delete",
- Code: 200,
- User: auditTestUser,
- Resource: crdName,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- },
- })
- })
- // test authorizer annotations, RBAC is required.
- ginkgo.It("should audit API calls to get a pod with unauthorized user.", func() {
- if !auth.IsRBACEnabled(f.ClientSet.RbacV1beta1()) {
- framework.Skipf("RBAC not enabled.")
- }
- ginkgo.By("Creating a kubernetes client that impersonates an unauthorized anonymous user")
- config, err := framework.LoadConfig()
- framework.ExpectNoError(err)
- config.Impersonate = restclient.ImpersonationConfig{
- UserName: "system:anonymous",
- Groups: []string{"system:unauthenticated"},
- }
- anonymousClient, err := clientset.NewForConfig(config)
- framework.ExpectNoError(err)
- _, err = anonymousClient.CoreV1().Pods(namespace).Get("another-audit-pod", metav1.GetOptions{})
- expectForbidden(err)
- expectEvents(f, []utils.AuditEvent{
- {
- Level: auditinternal.LevelRequest,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods/another-audit-pod", namespace),
- Verb: "get",
- Code: 403,
- User: auditTestUser,
- ImpersonatedUser: "system:anonymous",
- ImpersonatedGroups: "system:unauthenticated",
- Resource: "pods",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "forbid",
- },
- })
- })
- ginkgo.It("should list pods as impersonated user.", func() {
- ginkgo.By("Creating a kubernetes client that impersonates an authorized user")
- config, err := framework.LoadConfig()
- framework.ExpectNoError(err)
- config.Impersonate = restclient.ImpersonationConfig{
- UserName: "superman",
- Groups: []string{"system:masters"},
- }
- impersonatedClient, err := clientset.NewForConfig(config)
- framework.ExpectNoError(err)
- _, err = impersonatedClient.CoreV1().Pods(namespace).List(metav1.ListOptions{})
- framework.ExpectNoError(err, "failed to list pods")
- expectEvents(f, []utils.AuditEvent{
- {
- Level: auditinternal.LevelRequest,
- Stage: auditinternal.StageResponseComplete,
- RequestURI: fmt.Sprintf("/api/v1/namespaces/%s/pods", namespace),
- Verb: "list",
- Code: 200,
- User: auditTestUser,
- ImpersonatedUser: "superman",
- ImpersonatedGroups: "system:masters",
- Resource: "pods",
- Namespace: namespace,
- RequestObject: false,
- ResponseObject: false,
- AuthorizeDecision: "allow",
- },
- })
- })
- })
- func expectEvents(f *framework.Framework, expectedEvents []utils.AuditEvent) {
- // The default flush timeout is 30 seconds, therefore it should be enough to retry once
- // to find all expected events. However, we're waiting for 5 minutes to avoid flakes.
- pollingInterval := 30 * time.Second
- pollingTimeout := 5 * time.Minute
- err := wait.Poll(pollingInterval, pollingTimeout, func() (bool, error) {
- // Fetch the log stream.
- stream, err := f.ClientSet.CoreV1().RESTClient().Get().AbsPath("/logs/kube-apiserver-audit.log").Stream()
- if err != nil {
- return false, err
- }
- defer stream.Close()
- missingReport, err := utils.CheckAuditLines(stream, expectedEvents, auditv1.SchemeGroupVersion)
- if err != nil {
- e2elog.Logf("Failed to observe audit events: %v", err)
- } else if len(missingReport.MissingEvents) > 0 {
- e2elog.Logf(missingReport.String())
- }
- return len(missingReport.MissingEvents) == 0, nil
- })
- framework.ExpectNoError(err, "after %v failed to observe audit events", pollingTimeout)
- }
|