Skip to content

Commit aee9fde

Browse files
Charles Eckmanmtaufen
andcommitted
Add e2e test for validating JWTs as OIDC tokens
Adds an E2E test to deploy an agnhost container that runs the test. Co-authored-by: Michael Taufen <[email protected]>
1 parent 8508875 commit aee9fde

File tree

3 files changed

+105
-2
lines changed

3 files changed

+105
-2
lines changed

cluster/gce/config-test.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,9 @@ ROTATE_CERTIFICATES="${ROTATE_CERTIFICATES:-}"
517517
# into kube-controller-manager via `--concurrent-service-syncs`
518518
CONCURRENT_SERVICE_SYNCS="${CONCURRENT_SERVICE_SYNCS:-}"
519519

520-
SERVICEACCOUNT_ISSUER="https://kubernetes.io/${CLUSTER_NAME}"
520+
# The value kubernetes.default.svc is only usable in Pods and should only be
521+
# set for tests. DO NOT COPY THIS VALUE FOR PRODUCTION CLUSTERS.
522+
SERVICEACCOUNT_ISSUER="https://kubernetes.default.svc"
521523

522524
# Optional: Enable Node termination Handler for Preemptible and GPU VMs.
523525
# https://github.com/GoogleCloudPlatform/k8s-node-termination-handler

test/e2e/auth/service_accounts.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
authenticationv1 "k8s.io/api/authentication/v1"
2828
v1 "k8s.io/api/core/v1"
29+
rbacv1 "k8s.io/api/rbac/v1"
2930
apierrors "k8s.io/apimachinery/pkg/api/errors"
3031
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3132
"k8s.io/apimachinery/pkg/util/sets"
@@ -520,6 +521,106 @@ var _ = SIGDescribe("ServiceAccounts", func() {
520521
framework.Failf("Unexpected error: %v\n%s", err, logs)
521522
}
522523
})
524+
525+
ginkgo.It("should support OIDC discovery of service account issuer [Feature:ServiceAccountIssuerDiscovery]", func() {
526+
// Allow the test pod access to the OIDC discovery non-resource URLs.
527+
// The role should have already been automatically created as part of the
528+
// bootstrap policy, but not the role binding.
529+
const clusterRoleName = "system:service-account-issuer-discovery"
530+
crbName := fmt.Sprintf("%s-%s", f.Namespace.Name, clusterRoleName)
531+
if _, err := f.ClientSet.RbacV1().ClusterRoleBindings().Create(
532+
context.TODO(),
533+
&rbacv1.ClusterRoleBinding{
534+
ObjectMeta: metav1.ObjectMeta{
535+
Name: crbName,
536+
},
537+
Subjects: []rbacv1.Subject{
538+
{
539+
Kind: rbacv1.ServiceAccountKind,
540+
APIGroup: "",
541+
Name: "default",
542+
Namespace: f.Namespace.Name,
543+
},
544+
},
545+
RoleRef: rbacv1.RoleRef{
546+
Name: clusterRoleName,
547+
APIGroup: rbacv1.GroupName,
548+
Kind: "ClusterRole",
549+
},
550+
},
551+
metav1.CreateOptions{}); err != nil && !apierrors.IsAlreadyExists(err) {
552+
framework.Failf("Unexpected err creating ClusterRoleBinding %s: %v", crbName, err)
553+
}
554+
555+
// Create the pod with tokens.
556+
tokenPath := "/var/run/secrets/tokens"
557+
tokenName := "sa-token"
558+
audience := "oidc-discovery-test"
559+
tenMin := int64(10 * 60)
560+
561+
pod := &v1.Pod{
562+
ObjectMeta: metav1.ObjectMeta{Name: "oidc-discovery-validator"},
563+
Spec: v1.PodSpec{
564+
Containers: []v1.Container{{
565+
Name: "oidc-discovery-validator",
566+
Image: imageutils.GetE2EImage(imageutils.Agnhost),
567+
Args: []string{
568+
"test-service-account-issuer-discovery",
569+
"--in-cluster-discovery",
570+
"--token-path", path.Join(tokenPath, tokenName),
571+
"--audience", audience,
572+
},
573+
VolumeMounts: []v1.VolumeMount{{
574+
MountPath: tokenPath,
575+
Name: tokenName,
576+
ReadOnly: true,
577+
}},
578+
}},
579+
RestartPolicy: v1.RestartPolicyNever,
580+
ServiceAccountName: "default",
581+
Volumes: []v1.Volume{{
582+
Name: tokenName,
583+
VolumeSource: v1.VolumeSource{
584+
Projected: &v1.ProjectedVolumeSource{
585+
Sources: []v1.VolumeProjection{
586+
{
587+
ServiceAccountToken: &v1.ServiceAccountTokenProjection{
588+
Path: tokenName,
589+
ExpirationSeconds: &tenMin,
590+
Audience: audience,
591+
},
592+
},
593+
},
594+
},
595+
},
596+
}},
597+
},
598+
}
599+
pod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(context.TODO(), pod, metav1.CreateOptions{})
600+
framework.ExpectNoError(err)
601+
602+
framework.Logf("created pod")
603+
podErr := e2epod.WaitForPodSuccessInNamespace(f.ClientSet, pod.Name, f.Namespace.Name)
604+
605+
// Get the logs before calling ExpectNoError, so we can debug any errors.
606+
var logs string
607+
if err := wait.Poll(30*time.Second, 2*time.Minute, func() (done bool, err error) {
608+
framework.Logf("polling logs")
609+
logs, err = e2epod.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, pod.Spec.Containers[0].Name)
610+
if err != nil {
611+
framework.Logf("Error pulling logs: %v", err)
612+
return false, nil
613+
}
614+
return true, nil
615+
}); err != nil {
616+
framework.Failf("Unexpected error getting pod logs: %v\n%s", err, logs)
617+
} else {
618+
framework.Logf("Pod logs: \n%v", logs)
619+
}
620+
621+
framework.ExpectNoError(podErr)
622+
framework.Logf("completed pod")
623+
})
523624
})
524625

525626
var reportLogsParser = regexp.MustCompile("([a-zA-Z0-9-_]*)=([a-zA-Z0-9-_]*)$")

test/utils/image/manifest.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ const (
205205

206206
func initImageConfigs() map[int]Config {
207207
configs := map[int]Config{}
208-
configs[Agnhost] = Config{promoterE2eRegistry, "agnhost", "2.10"}
208+
configs[Agnhost] = Config{promoterE2eRegistry, "agnhost", "2.12"}
209209
configs[AgnhostPrivate] = Config{PrivateRegistry, "agnhost", "2.6"}
210210
configs[AuthenticatedAlpine] = Config{gcAuthenticatedRegistry, "alpine", "3.7"}
211211
configs[AuthenticatedWindowsNanoServer] = Config{gcAuthenticatedRegistry, "windows-nanoserver", "v1"}

0 commit comments

Comments
 (0)