mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			202 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			202 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| 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 (
 | |
| 	"encoding/base64"
 | |
| 	"encoding/json"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 
 | |
| 	authenticationv1 "k8s.io/api/authentication/v1"
 | |
| 	v1 "k8s.io/api/core/v1"
 | |
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | |
| 	"k8s.io/apiserver/pkg/authorization/authorizerfactory"
 | |
| 	utilfeature "k8s.io/apiserver/pkg/util/feature"
 | |
| 	utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
 | |
| 	clientset "k8s.io/client-go/kubernetes"
 | |
| 	certutil "k8s.io/client-go/util/cert"
 | |
| 	"k8s.io/kubernetes/pkg/features"
 | |
| 	"k8s.io/kubernetes/pkg/serviceaccount"
 | |
| 	"k8s.io/kubernetes/test/integration/framework"
 | |
| )
 | |
| 
 | |
| const ecdsaPrivateKey = `-----BEGIN EC PRIVATE KEY-----
 | |
| MHcCAQEEIEZmTmUhuanLjPA2CLquXivuwBDHTt5XYwgIr/kA1LtRoAoGCCqGSM49
 | |
| AwEHoUQDQgAEH6cuzP8XuD5wal6wf9M6xDljTOPLX2i8uIp/C/ASqiIGUeeKQtX0
 | |
| /IR3qCXyThP/dbCiHrF3v1cuhBOHY8CLVg==
 | |
| -----END EC PRIVATE KEY-----`
 | |
| 
 | |
| func TestServiceAccountTokenCreate(t *testing.T) {
 | |
| 	defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TokenRequest, true)()
 | |
| 
 | |
| 	// Build client config, clientset, and informers
 | |
| 	sk, err := certutil.ParsePrivateKeyPEM([]byte(ecdsaPrivateKey))
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("err: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// Start the server
 | |
| 	masterConfig := framework.NewIntegrationTestMasterConfig()
 | |
| 	masterConfig.GenericConfig.Authorization.Authorizer = authorizerfactory.NewAlwaysAllowAuthorizer()
 | |
| 	masterConfig.ExtraConfig.ServiceAccountIssuer = serviceaccount.JWTTokenGenerator("https://foo.bar.example.com", sk)
 | |
| 
 | |
| 	master, _, closeFn := framework.RunAMaster(masterConfig)
 | |
| 	defer closeFn()
 | |
| 
 | |
| 	cs, err := clientset.NewForConfig(master.GenericAPIServer.LoopbackClientConfig)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("err: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	sa := &v1.ServiceAccount{
 | |
| 		ObjectMeta: metav1.ObjectMeta{
 | |
| 			Name:      "test",
 | |
| 			Namespace: "default",
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	tr1 := &authenticationv1.TokenRequest{
 | |
| 		Spec: authenticationv1.TokenRequestSpec{
 | |
| 			Audiences: []string{"aud"},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	_, err = cs.CoreV1().ServiceAccounts(sa.Namespace).Create(sa)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("err: %v", err)
 | |
| 	}
 | |
| 	tr1, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, tr1)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("err: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	checkPayload(t, tr1.Status.Token, `"system:serviceaccount:default:test"`, "sub")
 | |
| 	checkPayload(t, tr1.Status.Token, `["aud"]`, "aud")
 | |
| 	checkPayload(t, tr1.Status.Token, "null", "kubernetes.io", "pod")
 | |
| 	checkPayload(t, tr1.Status.Token, "null", "kubernetes.io", "secret")
 | |
| 	checkPayload(t, tr1.Status.Token, `"default"`, "kubernetes.io", "namespace")
 | |
| 	checkPayload(t, tr1.Status.Token, `"test"`, "kubernetes.io", "serviceaccount", "name")
 | |
| 
 | |
| 	pod := &v1.Pod{
 | |
| 		ObjectMeta: metav1.ObjectMeta{
 | |
| 			Name:      "test-pod",
 | |
| 			Namespace: sa.Namespace,
 | |
| 		},
 | |
| 		Spec: v1.PodSpec{
 | |
| 			ServiceAccountName: sa.Name,
 | |
| 			Containers:         []v1.Container{{Name: "test-container", Image: "nginx"}},
 | |
| 		},
 | |
| 	}
 | |
| 	_, err = cs.CoreV1().Pods(pod.Namespace).Create(pod)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("err: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	tr2 := &authenticationv1.TokenRequest{
 | |
| 		Spec: authenticationv1.TokenRequestSpec{
 | |
| 			Audiences: []string{"aud"},
 | |
| 			BoundObjectRef: &authenticationv1.BoundObjectReference{
 | |
| 				Kind:       "Pod",
 | |
| 				APIVersion: "v1",
 | |
| 				Name:       pod.Name,
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	tr2, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, tr2)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("err: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	checkPayload(t, tr2.Status.Token, `"system:serviceaccount:default:test"`, "sub")
 | |
| 	checkPayload(t, tr2.Status.Token, `["aud"]`, "aud")
 | |
| 	checkPayload(t, tr2.Status.Token, `"test-pod"`, "kubernetes.io", "pod", "name")
 | |
| 	checkPayload(t, tr2.Status.Token, "null", "kubernetes.io", "secret")
 | |
| 	checkPayload(t, tr2.Status.Token, `"default"`, "kubernetes.io", "namespace")
 | |
| 	checkPayload(t, tr2.Status.Token, `"test"`, "kubernetes.io", "serviceaccount", "name")
 | |
| 
 | |
| 	secret := &v1.Secret{
 | |
| 		ObjectMeta: metav1.ObjectMeta{
 | |
| 			Name:      "test-secret",
 | |
| 			Namespace: sa.Namespace,
 | |
| 		},
 | |
| 	}
 | |
| 	_, err = cs.CoreV1().Secrets(secret.Namespace).Create(secret)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("err: %v", err)
 | |
| 	}
 | |
| 	tr3 := &authenticationv1.TokenRequest{
 | |
| 		Spec: authenticationv1.TokenRequestSpec{
 | |
| 			Audiences: []string{"aud"},
 | |
| 			BoundObjectRef: &authenticationv1.BoundObjectReference{
 | |
| 				Kind:       "Secret",
 | |
| 				APIVersion: "v1",
 | |
| 				Name:       secret.Name,
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	tr3, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, tr3)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("err: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	checkPayload(t, tr2.Status.Token, `"system:serviceaccount:default:test"`, "sub")
 | |
| 	checkPayload(t, tr2.Status.Token, `["aud"]`, "aud")
 | |
| 	checkPayload(t, tr2.Status.Token, `"test-pod"`, "kubernetes.io", "pod", "name")
 | |
| 	checkPayload(t, tr2.Status.Token, `null`, "kubernetes.io", "secret")
 | |
| 	checkPayload(t, tr2.Status.Token, `"default"`, "kubernetes.io", "namespace")
 | |
| 	checkPayload(t, tr2.Status.Token, `"test"`, "kubernetes.io", "serviceaccount", "name")
 | |
| }
 | |
| 
 | |
| func checkPayload(t *testing.T, tok string, want string, parts ...string) {
 | |
| 	t.Helper()
 | |
| 	got := getSubObject(t, getPayload(t, tok), parts...)
 | |
| 	if got != want {
 | |
| 		t.Errorf("unexpected payload.\nsaw:\t%v\nwant:\t%v", got, want)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func getSubObject(t *testing.T, b string, parts ...string) string {
 | |
| 	t.Helper()
 | |
| 	var obj interface{}
 | |
| 	obj = make(map[string]interface{})
 | |
| 	if err := json.Unmarshal([]byte(b), &obj); err != nil {
 | |
| 		t.Fatalf("err: %v", err)
 | |
| 	}
 | |
| 	for _, part := range parts {
 | |
| 		obj = obj.(map[string]interface{})[part]
 | |
| 	}
 | |
| 	out, err := json.Marshal(obj)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("err: %v", err)
 | |
| 	}
 | |
| 	return string(out)
 | |
| }
 | |
| 
 | |
| func getPayload(t *testing.T, b string) string {
 | |
| 	t.Helper()
 | |
| 	parts := strings.Split(b, ".")
 | |
| 	if len(parts) != 3 {
 | |
| 		t.Fatalf("token did not have three parts: %v", b)
 | |
| 	}
 | |
| 	payload, err := base64.RawURLEncoding.DecodeString(parts[1])
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("failed to base64 decode token: %v", err)
 | |
| 	}
 | |
| 	return string(payload)
 | |
| }
 |