mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
vendor files
This commit is contained in:
65
vendor/k8s.io/kubernetes/test/e2e/auth/BUILD
generated
vendored
Normal file
65
vendor/k8s.io/kubernetes/test/e2e/auth/BUILD
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"audit.go",
|
||||
"certificates.go",
|
||||
"framework.go",
|
||||
"metadata_concealment.go",
|
||||
"node_authz.go",
|
||||
"pod_security_policy.go",
|
||||
"service_accounts.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/test/e2e/auth",
|
||||
deps = [
|
||||
"//pkg/security/apparmor:go_default_library",
|
||||
"//pkg/security/podsecuritypolicy/seccomp:go_default_library",
|
||||
"//pkg/security/podsecuritypolicy/util:go_default_library",
|
||||
"//plugin/pkg/admission/serviceaccount:go_default_library",
|
||||
"//test/e2e/common: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/batch/v1: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/api/rbac/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/runtime/schema: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",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/certificates/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/cert:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
14
vendor/k8s.io/kubernetes/test/e2e/auth/OWNERS
generated
vendored
Normal file
14
vendor/k8s.io/kubernetes/test/e2e/auth/OWNERS
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
reviewers:
|
||||
- liggitt
|
||||
- mikedanese
|
||||
- smarterclayton
|
||||
- sttts
|
||||
- tallclair
|
||||
- ericchiang
|
||||
approvers:
|
||||
- liggitt
|
||||
- mikedanese
|
||||
- smarterclayton
|
||||
- sttts
|
||||
- tallclair
|
||||
- ericchiang
|
706
vendor/k8s.io/kubernetes/test/e2e/auth/audit.go
generated
vendored
Normal file
706
vendor/k8s.io/kubernetes/test/e2e/auth/audit.go
generated
vendored
Normal file
@ -0,0 +1,706 @@
|
||||
/*
|
||||
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 (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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", func() {
|
||||
f := framework.NewDefaultFramework("audit")
|
||||
BeforeEach(func() {
|
||||
framework.SkipUnlessProviderIs("gce")
|
||||
})
|
||||
|
||||
// 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 := 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,
|
||||
},
|
||||
},
|
||||
},
|
||||
// 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.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")
|
||||
for range configMapChan.ResultChan() {
|
||||
}
|
||||
|
||||
_, 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")
|
||||
},
|
||||
[]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.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")
|
||||
for range secretChan.ResultChan() {
|
||||
}
|
||||
|
||||
_, 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")
|
||||
},
|
||||
[]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,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expectedEvents := []auditEvent{}
|
||||
for _, t := range testCases {
|
||||
t.action()
|
||||
expectedEvents = append(expectedEvents, t.events...)
|
||||
}
|
||||
|
||||
expectAuditLines(f, expectedEvents)
|
||||
})
|
||||
})
|
||||
|
||||
type auditEvent struct {
|
||||
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.
|
||||
func expectAuditLines(f *framework.Framework, expected []auditEvent) {
|
||||
expectations := map[auditEvent]bool{}
|
||||
for _, event := range expected {
|
||||
expectations[event] = false
|
||||
}
|
||||
|
||||
// Fetch the log stream.
|
||||
stream, err := f.ClientSet.CoreV1().RESTClient().Get().AbsPath("/logs/kube-apiserver-audit.log").Stream()
|
||||
framework.ExpectNoError(err, "could not read audit log")
|
||||
defer stream.Close()
|
||||
|
||||
scanner := bufio.NewScanner(stream)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
event, err := parseAuditLine(line)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
// If the event was expected, mark it as found.
|
||||
if _, found := expectations[event]; found {
|
||||
expectations[event] = true
|
||||
}
|
||||
}
|
||||
framework.ExpectNoError(scanner.Err(), "error reading audit log")
|
||||
|
||||
for event, found := range expectations {
|
||||
Expect(found).To(BeTrue(), "Event %#v not found!", event)
|
||||
}
|
||||
}
|
||||
|
||||
func parseAuditLine(line string) (auditEvent, error) {
|
||||
var e v1beta1.Event
|
||||
if err := json.Unmarshal([]byte(line), &e); err != nil {
|
||||
return auditEvent{}, err
|
||||
}
|
||||
event := auditEvent{
|
||||
level: e.Level,
|
||||
stage: e.Stage,
|
||||
requestURI: e.RequestURI,
|
||||
verb: e.Verb,
|
||||
user: e.User.Username,
|
||||
}
|
||||
if e.ObjectRef != nil {
|
||||
event.namespace = e.ObjectRef.Namespace
|
||||
event.resource = e.ObjectRef.Resource
|
||||
}
|
||||
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
|
||||
}
|
121
vendor/k8s.io/kubernetes/test/e2e/auth/certificates.go
generated
vendored
Normal file
121
vendor/k8s.io/kubernetes/test/e2e/auth/certificates.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
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 (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/certificates/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
v1beta1client "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
|
||||
"k8s.io/client-go/util/cert"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
var _ = SIGDescribe("Certificates API", func() {
|
||||
f := framework.NewDefaultFramework("certificates")
|
||||
|
||||
It("should support building a client with a CSR", func() {
|
||||
const commonName = "tester-csr"
|
||||
|
||||
pk, err := cert.NewPrivateKey()
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
pkder := x509.MarshalPKCS1PrivateKey(pk)
|
||||
pkpem := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: pkder,
|
||||
})
|
||||
|
||||
csrb, err := cert.MakeCSR(pk, &pkix.Name{CommonName: commonName, Organization: []string{"system:masters"}}, nil, nil)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
csr := &v1beta1.CertificateSigningRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: commonName + "-",
|
||||
},
|
||||
Spec: v1beta1.CertificateSigningRequestSpec{
|
||||
Request: csrb,
|
||||
Usages: []v1beta1.KeyUsage{
|
||||
v1beta1.UsageSigning,
|
||||
v1beta1.UsageKeyEncipherment,
|
||||
v1beta1.UsageClientAuth,
|
||||
},
|
||||
},
|
||||
}
|
||||
csrs := f.ClientSet.CertificatesV1beta1().CertificateSigningRequests()
|
||||
|
||||
framework.Logf("creating CSR")
|
||||
csr, err = csrs.Create(csr)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
csrName := csr.Name
|
||||
|
||||
framework.Logf("approving CSR")
|
||||
framework.ExpectNoError(wait.Poll(5*time.Second, time.Minute, func() (bool, error) {
|
||||
csr.Status.Conditions = []v1beta1.CertificateSigningRequestCondition{
|
||||
{
|
||||
Type: v1beta1.CertificateApproved,
|
||||
Reason: "E2E",
|
||||
Message: "Set from an e2e test",
|
||||
},
|
||||
}
|
||||
csr, err = csrs.UpdateApproval(csr)
|
||||
if err != nil {
|
||||
csr, _ = csrs.Get(csrName, metav1.GetOptions{})
|
||||
framework.Logf("err updating approval: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}))
|
||||
|
||||
framework.Logf("waiting for CSR to be signed")
|
||||
framework.ExpectNoError(wait.Poll(5*time.Second, time.Minute, func() (bool, error) {
|
||||
csr, _ = csrs.Get(csrName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(csr.Status.Certificate) == 0 {
|
||||
framework.Logf("csr not signed yet")
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}))
|
||||
|
||||
framework.Logf("testing the client")
|
||||
rcfg, err := framework.LoadConfig()
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
rcfg.TLSClientConfig.CertData = csr.Status.Certificate
|
||||
rcfg.TLSClientConfig.KeyData = pkpem
|
||||
rcfg.TLSClientConfig.CertFile = ""
|
||||
rcfg.BearerToken = ""
|
||||
rcfg.AuthProvider = nil
|
||||
rcfg.Username = ""
|
||||
rcfg.Password = ""
|
||||
|
||||
newClient, err := v1beta1client.NewForConfig(rcfg)
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectNoError(newClient.CertificateSigningRequests().Delete(csrName, nil))
|
||||
})
|
||||
})
|
23
vendor/k8s.io/kubernetes/test/e2e/auth/framework.go
generated
vendored
Normal file
23
vendor/k8s.io/kubernetes/test/e2e/auth/framework.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
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 "github.com/onsi/ginkgo"
|
||||
|
||||
func SIGDescribe(text string, body func()) bool {
|
||||
return ginkgo.Describe("[sig-auth] "+text, body)
|
||||
}
|
63
vendor/k8s.io/kubernetes/test/e2e/auth/metadata_concealment.go
generated
vendored
Normal file
63
vendor/k8s.io/kubernetes/test/e2e/auth/metadata_concealment.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
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 (
|
||||
batch "k8s.io/api/batch/v1"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = SIGDescribe("Metadata Concealment", func() {
|
||||
f := framework.NewDefaultFramework("metadata-concealment")
|
||||
|
||||
It("should run a check-metadata-concealment job to completion", func() {
|
||||
framework.SkipUnlessProviderIs("gce")
|
||||
By("Creating a job")
|
||||
job := &batch.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "check-metadata-concealment",
|
||||
},
|
||||
Spec: batch.JobSpec{
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "check-metadata-concealment",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "check-metadata-concealment",
|
||||
Image: "gcr.io/google_containers/check-metadata-concealment:v0.0.2",
|
||||
},
|
||||
},
|
||||
RestartPolicy: v1.RestartPolicyOnFailure,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
job, err := framework.CreateJob(f.ClientSet, f.Namespace.Name, job)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Ensuring job reaches completions")
|
||||
err = framework.WaitForJobFinish(f.ClientSet, f.Namespace.Name, job.Name, int32(1))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
159
vendor/k8s.io/kubernetes/test/e2e/auth/node_authz.go
generated
vendored
Normal file
159
vendor/k8s.io/kubernetes/test/e2e/auth/node_authz.go
generated
vendored
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const (
|
||||
NodesGroup = "system:nodes"
|
||||
NodeNamePrefix = "system:node:"
|
||||
)
|
||||
|
||||
var _ = SIGDescribe("[Feature:NodeAuthorizer]", func() {
|
||||
|
||||
f := framework.NewDefaultFramework("node-authz")
|
||||
// client that will impersonate a node
|
||||
var c clientset.Interface
|
||||
var ns string
|
||||
var asUser string
|
||||
var defaultSaSecret string
|
||||
var nodeName string
|
||||
BeforeEach(func() {
|
||||
ns = f.Namespace.Name
|
||||
|
||||
nodeList, err := f.ClientSet.CoreV1().Nodes().List(metav1.ListOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(len(nodeList.Items)).NotTo(Equal(0))
|
||||
nodeName = nodeList.Items[0].Name
|
||||
asUser = NodeNamePrefix + nodeName
|
||||
sa, err := f.ClientSet.CoreV1().ServiceAccounts(ns).Get("default", metav1.GetOptions{})
|
||||
Expect(len(sa.Secrets)).NotTo(Equal(0))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defaultSaSecret = sa.Secrets[0].Name
|
||||
By("Creating a kubernetes client that impersonates a node")
|
||||
config, err := framework.LoadConfig()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
config.Impersonate = restclient.ImpersonationConfig{
|
||||
UserName: asUser,
|
||||
Groups: []string{NodesGroup},
|
||||
}
|
||||
c, err = clientset.NewForConfig(config)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
})
|
||||
It("Getting a non-existent secret should exit with the Forbidden error, not a NotFound error", func() {
|
||||
_, err := c.CoreV1().Secrets(ns).Get("foo", metav1.GetOptions{})
|
||||
Expect(apierrors.IsForbidden(err)).Should(Equal(true))
|
||||
})
|
||||
|
||||
It("Getting an existent secret should exit with the Forbidden error", func() {
|
||||
_, err := c.CoreV1().Secrets(ns).Get(defaultSaSecret, metav1.GetOptions{})
|
||||
Expect(apierrors.IsForbidden(err)).Should(Equal(true))
|
||||
})
|
||||
|
||||
It("Getting a secret for a workload the node has access to should succeed", func() {
|
||||
By("Create a secret for testing")
|
||||
secret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: "node-auth-secret",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"data": []byte("keep it secret"),
|
||||
},
|
||||
}
|
||||
_, err := f.ClientSet.CoreV1().Secrets(ns).Create(secret)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Node should not get the secret")
|
||||
_, err = c.CoreV1().Secrets(ns).Get(secret.Name, metav1.GetOptions{})
|
||||
Expect(apierrors.IsForbidden(err)).Should(Equal(true))
|
||||
|
||||
By("Create a pod that use the secret")
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pause",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "pause",
|
||||
Image: framework.GetPauseImageName(f.ClientSet),
|
||||
},
|
||||
},
|
||||
NodeName: nodeName,
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "node-auth-secret",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Secret: &v1.SecretVolumeSource{
|
||||
SecretName: secret.Name,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err = f.ClientSet.CoreV1().Pods(ns).Create(pod)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("The node should able to access the secret")
|
||||
err = wait.Poll(framework.Poll, 1*time.Minute, func() (bool, error) {
|
||||
_, err = c.CoreV1().Secrets(ns).Get(secret.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
framework.Logf("Failed to get secret %v, err: %v", secret.Name, err)
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("A node shouldn't be able to create an other node", func() {
|
||||
node := &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Node",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
}
|
||||
By(fmt.Sprintf("Create node foo by user: %v", asUser))
|
||||
_, err := c.CoreV1().Nodes().Create(node)
|
||||
Expect(apierrors.IsForbidden(err)).Should(Equal(true))
|
||||
})
|
||||
|
||||
It("A node shouldn't be able to delete an other node", func() {
|
||||
By(fmt.Sprintf("Create node foo by user: %v", asUser))
|
||||
err := c.CoreV1().Nodes().Delete("foo", &metav1.DeleteOptions{})
|
||||
Expect(apierrors.IsForbidden(err)).Should(Equal(true))
|
||||
})
|
||||
})
|
316
vendor/k8s.io/kubernetes/test/e2e/auth/pod_security_policy.go
generated
vendored
Normal file
316
vendor/k8s.io/kubernetes/test/e2e/auth/pod_security_policy.go
generated
vendored
Normal file
@ -0,0 +1,316 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
|
||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/authentication/serviceaccount"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp"
|
||||
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
|
||||
"k8s.io/kubernetes/test/e2e/common"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var (
|
||||
restrictivePSPTemplate = &extensionsv1beta1.PodSecurityPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "restrictive",
|
||||
Annotations: map[string]string{
|
||||
seccomp.AllowedProfilesAnnotationKey: "docker/default",
|
||||
seccomp.DefaultProfileAnnotationKey: "docker/default",
|
||||
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault,
|
||||
apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault,
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"kubernetes.io/cluster-service": "true",
|
||||
"addonmanager.kubernetes.io/mode": "Reconcile",
|
||||
},
|
||||
},
|
||||
Spec: extensionsv1beta1.PodSecurityPolicySpec{
|
||||
Privileged: false,
|
||||
AllowPrivilegeEscalation: boolPtr(false),
|
||||
RequiredDropCapabilities: []corev1.Capability{
|
||||
"AUDIT_WRITE",
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FOWNER",
|
||||
"FSETID",
|
||||
"KILL",
|
||||
"MKNOD",
|
||||
"NET_RAW",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SYS_CHROOT",
|
||||
},
|
||||
Volumes: []extensionsv1beta1.FSType{
|
||||
extensionsv1beta1.ConfigMap,
|
||||
extensionsv1beta1.EmptyDir,
|
||||
extensionsv1beta1.PersistentVolumeClaim,
|
||||
"projected",
|
||||
extensionsv1beta1.Secret,
|
||||
},
|
||||
HostNetwork: false,
|
||||
HostIPC: false,
|
||||
HostPID: false,
|
||||
RunAsUser: extensionsv1beta1.RunAsUserStrategyOptions{
|
||||
Rule: extensionsv1beta1.RunAsUserStrategyMustRunAsNonRoot,
|
||||
},
|
||||
SELinux: extensionsv1beta1.SELinuxStrategyOptions{
|
||||
Rule: extensionsv1beta1.SELinuxStrategyRunAsAny,
|
||||
},
|
||||
SupplementalGroups: extensionsv1beta1.SupplementalGroupsStrategyOptions{
|
||||
Rule: extensionsv1beta1.SupplementalGroupsStrategyRunAsAny,
|
||||
},
|
||||
FSGroup: extensionsv1beta1.FSGroupStrategyOptions{
|
||||
Rule: extensionsv1beta1.FSGroupStrategyRunAsAny,
|
||||
},
|
||||
ReadOnlyRootFilesystem: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
var _ = SIGDescribe("PodSecurityPolicy", func() {
|
||||
f := framework.NewDefaultFramework("podsecuritypolicy")
|
||||
f.SkipPrivilegedPSPBinding = true
|
||||
|
||||
// Client that will impersonate the default service account, in order to run
|
||||
// with reduced privileges.
|
||||
var c clientset.Interface
|
||||
var ns string // Test namespace, for convenience
|
||||
BeforeEach(func() {
|
||||
if !framework.IsPodSecurityPolicyEnabled(f) {
|
||||
framework.Skipf("PodSecurityPolicy not enabled")
|
||||
}
|
||||
if !framework.IsRBACEnabled(f) {
|
||||
framework.Skipf("RBAC not enabled")
|
||||
}
|
||||
ns = f.Namespace.Name
|
||||
|
||||
By("Creating a kubernetes client that impersonates the default service account")
|
||||
config, err := framework.LoadConfig()
|
||||
framework.ExpectNoError(err)
|
||||
config.Impersonate = restclient.ImpersonationConfig{
|
||||
UserName: serviceaccount.MakeUsername(ns, "default"),
|
||||
Groups: serviceaccount.MakeGroupNames(ns),
|
||||
}
|
||||
c, err = clientset.NewForConfig(config)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
By("Binding the edit role to the default SA")
|
||||
framework.BindClusterRole(f.ClientSet.RbacV1beta1(), "edit", ns,
|
||||
rbacv1beta1.Subject{Kind: rbacv1beta1.ServiceAccountKind, Namespace: ns, Name: "default"})
|
||||
})
|
||||
|
||||
It("should forbid pod creation when no PSP is available", func() {
|
||||
By("Running a restricted pod")
|
||||
_, err := c.Core().Pods(ns).Create(restrictedPod(f, "restricted"))
|
||||
expectForbidden(err)
|
||||
})
|
||||
|
||||
It("should enforce the restricted PodSecurityPolicy", func() {
|
||||
By("Creating & Binding a restricted policy for the test service account")
|
||||
_, cleanup := createAndBindPSP(f, restrictivePSPTemplate)
|
||||
defer cleanup()
|
||||
|
||||
By("Running a restricted pod")
|
||||
pod, err := c.Core().Pods(ns).Create(restrictedPod(f, "allowed"))
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectNoError(framework.WaitForPodNameRunningInNamespace(c, pod.Name, pod.Namespace))
|
||||
|
||||
testPrivilegedPods(f, func(pod *v1.Pod) {
|
||||
_, err := c.Core().Pods(ns).Create(pod)
|
||||
expectForbidden(err)
|
||||
})
|
||||
})
|
||||
|
||||
It("should allow pods under the privileged PodSecurityPolicy", func() {
|
||||
By("Creating & Binding a privileged policy for the test service account")
|
||||
// Ensure that the permissive policy is used even in the presence of the restricted policy.
|
||||
_, cleanup := createAndBindPSP(f, restrictivePSPTemplate)
|
||||
defer cleanup()
|
||||
expectedPSP, cleanup := createAndBindPSP(f, framework.PrivilegedPSP("permissive"))
|
||||
defer cleanup()
|
||||
|
||||
testPrivilegedPods(f, func(pod *v1.Pod) {
|
||||
p, err := c.Core().Pods(ns).Create(pod)
|
||||
framework.ExpectNoError(err)
|
||||
framework.ExpectNoError(framework.WaitForPodNameRunningInNamespace(c, p.Name, p.Namespace))
|
||||
|
||||
// Verify expected PSP was used.
|
||||
p, err = c.Core().Pods(ns).Get(p.Name, metav1.GetOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
validated, found := p.Annotations[psputil.ValidatedPSPAnnotation]
|
||||
Expect(found).To(BeTrue(), "PSP annotation not found")
|
||||
Expect(validated).To(Equal(expectedPSP.Name), "Unexpected validated PSP")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func expectForbidden(err error) {
|
||||
Expect(err).To(HaveOccurred(), "should be forbidden")
|
||||
Expect(apierrs.IsForbidden(err)).To(BeTrue(), "should be forbidden error")
|
||||
}
|
||||
|
||||
func testPrivilegedPods(f *framework.Framework, tester func(pod *v1.Pod)) {
|
||||
By("Running a privileged pod", func() {
|
||||
privileged := restrictedPod(f, "privileged")
|
||||
privileged.Spec.Containers[0].SecurityContext.Privileged = boolPtr(true)
|
||||
privileged.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = nil
|
||||
tester(privileged)
|
||||
})
|
||||
|
||||
By("Running a HostPath pod", func() {
|
||||
hostpath := restrictedPod(f, "hostpath")
|
||||
hostpath.Spec.Containers[0].VolumeMounts = []v1.VolumeMount{{
|
||||
Name: "hp",
|
||||
MountPath: "/hp",
|
||||
}}
|
||||
hostpath.Spec.Volumes = []v1.Volume{{
|
||||
Name: "hp",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{Path: "/tmp"},
|
||||
},
|
||||
}}
|
||||
tester(hostpath)
|
||||
})
|
||||
|
||||
By("Running a HostNetwork pod", func() {
|
||||
hostnet := restrictedPod(f, "hostnet")
|
||||
hostnet.Spec.HostNetwork = true
|
||||
tester(hostnet)
|
||||
})
|
||||
|
||||
By("Running a HostPID pod", func() {
|
||||
hostpid := restrictedPod(f, "hostpid")
|
||||
hostpid.Spec.HostPID = true
|
||||
tester(hostpid)
|
||||
})
|
||||
|
||||
By("Running a HostIPC pod", func() {
|
||||
hostipc := restrictedPod(f, "hostipc")
|
||||
hostipc.Spec.HostIPC = true
|
||||
tester(hostipc)
|
||||
})
|
||||
|
||||
if common.IsAppArmorSupported() {
|
||||
By("Running a custom AppArmor profile pod", func() {
|
||||
aa := restrictedPod(f, "apparmor")
|
||||
// Every node is expected to have the docker-default profile.
|
||||
aa.Annotations[apparmor.ContainerAnnotationKeyPrefix+"pause"] = "localhost/docker-default"
|
||||
tester(aa)
|
||||
})
|
||||
}
|
||||
|
||||
By("Running an unconfined Seccomp pod", func() {
|
||||
unconfined := restrictedPod(f, "seccomp")
|
||||
unconfined.Annotations[v1.SeccompPodAnnotationKey] = "unconfined"
|
||||
tester(unconfined)
|
||||
})
|
||||
|
||||
By("Running a CAP_SYS_ADMIN pod", func() {
|
||||
sysadmin := restrictedPod(f, "sysadmin")
|
||||
sysadmin.Spec.Containers[0].SecurityContext.Capabilities = &v1.Capabilities{
|
||||
Add: []v1.Capability{"CAP_SYS_ADMIN"},
|
||||
}
|
||||
sysadmin.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = nil
|
||||
tester(sysadmin)
|
||||
})
|
||||
}
|
||||
|
||||
func createAndBindPSP(f *framework.Framework, pspTemplate *extensionsv1beta1.PodSecurityPolicy) (psp *extensionsv1beta1.PodSecurityPolicy, cleanup func()) {
|
||||
// Create the PodSecurityPolicy object.
|
||||
psp = pspTemplate.DeepCopy()
|
||||
// Add the namespace to the name to ensure uniqueness and tie it to the namespace.
|
||||
ns := f.Namespace.Name
|
||||
name := fmt.Sprintf("%s-%s", ns, psp.Name)
|
||||
psp.Name = name
|
||||
psp, err := f.ClientSet.ExtensionsV1beta1().PodSecurityPolicies().Create(psp)
|
||||
framework.ExpectNoError(err, "Failed to create PSP")
|
||||
|
||||
// Create the Role to bind it to the namespace.
|
||||
_, err = f.ClientSet.RbacV1beta1().Roles(ns).Create(&rbacv1beta1.Role{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Rules: []rbacv1beta1.PolicyRule{{
|
||||
APIGroups: []string{"extensions"},
|
||||
Resources: []string{"podsecuritypolicies"},
|
||||
ResourceNames: []string{name},
|
||||
Verbs: []string{"use"},
|
||||
}},
|
||||
})
|
||||
framework.ExpectNoError(err, "Failed to create PSP role")
|
||||
|
||||
// Bind the role to the namespace.
|
||||
framework.BindRoleInNamespace(f.ClientSet.RbacV1beta1(), name, ns, rbacv1beta1.Subject{
|
||||
Kind: rbacv1beta1.ServiceAccountKind,
|
||||
Namespace: ns,
|
||||
Name: "default",
|
||||
})
|
||||
framework.ExpectNoError(framework.WaitForNamedAuthorizationUpdate(f.ClientSet.AuthorizationV1beta1(),
|
||||
serviceaccount.MakeUsername(ns, "default"), ns, "use", name,
|
||||
schema.GroupResource{Group: "extensions", Resource: "podsecuritypolicies"}, true))
|
||||
|
||||
return psp, func() {
|
||||
// Cleanup non-namespaced PSP object.
|
||||
f.ClientSet.ExtensionsV1beta1().PodSecurityPolicies().Delete(name, &metav1.DeleteOptions{})
|
||||
}
|
||||
}
|
||||
|
||||
func restrictedPod(f *framework.Framework, name string) *v1.Pod {
|
||||
return &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Annotations: map[string]string{
|
||||
v1.SeccompPodAnnotationKey: "docker/default",
|
||||
apparmor.ContainerAnnotationKeyPrefix + "pause": apparmor.ProfileRuntimeDefault,
|
||||
},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{{
|
||||
Name: "pause",
|
||||
Image: framework.GetPauseImageName(f.ClientSet),
|
||||
SecurityContext: &v1.SecurityContext{
|
||||
AllowPrivilegeEscalation: boolPtr(false),
|
||||
RunAsUser: intPtr(65534),
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func boolPtr(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
func intPtr(i int64) *int64 {
|
||||
return &i
|
||||
}
|
376
vendor/k8s.io/kubernetes/test/e2e/auth/service_accounts.go
generated
vendored
Normal file
376
vendor/k8s.io/kubernetes/test/e2e/auth/service_accounts.go
generated
vendored
Normal file
@ -0,0 +1,376 @@
|
||||
/*
|
||||
Copyright 2014 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 (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
imageutils "k8s.io/kubernetes/test/utils/image"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var mountImage = imageutils.GetE2EImage(imageutils.Mounttest)
|
||||
|
||||
var _ = SIGDescribe("ServiceAccounts", func() {
|
||||
f := framework.NewDefaultFramework("svcaccounts")
|
||||
|
||||
It("should ensure a single API token exists", func() {
|
||||
// wait for the service account to reference a single secret
|
||||
var secrets []v1.ObjectReference
|
||||
framework.ExpectNoError(wait.Poll(time.Millisecond*500, time.Second*10, func() (bool, error) {
|
||||
By("waiting for a single token reference")
|
||||
sa, err := f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Get("default", metav1.GetOptions{})
|
||||
if apierrors.IsNotFound(err) {
|
||||
framework.Logf("default service account was not found")
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
framework.Logf("error getting default service account: %v", err)
|
||||
return false, err
|
||||
}
|
||||
switch len(sa.Secrets) {
|
||||
case 0:
|
||||
framework.Logf("default service account has no secret references")
|
||||
return false, nil
|
||||
case 1:
|
||||
framework.Logf("default service account has a single secret reference")
|
||||
secrets = sa.Secrets
|
||||
return true, nil
|
||||
default:
|
||||
return false, fmt.Errorf("default service account has too many secret references: %#v", sa.Secrets)
|
||||
}
|
||||
}))
|
||||
|
||||
// make sure the reference doesn't flutter
|
||||
{
|
||||
By("ensuring the single token reference persists")
|
||||
time.Sleep(2 * time.Second)
|
||||
sa, err := f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Get("default", metav1.GetOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
Expect(sa.Secrets).To(Equal(secrets))
|
||||
}
|
||||
|
||||
// delete the referenced secret
|
||||
By("deleting the service account token")
|
||||
framework.ExpectNoError(f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(secrets[0].Name, nil))
|
||||
|
||||
// wait for the referenced secret to be removed, and another one autocreated
|
||||
framework.ExpectNoError(wait.Poll(time.Millisecond*500, framework.ServiceAccountProvisionTimeout, func() (bool, error) {
|
||||
By("waiting for a new token reference")
|
||||
sa, err := f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Get("default", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
framework.Logf("error getting default service account: %v", err)
|
||||
return false, err
|
||||
}
|
||||
switch len(sa.Secrets) {
|
||||
case 0:
|
||||
framework.Logf("default service account has no secret references")
|
||||
return false, nil
|
||||
case 1:
|
||||
if sa.Secrets[0] == secrets[0] {
|
||||
framework.Logf("default service account still has the deleted secret reference")
|
||||
return false, nil
|
||||
}
|
||||
framework.Logf("default service account has a new single secret reference")
|
||||
secrets = sa.Secrets
|
||||
return true, nil
|
||||
default:
|
||||
return false, fmt.Errorf("default service account has too many secret references: %#v", sa.Secrets)
|
||||
}
|
||||
}))
|
||||
|
||||
// make sure the reference doesn't flutter
|
||||
{
|
||||
By("ensuring the single token reference persists")
|
||||
time.Sleep(2 * time.Second)
|
||||
sa, err := f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Get("default", metav1.GetOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
Expect(sa.Secrets).To(Equal(secrets))
|
||||
}
|
||||
|
||||
// delete the reference from the service account
|
||||
By("deleting the reference to the service account token")
|
||||
{
|
||||
sa, err := f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Get("default", metav1.GetOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
sa.Secrets = nil
|
||||
_, updateErr := f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Update(sa)
|
||||
framework.ExpectNoError(updateErr)
|
||||
}
|
||||
|
||||
// wait for another one to be autocreated
|
||||
framework.ExpectNoError(wait.Poll(time.Millisecond*500, framework.ServiceAccountProvisionTimeout, func() (bool, error) {
|
||||
By("waiting for a new token to be created and added")
|
||||
sa, err := f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Get("default", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
framework.Logf("error getting default service account: %v", err)
|
||||
return false, err
|
||||
}
|
||||
switch len(sa.Secrets) {
|
||||
case 0:
|
||||
framework.Logf("default service account has no secret references")
|
||||
return false, nil
|
||||
case 1:
|
||||
framework.Logf("default service account has a new single secret reference")
|
||||
secrets = sa.Secrets
|
||||
return true, nil
|
||||
default:
|
||||
return false, fmt.Errorf("default service account has too many secret references: %#v", sa.Secrets)
|
||||
}
|
||||
}))
|
||||
|
||||
// make sure the reference doesn't flutter
|
||||
{
|
||||
By("ensuring the single token reference persists")
|
||||
time.Sleep(2 * time.Second)
|
||||
sa, err := f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Get("default", metav1.GetOptions{})
|
||||
framework.ExpectNoError(err)
|
||||
Expect(sa.Secrets).To(Equal(secrets))
|
||||
}
|
||||
})
|
||||
|
||||
framework.ConformanceIt("should mount an API token into pods ", func() {
|
||||
var tokenContent string
|
||||
var rootCAContent string
|
||||
|
||||
// Standard get, update retry loop
|
||||
framework.ExpectNoError(wait.Poll(time.Millisecond*500, framework.ServiceAccountProvisionTimeout, func() (bool, error) {
|
||||
By("getting the auto-created API token")
|
||||
sa, err := f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Get("default", metav1.GetOptions{})
|
||||
if apierrors.IsNotFound(err) {
|
||||
framework.Logf("default service account was not found")
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
framework.Logf("error getting default service account: %v", err)
|
||||
return false, err
|
||||
}
|
||||
if len(sa.Secrets) == 0 {
|
||||
framework.Logf("default service account has no secret references")
|
||||
return false, nil
|
||||
}
|
||||
for _, secretRef := range sa.Secrets {
|
||||
secret, err := f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Get(secretRef.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
framework.Logf("Error getting secret %s: %v", secretRef.Name, err)
|
||||
continue
|
||||
}
|
||||
if secret.Type == v1.SecretTypeServiceAccountToken {
|
||||
tokenContent = string(secret.Data[v1.ServiceAccountTokenKey])
|
||||
rootCAContent = string(secret.Data[v1.ServiceAccountRootCAKey])
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
framework.Logf("default service account has no secret references to valid service account tokens")
|
||||
return false, nil
|
||||
}))
|
||||
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "pod-service-account-" + string(uuid.NewUUID()) + "-",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "token-test",
|
||||
Image: mountImage,
|
||||
Args: []string{
|
||||
fmt.Sprintf("--file_content=%s/%s", serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountTokenKey),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "root-ca-test",
|
||||
Image: mountImage,
|
||||
Args: []string{
|
||||
fmt.Sprintf("--file_content=%s/%s", serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountRootCAKey),
|
||||
},
|
||||
},
|
||||
},
|
||||
RestartPolicy: v1.RestartPolicyNever,
|
||||
},
|
||||
}
|
||||
|
||||
pod.Spec.Containers = append(pod.Spec.Containers, v1.Container{
|
||||
Name: "namespace-test",
|
||||
Image: mountImage,
|
||||
Args: []string{
|
||||
fmt.Sprintf("--file_content=%s/%s", serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountNamespaceKey),
|
||||
},
|
||||
})
|
||||
|
||||
f.TestContainerOutput("consume service account token", pod, 0, []string{
|
||||
fmt.Sprintf(`content of file "%s/%s": %s`, serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountTokenKey, tokenContent),
|
||||
})
|
||||
f.TestContainerOutput("consume service account root CA", pod, 1, []string{
|
||||
fmt.Sprintf(`content of file "%s/%s": %s`, serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountRootCAKey, rootCAContent),
|
||||
})
|
||||
|
||||
f.TestContainerOutput("consume service account namespace", pod, 2, []string{
|
||||
fmt.Sprintf(`content of file "%s/%s": %s`, serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountNamespaceKey, f.Namespace.Name),
|
||||
})
|
||||
})
|
||||
|
||||
framework.ConformanceIt("should allow opting out of API token automount ", func() {
|
||||
var err error
|
||||
trueValue := true
|
||||
falseValue := false
|
||||
mountSA := &v1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: "mount"}, AutomountServiceAccountToken: &trueValue}
|
||||
nomountSA := &v1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: "nomount"}, AutomountServiceAccountToken: &falseValue}
|
||||
mountSA, err = f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Create(mountSA)
|
||||
framework.ExpectNoError(err)
|
||||
nomountSA, err = f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Create(nomountSA)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
// Standard get, update retry loop
|
||||
framework.ExpectNoError(wait.Poll(time.Millisecond*500, framework.ServiceAccountProvisionTimeout, func() (bool, error) {
|
||||
By("getting the auto-created API token")
|
||||
sa, err := f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Get(mountSA.Name, metav1.GetOptions{})
|
||||
if apierrors.IsNotFound(err) {
|
||||
framework.Logf("mount service account was not found")
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
framework.Logf("error getting mount service account: %v", err)
|
||||
return false, err
|
||||
}
|
||||
if len(sa.Secrets) == 0 {
|
||||
framework.Logf("mount service account has no secret references")
|
||||
return false, nil
|
||||
}
|
||||
for _, secretRef := range sa.Secrets {
|
||||
secret, err := f.ClientSet.CoreV1().Secrets(f.Namespace.Name).Get(secretRef.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
framework.Logf("Error getting secret %s: %v", secretRef.Name, err)
|
||||
continue
|
||||
}
|
||||
if secret.Type == v1.SecretTypeServiceAccountToken {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
framework.Logf("default service account has no secret references to valid service account tokens")
|
||||
return false, nil
|
||||
}))
|
||||
|
||||
testcases := []struct {
|
||||
PodName string
|
||||
ServiceAccountName string
|
||||
AutomountPodSpec *bool
|
||||
ExpectTokenVolume bool
|
||||
}{
|
||||
{
|
||||
PodName: "pod-service-account-defaultsa",
|
||||
ServiceAccountName: "default",
|
||||
AutomountPodSpec: nil,
|
||||
ExpectTokenVolume: true, // default is true
|
||||
},
|
||||
{
|
||||
PodName: "pod-service-account-mountsa",
|
||||
ServiceAccountName: mountSA.Name,
|
||||
AutomountPodSpec: nil,
|
||||
ExpectTokenVolume: true,
|
||||
},
|
||||
{
|
||||
PodName: "pod-service-account-nomountsa",
|
||||
ServiceAccountName: nomountSA.Name,
|
||||
AutomountPodSpec: nil,
|
||||
ExpectTokenVolume: false,
|
||||
},
|
||||
|
||||
// Make sure pod spec trumps when opting in
|
||||
{
|
||||
PodName: "pod-service-account-defaultsa-mountspec",
|
||||
ServiceAccountName: "default",
|
||||
AutomountPodSpec: &trueValue,
|
||||
ExpectTokenVolume: true,
|
||||
},
|
||||
{
|
||||
PodName: "pod-service-account-mountsa-mountspec",
|
||||
ServiceAccountName: mountSA.Name,
|
||||
AutomountPodSpec: &trueValue,
|
||||
ExpectTokenVolume: true,
|
||||
},
|
||||
{
|
||||
PodName: "pod-service-account-nomountsa-mountspec",
|
||||
ServiceAccountName: nomountSA.Name,
|
||||
AutomountPodSpec: &trueValue,
|
||||
ExpectTokenVolume: true, // pod spec trumps
|
||||
},
|
||||
|
||||
// Make sure pod spec trumps when opting out
|
||||
{
|
||||
PodName: "pod-service-account-defaultsa-nomountspec",
|
||||
ServiceAccountName: "default",
|
||||
AutomountPodSpec: &falseValue,
|
||||
ExpectTokenVolume: false, // pod spec trumps
|
||||
},
|
||||
{
|
||||
PodName: "pod-service-account-mountsa-nomountspec",
|
||||
ServiceAccountName: mountSA.Name,
|
||||
AutomountPodSpec: &falseValue,
|
||||
ExpectTokenVolume: false, // pod spec trumps
|
||||
},
|
||||
{
|
||||
PodName: "pod-service-account-nomountsa-nomountspec",
|
||||
ServiceAccountName: nomountSA.Name,
|
||||
AutomountPodSpec: &falseValue,
|
||||
ExpectTokenVolume: false, // pod spec trumps
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: tc.PodName},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{{Name: "token-test", Image: mountImage}},
|
||||
RestartPolicy: v1.RestartPolicyNever,
|
||||
ServiceAccountName: tc.ServiceAccountName,
|
||||
AutomountServiceAccountToken: tc.AutomountPodSpec,
|
||||
},
|
||||
}
|
||||
createdPod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(pod)
|
||||
framework.ExpectNoError(err)
|
||||
framework.Logf("created pod %s", tc.PodName)
|
||||
|
||||
hasServiceAccountTokenVolume := false
|
||||
for _, c := range createdPod.Spec.Containers {
|
||||
for _, vm := range c.VolumeMounts {
|
||||
if vm.MountPath == serviceaccount.DefaultAPITokenMountPath {
|
||||
hasServiceAccountTokenVolume = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hasServiceAccountTokenVolume != tc.ExpectTokenVolume {
|
||||
framework.Failf("%s: expected volume=%v, got %v (%#v)", tc.PodName, tc.ExpectTokenVolume, hasServiceAccountTokenVolume, createdPod)
|
||||
} else {
|
||||
framework.Logf("pod %s service account token volume mount: %v", tc.PodName, hasServiceAccountTokenVolume)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
Reference in New Issue
Block a user