vendor files

This commit is contained in:
Serguei Bezverkhi
2018-01-09 13:57:14 -05:00
parent 558bc6c02a
commit 7b24313bd6
16547 changed files with 4527373 additions and 0 deletions

65
vendor/k8s.io/kubernetes/test/e2e/auth/BUILD generated vendored Normal file
View 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
View 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
View 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
View 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
View 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)
}

View 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
View 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))
})
})

View 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
}

View 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)
}
}
})
})