mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-01 07:47:56 +00:00
Merge pull request #53119 from loburm/audit-e2e
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Improve e2e tests of audit logging. Now test includes: * Verbs: create, list, watch, delete, get, update, patch. * Resources: pods, deployments, secrets, config maps, custom resource definition. * More fields: user, resource, level, stage, presence of request and response objects. Fixes #49653
This commit is contained in:
commit
0b1da1fb37
@ -19,12 +19,18 @@ go_library(
|
||||
"//plugin/pkg/admission/serviceaccount:go_default_library",
|
||||
"//test/e2e/framework:go_default_library",
|
||||
"//test/utils/image:go_default_library",
|
||||
"//vendor/github.com/evanphx/json-patch:go_default_library",
|
||||
"//vendor/github.com/onsi/ginkgo:go_default_library",
|
||||
"//vendor/github.com/onsi/gomega:go_default_library",
|
||||
"//vendor/k8s.io/api/certificates/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset:go_default_library",
|
||||
"//vendor/k8s.io/apiextensions-apiserver/test/integration/testserver:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/apis/audit/v1beta1:go_default_library",
|
||||
|
@ -23,83 +23,624 @@ import (
|
||||
"strings"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
extensions "k8s.io/api/extensions/v1beta1"
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
"k8s.io/apiextensions-apiserver/test/integration/testserver"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apiserver/pkg/apis/audit/v1beta1"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
imageutils "k8s.io/kubernetes/test/utils/image"
|
||||
|
||||
"github.com/evanphx/json-patch"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var (
|
||||
watchTestTimeout int64 = 1
|
||||
auditTestUser = "kubecfg"
|
||||
|
||||
crd = testserver.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{})
|
||||
)
|
||||
|
||||
var _ = SIGDescribe("Advanced Audit [Feature:Audit]", func() {
|
||||
f := framework.NewDefaultFramework("audit")
|
||||
|
||||
It("should audit API calls", func() {
|
||||
namespace := f.Namespace.Name
|
||||
|
||||
// Create & Delete pod
|
||||
pod := &apiv1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "audit-pod",
|
||||
config, err := framework.LoadConfig()
|
||||
framework.ExpectNoError(err, "failed to load config")
|
||||
apiExtensionClient, err := clientset.NewForConfig(config)
|
||||
framework.ExpectNoError(err, "failed to initialize apiExtensionClient")
|
||||
|
||||
testCases := []struct {
|
||||
action func()
|
||||
events []auditEvent
|
||||
}{
|
||||
// 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: framework.GetPauseImageName(f.ClientSet),
|
||||
}},
|
||||
},
|
||||
}
|
||||
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")
|
||||
for range podChan.ResultChan() {
|
||||
}
|
||||
|
||||
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)
|
||||
},
|
||||
[]auditEvent{
|
||||
{
|
||||
v1beta1.LevelRequestResponse,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/pods", namespace),
|
||||
"create",
|
||||
201,
|
||||
auditTestUser,
|
||||
"pods",
|
||||
namespace,
|
||||
true,
|
||||
true,
|
||||
}, {
|
||||
v1beta1.LevelRequest,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
|
||||
"get",
|
||||
200,
|
||||
auditTestUser,
|
||||
"pods",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelRequest,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/pods", namespace),
|
||||
"list",
|
||||
200,
|
||||
auditTestUser,
|
||||
"pods",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelRequest,
|
||||
v1beta1.StageResponseStarted,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/pods?timeoutSeconds=%d&watch=true", namespace, watchTestTimeout),
|
||||
"watch",
|
||||
200,
|
||||
auditTestUser,
|
||||
"pods",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelRequest,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/pods?timeoutSeconds=%d&watch=true", namespace, watchTestTimeout),
|
||||
"watch",
|
||||
200,
|
||||
auditTestUser,
|
||||
"pods",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelRequestResponse,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
|
||||
"update",
|
||||
200,
|
||||
auditTestUser,
|
||||
"pods",
|
||||
namespace,
|
||||
true,
|
||||
true,
|
||||
}, {
|
||||
v1beta1.LevelRequestResponse,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
|
||||
"patch",
|
||||
200,
|
||||
auditTestUser,
|
||||
"pods",
|
||||
namespace,
|
||||
true,
|
||||
true,
|
||||
}, {
|
||||
v1beta1.LevelRequestResponse,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
|
||||
"delete",
|
||||
200,
|
||||
auditTestUser,
|
||||
"pods",
|
||||
namespace,
|
||||
true,
|
||||
true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{{
|
||||
Name: "pause",
|
||||
Image: framework.GetPauseImageName(f.ClientSet),
|
||||
}},
|
||||
// Create, get, update, patch, delete, list, watch deployments.
|
||||
{
|
||||
func() {
|
||||
podLabels := map[string]string{"name": "audit-deployment-pod"}
|
||||
d := framework.NewDeployment("audit-deployment", int32(1), podLabels, "redis", imageutils.GetE2EImage(imageutils.Redis), extensions.RecreateDeploymentStrategyType)
|
||||
|
||||
_, err := f.ClientSet.Extensions().Deployments(namespace).Create(d)
|
||||
framework.ExpectNoError(err, "failed to create audit-deployment")
|
||||
|
||||
_, err = f.ClientSet.Extensions().Deployments(namespace).Get(d.Name, metav1.GetOptions{})
|
||||
framework.ExpectNoError(err, "failed to get audit-deployment")
|
||||
|
||||
deploymentChan, err := f.ClientSet.Extensions().Deployments(namespace).Watch(watchOptions)
|
||||
framework.ExpectNoError(err, "failed to create watch for deployments")
|
||||
for range deploymentChan.ResultChan() {
|
||||
}
|
||||
|
||||
_, err = f.ClientSet.Extensions().Deployments(namespace).Update(d)
|
||||
framework.ExpectNoError(err, "failed to update audit-deployment")
|
||||
|
||||
_, err = f.ClientSet.Extensions().Deployments(namespace).Patch(d.Name, types.JSONPatchType, patch)
|
||||
framework.ExpectNoError(err, "failed to patch deployment")
|
||||
|
||||
_, err = f.ClientSet.Extensions().Deployments(namespace).List(metav1.ListOptions{})
|
||||
framework.ExpectNoError(err, "failed to create list deployments")
|
||||
|
||||
err = f.ClientSet.Extensions().Deployments(namespace).Delete("audit-deployment", &metav1.DeleteOptions{})
|
||||
framework.ExpectNoError(err, "failed to delete deployments")
|
||||
},
|
||||
[]auditEvent{
|
||||
{
|
||||
v1beta1.LevelRequestResponse,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/apis/extensions/v1beta1/namespaces/%s/deployments", namespace),
|
||||
"create",
|
||||
201,
|
||||
auditTestUser,
|
||||
"deployments",
|
||||
namespace,
|
||||
true,
|
||||
true,
|
||||
}, {
|
||||
v1beta1.LevelRequest,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/apis/extensions/v1beta1/namespaces/%s/deployments/audit-deployment", namespace),
|
||||
"get",
|
||||
200,
|
||||
auditTestUser,
|
||||
"deployments",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelRequest,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/apis/extensions/v1beta1/namespaces/%s/deployments", namespace),
|
||||
"list",
|
||||
200,
|
||||
auditTestUser,
|
||||
"deployments",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelRequest,
|
||||
v1beta1.StageResponseStarted,
|
||||
fmt.Sprintf("/apis/extensions/v1beta1/namespaces/%s/deployments?timeoutSeconds=%d&watch=true", namespace, watchTestTimeout),
|
||||
"watch",
|
||||
200,
|
||||
auditTestUser,
|
||||
"deployments",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelRequest,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/apis/extensions/v1beta1/namespaces/%s/deployments?timeoutSeconds=%d&watch=true", namespace, watchTestTimeout),
|
||||
"watch",
|
||||
200,
|
||||
auditTestUser,
|
||||
"deployments",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelRequestResponse,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/apis/extensions/v1beta1/namespaces/%s/deployments/audit-deployment", namespace),
|
||||
"update",
|
||||
200,
|
||||
auditTestUser,
|
||||
"deployments",
|
||||
namespace,
|
||||
true,
|
||||
true,
|
||||
}, {
|
||||
v1beta1.LevelRequestResponse,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/apis/extensions/v1beta1/namespaces/%s/deployments/audit-deployment", namespace),
|
||||
"patch",
|
||||
200,
|
||||
auditTestUser,
|
||||
"deployments",
|
||||
namespace,
|
||||
true,
|
||||
true,
|
||||
}, {
|
||||
v1beta1.LevelRequestResponse,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/apis/extensions/v1beta1/namespaces/%s/deployments/audit-deployment", namespace),
|
||||
"delete",
|
||||
200,
|
||||
auditTestUser,
|
||||
"deployments",
|
||||
namespace,
|
||||
true,
|
||||
true,
|
||||
},
|
||||
},
|
||||
},
|
||||
// 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.Core().ConfigMaps(namespace).Create(configMap)
|
||||
framework.ExpectNoError(err, "failed to create audit-configmap")
|
||||
|
||||
_, err = f.ClientSet.Core().ConfigMaps(namespace).Get(configMap.Name, metav1.GetOptions{})
|
||||
framework.ExpectNoError(err, "failed to get audit-configmap")
|
||||
|
||||
configMapChan, err := f.ClientSet.Core().ConfigMaps(namespace).Watch(watchOptions)
|
||||
framework.ExpectNoError(err, "failed to create watch for config maps")
|
||||
for range configMapChan.ResultChan() {
|
||||
}
|
||||
|
||||
_, err = f.ClientSet.Core().ConfigMaps(namespace).Update(configMap)
|
||||
framework.ExpectNoError(err, "failed to update audit-configmap")
|
||||
|
||||
_, err = f.ClientSet.Core().ConfigMaps(namespace).Patch(configMap.Name, types.JSONPatchType, patch)
|
||||
framework.ExpectNoError(err, "failed to patch configmap")
|
||||
|
||||
_, err = f.ClientSet.Core().ConfigMaps(namespace).List(metav1.ListOptions{})
|
||||
framework.ExpectNoError(err, "failed to list config maps")
|
||||
|
||||
err = f.ClientSet.Core().ConfigMaps(namespace).Delete(configMap.Name, &metav1.DeleteOptions{})
|
||||
framework.ExpectNoError(err, "failed to delete audit-configmap")
|
||||
},
|
||||
[]auditEvent{
|
||||
{
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/configmaps", namespace),
|
||||
"create",
|
||||
201,
|
||||
auditTestUser,
|
||||
"configmaps",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
|
||||
"get",
|
||||
200,
|
||||
auditTestUser,
|
||||
"configmaps",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/configmaps", namespace),
|
||||
"list",
|
||||
200,
|
||||
auditTestUser,
|
||||
"configmaps",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseStarted,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/configmaps?timeoutSeconds=%d&watch=true", namespace, watchTestTimeout),
|
||||
"watch",
|
||||
200,
|
||||
auditTestUser,
|
||||
"configmaps",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/configmaps?timeoutSeconds=%d&watch=true", namespace, watchTestTimeout),
|
||||
"watch",
|
||||
200,
|
||||
auditTestUser,
|
||||
"configmaps",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
|
||||
"update",
|
||||
200,
|
||||
auditTestUser,
|
||||
"configmaps",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
|
||||
"patch",
|
||||
200,
|
||||
auditTestUser,
|
||||
"configmaps",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/configmaps/audit-configmap", namespace),
|
||||
"delete",
|
||||
200,
|
||||
auditTestUser,
|
||||
"configmaps",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
},
|
||||
},
|
||||
// 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.Core().Secrets(namespace).Create(secret)
|
||||
framework.ExpectNoError(err, "failed to create audit-secret")
|
||||
|
||||
_, err = f.ClientSet.Core().Secrets(namespace).Get(secret.Name, metav1.GetOptions{})
|
||||
framework.ExpectNoError(err, "failed to get audit-secret")
|
||||
|
||||
secretChan, err := f.ClientSet.Core().Secrets(namespace).Watch(watchOptions)
|
||||
framework.ExpectNoError(err, "failed to create watch for secrets")
|
||||
for range secretChan.ResultChan() {
|
||||
}
|
||||
|
||||
_, err = f.ClientSet.Core().Secrets(namespace).Update(secret)
|
||||
framework.ExpectNoError(err, "failed to update audit-secret")
|
||||
|
||||
_, err = f.ClientSet.Core().Secrets(namespace).Patch(secret.Name, types.JSONPatchType, patch)
|
||||
framework.ExpectNoError(err, "failed to patch secret")
|
||||
|
||||
_, err = f.ClientSet.Core().Secrets(namespace).List(metav1.ListOptions{})
|
||||
framework.ExpectNoError(err, "failed to list secrets")
|
||||
|
||||
err = f.ClientSet.Core().Secrets(namespace).Delete(secret.Name, &metav1.DeleteOptions{})
|
||||
framework.ExpectNoError(err, "failed to delete audit-secret")
|
||||
},
|
||||
[]auditEvent{
|
||||
{
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/secrets", namespace),
|
||||
"create",
|
||||
201,
|
||||
auditTestUser,
|
||||
"secrets",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/secrets/audit-secret", namespace),
|
||||
"get",
|
||||
200,
|
||||
auditTestUser,
|
||||
"secrets",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/secrets", namespace),
|
||||
"list",
|
||||
200,
|
||||
auditTestUser,
|
||||
"secrets",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseStarted,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/secrets?timeoutSeconds=%d&watch=true", namespace, watchTestTimeout),
|
||||
"watch",
|
||||
200,
|
||||
auditTestUser,
|
||||
"secrets",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/secrets?timeoutSeconds=%d&watch=true", namespace, watchTestTimeout),
|
||||
"watch",
|
||||
200,
|
||||
auditTestUser,
|
||||
"secrets",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/secrets/audit-secret", namespace),
|
||||
"update",
|
||||
200,
|
||||
auditTestUser,
|
||||
"secrets",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/secrets/audit-secret", namespace),
|
||||
"patch",
|
||||
200,
|
||||
auditTestUser,
|
||||
"secrets",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
}, {
|
||||
v1beta1.LevelMetadata,
|
||||
v1beta1.StageResponseComplete,
|
||||
fmt.Sprintf("/api/v1/namespaces/%s/secrets/audit-secret", namespace),
|
||||
"delete",
|
||||
200,
|
||||
auditTestUser,
|
||||
"secrets",
|
||||
namespace,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
},
|
||||
},
|
||||
// Create and delete custom resource definition.
|
||||
{
|
||||
func() {
|
||||
_, err = testserver.CreateNewCustomResourceDefinition(crd, apiExtensionClient, f.ClientPool)
|
||||
framework.ExpectNoError(err, "failed to create custom resource definition")
|
||||
testserver.DeleteCustomResourceDefinition(crd, apiExtensionClient)
|
||||
},
|
||||
[]auditEvent{
|
||||
{
|
||||
level: v1beta1.LevelRequestResponse,
|
||||
stage: v1beta1.StageResponseComplete,
|
||||
requestURI: "/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions",
|
||||
verb: "create",
|
||||
code: 201,
|
||||
user: auditTestUser,
|
||||
resource: "customresourcedefinitions",
|
||||
requestObject: true,
|
||||
responseObject: true,
|
||||
}, {
|
||||
level: v1beta1.LevelMetadata,
|
||||
stage: v1beta1.StageResponseComplete,
|
||||
requestURI: fmt.Sprintf("/apis/%s/v1beta1/%s", crdNamespace, crdName),
|
||||
verb: "create",
|
||||
code: 201,
|
||||
user: auditTestUser,
|
||||
resource: crdName,
|
||||
requestObject: false,
|
||||
responseObject: false,
|
||||
}, {
|
||||
level: v1beta1.LevelRequestResponse,
|
||||
stage: v1beta1.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,
|
||||
}, {
|
||||
level: v1beta1.LevelMetadata,
|
||||
stage: v1beta1.StageResponseComplete,
|
||||
requestURI: fmt.Sprintf("/apis/%s/v1beta1/%s/setup-instance", crdNamespace, crdName),
|
||||
verb: "delete",
|
||||
code: 200,
|
||||
user: auditTestUser,
|
||||
resource: crdName,
|
||||
requestObject: false,
|
||||
responseObject: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
f.PodClient().CreateSync(pod)
|
||||
f.PodClient().DeleteSync(pod.Name, &metav1.DeleteOptions{}, framework.DefaultPodDeletionTimeout)
|
||||
|
||||
// Create, Read, Delete secret
|
||||
secret := &apiv1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "audit-secret",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"top-secret": []byte("foo-bar"),
|
||||
},
|
||||
expectedEvents := []auditEvent{}
|
||||
for _, t := range testCases {
|
||||
t.action()
|
||||
expectedEvents = append(expectedEvents, t.events...)
|
||||
}
|
||||
_, err := f.ClientSet.Core().Secrets(f.Namespace.Name).Create(secret)
|
||||
framework.ExpectNoError(err, "failed to create audit-secret")
|
||||
_, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Get(secret.Name, metav1.GetOptions{})
|
||||
framework.ExpectNoError(err, "failed to get audit-secret")
|
||||
err = f.ClientSet.Core().Secrets(f.Namespace.Name).Delete(secret.Name, &metav1.DeleteOptions{})
|
||||
framework.ExpectNoError(err, "failed to delete audit-secret")
|
||||
|
||||
expectedEvents := []auditEvent{{
|
||||
method: "create",
|
||||
namespace: namespace,
|
||||
uri: fmt.Sprintf("/api/v1/namespaces/%s/pods", namespace),
|
||||
response: "201",
|
||||
}, {
|
||||
method: "delete",
|
||||
namespace: namespace,
|
||||
uri: fmt.Sprintf("/api/v1/namespaces/%s/pods/%s", namespace, pod.Name),
|
||||
response: "200",
|
||||
}, {
|
||||
method: "create",
|
||||
namespace: namespace,
|
||||
uri: fmt.Sprintf("/api/v1/namespaces/%s/secrets", namespace),
|
||||
response: "201",
|
||||
}, {
|
||||
method: "get",
|
||||
namespace: namespace,
|
||||
uri: fmt.Sprintf("/api/v1/namespaces/%s/secrets/%s", namespace, secret.Name),
|
||||
response: "200",
|
||||
}, {
|
||||
method: "delete",
|
||||
namespace: namespace,
|
||||
uri: fmt.Sprintf("/api/v1/namespaces/%s/secrets/%s", namespace, secret.Name),
|
||||
response: "200",
|
||||
}}
|
||||
expectAuditLines(f, expectedEvents)
|
||||
})
|
||||
})
|
||||
|
||||
type auditEvent struct {
|
||||
method, namespace, uri, response string
|
||||
level v1beta1.Level
|
||||
stage v1beta1.Stage
|
||||
requestURI string
|
||||
verb string
|
||||
code int32
|
||||
user string
|
||||
resource string
|
||||
namespace string
|
||||
requestObject bool
|
||||
responseObject bool
|
||||
}
|
||||
|
||||
// Search the audit log for the expected audit lines.
|
||||
@ -134,46 +675,28 @@ func expectAuditLines(f *framework.Framework, expected []auditEvent) {
|
||||
|
||||
func parseAuditLine(line string) (auditEvent, error) {
|
||||
var e v1beta1.Event
|
||||
if err := json.Unmarshal([]byte(line), &e); err == nil {
|
||||
event := auditEvent{
|
||||
method: e.Verb,
|
||||
uri: e.RequestURI,
|
||||
}
|
||||
if e.ObjectRef != nil {
|
||||
event.namespace = e.ObjectRef.Namespace
|
||||
}
|
||||
if e.ResponseStatus != nil {
|
||||
event.response = fmt.Sprintf("%d", e.ResponseStatus.Code)
|
||||
}
|
||||
return event, nil
|
||||
if err := json.Unmarshal([]byte(line), &e); err != nil {
|
||||
return auditEvent{}, err
|
||||
}
|
||||
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 3 {
|
||||
return auditEvent{}, fmt.Errorf("could not parse audit line: %s", line)
|
||||
event := auditEvent{
|
||||
level: e.Level,
|
||||
stage: e.Stage,
|
||||
requestURI: e.RequestURI,
|
||||
verb: e.Verb,
|
||||
user: e.User.Username,
|
||||
}
|
||||
// Ignore first field (timestamp)
|
||||
if fields[1] != "AUDIT:" {
|
||||
return auditEvent{}, fmt.Errorf("unexpected audit line format: %s", line)
|
||||
if e.ObjectRef != nil {
|
||||
event.namespace = e.ObjectRef.Namespace
|
||||
event.resource = e.ObjectRef.Resource
|
||||
}
|
||||
fields = fields[2:]
|
||||
event := auditEvent{}
|
||||
for _, f := range fields {
|
||||
parts := strings.SplitN(f, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return auditEvent{}, fmt.Errorf("could not parse audit line (part: %q): %s", f, line)
|
||||
}
|
||||
value := strings.Trim(parts[1], "\"")
|
||||
switch parts[0] {
|
||||
case "method":
|
||||
event.method = value
|
||||
case "namespace":
|
||||
event.namespace = value
|
||||
case "uri":
|
||||
event.uri = value
|
||||
case "response":
|
||||
event.response = value
|
||||
}
|
||||
if e.ResponseStatus != nil {
|
||||
event.code = e.ResponseStatus.Code
|
||||
}
|
||||
if e.ResponseObject != nil {
|
||||
event.responseObject = true
|
||||
}
|
||||
if e.RequestObject != nil {
|
||||
event.requestObject = true
|
||||
}
|
||||
return event, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user