mirror of https://github.com/k3s-io/k3s
Merge pull request #71489 from tallclair/audit-test
Split audit test cases into separate testspull/564/head
commit
3724e2e5a0
|
@ -54,48 +54,16 @@ var (
|
|||
patch, _ = json.Marshal(jsonpatch.Patch{})
|
||||
)
|
||||
|
||||
var _ = SIGDescribe("Advanced Audit", func() {
|
||||
// TODO: Get rid of [DisabledForLargeClusters] when feature request #53455 is ready.
|
||||
var _ = SIGDescribe("Advanced Audit [DisabledForLargeClusters]", func() {
|
||||
f := framework.NewDefaultFramework("audit")
|
||||
var namespace string
|
||||
BeforeEach(func() {
|
||||
framework.SkipUnlessProviderIs("gce")
|
||||
namespace = f.Namespace.Name
|
||||
})
|
||||
|
||||
// TODO: Get rid of [DisabledForLargeClusters] when feature request #53455 is ready.
|
||||
It("should audit API calls [DisabledForLargeClusters]", func() {
|
||||
namespace := f.Namespace.Name
|
||||
|
||||
config, err := framework.LoadConfig()
|
||||
framework.ExpectNoError(err, "failed to load config")
|
||||
apiExtensionClient, err := apiextensionclientset.NewForConfig(config)
|
||||
framework.ExpectNoError(err, "failed to initialize apiExtensionClient")
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
testCases := []struct {
|
||||
action func()
|
||||
events []utils.AuditEvent
|
||||
}{
|
||||
// Create, get, update, patch, delete, list, watch pods.
|
||||
{
|
||||
func() {
|
||||
It("should audit API calls to create, get, update, patch, delete, list, watch pods.", func() {
|
||||
pod := &apiv1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "audit-pod",
|
||||
|
@ -127,8 +95,8 @@ var _ = SIGDescribe("Advanced Audit", func() {
|
|||
framework.ExpectNoError(err, "failed to patch pod")
|
||||
|
||||
f.PodClient().DeleteSync(pod.Name, &metav1.DeleteOptions{}, framework.DefaultPodDeletionTimeout)
|
||||
},
|
||||
[]utils.AuditEvent{
|
||||
|
||||
expectEvents(f, []utils.AuditEvent{
|
||||
{
|
||||
Level: auditinternal.LevelRequestResponse,
|
||||
Stage: auditinternal.StageResponseComplete,
|
||||
|
@ -226,11 +194,10 @@ var _ = SIGDescribe("Advanced Audit", func() {
|
|||
ResponseObject: true,
|
||||
AuthorizeDecision: "allow",
|
||||
},
|
||||
},
|
||||
},
|
||||
// Create, get, update, patch, delete, list, watch deployments.
|
||||
{
|
||||
func() {
|
||||
})
|
||||
})
|
||||
|
||||
It("should audit API calls to 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), apps.RecreateDeploymentStrategyType)
|
||||
|
||||
|
@ -255,8 +222,8 @@ var _ = SIGDescribe("Advanced Audit", func() {
|
|||
|
||||
err = f.ClientSet.AppsV1().Deployments(namespace).Delete("audit-deployment", &metav1.DeleteOptions{})
|
||||
framework.ExpectNoError(err, "failed to delete deployments")
|
||||
},
|
||||
[]utils.AuditEvent{
|
||||
|
||||
expectEvents(f, []utils.AuditEvent{
|
||||
{
|
||||
Level: auditinternal.LevelRequestResponse,
|
||||
Stage: auditinternal.StageResponseComplete,
|
||||
|
@ -354,11 +321,10 @@ var _ = SIGDescribe("Advanced Audit", func() {
|
|||
ResponseObject: true,
|
||||
AuthorizeDecision: "allow",
|
||||
},
|
||||
},
|
||||
},
|
||||
// Create, get, update, patch, delete, list, watch configmaps.
|
||||
{
|
||||
func() {
|
||||
})
|
||||
})
|
||||
|
||||
It("should audit API calls to create, get, update, patch, delete, list, watch configmaps.", func() {
|
||||
configMap := &apiv1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "audit-configmap",
|
||||
|
@ -389,8 +355,8 @@ var _ = SIGDescribe("Advanced Audit", func() {
|
|||
|
||||
err = f.ClientSet.CoreV1().ConfigMaps(namespace).Delete(configMap.Name, &metav1.DeleteOptions{})
|
||||
framework.ExpectNoError(err, "failed to delete audit-configmap")
|
||||
},
|
||||
[]utils.AuditEvent{
|
||||
|
||||
expectEvents(f, []utils.AuditEvent{
|
||||
{
|
||||
Level: auditinternal.LevelMetadata,
|
||||
Stage: auditinternal.StageResponseComplete,
|
||||
|
@ -488,11 +454,10 @@ var _ = SIGDescribe("Advanced Audit", func() {
|
|||
ResponseObject: false,
|
||||
AuthorizeDecision: "allow",
|
||||
},
|
||||
},
|
||||
},
|
||||
// Create, get, update, patch, delete, list, watch secrets.
|
||||
{
|
||||
func() {
|
||||
})
|
||||
})
|
||||
|
||||
It("should audit API calls to create, get, update, patch, delete, list, watch secrets.", func() {
|
||||
secret := &apiv1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "audit-secret",
|
||||
|
@ -522,8 +487,8 @@ var _ = SIGDescribe("Advanced Audit", func() {
|
|||
|
||||
err = f.ClientSet.CoreV1().Secrets(namespace).Delete(secret.Name, &metav1.DeleteOptions{})
|
||||
framework.ExpectNoError(err, "failed to delete audit-secret")
|
||||
},
|
||||
[]utils.AuditEvent{
|
||||
|
||||
expectEvents(f, []utils.AuditEvent{
|
||||
{
|
||||
Level: auditinternal.LevelMetadata,
|
||||
Stage: auditinternal.StageResponseComplete,
|
||||
|
@ -621,16 +586,21 @@ var _ = SIGDescribe("Advanced Audit", func() {
|
|||
ResponseObject: false,
|
||||
AuthorizeDecision: "allow",
|
||||
},
|
||||
},
|
||||
},
|
||||
// Create and delete custom resource definition.
|
||||
{
|
||||
func() {
|
||||
})
|
||||
})
|
||||
|
||||
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")
|
||||
fixtures.DeleteCustomResourceDefinition(crd, apiExtensionClient)
|
||||
},
|
||||
[]utils.AuditEvent{
|
||||
err = fixtures.DeleteCustomResourceDefinition(crd, apiExtensionClient)
|
||||
framework.ExpectNoError(err, "failed to delete custom resource definition")
|
||||
|
||||
expectEvents(f, []utils.AuditEvent{
|
||||
{
|
||||
Level: auditinternal.LevelRequestResponse,
|
||||
Stage: auditinternal.StageResponseComplete,
|
||||
|
@ -676,47 +646,29 @@ var _ = SIGDescribe("Advanced Audit", func() {
|
|||
ResponseObject: false,
|
||||
AuthorizeDecision: "allow",
|
||||
},
|
||||
},
|
||||
},
|
||||
// List pods as impersonated user.
|
||||
{
|
||||
func() {
|
||||
_, err = impersonatedClient.CoreV1().Pods(namespace).List(metav1.ListOptions{})
|
||||
framework.ExpectNoError(err, "failed to list pods")
|
||||
},
|
||||
[]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",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// test authorizer annotations, RBAC is required.
|
||||
annotationTestCases := []struct {
|
||||
action func()
|
||||
events []utils.AuditEvent
|
||||
}{
|
||||
It("should audit API calls to get a pod with unauthorized user.", func() {
|
||||
if !framework.IsRBACEnabled(f) {
|
||||
framework.Skipf("RBAC not enabled.")
|
||||
}
|
||||
|
||||
// get a pod with unauthorized user
|
||||
{
|
||||
func() {
|
||||
_, err := anonymousClient.CoreV1().Pods(namespace).Get("another-audit-pod", metav1.GetOptions{})
|
||||
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)
|
||||
},
|
||||
[]utils.AuditEvent{
|
||||
|
||||
expectEvents(f, []utils.AuditEvent{
|
||||
{
|
||||
Level: auditinternal.LevelRequest,
|
||||
Stage: auditinternal.StageResponseComplete,
|
||||
|
@ -732,24 +684,50 @@ var _ = SIGDescribe("Advanced Audit", func() {
|
|||
ResponseObject: false,
|
||||
AuthorizeDecision: "forbid",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if framework.IsRBACEnabled(f) {
|
||||
testCases = append(testCases, annotationTestCases...)
|
||||
}
|
||||
expectedEvents := []utils.AuditEvent{}
|
||||
for _, t := range testCases {
|
||||
t.action()
|
||||
expectedEvents = append(expectedEvents, t.events...)
|
||||
It("should list pods as impersonated user.", func() {
|
||||
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) {
|
||||
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 {
|
||||
|
@ -765,5 +743,4 @@ var _ = SIGDescribe("Advanced Audit", func() {
|
|||
return len(missing) == 0, nil
|
||||
})
|
||||
framework.ExpectNoError(err, "after %v failed to observe audit events", pollingTimeout)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue