Skip to content
This repository has been archived by the owner on Jun 26, 2023. It is now read-only.

[MTB] added block other tenant resources benchmark #1318

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 16 additions & 3 deletions benchmarks/kubectl-mtb/internal/kubectl-mtb/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func initConfig() error {
}

// create the K8s clientset
benchmarkRunOptions.KClient, err = kubernetes.NewForConfig(config)
benchmarkRunOptions.ClusterAdminClient, err = kubernetes.NewForConfig(config)
if err != nil {
return err
}
Expand All @@ -75,11 +75,21 @@ func initConfig() error {
tenantConfig.Impersonate.UserName = benchmarkRunOptions.Tenant

// create the tenant clientset
benchmarkRunOptions.TClient, err = kubernetes.NewForConfig(tenantConfig)
benchmarkRunOptions.Tenant1Client, err = kubernetes.NewForConfig(tenantConfig)
if err != nil {
return err
}

if benchmarkRunOptions.OtherNamespace != "" && benchmarkRunOptions.OtherTenant != "" {
otherTenantConfig := config
otherTenantConfig.Impersonate.UserName = benchmarkRunOptions.OtherTenant

benchmarkRunOptions.Tenant2Client, err = kubernetes.NewForConfig(tenantConfig)
if err != nil {
return err
}
}

return err
}

Expand Down Expand Up @@ -130,12 +140,15 @@ func validateFlags(cmd *cobra.Command) error {
return fmt.Errorf("tenant namespace must be set via --namespace or -n")
}

benchmarkRunOptions.OtherNamespace, _ = cmd.Flags().GetString("other-namespace")
benchmarkRunOptions.OtherTenant, _ = cmd.Flags().GetString("other-tenant-admin")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can add the arguments in a list itself like -n test test2 --as bob-k8s-access bob2-k8s-access?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great idea! @phoenixking25 what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the Cobra CLI package allows this:

cmd --slice a --slice b --slice c,d,e

spf13/cobra#661


err := initConfig()
if err != nil {
return err
}

_, err = benchmarkRunOptions.KClient.CoreV1().Namespaces().Get(context.TODO(), benchmarkRunOptions.TenantNamespace, metav1.GetOptions{})
_, err = benchmarkRunOptions.ClusterAdminClient.CoreV1().Namespaces().Get(context.TODO(), benchmarkRunOptions.TenantNamespace, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("invalid namespace")
}
Expand Down
3 changes: 2 additions & 1 deletion benchmarks/kubectl-mtb/internal/mtb_builder/tpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ func BenchmarkFileTemplate() []byte {

import (
"fmt"
"sigs.k8s.io/multi-tenancy/benchmarks/kubectl-mtb/types"

"k8s.io/client-go/kubernetes"
"sigs.k8s.io/multi-tenancy/benchmarks/kubectl-mtb/bundle/box"
"sigs.k8s.io/multi-tenancy/benchmarks/kubectl-mtb/pkg/benchmark"
"sigs.k8s.io/multi-tenancy/benchmarks/kubectl-mtb/test"
)


var b = &benchmark.Benchmark{

PreRun: func(options types.RunOptions) error {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ var b = &benchmark.Benchmark{
return nil
},
Run: func(options types.RunOptions) error {
resources := []utils.GroupResource{}
var resources []utils.GroupResource

lists, err := options.KClient.Discovery().ServerPreferredResources()
lists, err := options.ClusterAdminClient.Discovery().ServerPreferredResources()
if err != nil {
options.Logger.Debug(err.Error())
return err
Expand Down Expand Up @@ -51,18 +51,11 @@ var b = &benchmark.Benchmark{
}
}

for _, resource := range resources {
for _, verb := range verbs {
access, msg, err := utils.RunAccessCheck(options.TClient, "", resource, verb)
if err != nil {
options.Logger.Debug(err.Error())
return err
}
if access {
return fmt.Errorf(msg)
}
}
err = utils.CheckAccessOnResourcesInNamespace(options.Tenant1Client, "", resources, verbs)
if err != nil {
return err
}

return nil
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func TestMain(m *testing.M) {
if err != nil {
return err
}
options.KClient = k8sClient
options.ClusterAdminClient = k8sClient
options.Logger = log.GetLogger(true)
rest := k8sClient.CoreV1().RESTClient()
apiExtensions, err = apiextensionspkg.NewForConfig(cfg)
Expand All @@ -76,7 +76,7 @@ func TestMain(m *testing.M) {
tenantConfig := testClient.Config
tenantConfig.Impersonate.UserName = "system:serviceaccount:" + namespace + ":" + saName
tenantClient, _ = kubernetes.NewForConfig(tenantConfig)
options.TClient = tenantClient
options.Tenant1Client = tenantClient
options.Tenant = "system:serviceaccount:" + namespace + ":" + saName
options.TenantNamespace = namespace
return nil
Expand Down Expand Up @@ -227,4 +227,4 @@ func testWithClusterAdminRole(t *testing.T) (preRun bool, run bool) {
return true, false
}
return true, true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ var b = &benchmark.Benchmark{
},
}

access, msg, err := utils.RunAccessCheck(options.TClient, options.TenantNamespace, resource, "create")
access, msg, err := utils.RunAccessCheck(options.Tenant1Client, options.TenantNamespace, resource, "create")
if err != nil {
options.Logger.Debug(err.Error())
return err
Expand All @@ -49,7 +49,7 @@ var b = &benchmark.Benchmark{

// Try to create a pod as tenant-admin impersonation
pod := podSpec.MakeSecPod()
_, err = options.TClient.CoreV1().Pods(options.TenantNamespace).Create(context.TODO(), pod, metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}})
_, err = options.Tenant1Client.CoreV1().Pods(options.TenantNamespace).Create(context.TODO(), pod, metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}})
if err == nil {
options.Logger.Debug("Created pod with Spec: ", pod.Spec)
return fmt.Errorf("Tenant should not be able to create pods with add capabilities")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func TestMain(m *testing.M) {
if err != nil {
return err
}
options.KClient = k8sClient
options.ClusterAdminClient = k8sClient
options.Logger = log.GetLogger(true)
rest := k8sClient.CoreV1().RESTClient()
apiExtensions, err = apiextensionspkg.NewForConfig(cfg)
Expand All @@ -78,7 +78,7 @@ func TestMain(m *testing.M) {
tenantConfig := testClient.Config
tenantConfig.Impersonate.UserName = "system:serviceaccount:" + namespace + ":" + saName
tenantClient, _ = kubernetes.NewForConfig(tenantConfig)
options.TClient = tenantClient
options.Tenant1Client = tenantClient
options.Tenant = "system:serviceaccount:" + namespace + ":" + saName
options.TenantNamespace = namespace
return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var b = &benchmark.Benchmark{
},
Run: func(options types.RunOptions) error {

lists, err := options.KClient.Discovery().ServerPreferredResources()
lists, err := options.ClusterAdminClient.Discovery().ServerPreferredResources()
if err != nil {
return nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ var b = &benchmark.Benchmark{
verbs := []string{"create", "update", "patch", "delete", "deletecollection"}
for _, resource := range resources {
for _, verb := range verbs {
access, msg, err := utils.RunAccessCheck(options.TClient, options.TenantNamespace, resource, verb)
access, msg, err := utils.RunAccessCheck(options.Tenant1Client, options.TenantNamespace, resource, verb)
if err != nil {
options.Logger.Debug(err.Error())
return err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func TestMain(m *testing.M) {
if err != nil {
return err
}
options.KClient = k8sClient
options.ClusterAdminClient = k8sClient
options.Logger = log.GetLogger(true)
rest := k8sClient.CoreV1().RESTClient()
apiExtensions, err = apiextensionspkg.NewForConfig(cfg)
Expand All @@ -76,7 +76,7 @@ func TestMain(m *testing.M) {
tenantConfig := testClient.Config
tenantConfig.Impersonate.UserName = "system:serviceaccount:" + namespace + ":" + saName
tenantClient, _ = kubernetes.NewForConfig(tenantConfig)
options.TClient = tenantClient
options.Tenant1Client = tenantClient
options.Tenant = "system:serviceaccount:" + namespace + ":" + saName
options.TenantNamespace = namespace
return nil
Expand Down Expand Up @@ -206,4 +206,4 @@ func testWithRbacPrivileges(t *testing.T) (preRun bool, run bool) {
return true, false
}
return true, true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Block access to other tenant resources <small>[MTB-PL1-CC-TI-2] </small>

**Profile Applicability:**

2

**Type:**

Configuration

**Category:**

Tenant Protection

**Description:**

Access controls should be configured so that a tenant cannot view, edit, create, or delete namespaced resources belonging to another tenant.

**Rationale:**

Tenant resources should be isolated from other tenants.


**Audit:**

Run the following commands to retrieve the list of namespaced resources available in Tenant B
```bash
kubectl --kubeconfig tenant-b api-resources --namespaced=true
```
For each namespaced resource, and each verb (get, list, create, update, patch, watch, delete, and deletecollection) issue the following command
```bash
kubectl --kubeconfig tenant-a -n b1 &lt;verb&gt; &lt;resource&gt;
```
Each command must return &#39;no&#39;


**namespaceRequired:**

2

Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package blockothertenantresources

import (
"fmt"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/multi-tenancy/benchmarks/kubectl-mtb/test/utils"
"sigs.k8s.io/multi-tenancy/benchmarks/kubectl-mtb/types"

"sigs.k8s.io/multi-tenancy/benchmarks/kubectl-mtb/bundle/box"
"sigs.k8s.io/multi-tenancy/benchmarks/kubectl-mtb/pkg/benchmark"
"sigs.k8s.io/multi-tenancy/benchmarks/kubectl-mtb/test"
)

var verbs = []string{"get", "update"}

var b = &benchmark.Benchmark{

PreRun: func(options types.RunOptions) error {

return nil
},

Run: func(options types.RunOptions) error {
var resources []utils.GroupResource

lists, err := options.ClusterAdminClient.Discovery().ServerPreferredResources()
if err != nil {
options.Logger.Debug(err.Error())
return err
}

for _, list := range lists {
if len(list.APIResources) == 0 {
continue
}
gv, err := schema.ParseGroupVersion(list.GroupVersion)
if err != nil {
continue
}
for _, resource := range list.APIResources {
if len(resource.Verbs) == 0 || !resource.Namespaced {
continue
}

resources = append(resources, utils.GroupResource{
APIGroup: gv.Group,
APIResource: resource,
})
}
}

err = utils.CheckAccessOnResourcesInNamespace(options.Tenant2Client, options.TenantNamespace, resources, verbs)
if err != nil {
return err
}

err = utils.CheckAccessOnResourcesInNamespace(options.Tenant1Client, options.OtherNamespace, resources, verbs)
if err != nil {
return err
}
return nil
},
}

func init() {
// Get the []byte representation of a file, or an error if it doesn't exist:
err := b.ReadConfig(box.Get("block_other_tenant_resources/config.yaml"))
if err != nil {
fmt.Println(err)
}

test.BenchmarkSuite.Add(b);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package blockothertenantresources
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
id: MTB-PL1-CC-TI-2
title: Block access to other tenant resources
benchmarkType: Configuration
category: Tenant Protection
description: Access controls should be configured so that a tenant cannot view, edit, create, or delete namespaced resources belonging to another tenant.
remediation:
profileLevel: 2
namespaceRequired: 2
rationale: Tenant resources should be isolated from other tenants.
Audit: |
Run the following commands to retrieve the list of namespaced resources available in Tenant B
```bash
kubectl --kubeconfig tenant-b api-resources --namespaced=true
```
For each namespaced resource, and each verb (get, list, create, update, patch, watch, delete, and deletecollection) issue the following command
```bash
kubectl --kubeconfig tenant-a -n b1 <verb> <resource>
```
Each command must return 'no'
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var b = &benchmark.Benchmark{
},
}

access, msg, err := utils.RunAccessCheck(options.TClient, options.TenantNamespace, resource, "create")
access, msg, err := utils.RunAccessCheck(options.Tenant1Client, options.TenantNamespace, resource, "create")
if err != nil {
options.Logger.Debug(err.Error())
return err
Expand All @@ -47,7 +47,7 @@ var b = &benchmark.Benchmark{

// Try to create a pod as tenant-admin impersonation
pod := podSpec.MakeSecPod()
_, err = options.TClient.CoreV1().Pods(options.TenantNamespace).Create(context.TODO(), pod, metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}})
_, err = options.Tenant1Client.CoreV1().Pods(options.TenantNamespace).Create(context.TODO(), pod, metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}})
if err == nil {
return fmt.Errorf("Tenant must be unable to create pod that sets AllowPrivilegeEscalation to true")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func TestMain(m *testing.M) {
if err != nil {
return err
}
options.KClient = k8sClient
options.ClusterAdminClient = k8sClient
options.Logger = log.GetLogger(true)
rest := k8sClient.CoreV1().RESTClient()
apiExtensions, err = apiextensionspkg.NewForConfig(cfg)
Expand All @@ -78,7 +78,7 @@ func TestMain(m *testing.M) {
tenantConfig := testClient.Config
tenantConfig.Impersonate.UserName = "system:serviceaccount:" + namespace + ":" + saName
tenantClient, _ = kubernetes.NewForConfig(tenantConfig)
options.TClient = tenantClient
options.Tenant1Client = tenantClient
options.Tenant = "system:serviceaccount:" + namespace + ":" + saName
options.TenantNamespace = namespace
return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var b = &benchmark.Benchmark{
},
}

access, msg, err := utils.RunAccessCheck(options.TClient, options.TenantNamespace, resource, "create")
access, msg, err := utils.RunAccessCheck(options.Tenant1Client, options.TenantNamespace, resource, "create")
if err != nil {
options.Logger.Debug(err.Error())
return err
Expand All @@ -47,7 +47,7 @@ var b = &benchmark.Benchmark{

// Try to create a pod as tenant-admin impersonation
pod := podSpec.MakeSecPod()
_, err = options.TClient.CoreV1().Pods(options.TenantNamespace).Create(context.TODO(), pod, metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}})
_, err = options.Tenant1Client.CoreV1().Pods(options.TenantNamespace).Create(context.TODO(), pod, metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}})
if err == nil {
return fmt.Errorf("Tenant must be unable to create pod that sets privileged to true")
}
Expand Down