2017-05-30 15:15:38 -04:00
/ *
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 (
2020-01-27 21:19:44 -05:00
"context"
2017-11-20 12:57:24 -05:00
"fmt"
2021-10-29 18:41:02 -04:00
"os"
2024-10-15 02:30:51 -04:00
"path/filepath"
2018-11-04 16:44:43 -05:00
"strings"
2017-05-30 15:15:38 -04:00
"testing"
"time"
2024-10-15 02:30:51 -04:00
authenticationv1 "k8s.io/api/authentication/v1"
2019-04-04 07:06:52 -04:00
coordination "k8s.io/api/coordination/v1"
corev1 "k8s.io/api/core/v1"
2021-03-31 16:54:55 -04:00
policy "k8s.io/api/policy/v1"
2024-10-15 02:30:51 -04:00
rbacv1 "k8s.io/api/rbac/v1"
2025-07-04 11:43:31 -04:00
resourceapi "k8s.io/api/resource/v1"
2019-02-25 19:25:49 -05:00
storagev1 "k8s.io/api/storage/v1"
2019-11-12 03:43:58 -05:00
apierrors "k8s.io/apimachinery/pkg/api/errors"
2017-05-30 15:15:38 -04:00
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2017-11-20 12:57:24 -05:00
"k8s.io/apimachinery/pkg/types"
2025-02-27 14:52:51 -05:00
"k8s.io/apimachinery/pkg/util/rand"
2018-01-09 16:44:55 -05:00
"k8s.io/apimachinery/pkg/util/wait"
2023-07-13 14:00:12 -04:00
utilfeature "k8s.io/apiserver/pkg/util/feature"
2019-04-04 07:06:52 -04:00
clientset "k8s.io/client-go/kubernetes"
2024-10-15 02:30:51 -04:00
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
2023-07-13 14:00:12 -04:00
featuregatetesting "k8s.io/component-base/featuregate/testing"
2018-11-04 16:44:43 -05:00
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
2024-10-15 02:30:51 -04:00
kubecontrollermanagertesting "k8s.io/kubernetes/cmd/kube-controller-manager/app/testing"
2023-07-13 14:00:12 -04:00
"k8s.io/kubernetes/pkg/features"
2017-05-30 15:15:38 -04:00
"k8s.io/kubernetes/test/integration/framework"
2024-10-15 02:30:51 -04:00
"k8s.io/kubernetes/test/utils/kubeconfig"
2024-04-11 08:36:44 -04:00
"k8s.io/utils/ptr"
2017-05-30 15:15:38 -04:00
)
func TestNodeAuthorizer ( t * testing . T ) {
2017-07-17 18:59:15 -04:00
const (
// Define credentials
2020-02-04 13:36:05 -05:00
// Fake values for testing.
2017-07-17 18:59:15 -04:00
tokenMaster = "master-token"
tokenNodeUnknown = "unknown-token"
tokenNode1 = "node1-token"
tokenNode2 = "node2-token"
)
2021-10-29 18:41:02 -04:00
tokenFile , err := os . CreateTemp ( "" , "kubeconfig" )
2018-09-03 18:26:09 -04:00
if err != nil {
2017-05-30 15:15:38 -04:00
t . Fatal ( err )
}
2018-09-03 18:26:09 -04:00
tokenFile . WriteString ( strings . Join ( [ ] string {
fmt . Sprintf ( ` %s,admin,uid1,"system:masters" ` , tokenMaster ) ,
fmt . Sprintf ( ` %s,unknown,uid2,"system:nodes" ` , tokenNodeUnknown ) ,
fmt . Sprintf ( ` %s,system:node:node1,uid3,"system:nodes" ` , tokenNode1 ) ,
fmt . Sprintf ( ` %s,system:node:node2,uid4,"system:nodes" ` , tokenNode2 ) ,
} , "\n" ) )
tokenFile . Close ( )
2024-04-23 04:39:47 -04:00
featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . DynamicResourceAllocation , true )
2023-07-13 14:00:12 -04:00
2018-09-03 18:26:09 -04:00
server := kubeapiservertesting . StartTestServerOrDie ( t , nil , [ ] string {
2023-07-13 14:00:12 -04:00
"--runtime-config=api/all=true" ,
2018-09-03 18:26:09 -04:00
"--authorization-mode" , "Node,RBAC" ,
"--token-auth-file" , tokenFile . Name ( ) ,
"--enable-admission-plugins" , "NodeRestriction" ,
// The "default" SA is not installed, causing the ServiceAccount plugin to retry for ~1s per
// API request.
2019-01-18 19:26:27 -05:00
"--disable-admission-plugins" , "ServiceAccount,TaintNodesByCondition" ,
2018-09-03 18:26:09 -04:00
} , framework . SharedEtcd ( ) )
defer server . TearDownFn ( )
// Build client config and superuser clientset
clientConfig := server . ClientConfig
superuserClient , superuserClientExternal := clientsetForToken ( tokenMaster , clientConfig )
2017-05-30 15:15:38 -04:00
// Create objects
2020-02-08 12:30:21 -05:00
if _ , err := superuserClient . CoreV1 ( ) . Namespaces ( ) . Create ( context . TODO ( ) , & corev1 . Namespace { ObjectMeta : metav1 . ObjectMeta { Name : "ns" } } , metav1 . CreateOptions { } ) ; err != nil {
2018-09-03 18:26:09 -04:00
t . Fatal ( err )
}
2020-02-08 12:30:21 -05:00
if _ , err := superuserClient . CoreV1 ( ) . Secrets ( "ns" ) . Create ( context . TODO ( ) , & corev1 . Secret { ObjectMeta : metav1 . ObjectMeta { Name : "mysecret" } } , metav1 . CreateOptions { } ) ; err != nil {
2017-05-30 15:15:38 -04:00
t . Fatal ( err )
}
2020-02-08 12:30:21 -05:00
if _ , err := superuserClient . CoreV1 ( ) . Secrets ( "ns" ) . Create ( context . TODO ( ) , & corev1 . Secret { ObjectMeta : metav1 . ObjectMeta { Name : "mypvsecret" } } , metav1 . CreateOptions { } ) ; err != nil {
2017-05-30 15:15:38 -04:00
t . Fatal ( err )
}
2020-02-08 12:30:21 -05:00
if _ , err := superuserClient . CoreV1 ( ) . ConfigMaps ( "ns" ) . Create ( context . TODO ( ) , & corev1 . ConfigMap { ObjectMeta : metav1 . ObjectMeta { Name : "myconfigmap" } } , metav1 . CreateOptions { } ) ; err != nil {
2017-05-30 15:15:38 -04:00
t . Fatal ( err )
}
2025-07-04 11:43:31 -04:00
if _ , err := superuserClient . ResourceV1 ( ) . ResourceClaims ( "ns" ) . Create ( context . TODO ( ) , & resourceapi . ResourceClaim { ObjectMeta : metav1 . ObjectMeta { Name : "mynamedresourceclaim" } } , metav1 . CreateOptions { } ) ; err != nil {
2023-07-13 14:00:12 -04:00
t . Fatal ( err )
}
2025-07-04 11:43:31 -04:00
if _ , err := superuserClient . ResourceV1 ( ) . ResourceClaims ( "ns" ) . Create ( context . TODO ( ) , & resourceapi . ResourceClaim { ObjectMeta : metav1 . ObjectMeta { Name : "mytemplatizedresourceclaim" } } , metav1 . CreateOptions { } ) ; err != nil {
2023-07-13 14:00:12 -04:00
t . Fatal ( err )
}
2025-07-04 11:43:31 -04:00
if _ , err := superuserClient . ResourceV1 ( ) . ResourceSlices ( ) . Create ( context . TODO ( ) , & resourceapi . ResourceSlice { ObjectMeta : metav1 . ObjectMeta { Name : "myslice1" } , Spec : resourceapi . ResourceSliceSpec { NodeName : ptr . To ( "node1" ) , Driver : "dra.example.com" , Pool : resourceapi . ResourcePool { Name : "node1-slice" , ResourceSliceCount : 1 } } } , metav1 . CreateOptions { } ) ; err != nil {
2024-04-11 08:36:44 -04:00
t . Fatal ( err )
}
2025-07-04 11:43:31 -04:00
if _ , err := superuserClient . ResourceV1 ( ) . ResourceSlices ( ) . Create ( context . TODO ( ) , & resourceapi . ResourceSlice { ObjectMeta : metav1 . ObjectMeta { Name : "myslice2" } , Spec : resourceapi . ResourceSliceSpec { NodeName : ptr . To ( "node2" ) , Driver : "dra.example.com" , Pool : resourceapi . ResourcePool { Name : "node2-slice" , ResourceSliceCount : 1 } } } , metav1 . CreateOptions { } ) ; err != nil {
2024-04-11 08:36:44 -04:00
t . Fatal ( err )
}
2023-07-13 14:00:12 -04:00
2018-01-16 23:39:11 -05:00
pvName := "mypv"
2020-02-07 21:16:47 -05:00
if _ , err := superuserClientExternal . StorageV1 ( ) . VolumeAttachments ( ) . Create ( context . TODO ( ) , & storagev1 . VolumeAttachment {
2018-01-16 23:39:11 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "myattachment" } ,
2019-02-25 19:25:49 -05:00
Spec : storagev1 . VolumeAttachmentSpec {
2018-01-16 23:39:11 -05:00
Attacher : "foo" ,
2019-02-25 19:25:49 -05:00
Source : storagev1 . VolumeAttachmentSource { PersistentVolumeName : & pvName } ,
2018-01-16 23:39:11 -05:00
NodeName : "node2" ,
} ,
2020-02-08 12:30:21 -05:00
} , metav1 . CreateOptions { } ) ; err != nil {
2018-01-16 23:39:11 -05:00
t . Fatal ( err )
}
2020-02-07 21:16:47 -05:00
if _ , err := superuserClient . CoreV1 ( ) . PersistentVolumeClaims ( "ns" ) . Create ( context . TODO ( ) , & corev1 . PersistentVolumeClaim {
2017-05-30 15:15:38 -04:00
ObjectMeta : metav1 . ObjectMeta { Name : "mypvc" } ,
2019-04-04 07:06:52 -04:00
Spec : corev1 . PersistentVolumeClaimSpec {
AccessModes : [ ] corev1 . PersistentVolumeAccessMode { corev1 . ReadOnlyMany } ,
2023-06-14 04:26:56 -04:00
Resources : corev1 . VolumeResourceRequirements { Requests : corev1 . ResourceList { corev1 . ResourceStorage : resource . MustParse ( "1" ) } } ,
2017-05-30 15:15:38 -04:00
} ,
2020-02-08 12:30:21 -05:00
} , metav1 . CreateOptions { } ) ; err != nil {
2017-05-30 15:15:38 -04:00
t . Fatal ( err )
}
2017-11-20 12:57:24 -05:00
2020-02-07 21:16:47 -05:00
if _ , err := superuserClient . CoreV1 ( ) . PersistentVolumes ( ) . Create ( context . TODO ( ) , & corev1 . PersistentVolume {
2017-05-30 15:15:38 -04:00
ObjectMeta : metav1 . ObjectMeta { Name : "mypv" } ,
2019-04-04 07:06:52 -04:00
Spec : corev1 . PersistentVolumeSpec {
AccessModes : [ ] corev1 . PersistentVolumeAccessMode { corev1 . ReadOnlyMany } ,
Capacity : corev1 . ResourceList { corev1 . ResourceStorage : resource . MustParse ( "1" ) } ,
ClaimRef : & corev1 . ObjectReference { Namespace : "ns" , Name : "mypvc" } ,
PersistentVolumeSource : corev1 . PersistentVolumeSource { AzureFile : & corev1 . AzureFilePersistentVolumeSource { ShareName : "default" , SecretName : "mypvsecret" } } ,
2017-05-30 15:15:38 -04:00
} ,
2020-02-08 12:30:21 -05:00
} , metav1 . CreateOptions { } ) ; err != nil {
2017-05-30 15:15:38 -04:00
t . Fatal ( err )
}
2018-01-09 16:44:55 -05:00
getSecret := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2020-02-07 21:16:47 -05:00
_ , err := client . CoreV1 ( ) . Secrets ( "ns" ) . Get ( context . TODO ( ) , "mysecret" , metav1 . GetOptions { } )
2018-01-09 16:44:55 -05:00
return err
}
2017-05-30 15:15:38 -04:00
}
2018-01-09 16:44:55 -05:00
getPVSecret := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2020-02-07 21:16:47 -05:00
_ , err := client . CoreV1 ( ) . Secrets ( "ns" ) . Get ( context . TODO ( ) , "mypvsecret" , metav1 . GetOptions { } )
2018-01-09 16:44:55 -05:00
return err
}
2017-05-30 15:15:38 -04:00
}
2018-01-09 16:44:55 -05:00
getConfigMap := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2020-02-07 21:16:47 -05:00
_ , err := client . CoreV1 ( ) . ConfigMaps ( "ns" ) . Get ( context . TODO ( ) , "myconfigmap" , metav1 . GetOptions { } )
2018-01-09 16:44:55 -05:00
return err
}
2017-05-30 15:15:38 -04:00
}
2018-01-09 16:44:55 -05:00
getPVC := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2020-02-07 21:16:47 -05:00
_ , err := client . CoreV1 ( ) . PersistentVolumeClaims ( "ns" ) . Get ( context . TODO ( ) , "mypvc" , metav1 . GetOptions { } )
2018-01-09 16:44:55 -05:00
return err
}
2017-05-30 15:15:38 -04:00
}
2018-01-09 16:44:55 -05:00
getPV := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2020-02-07 21:16:47 -05:00
_ , err := client . CoreV1 ( ) . PersistentVolumes ( ) . Get ( context . TODO ( ) , "mypv" , metav1 . GetOptions { } )
2018-01-09 16:44:55 -05:00
return err
}
2017-05-30 15:15:38 -04:00
}
2019-05-08 13:12:48 -04:00
getVolumeAttachment := func ( client clientset . Interface ) func ( ) error {
2018-01-16 23:39:11 -05:00
return func ( ) error {
2020-02-07 21:16:47 -05:00
_ , err := client . StorageV1 ( ) . VolumeAttachments ( ) . Get ( context . TODO ( ) , "myattachment" , metav1 . GetOptions { } )
2018-01-16 23:39:11 -05:00
return err
}
}
2023-07-13 14:00:12 -04:00
getResourceClaim := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2025-07-04 11:43:31 -04:00
_ , err := client . ResourceV1 ( ) . ResourceClaims ( "ns" ) . Get ( context . TODO ( ) , "mynamedresourceclaim" , metav1 . GetOptions { } )
2023-07-13 14:00:12 -04:00
return err
}
}
getResourceClaimTemplate := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2025-07-04 11:43:31 -04:00
_ , err := client . ResourceV1 ( ) . ResourceClaims ( "ns" ) . Get ( context . TODO ( ) , "mytemplatizedresourceclaim" , metav1 . GetOptions { } )
2023-07-13 14:00:12 -04:00
return err
}
}
2024-04-11 08:36:44 -04:00
deleteResourceSliceCollection := func ( client clientset . Interface , nodeName * string ) func ( ) error {
return func ( ) error {
var listOptions metav1 . ListOptions
if nodeName != nil {
2024-06-18 11:47:29 -04:00
listOptions . FieldSelector = resourceapi . ResourceSliceSelectorNodeName + "=" + * nodeName
2024-04-11 08:36:44 -04:00
}
2025-07-04 11:43:31 -04:00
return client . ResourceV1 ( ) . ResourceSlices ( ) . DeleteCollection ( context . TODO ( ) , metav1 . DeleteOptions { } , listOptions )
2024-04-11 08:36:44 -04:00
}
}
2023-07-13 14:00:12 -04:00
addResourceClaimTemplateReference := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
_ , err := client . CoreV1 ( ) . Pods ( "ns" ) . Patch ( context . TODO ( ) , "node2normalpod" , types . MergePatchType ,
[ ] byte ( ` { "status": { "resourceClaimStatuses":[ { "name":"templateclaim","resourceClaimName":"mytemplatizedresourceclaim"}]}} ` ) ,
metav1 . PatchOptions { } , "status" )
return err
}
}
removeResourceClaimReference := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
_ , err := client . CoreV1 ( ) . Pods ( "ns" ) . Patch ( context . TODO ( ) , "node2normalpod" , types . MergePatchType ,
[ ] byte ( ` { "status": { "resourceClaimStatuses":null}} ` ) ,
metav1 . PatchOptions { } , "status" )
return err
}
}
2017-05-30 15:15:38 -04:00
2018-01-09 16:44:55 -05:00
createNode2NormalPod := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2020-02-07 21:16:47 -05:00
_ , err := client . CoreV1 ( ) . Pods ( "ns" ) . Create ( context . TODO ( ) , & corev1 . Pod {
2018-01-09 16:44:55 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "node2normalpod" } ,
2019-04-04 07:06:52 -04:00
Spec : corev1 . PodSpec {
2018-01-09 16:44:55 -05:00
NodeName : "node2" ,
2019-04-04 07:06:52 -04:00
Containers : [ ] corev1 . Container { { Name : "image" , Image : "busybox" } } ,
Volumes : [ ] corev1 . Volume {
{ Name : "secret" , VolumeSource : corev1 . VolumeSource { Secret : & corev1 . SecretVolumeSource { SecretName : "mysecret" } } } ,
{ Name : "cm" , VolumeSource : corev1 . VolumeSource { ConfigMap : & corev1 . ConfigMapVolumeSource { LocalObjectReference : corev1 . LocalObjectReference { Name : "myconfigmap" } } } } ,
{ Name : "pvc" , VolumeSource : corev1 . VolumeSource { PersistentVolumeClaim : & corev1 . PersistentVolumeClaimVolumeSource { ClaimName : "mypvc" } } } ,
2018-01-09 16:44:55 -05:00
} ,
2023-07-13 14:00:12 -04:00
ResourceClaims : [ ] corev1 . PodResourceClaim {
2025-07-06 16:02:04 -04:00
{ Name : "namedclaim" , ResourceClaimName : ptr . To ( "mynamedresourceclaim" ) } ,
{ Name : "templateclaim" , ResourceClaimTemplateName : ptr . To ( "myresourceclaimtemplate" ) } ,
2023-07-13 14:00:12 -04:00
} ,
2017-05-30 15:15:38 -04:00
} ,
2020-02-08 12:30:21 -05:00
} , metav1 . CreateOptions { } )
2018-01-09 16:44:55 -05:00
return err
}
2017-05-30 15:15:38 -04:00
}
2018-01-09 16:44:55 -05:00
updateNode2NormalPodStatus := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
startTime := metav1 . NewTime ( time . Now ( ) )
2020-02-07 21:16:47 -05:00
_ , err := client . CoreV1 ( ) . Pods ( "ns" ) . UpdateStatus ( context . TODO ( ) , & corev1 . Pod {
2018-01-09 16:44:55 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "node2normalpod" } ,
2019-04-04 07:06:52 -04:00
Status : corev1 . PodStatus { StartTime : & startTime } ,
2020-02-08 12:30:21 -05:00
} , metav1 . UpdateOptions { } )
2018-01-09 16:44:55 -05:00
return err
}
2017-05-30 15:15:38 -04:00
}
2018-01-09 16:44:55 -05:00
deleteNode2NormalPod := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
zero := int64 ( 0 )
2020-03-01 12:24:42 -05:00
return client . CoreV1 ( ) . Pods ( "ns" ) . Delete ( context . TODO ( ) , "node2normalpod" , metav1 . DeleteOptions { GracePeriodSeconds : & zero } )
2018-01-09 16:44:55 -05:00
}
2017-05-30 15:15:38 -04:00
}
2018-01-09 16:44:55 -05:00
createNode2MirrorPod := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2020-06-21 11:00:17 -04:00
const nodeName = "node2"
node , err := client . CoreV1 ( ) . Nodes ( ) . Get ( context . TODO ( ) , nodeName , metav1 . GetOptions { } )
if err != nil {
return err
}
controller := true
_ , err = client . CoreV1 ( ) . Pods ( "ns" ) . Create ( context . TODO ( ) , & corev1 . Pod {
2018-01-09 16:44:55 -05:00
ObjectMeta : metav1 . ObjectMeta {
Name : "node2mirrorpod" ,
2019-04-04 07:06:52 -04:00
Annotations : map [ string ] string { corev1 . MirrorPodAnnotationKey : "true" } ,
2020-06-21 11:00:17 -04:00
OwnerReferences : [ ] metav1 . OwnerReference { {
APIVersion : corev1 . SchemeGroupVersion . String ( ) ,
Kind : "Node" ,
Name : nodeName ,
UID : node . UID ,
Controller : & controller ,
} } ,
2018-01-09 16:44:55 -05:00
} ,
2019-04-04 07:06:52 -04:00
Spec : corev1 . PodSpec {
2020-06-21 11:00:17 -04:00
NodeName : nodeName ,
2019-04-04 07:06:52 -04:00
Containers : [ ] corev1 . Container { { Name : "image" , Image : "busybox" } } ,
2018-01-09 16:44:55 -05:00
} ,
2020-02-08 12:30:21 -05:00
} , metav1 . CreateOptions { } )
2018-01-09 16:44:55 -05:00
return err
}
2017-05-30 15:15:38 -04:00
}
2018-01-09 16:44:55 -05:00
deleteNode2MirrorPod := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
zero := int64 ( 0 )
2020-03-01 12:24:42 -05:00
return client . CoreV1 ( ) . Pods ( "ns" ) . Delete ( context . TODO ( ) , "node2mirrorpod" , metav1 . DeleteOptions { GracePeriodSeconds : & zero } )
2018-01-09 16:44:55 -05:00
}
2017-05-30 15:15:38 -04:00
}
2018-01-09 16:44:55 -05:00
createNode2 := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2020-02-08 12:30:21 -05:00
_ , err := client . CoreV1 ( ) . Nodes ( ) . Create ( context . TODO ( ) , & corev1 . Node { ObjectMeta : metav1 . ObjectMeta { Name : "node2" } } , metav1 . CreateOptions { } )
2018-01-09 16:44:55 -05:00
return err
}
2017-05-30 15:15:38 -04:00
}
2018-01-09 16:44:55 -05:00
updateNode2Status := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2020-02-07 21:16:47 -05:00
_ , err := client . CoreV1 ( ) . Nodes ( ) . UpdateStatus ( context . TODO ( ) , & corev1 . Node {
2018-01-09 16:44:55 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "node2" } ,
2019-04-04 07:06:52 -04:00
Status : corev1 . NodeStatus { } ,
2020-02-08 12:30:21 -05:00
} , metav1 . UpdateOptions { } )
2018-01-09 16:44:55 -05:00
return err
}
2017-05-30 15:15:38 -04:00
}
2018-01-09 16:44:55 -05:00
deleteNode2 := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2020-03-01 12:24:42 -05:00
return client . CoreV1 ( ) . Nodes ( ) . Delete ( context . TODO ( ) , "node2" , metav1 . DeleteOptions { } )
2018-01-09 16:44:55 -05:00
}
2017-05-30 15:15:38 -04:00
}
2018-01-09 16:44:55 -05:00
createNode2NormalPodEviction := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2019-01-09 10:26:14 -05:00
zero := int64 ( 0 )
2021-03-31 16:54:55 -04:00
return client . PolicyV1 ( ) . Evictions ( "ns" ) . Evict ( context . TODO ( ) , & policy . Eviction {
2018-01-09 16:44:55 -05:00
TypeMeta : metav1 . TypeMeta {
2021-03-31 16:54:55 -04:00
APIVersion : "policy/v1" ,
2018-01-09 16:44:55 -05:00
Kind : "Eviction" ,
} ,
ObjectMeta : metav1 . ObjectMeta {
Name : "node2normalpod" ,
Namespace : "ns" ,
} ,
2019-01-09 10:26:14 -05:00
DeleteOptions : & metav1 . DeleteOptions { GracePeriodSeconds : & zero } ,
2018-01-09 16:44:55 -05:00
} )
}
2017-07-11 14:53:12 -04:00
}
2018-01-09 16:44:55 -05:00
createNode2MirrorPodEviction := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2019-01-09 10:26:14 -05:00
zero := int64 ( 0 )
2021-03-31 16:54:55 -04:00
return client . PolicyV1 ( ) . Evictions ( "ns" ) . Evict ( context . TODO ( ) , & policy . Eviction {
2018-01-09 16:44:55 -05:00
TypeMeta : metav1 . TypeMeta {
2021-03-31 16:54:55 -04:00
APIVersion : "policy/v1" ,
2018-01-09 16:44:55 -05:00
Kind : "Eviction" ,
} ,
ObjectMeta : metav1 . ObjectMeta {
Name : "node2mirrorpod" ,
Namespace : "ns" ,
} ,
2019-01-09 10:26:14 -05:00
DeleteOptions : & metav1 . DeleteOptions { GracePeriodSeconds : & zero } ,
2018-01-09 16:44:55 -05:00
} )
}
2017-07-11 14:53:12 -04:00
}
2017-05-30 15:15:38 -04:00
2017-11-20 12:57:24 -05:00
capacity := 50
2018-01-09 16:44:55 -05:00
updatePVCCapacity := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
capacity ++
statusString := fmt . Sprintf ( "{\"status\": {\"capacity\": {\"storage\": \"%dG\"}}}" , capacity )
patchBytes := [ ] byte ( statusString )
2020-02-08 12:30:21 -05:00
_ , err := client . CoreV1 ( ) . PersistentVolumeClaims ( "ns" ) . Patch ( context . TODO ( ) , "mypvc" , types . StrategicMergePatchType , patchBytes , metav1 . PatchOptions { } , "status" )
2018-01-09 16:44:55 -05:00
return err
}
2017-11-20 12:57:24 -05:00
}
2018-01-09 16:44:55 -05:00
updatePVCPhase := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
patchBytes := [ ] byte ( ` { "status": { "phase": "Bound"}} ` )
2020-02-08 12:30:21 -05:00
_ , err := client . CoreV1 ( ) . PersistentVolumeClaims ( "ns" ) . Patch ( context . TODO ( ) , "mypvc" , types . StrategicMergePatchType , patchBytes , metav1 . PatchOptions { } , "status" )
2018-01-09 16:44:55 -05:00
return err
}
2017-11-20 12:57:24 -05:00
}
2018-06-21 17:24:59 -04:00
getNode1Lease := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2020-02-07 21:16:47 -05:00
_ , err := client . CoordinationV1 ( ) . Leases ( corev1 . NamespaceNodeLease ) . Get ( context . TODO ( ) , "node1" , metav1 . GetOptions { } )
2018-06-21 17:24:59 -04:00
return err
}
}
node1LeaseDurationSeconds := int32 ( 40 )
createNode1Lease := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
lease := & coordination . Lease {
ObjectMeta : metav1 . ObjectMeta {
Name : "node1" ,
} ,
Spec : coordination . LeaseSpec {
2025-07-06 16:02:04 -04:00
HolderIdentity : ptr . To ( "node1" ) ,
LeaseDurationSeconds : ptr . To [ int32 ] ( node1LeaseDurationSeconds ) ,
2018-06-21 17:24:59 -04:00
RenewTime : & metav1 . MicroTime { Time : time . Now ( ) } ,
} ,
}
2020-02-08 12:30:21 -05:00
_ , err := client . CoordinationV1 ( ) . Leases ( corev1 . NamespaceNodeLease ) . Create ( context . TODO ( ) , lease , metav1 . CreateOptions { } )
2018-06-21 17:24:59 -04:00
return err
}
}
updateNode1Lease := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2020-02-07 21:16:47 -05:00
lease , err := client . CoordinationV1 ( ) . Leases ( corev1 . NamespaceNodeLease ) . Get ( context . TODO ( ) , "node1" , metav1 . GetOptions { } )
2018-06-21 17:24:59 -04:00
if err != nil {
return err
}
lease . Spec . RenewTime = & metav1 . MicroTime { Time : time . Now ( ) }
2020-02-08 12:30:21 -05:00
_ , err = client . CoordinationV1 ( ) . Leases ( corev1 . NamespaceNodeLease ) . Update ( context . TODO ( ) , lease , metav1 . UpdateOptions { } )
2018-06-21 17:24:59 -04:00
return err
}
}
patchNode1Lease := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
node1LeaseDurationSeconds ++
bs := [ ] byte ( fmt . Sprintf ( ` { "spec": { "leaseDurationSeconds": %d}} ` , node1LeaseDurationSeconds ) )
2020-02-08 12:30:21 -05:00
_ , err := client . CoordinationV1 ( ) . Leases ( corev1 . NamespaceNodeLease ) . Patch ( context . TODO ( ) , "node1" , types . StrategicMergePatchType , bs , metav1 . PatchOptions { } )
2018-06-21 17:24:59 -04:00
return err
}
}
deleteNode1Lease := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
2020-03-01 12:24:42 -05:00
return client . CoordinationV1 ( ) . Leases ( corev1 . NamespaceNodeLease ) . Delete ( context . TODO ( ) , "node1" , metav1 . DeleteOptions { } )
2018-06-21 17:24:59 -04:00
}
}
2019-05-08 13:12:48 -04:00
getNode1CSINode := func ( client clientset . Interface ) func ( ) error {
2018-09-03 18:26:09 -04:00
return func ( ) error {
2020-02-07 21:16:47 -05:00
_ , err := client . StorageV1 ( ) . CSINodes ( ) . Get ( context . TODO ( ) , "node1" , metav1 . GetOptions { } )
2018-09-03 18:26:09 -04:00
return err
}
}
2019-05-08 13:12:48 -04:00
createNode1CSINode := func ( client clientset . Interface ) func ( ) error {
2018-09-03 18:26:09 -04:00
return func ( ) error {
2019-10-03 21:42:17 -04:00
nodeInfo := & storagev1 . CSINode {
2018-09-03 18:26:09 -04:00
ObjectMeta : metav1 . ObjectMeta {
Name : "node1" ,
} ,
2019-10-03 21:42:17 -04:00
Spec : storagev1 . CSINodeSpec {
Drivers : [ ] storagev1 . CSINodeDriver {
2018-11-08 16:12:54 -05:00
{
2019-03-01 21:30:24 -05:00
Name : "com.example.csi.driver1" ,
2018-11-08 16:12:54 -05:00
NodeID : "com.example.csi/node1" ,
TopologyKeys : [ ] string { "com.example.csi/zone" } ,
} ,
} ,
} ,
2018-09-03 18:26:09 -04:00
}
2020-02-08 12:30:21 -05:00
_ , err := client . StorageV1 ( ) . CSINodes ( ) . Create ( context . TODO ( ) , nodeInfo , metav1 . CreateOptions { } )
2018-09-03 18:26:09 -04:00
return err
}
}
2019-05-08 13:12:48 -04:00
updateNode1CSINode := func ( client clientset . Interface ) func ( ) error {
2018-09-03 18:26:09 -04:00
return func ( ) error {
2020-02-07 21:16:47 -05:00
nodeInfo , err := client . StorageV1 ( ) . CSINodes ( ) . Get ( context . TODO ( ) , "node1" , metav1 . GetOptions { } )
2018-09-03 18:26:09 -04:00
if err != nil {
return err
}
2019-10-03 21:42:17 -04:00
nodeInfo . Spec . Drivers = [ ] storagev1 . CSINodeDriver {
2018-09-03 18:26:09 -04:00
{
2019-03-01 21:30:24 -05:00
Name : "com.example.csi.driver2" ,
2018-09-03 18:26:09 -04:00
NodeID : "com.example.csi/node1" ,
TopologyKeys : [ ] string { "com.example.csi/rack" } ,
} ,
}
2020-02-08 12:30:21 -05:00
_ , err = client . StorageV1 ( ) . CSINodes ( ) . Update ( context . TODO ( ) , nodeInfo , metav1 . UpdateOptions { } )
2018-09-03 18:26:09 -04:00
return err
}
}
2019-05-08 13:12:48 -04:00
patchNode1CSINode := func ( client clientset . Interface ) func ( ) error {
2018-09-03 18:26:09 -04:00
return func ( ) error {
2019-03-01 21:30:24 -05:00
bs := [ ] byte ( fmt . Sprintf ( ` { "csiDrivers": [ { "driver": "net.example.storage.driver2", "nodeID": "net.example.storage/node1", "topologyKeys": [ "net.example.storage/region" ] } ] } ` ) )
2018-09-03 18:26:09 -04:00
// StrategicMergePatch is unsupported by CRs. Falling back to MergePatch
2020-02-08 12:30:21 -05:00
_ , err := client . StorageV1 ( ) . CSINodes ( ) . Patch ( context . TODO ( ) , "node1" , types . MergePatchType , bs , metav1 . PatchOptions { } )
2018-09-03 18:26:09 -04:00
return err
}
}
2019-05-08 13:12:48 -04:00
deleteNode1CSINode := func ( client clientset . Interface ) func ( ) error {
2018-09-03 18:26:09 -04:00
return func ( ) error {
2020-03-01 12:24:42 -05:00
return client . StorageV1 ( ) . CSINodes ( ) . Delete ( context . TODO ( ) , "node1" , metav1 . DeleteOptions { } )
2018-09-03 18:26:09 -04:00
}
}
2018-01-16 23:24:20 -05:00
nodeanonClient , _ := clientsetForToken ( tokenNodeUnknown , clientConfig )
2018-01-16 23:39:11 -05:00
node1Client , node1ClientExternal := clientsetForToken ( tokenNode1 , clientConfig )
node2Client , node2ClientExternal := clientsetForToken ( tokenNode2 , clientConfig )
2019-03-01 21:30:24 -05:00
_ , csiNode1Client := clientsetForToken ( tokenNode1 , clientConfig )
_ , csiNode2Client := clientsetForToken ( tokenNode2 , clientConfig )
2017-05-30 15:15:38 -04:00
// all node requests from node1 and unknown node fail
expectForbidden ( t , getSecret ( nodeanonClient ) )
expectForbidden ( t , getPVSecret ( nodeanonClient ) )
expectForbidden ( t , getConfigMap ( nodeanonClient ) )
expectForbidden ( t , getPVC ( nodeanonClient ) )
expectForbidden ( t , getPV ( nodeanonClient ) )
2023-07-13 14:00:12 -04:00
expectForbidden ( t , getResourceClaim ( nodeanonClient ) )
expectForbidden ( t , getResourceClaimTemplate ( nodeanonClient ) )
2017-05-30 15:15:38 -04:00
expectForbidden ( t , createNode2NormalPod ( nodeanonClient ) )
2017-07-11 14:53:12 -04:00
expectForbidden ( t , deleteNode2NormalPod ( nodeanonClient ) )
expectForbidden ( t , createNode2MirrorPodEviction ( nodeanonClient ) )
2017-05-30 15:15:38 -04:00
expectForbidden ( t , createNode2 ( nodeanonClient ) )
expectForbidden ( t , updateNode2Status ( nodeanonClient ) )
expectForbidden ( t , deleteNode2 ( nodeanonClient ) )
expectForbidden ( t , getSecret ( node1Client ) )
expectForbidden ( t , getPVSecret ( node1Client ) )
expectForbidden ( t , getConfigMap ( node1Client ) )
expectForbidden ( t , getPVC ( node1Client ) )
expectForbidden ( t , getPV ( node1Client ) )
2023-07-13 14:00:12 -04:00
expectForbidden ( t , getResourceClaim ( node1Client ) )
expectForbidden ( t , getResourceClaimTemplate ( node1Client ) )
2017-05-30 15:15:38 -04:00
expectForbidden ( t , createNode2NormalPod ( nodeanonClient ) )
2017-07-11 14:53:12 -04:00
expectNotFound ( t , createNode2MirrorPodEviction ( node1Client ) )
2017-05-30 15:15:38 -04:00
expectForbidden ( t , createNode2 ( node1Client ) )
2020-06-07 09:13:53 -04:00
expectNotFound ( t , updateNode2Status ( node1Client ) )
2017-05-30 15:15:38 -04:00
expectForbidden ( t , deleteNode2 ( node1Client ) )
// related object requests from node2 fail
expectForbidden ( t , getSecret ( node2Client ) )
expectForbidden ( t , getPVSecret ( node2Client ) )
expectForbidden ( t , getConfigMap ( node2Client ) )
expectForbidden ( t , getPVC ( node2Client ) )
expectForbidden ( t , getPV ( node2Client ) )
2023-07-13 14:00:12 -04:00
expectForbidden ( t , getResourceClaim ( node2Client ) )
expectForbidden ( t , getResourceClaimTemplate ( node2Client ) )
2017-11-20 12:57:24 -05:00
2017-05-30 15:15:38 -04:00
expectForbidden ( t , createNode2NormalPod ( nodeanonClient ) )
// mirror pod and self node lifecycle is allowed
2020-06-21 11:00:17 -04:00
expectAllowed ( t , createNode2 ( node2Client ) )
expectAllowed ( t , updateNode2Status ( node2Client ) )
expectForbidden ( t , createNode2MirrorPod ( nodeanonClient ) )
expectForbidden ( t , deleteNode2MirrorPod ( nodeanonClient ) )
expectForbidden ( t , createNode2MirrorPod ( node1Client ) )
expectNotFound ( t , deleteNode2MirrorPod ( node1Client ) )
// create a pod as an admin to add object references
expectAllowed ( t , createNode2NormalPod ( superuserClient ) )
2017-05-30 15:15:38 -04:00
expectAllowed ( t , createNode2MirrorPod ( node2Client ) )
expectAllowed ( t , deleteNode2MirrorPod ( node2Client ) )
2017-07-11 14:53:12 -04:00
expectAllowed ( t , createNode2MirrorPod ( node2Client ) )
expectAllowed ( t , createNode2MirrorPodEviction ( node2Client ) )
2018-11-14 00:42:06 -05:00
// self deletion is not allowed
expectForbidden ( t , deleteNode2 ( node2Client ) )
2020-06-07 09:13:53 -04:00
// modification of another node's status is not allowed
expectForbidden ( t , updateNode2Status ( node1Client ) )
2017-05-30 15:15:38 -04:00
// unidentifiable node and node1 are still forbidden
expectForbidden ( t , getSecret ( nodeanonClient ) )
expectForbidden ( t , getPVSecret ( nodeanonClient ) )
expectForbidden ( t , getConfigMap ( nodeanonClient ) )
expectForbidden ( t , getPVC ( nodeanonClient ) )
expectForbidden ( t , getPV ( nodeanonClient ) )
2023-07-13 14:00:12 -04:00
expectForbidden ( t , getResourceClaim ( nodeanonClient ) )
expectForbidden ( t , getResourceClaimTemplate ( nodeanonClient ) )
2017-05-30 15:15:38 -04:00
expectForbidden ( t , createNode2NormalPod ( nodeanonClient ) )
expectForbidden ( t , updateNode2NormalPodStatus ( nodeanonClient ) )
expectForbidden ( t , deleteNode2NormalPod ( nodeanonClient ) )
2017-07-11 14:53:12 -04:00
expectForbidden ( t , createNode2NormalPodEviction ( nodeanonClient ) )
2017-05-30 15:15:38 -04:00
expectForbidden ( t , createNode2MirrorPod ( nodeanonClient ) )
expectForbidden ( t , deleteNode2MirrorPod ( nodeanonClient ) )
2017-07-11 14:53:12 -04:00
expectForbidden ( t , createNode2MirrorPodEviction ( nodeanonClient ) )
2017-05-30 15:15:38 -04:00
expectForbidden ( t , getSecret ( node1Client ) )
expectForbidden ( t , getPVSecret ( node1Client ) )
expectForbidden ( t , getConfigMap ( node1Client ) )
expectForbidden ( t , getPVC ( node1Client ) )
expectForbidden ( t , getPV ( node1Client ) )
2023-07-13 14:00:12 -04:00
expectForbidden ( t , getResourceClaim ( node1Client ) )
expectForbidden ( t , getResourceClaimTemplate ( node1Client ) )
2017-05-30 15:15:38 -04:00
expectForbidden ( t , createNode2NormalPod ( node1Client ) )
expectForbidden ( t , updateNode2NormalPodStatus ( node1Client ) )
expectForbidden ( t , deleteNode2NormalPod ( node1Client ) )
2017-07-11 14:53:12 -04:00
expectForbidden ( t , createNode2NormalPodEviction ( node1Client ) )
2017-05-30 15:15:38 -04:00
expectForbidden ( t , createNode2MirrorPod ( node1Client ) )
2017-07-11 14:53:12 -04:00
expectNotFound ( t , deleteNode2MirrorPod ( node1Client ) )
expectNotFound ( t , createNode2MirrorPodEviction ( node1Client ) )
2017-05-30 15:15:38 -04:00
// node2 can get referenced objects now
expectAllowed ( t , getSecret ( node2Client ) )
expectAllowed ( t , getPVSecret ( node2Client ) )
expectAllowed ( t , getConfigMap ( node2Client ) )
expectAllowed ( t , getPVC ( node2Client ) )
expectAllowed ( t , getPV ( node2Client ) )
2017-11-20 12:57:24 -05:00
2023-07-13 14:00:12 -04:00
// node2 can only get direct claim references
expectAllowed ( t , getResourceClaim ( node2Client ) )
expectForbidden ( t , getResourceClaimTemplate ( node2Client ) )
// node cannot add a claim reference
expectForbidden ( t , addResourceClaimTemplateReference ( node2Client ) )
// superuser can add a claim reference
expectAllowed ( t , addResourceClaimTemplateReference ( superuserClient ) )
// node can get direct and template claim references
expectAllowed ( t , getResourceClaim ( node2Client ) )
expectAllowed ( t , getResourceClaimTemplate ( node2Client ) )
// node cannot remove a claim reference
expectForbidden ( t , removeResourceClaimReference ( node2Client ) )
// superuser can remove a claim reference
expectAllowed ( t , removeResourceClaimReference ( superuserClient ) )
// node2 can only get direct claim references
expectAllowed ( t , getResourceClaim ( node2Client ) )
expectForbidden ( t , getResourceClaimTemplate ( node2Client ) )
2017-05-30 15:15:38 -04:00
expectForbidden ( t , createNode2NormalPod ( node2Client ) )
expectAllowed ( t , updateNode2NormalPodStatus ( node2Client ) )
expectAllowed ( t , deleteNode2NormalPod ( node2Client ) )
expectAllowed ( t , createNode2MirrorPod ( node2Client ) )
expectAllowed ( t , deleteNode2MirrorPod ( node2Client ) )
2018-02-20 14:28:28 -05:00
2017-07-11 14:53:12 -04:00
// recreate as an admin to test eviction
expectAllowed ( t , createNode2NormalPod ( superuserClient ) )
expectAllowed ( t , createNode2MirrorPod ( superuserClient ) )
expectAllowed ( t , createNode2NormalPodEviction ( node2Client ) )
expectAllowed ( t , createNode2MirrorPodEviction ( node2Client ) )
2020-06-21 11:00:17 -04:00
// clean up node2
expectAllowed ( t , deleteNode2 ( superuserClient ) )
2017-11-20 12:57:24 -05:00
// re-create a pod as an admin to add object references
expectAllowed ( t , createNode2NormalPod ( superuserClient ) )
2018-01-16 23:39:11 -05:00
2017-11-20 12:57:24 -05:00
expectForbidden ( t , updatePVCCapacity ( node1Client ) )
expectAllowed ( t , updatePVCCapacity ( node2Client ) )
expectForbidden ( t , updatePVCPhase ( node2Client ) )
2018-01-16 23:39:11 -05:00
// Enabled CSIPersistentVolume feature
expectForbidden ( t , getVolumeAttachment ( node1ClientExternal ) )
expectAllowed ( t , getVolumeAttachment ( node2ClientExternal ) )
2018-02-23 14:24:43 -05:00
2018-02-20 14:28:28 -05:00
// create node2 again
expectAllowed ( t , createNode2 ( node2Client ) )
// clean up node2
2018-11-14 00:42:06 -05:00
expectAllowed ( t , deleteNode2 ( superuserClient ) )
2018-02-20 14:28:28 -05:00
2018-06-21 17:24:59 -04:00
// node1 allowed to operate on its own lease
expectAllowed ( t , createNode1Lease ( node1Client ) )
expectAllowed ( t , getNode1Lease ( node1Client ) )
expectAllowed ( t , updateNode1Lease ( node1Client ) )
expectAllowed ( t , patchNode1Lease ( node1Client ) )
expectAllowed ( t , deleteNode1Lease ( node1Client ) )
// node2 not allowed to operate on another node's lease
expectForbidden ( t , createNode1Lease ( node2Client ) )
expectForbidden ( t , getNode1Lease ( node2Client ) )
expectForbidden ( t , updateNode1Lease ( node2Client ) )
expectForbidden ( t , patchNode1Lease ( node2Client ) )
expectForbidden ( t , deleteNode1Lease ( node2Client ) )
2018-08-28 03:00:00 -04:00
2019-03-01 21:30:24 -05:00
// node1 allowed to operate on its own CSINode
expectAllowed ( t , createNode1CSINode ( csiNode1Client ) )
expectAllowed ( t , getNode1CSINode ( csiNode1Client ) )
expectAllowed ( t , updateNode1CSINode ( csiNode1Client ) )
expectAllowed ( t , patchNode1CSINode ( csiNode1Client ) )
expectAllowed ( t , deleteNode1CSINode ( csiNode1Client ) )
// node2 not allowed to operate on another node's CSINode
expectForbidden ( t , createNode1CSINode ( csiNode2Client ) )
expectForbidden ( t , getNode1CSINode ( csiNode2Client ) )
expectForbidden ( t , updateNode1CSINode ( csiNode2Client ) )
expectForbidden ( t , patchNode1CSINode ( csiNode2Client ) )
expectForbidden ( t , deleteNode1CSINode ( csiNode2Client ) )
2024-04-11 08:36:44 -04:00
// Always allowed. Permission to delete specific objects is checked per object.
// Beware, this is destructive!
expectAllowed ( t , deleteResourceSliceCollection ( csiNode1Client , ptr . To ( "node1" ) ) )
// One slice must have been deleted, the other not.
2025-07-04 11:43:31 -04:00
slices , err := superuserClient . ResourceV1 ( ) . ResourceSlices ( ) . List ( context . TODO ( ) , metav1 . ListOptions { } )
2024-04-11 08:36:44 -04:00
if err != nil {
t . Fatal ( err )
}
if len ( slices . Items ) != 1 {
t . Fatalf ( "unexpected slices: %v" , slices . Items )
}
2025-07-04 11:43:31 -04:00
if ptr . Deref ( slices . Items [ 0 ] . Spec . NodeName , "" ) != "node2" {
2024-04-11 08:36:44 -04:00
t . Fatal ( "wrong slice deleted" )
}
// Superuser can delete.
expectAllowed ( t , deleteResourceSliceCollection ( superuserClient , nil ) )
2025-07-04 11:43:31 -04:00
slices , err = superuserClient . ResourceV1 ( ) . ResourceSlices ( ) . List ( context . TODO ( ) , metav1 . ListOptions { } )
2024-04-11 08:36:44 -04:00
if err != nil {
t . Fatal ( err )
}
if len ( slices . Items ) != 0 {
t . Fatalf ( "unexpected slices: %v" , slices . Items )
}
2017-05-30 15:15:38 -04:00
}
2018-01-09 16:44:55 -05:00
// expect executes a function a set number of times until it either returns the
// expected error or executes too many times. It returns if the retries timed
// out and the last error returned by the method.
2018-01-16 23:39:11 -05:00
func expect ( t * testing . T , f func ( ) error , wantErr func ( error ) bool ) ( timeout bool , lastErr error ) {
t . Helper ( )
2018-01-09 16:44:55 -05:00
err := wait . PollImmediate ( time . Second , 30 * time . Second , func ( ) ( bool , error ) {
2018-01-16 23:39:11 -05:00
t . Helper ( )
2018-01-09 16:44:55 -05:00
lastErr = f ( )
if wantErr ( lastErr ) {
return true , nil
}
2018-01-16 23:39:11 -05:00
t . Logf ( "unexpected response, will retry: %v" , lastErr )
2018-01-09 16:44:55 -05:00
return false , nil
} )
return err == nil , lastErr
}
func expectForbidden ( t * testing . T , f func ( ) error ) {
t . Helper ( )
2019-11-12 03:43:58 -05:00
if ok , err := expect ( t , f , apierrors . IsForbidden ) ; ! ok {
2018-01-09 16:44:55 -05:00
t . Errorf ( "Expected forbidden error, got %v" , err )
2017-05-30 15:15:38 -04:00
}
}
2018-01-09 16:44:55 -05:00
func expectNotFound ( t * testing . T , f func ( ) error ) {
t . Helper ( )
2019-11-12 03:43:58 -05:00
if ok , err := expect ( t , f , apierrors . IsNotFound ) ; ! ok {
2018-01-09 16:44:55 -05:00
t . Errorf ( "Expected notfound error, got %v" , err )
2017-07-11 14:53:12 -04:00
}
}
2018-01-09 16:44:55 -05:00
func expectAllowed ( t * testing . T , f func ( ) error ) {
t . Helper ( )
2018-01-16 23:39:11 -05:00
if ok , err := expect ( t , f , func ( e error ) bool { return e == nil } ) ; ! ok {
2018-01-09 16:44:55 -05:00
t . Errorf ( "Expected no error, got %v" , err )
2017-05-30 15:15:38 -04:00
}
}
2024-10-15 02:30:51 -04:00
func checkNilError ( t * testing . T , err error ) {
t . Helper ( )
if err != nil {
t . Fatalf ( "unexpected error: %v" , err )
}
}
func expectedForbiddenMessage ( t * testing . T , f func ( ) error , expectedMessage string ) {
t . Helper ( )
if ok , err := expect ( t , f , func ( e error ) bool { return apierrors . IsForbidden ( e ) && strings . Contains ( e . Error ( ) , expectedMessage ) } ) ; ! ok {
t . Errorf ( "Expected forbidden error with message %q, got %v" , expectedMessage , err )
}
}
// TestNodeRestrictionServiceAccount is an integration test to verify that
// the NodeRestriction admission plugin
// - forbids kubelet to request a token for a service account token that's not bound to a pod
// - forbids kubelet to request a token for a service account token when pod is not found
// - kubelet successfully requests a token for a service account token when pod is found and uid matches
// This test is run with the ServiceAccountNodeAudienceRestriction feature disabled
// to validate the default behavior of the NodeRestriction admission plugin.
func TestNodeRestrictionServiceAccount ( t * testing . T ) {
const (
// Define credentials
// Fake values for testing.
tokenMaster = "master-token"
tokenNode1 = "node1-token"
tokenNode2 = "node2-token"
)
tokenFile , err := os . CreateTemp ( "" , "kubeconfig" )
checkNilError ( t , err )
_ , err = tokenFile . WriteString ( strings . Join ( [ ] string {
fmt . Sprintf ( ` %s,admin,uid1,"system:masters" ` , tokenMaster ) ,
fmt . Sprintf ( ` %s,system:node:node1,uid3,"system:nodes" ` , tokenNode1 ) ,
fmt . Sprintf ( ` %s,system:node:node2,uid4,"system:nodes" ` , tokenNode2 ) ,
} , "\n" ) )
checkNilError ( t , err )
checkNilError ( t , tokenFile . Close ( ) )
featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . ServiceAccountNodeAudienceRestriction , false )
server := kubeapiservertesting . StartTestServerOrDie ( t , nil , [ ] string {
"--runtime-config=api/all=true" ,
"--authorization-mode" , "Node,RBAC" ,
"--token-auth-file" , tokenFile . Name ( ) ,
"--enable-admission-plugins" , "NodeRestriction" ,
"--disable-admission-plugins" , "ServiceAccount,TaintNodesByCondition" ,
} , framework . SharedEtcd ( ) )
defer server . TearDownFn ( )
// Build client config and superuser clientset
clientConfig := server . ClientConfig
superuserClient , _ := clientsetForToken ( tokenMaster , clientConfig )
if _ , err := superuserClient . CoreV1 ( ) . Namespaces ( ) . Create ( context . TODO ( ) , & corev1 . Namespace { ObjectMeta : metav1 . ObjectMeta { Name : "ns" } } , metav1 . CreateOptions { } ) ; err != nil {
t . Fatal ( err )
}
// RBAC permissions are required to test service account token requests
// using the node client because we cannot rely on the node authorizer since we have not
// configured the references needed for it to work. This test is focused on exercising
// the node admission logic, not the node authorizer logic. We want to know what happens
// in admission if the request was already authorized.
configureRBACForServiceAccountToken ( t , superuserClient )
createTokenRequestNodeNotBoundToPod := func ( client clientset . Interface ) func ( ) error {
return func ( ) error {
_ , err := client . CoreV1 ( ) . ServiceAccounts ( "ns" ) . CreateToken ( context . TODO ( ) , "default" , & authenticationv1 . TokenRequest { } , metav1 . CreateOptions { } )
return err
}
}
node1Client , _ := clientsetForToken ( tokenNode1 , clientConfig )
createNode ( t , node1Client , "node1" )
node2Client , _ := clientsetForToken ( tokenNode2 , clientConfig )
t . Run ( "service account token request is forbidden when not bound to a pod" , func ( t * testing . T ) {
expectedForbiddenMessage ( t , createTokenRequestNodeNotBoundToPod ( node1Client ) , "node requested token not bound to a pod" )
} )
t . Run ( "service account token request is forbidden when pod is not found" , func ( t * testing . T ) {
2025-02-27 14:52:51 -05:00
expectNotFound ( t , createTokenRequest ( node1Client , "uid1" , tokenRequestWithAudiences ( "" ) ) )
2024-10-15 02:30:51 -04:00
} )
t . Run ( "uid in token request does not match pod uid is forbidden" , func ( t * testing . T ) {
createPod ( t , superuserClient , nil )
2025-02-27 14:52:51 -05:00
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , "random-uid" , tokenRequestWithAudiences ( "" ) ) , "the UID in the bound object reference (random-uid) does not match the UID in record. The object might have been deleted and then recreated" )
2024-10-15 02:30:51 -04:00
deletePod ( t , superuserClient , "pod1" )
} )
t . Run ( "node requesting token for pod bound to different node is forbidden" , func ( t * testing . T ) {
pod := createPod ( t , superuserClient , nil )
2025-02-27 14:52:51 -05:00
expectedForbiddenMessage ( t , createTokenRequest ( node2Client , pod . UID , tokenRequestWithAudiences ( "" ) ) , "node requested token bound to a pod scheduled on a different node" )
2024-10-15 02:30:51 -04:00
deletePod ( t , superuserClient , "pod1" )
} )
t . Run ( "service account token request is successful" , func ( t * testing . T ) {
// create a pod as an admin to add object references
pod := createPod ( t , superuserClient , nil )
2025-02-27 14:52:51 -05:00
createServiceAccount ( t , superuserClient , "ns" , "default" )
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "" ) ) )
2024-10-15 02:30:51 -04:00
} )
}
// TestNodeRestrictionServiceAccountAudience is an integration test to verify that
// the NodeRestriction admission plugin
// - allows kubelet to request a token for a service account that's in the pod spec
// 1. pod --> ephemeral --> pvc --> pv --> csi --> driver --> tokenrequest with audience
// 2. pod --> pvc --> pv --> csi --> driver --> tokenrequest with audience
// 3. pod --> csi --> driver --> tokenrequest with audience
// 4. pod --> projected --> service account token with audience
//
2025-02-27 14:52:51 -05:00
// - allows kubelet to request a token for a service account based on subjectacccessreview
// 1. clusterrole and clusterrolebinding allowing a node to request specific audience for a service account
// 2. clusterrole and rolebinding allowing a node to request specific audience for a service account
// 3. role and rolebinding allowing a node to request specific audience for a service account in a namespace
// 4. clusterrole and rolebinding allowing a node to request specific audience for any service account in a namespace
// 5. clusterrole and rolebinding allowing any node to request any audience for a service account in a namespace
// 6. clusterrole and rolebinding allowing any node to request any audience for any service account in a namespace
// 7. api server audience "" configured with rbac role
//
2024-10-15 02:30:51 -04:00
// - forbids kubelet to request a token for a service account that's not in the pod spec
// when the ServiceAccountNodeAudienceRestriction feature is enabled.
func TestNodeRestrictionServiceAccountAudience ( t * testing . T ) {
const (
// Define credentials
// Fake values for testing.
tokenMaster = "master-token"
tokenNode1 = "node1-token"
)
tokenFile , err := os . CreateTemp ( "" , "kubeconfig" )
checkNilError ( t , err )
_ , err = tokenFile . WriteString ( strings . Join ( [ ] string {
fmt . Sprintf ( ` %s,admin,uid1,"system:masters" ` , tokenMaster ) ,
fmt . Sprintf ( ` %s,system:node:node1,uid3,"system:nodes" ` , tokenNode1 ) ,
} , "\n" ) )
checkNilError ( t , err )
checkNilError ( t , tokenFile . Close ( ) )
featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . ServiceAccountNodeAudienceRestriction , true )
server := kubeapiservertesting . StartTestServerOrDie ( t , nil , [ ] string {
"--runtime-config=api/all=true" ,
"--authorization-mode" , "Node,RBAC" ,
"--token-auth-file" , tokenFile . Name ( ) ,
"--enable-admission-plugins" , "NodeRestriction" ,
"--disable-admission-plugins" , "TaintNodesByCondition" ,
} , framework . SharedEtcd ( ) )
defer server . TearDownFn ( )
kubeConfigFile := createKubeConfigFileForRestConfig ( t , server . ClientConfig )
ctx := testContext ( t )
kcm := kubecontrollermanagertesting . StartTestServerOrDie ( ctx , [ ] string {
"--kubeconfig=" + kubeConfigFile ,
"--controllers=ephemeral-volume-controller" , // we need this controller to test the ephemeral volume source in the pod
"--leader-elect=false" , // KCM leader election calls os.Exit when it ends, so it is easier to just turn it off altogether
} )
defer kcm . TearDownFn ( )
// Build client config and superuser clientset
clientConfig := server . ClientConfig
superuserClient , _ := clientsetForToken ( tokenMaster , clientConfig )
if _ , err := superuserClient . CoreV1 ( ) . Namespaces ( ) . Create ( context . TODO ( ) , & corev1 . Namespace { ObjectMeta : metav1 . ObjectMeta { Name : "ns" } } , metav1 . CreateOptions { } ) ; err != nil {
t . Fatal ( err )
}
// RBAC permissions are required to test service account token requests
// using the node client because we cannot rely on the node authorizer since we have not
// configured the references needed for it to work. This test is focused on exercising
// the node admission logic, not the node authorizer logic. We want to know what happens
// in admission if the request was already authorized.
configureRBACForServiceAccountToken ( t , superuserClient )
_ , err = superuserClient . CoreV1 ( ) . PersistentVolumeClaims ( "ns" ) . Create ( context . TODO ( ) , & corev1 . PersistentVolumeClaim {
ObjectMeta : metav1 . ObjectMeta { Name : "mypvc" } ,
Spec : corev1 . PersistentVolumeClaimSpec {
AccessModes : [ ] corev1 . PersistentVolumeAccessMode { corev1 . ReadOnlyMany } ,
Resources : corev1 . VolumeResourceRequirements { Requests : corev1 . ResourceList { corev1 . ResourceStorage : resource . MustParse ( "1" ) } } ,
VolumeName : "mypv" ,
} ,
} , metav1 . CreateOptions { } )
checkNilError ( t , err )
_ , err = superuserClient . CoreV1 ( ) . PersistentVolumes ( ) . Create ( context . TODO ( ) , & corev1 . PersistentVolume {
ObjectMeta : metav1 . ObjectMeta { Name : "mypv" } ,
Spec : corev1 . PersistentVolumeSpec {
AccessModes : [ ] corev1 . PersistentVolumeAccessMode { corev1 . ReadOnlyMany } ,
Capacity : corev1 . ResourceList { corev1 . ResourceStorage : resource . MustParse ( "1" ) } ,
ClaimRef : & corev1 . ObjectReference { Namespace : "ns" , Name : "mypvc" } ,
PersistentVolumeSource : corev1 . PersistentVolumeSource { CSI : & corev1 . CSIPersistentVolumeSource { Driver : "com.example.csi.mydriver" , VolumeHandle : "handle" } } ,
} ,
} , metav1 . CreateOptions { } )
checkNilError ( t , err )
2025-02-05 15:06:04 -05:00
_ , err = superuserClient . CoreV1 ( ) . PersistentVolumeClaims ( "ns" ) . Create ( context . TODO ( ) , & corev1 . PersistentVolumeClaim {
ObjectMeta : metav1 . ObjectMeta { Name : "mypvc-azurefile" } ,
Spec : corev1 . PersistentVolumeClaimSpec {
AccessModes : [ ] corev1 . PersistentVolumeAccessMode { corev1 . ReadOnlyMany } ,
Resources : corev1 . VolumeResourceRequirements { Requests : corev1 . ResourceList { corev1 . ResourceStorage : resource . MustParse ( "1" ) } } ,
VolumeName : "mypv-azurefile" ,
} ,
} , metav1 . CreateOptions { } )
checkNilError ( t , err )
_ , err = superuserClient . CoreV1 ( ) . PersistentVolumes ( ) . Create ( context . TODO ( ) , & corev1 . PersistentVolume {
ObjectMeta : metav1 . ObjectMeta { Name : "mypv-azurefile" } ,
Spec : corev1 . PersistentVolumeSpec {
AccessModes : [ ] corev1 . PersistentVolumeAccessMode { corev1 . ReadOnlyMany } ,
Capacity : corev1 . ResourceList { corev1 . ResourceStorage : resource . MustParse ( "1" ) } ,
ClaimRef : & corev1 . ObjectReference { Namespace : "ns" , Name : "mypvc-azurefile" } ,
PersistentVolumeSource : corev1 . PersistentVolumeSource { AzureFile : & corev1 . AzureFilePersistentVolumeSource { ShareName : "share" , SecretName : "secret" } } ,
} ,
} , metav1 . CreateOptions { } )
checkNilError ( t , err )
2024-10-15 02:30:51 -04:00
node1Client , _ := clientsetForToken ( tokenNode1 , clientConfig )
createNode ( t , node1Client , "node1" )
2025-02-27 14:52:51 -05:00
createServiceAccount ( t , superuserClient , "ns" , "default" )
2024-10-15 02:30:51 -04:00
t . Run ( "projected volume source with empty audience works" , func ( t * testing . T ) {
projectedVolumeSourceEmptyAudience := & corev1 . ProjectedVolumeSource { Sources : [ ] corev1 . VolumeProjection { { ServiceAccountToken : & corev1 . ServiceAccountTokenProjection { Audience : "" , Path : "path" } } } }
pod := createPod ( t , superuserClient , [ ] corev1 . Volume { { Name : "foo" , VolumeSource : corev1 . VolumeSource { Projected : projectedVolumeSourceEmptyAudience } } } )
2025-02-27 14:52:51 -05:00
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "" ) ) )
2024-10-15 02:30:51 -04:00
deletePod ( t , superuserClient , "pod1" )
} )
t . Run ( "projected volume source with non-empty audience works" , func ( t * testing . T ) {
projectedVolumeSource := & corev1 . ProjectedVolumeSource { Sources : [ ] corev1 . VolumeProjection { { ServiceAccountToken : & corev1 . ServiceAccountTokenProjection { Audience : "projected-audience" , Path : "path" } } } }
pod := createPod ( t , superuserClient , [ ] corev1 . Volume { { Name : "foo" , VolumeSource : corev1 . VolumeSource { Projected : projectedVolumeSource } } } )
2025-02-27 14:52:51 -05:00
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "projected-audience" ) ) )
2024-10-15 02:30:51 -04:00
deletePod ( t , superuserClient , "pod1" )
} )
t . Run ( "pod --> csi --> driver --> tokenrequest with audience forbidden - CSI driver not found" , func ( t * testing . T ) {
csiDriverVolumeSource := & corev1 . CSIVolumeSource { Driver : "com.example.csi.mydriver" }
pod := createPod ( t , superuserClient , [ ] corev1 . Volume { { Name : "foo" , VolumeSource : corev1 . VolumeSource { CSI : csiDriverVolumeSource } } } )
2025-02-27 14:52:51 -05:00
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "csidrivernotfound-audience" ) ) , ` error validating audience "csidrivernotfound-audience": csidriver.storage.k8s.io "com.example.csi.mydriver" not found ` )
2024-10-15 02:30:51 -04:00
deletePod ( t , superuserClient , "pod1" )
} )
t . Run ( "pod --> csi --> driver --> tokenrequest with audience works" , func ( t * testing . T ) {
2025-02-05 15:06:04 -05:00
createCSIDriver ( t , superuserClient , "csidriver-audience" , "com.example.csi.mydriver" )
2024-10-15 02:30:51 -04:00
csiDriverVolumeSource := & corev1 . CSIVolumeSource { Driver : "com.example.csi.mydriver" }
pod := createPod ( t , superuserClient , [ ] corev1 . Volume { { Name : "foo" , VolumeSource : corev1 . VolumeSource { CSI : csiDriverVolumeSource } } } )
2025-02-27 14:52:51 -05:00
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "csidriver-audience" ) ) )
2024-10-15 02:30:51 -04:00
deletePod ( t , superuserClient , "pod1" )
2025-02-05 15:06:04 -05:00
deleteCSIDriver ( t , superuserClient , "com.example.csi.mydriver" )
2024-10-15 02:30:51 -04:00
} )
t . Run ( "pod --> pvc --> pv --> csi --> driver --> tokenrequest with audience forbidden - CSI driver not found" , func ( t * testing . T ) {
persistentVolumeClaimVolumeSource := & corev1 . PersistentVolumeClaimVolumeSource { ClaimName : "mypvc" }
pod := createPod ( t , superuserClient , [ ] corev1 . Volume { { Name : "foo" , VolumeSource : corev1 . VolumeSource { PersistentVolumeClaim : persistentVolumeClaimVolumeSource } } } )
2025-02-27 14:52:51 -05:00
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "pvc-csidrivernotfound-audience" ) ) , ` error validating audience "pvc-csidrivernotfound-audience": csidriver.storage.k8s.io "com.example.csi.mydriver" not found ` )
2024-10-15 02:30:51 -04:00
deletePod ( t , superuserClient , "pod1" )
} )
t . Run ( "pod --> pvc --> pv --> csi --> driver --> tokenrequest with audience forbidden - pvc not found" , func ( t * testing . T ) {
2025-02-05 15:06:04 -05:00
createCSIDriver ( t , superuserClient , "pvcnotfound-audience" , "com.example.csi.mydriver" )
2024-10-15 02:30:51 -04:00
persistentVolumeClaimVolumeSource := & corev1 . PersistentVolumeClaimVolumeSource { ClaimName : "mypvc1" }
pod := createPod ( t , superuserClient , [ ] corev1 . Volume { { Name : "foo" , VolumeSource : corev1 . VolumeSource { PersistentVolumeClaim : persistentVolumeClaimVolumeSource } } } )
2025-02-27 14:52:51 -05:00
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "pvcnotfound-audience" ) ) , ` error validating audience "pvcnotfound-audience": persistentvolumeclaim "mypvc1" not found ` )
2024-10-15 02:30:51 -04:00
deletePod ( t , superuserClient , "pod1" )
2025-02-05 15:06:04 -05:00
deleteCSIDriver ( t , superuserClient , "com.example.csi.mydriver" )
2024-10-15 02:30:51 -04:00
} )
t . Run ( "pod --> pvc --> pv --> csi --> driver --> tokenrequest with audience works" , func ( t * testing . T ) {
2025-02-05 15:06:04 -05:00
createCSIDriver ( t , superuserClient , "pvccsidriver-audience" , "com.example.csi.mydriver" )
2024-10-15 02:30:51 -04:00
persistentVolumeClaimVolumeSource := & corev1 . PersistentVolumeClaimVolumeSource { ClaimName : "mypvc" }
pod := createPod ( t , superuserClient , [ ] corev1 . Volume { { Name : "foo" , VolumeSource : corev1 . VolumeSource { PersistentVolumeClaim : persistentVolumeClaimVolumeSource } } } )
2025-02-27 14:52:51 -05:00
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "pvccsidriver-audience" ) ) )
2024-10-15 02:30:51 -04:00
deletePod ( t , superuserClient , "pod1" )
2025-02-05 15:06:04 -05:00
deleteCSIDriver ( t , superuserClient , "com.example.csi.mydriver" )
2024-10-15 02:30:51 -04:00
} )
t . Run ( "pod --> ephemeral --> pvc --> pv --> csi --> driver --> tokenrequest with audience forbidden - CSI driver not found" , func ( t * testing . T ) {
ephemeralVolumeSource := & corev1 . EphemeralVolumeSource { VolumeClaimTemplate : & corev1 . PersistentVolumeClaimTemplate {
Spec : corev1 . PersistentVolumeClaimSpec {
AccessModes : [ ] corev1 . PersistentVolumeAccessMode { corev1 . ReadOnlyMany } ,
Resources : corev1 . VolumeResourceRequirements { Requests : corev1 . ResourceList { corev1 . ResourceStorage : resource . MustParse ( "1" ) } } ,
VolumeName : "mypv" ,
} } }
pod := createPod ( t , superuserClient , [ ] corev1 . Volume { { Name : "foo" , VolumeSource : corev1 . VolumeSource { Ephemeral : ephemeralVolumeSource } } } )
2025-02-27 14:52:51 -05:00
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "ephemeral-csidrivernotfound-audience" ) ) , ` error validating audience "ephemeral-csidrivernotfound-audience": csidriver.storage.k8s.io "com.example.csi.mydriver" not found ` )
2024-10-15 02:30:51 -04:00
deletePod ( t , superuserClient , "pod1" )
} )
t . Run ( "pod --> ephemeral --> pvc --> pv --> csi --> driver --> tokenrequest with audience works" , func ( t * testing . T ) {
2025-02-05 15:06:04 -05:00
createCSIDriver ( t , superuserClient , "ephemeralcsidriver-audience" , "com.example.csi.mydriver" )
2024-10-15 02:30:51 -04:00
ephemeralVolumeSource := & corev1 . EphemeralVolumeSource { VolumeClaimTemplate : & corev1 . PersistentVolumeClaimTemplate {
Spec : corev1 . PersistentVolumeClaimSpec {
AccessModes : [ ] corev1 . PersistentVolumeAccessMode { corev1 . ReadOnlyMany } ,
Resources : corev1 . VolumeResourceRequirements { Requests : corev1 . ResourceList { corev1 . ResourceStorage : resource . MustParse ( "1" ) } } ,
VolumeName : "mypv" ,
} } }
pod := createPod ( t , superuserClient , [ ] corev1 . Volume { { Name : "foo" , VolumeSource : corev1 . VolumeSource { Ephemeral : ephemeralVolumeSource } } } )
2025-02-27 14:52:51 -05:00
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "ephemeralcsidriver-audience" ) ) )
2024-10-15 02:30:51 -04:00
deletePod ( t , superuserClient , "pod1" )
2025-02-05 15:06:04 -05:00
deleteCSIDriver ( t , superuserClient , "com.example.csi.mydriver" )
2024-10-15 02:30:51 -04:00
} )
t . Run ( "csidriver exists but tokenrequest audience not found should be forbidden" , func ( t * testing . T ) {
2025-02-05 15:06:04 -05:00
createCSIDriver ( t , superuserClient , "csidriver-audience" , "com.example.csi.mydriver" )
2024-10-15 02:30:51 -04:00
pod := createPod ( t , superuserClient , nil )
2025-02-27 14:52:51 -05:00
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "csidriver-audience-not-found" ) ) , ` audience "csidriver-audience-not-found" not found in pod spec volume ` )
2024-10-15 02:30:51 -04:00
deletePod ( t , superuserClient , "pod1" )
2025-02-05 15:06:04 -05:00
deleteCSIDriver ( t , superuserClient , "com.example.csi.mydriver" )
2024-10-15 02:30:51 -04:00
} )
t . Run ( "pvc and csidriver exists but tokenrequest audience not found should be forbidden" , func ( t * testing . T ) {
2025-02-05 15:06:04 -05:00
createCSIDriver ( t , superuserClient , "csidriver-audience" , "com.example.csi.mydriver" )
2024-10-15 02:30:51 -04:00
persistentVolumeClaimVolumeSource := & corev1 . PersistentVolumeClaimVolumeSource { ClaimName : "mypvc" }
pod := createPod ( t , superuserClient , [ ] corev1 . Volume { { Name : "foo" , VolumeSource : corev1 . VolumeSource { PersistentVolumeClaim : persistentVolumeClaimVolumeSource } } } )
2025-02-27 14:52:51 -05:00
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "csidriver-audience-not-found" ) ) , ` audience "csidriver-audience-not-found" not found in pod spec volume ` )
2024-10-15 02:30:51 -04:00
deletePod ( t , superuserClient , "pod1" )
2025-02-05 15:06:04 -05:00
deleteCSIDriver ( t , superuserClient , "com.example.csi.mydriver" )
2024-10-15 02:30:51 -04:00
} )
t . Run ( "ephemeral volume source with audience not found should be forbidden" , func ( t * testing . T ) {
2025-02-05 15:06:04 -05:00
createCSIDriver ( t , superuserClient , "csidriver-audience" , "com.example.csi.mydriver" )
2024-10-15 02:30:51 -04:00
ephemeralVolumeSource := & corev1 . EphemeralVolumeSource { VolumeClaimTemplate : & corev1 . PersistentVolumeClaimTemplate {
Spec : corev1 . PersistentVolumeClaimSpec {
AccessModes : [ ] corev1 . PersistentVolumeAccessMode { corev1 . ReadOnlyMany } ,
Resources : corev1 . VolumeResourceRequirements { Requests : corev1 . ResourceList { corev1 . ResourceStorage : resource . MustParse ( "1" ) } } ,
VolumeName : "mypv" ,
} } }
pod := createPod ( t , superuserClient , [ ] corev1 . Volume { { Name : "foo" , VolumeSource : corev1 . VolumeSource { Ephemeral : ephemeralVolumeSource } } } )
2025-02-27 14:52:51 -05:00
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "csidriver-audience-not-found" ) ) , ` audience "csidriver-audience-not-found" not found in pod spec volume ` )
2024-10-15 02:30:51 -04:00
deletePod ( t , superuserClient , "pod1" )
2025-02-05 15:06:04 -05:00
deleteCSIDriver ( t , superuserClient , "com.example.csi.mydriver" )
} )
t . Run ( "intree pv to csi migration, pod --> csi --> driver --> tokenrequest with audience works" , func ( t * testing . T ) {
createCSIDriver ( t , superuserClient , "csidriver-audience" , "file.csi.azure.com" )
pod := createPod ( t , superuserClient , [ ] corev1 . Volume { { Name : "foo" , VolumeSource : corev1 . VolumeSource { PersistentVolumeClaim : & corev1 . PersistentVolumeClaimVolumeSource { ClaimName : "mypvc-azurefile" } } } } )
2025-02-27 14:52:51 -05:00
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "csidriver-audience" ) ) )
2025-02-05 15:06:04 -05:00
deletePod ( t , superuserClient , "pod1" )
deleteCSIDriver ( t , superuserClient , "file.csi.azure.com" )
} )
t . Run ( "intree inline volume to csi migration, pod --> csi --> driver --> tokenrequest with audience works" , func ( t * testing . T ) {
createCSIDriver ( t , superuserClient , "csidriver-audience" , "file.csi.azure.com" )
pod := createPod ( t , superuserClient , [ ] corev1 . Volume { { Name : "foo" , VolumeSource : corev1 . VolumeSource { AzureFile : & corev1 . AzureFileVolumeSource { ShareName : "default" , SecretName : "mypvsecret" } } } } )
2025-02-27 14:52:51 -05:00
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "csidriver-audience" ) ) )
2025-02-05 15:06:04 -05:00
deletePod ( t , superuserClient , "pod1" )
deleteCSIDriver ( t , superuserClient , "file.csi.azure.com" )
2024-10-15 02:30:51 -04:00
} )
t . Run ( "token request with multiple audiences should be forbidden" , func ( t * testing . T ) {
pod := createPod ( t , superuserClient , nil )
2025-02-27 14:52:51 -05:00
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" , "audience2" ) ) , "node may only request 0 or 1 audiences" )
deletePod ( t , superuserClient , "pod1" )
} )
t . Run ( "clusterrole and clusterrolebinding allowing node1 to request audience1 should work" , func ( t * testing . T ) {
cr := & rbacv1 . ClusterRole {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-node1" } ,
Rules : [ ] rbacv1 . PolicyRule { {
APIGroups : [ ] string { "" } ,
Resources : [ ] string { "audience1" } ,
Verbs : [ ] string { "request-serviceaccounts-token-audience" } ,
ResourceNames : [ ] string { "some-random-name" } ,
} } ,
}
crb := & rbacv1 . ClusterRoleBinding {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-node1" } ,
Subjects : [ ] rbacv1 . Subject { { Kind : "User" , Name : "system:node:node1" } } ,
RoleRef : rbacv1 . RoleRef { APIGroup : rbacv1 . GroupName , Kind : "ClusterRole" , Name : "audience-access-for-node1" } ,
}
createServiceAccount ( t , superuserClient , "ns" , "some-random-name" )
pod := createPod ( t , superuserClient , nil , podWithServiceAccountName ( "some-random-name" ) )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) , tokenRequestWithName ( "some-random-name" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience2" ) , tokenRequestWithName ( "some-random-name" ) ) , ` audience "audience2" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
createRBACClusterRole ( t , cr , superuserClient )
createRBACClusterRoleBinding ( t , crb , superuserClient )
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) , tokenRequestWithName ( "some-random-name" ) ) )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience2" ) , tokenRequestWithName ( "some-random-name" ) ) , ` audience "audience2" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
deleteRBACClusterRole ( t , cr , superuserClient )
deleteRBACClusterRoleBinding ( t , crb , superuserClient )
// After the delete use a expectedForbiddenMessage to wait for the RBAC authorizer to catch up.
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) , tokenRequestWithName ( "some-random-name" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
deletePod ( t , superuserClient , "pod1" )
deleteServiceAccount ( t , superuserClient , "ns" , "some-random-name" )
} )
t . Run ( "clusterrole and rolebinding in a namespace allowing node1 to request audience1 should work" , func ( t * testing . T ) {
cr := & rbacv1 . ClusterRole {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-node1" } ,
Rules : [ ] rbacv1 . PolicyRule { {
APIGroups : [ ] string { "" } ,
Resources : [ ] string { "audience1" } ,
Verbs : [ ] string { "request-serviceaccounts-token-audience" } ,
ResourceNames : [ ] string { "default" } ,
} } ,
}
rb := & rbacv1 . RoleBinding {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-node1" , Namespace : "ns" } ,
Subjects : [ ] rbacv1 . Subject { { Kind : "User" , Name : "system:node:node1" } } ,
RoleRef : rbacv1 . RoleRef { APIGroup : rbacv1 . GroupName , Kind : "ClusterRole" , Name : "audience-access-for-node1" } ,
}
pod := createPod ( t , superuserClient , nil )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience2" ) ) , ` audience "audience2" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
createRBACClusterRole ( t , cr , superuserClient )
createRBACRoleBinding ( t , rb , superuserClient )
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience2" ) ) , ` audience "audience2" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
deleteRBACClusterRole ( t , cr , superuserClient )
deleteRBACRoleBinding ( t , rb , superuserClient )
// After the delete use a expectedForbiddenMessage to wait for the RBAC authorizer to catch up.
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
deletePod ( t , superuserClient , "pod1" )
} )
t . Run ( "role and rolebinding in a namespace allowing node1 to request audience1 should work" , func ( t * testing . T ) {
role := & rbacv1 . Role {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-node1" , Namespace : "ns" } ,
Rules : [ ] rbacv1 . PolicyRule { {
APIGroups : [ ] string { "" } ,
Resources : [ ] string { "audience1" } ,
Verbs : [ ] string { "request-serviceaccounts-token-audience" } ,
ResourceNames : [ ] string { "default" } ,
} } ,
}
rb := & rbacv1 . RoleBinding {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-node1" , Namespace : "ns" } ,
Subjects : [ ] rbacv1 . Subject { { Kind : "User" , Name : "system:node:node1" } } ,
RoleRef : rbacv1 . RoleRef { APIGroup : rbacv1 . GroupName , Kind : "Role" , Name : "audience-access-for-node1" } ,
}
pod := createPod ( t , superuserClient , nil )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience2" ) ) , ` audience "audience2" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
createRBACRole ( t , role , superuserClient )
createRBACRoleBinding ( t , rb , superuserClient )
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience2" ) ) , ` audience "audience2" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
deleteRBACRole ( t , role , superuserClient )
deleteRBACRoleBinding ( t , rb , superuserClient )
// After the delete use a expectedForbiddenMessage to wait for the RBAC authorizer to catch up.
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
deletePod ( t , superuserClient , "pod1" )
} )
t . Run ( "clusterrole and rolebinding in a namespace allowing node1 to request audience1, wildcard for service account names" , func ( t * testing . T ) {
role := & rbacv1 . Role {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-node1" , Namespace : "ns" } ,
Rules : [ ] rbacv1 . PolicyRule { {
APIGroups : [ ] string { "" } ,
Resources : [ ] string { "audience1" } ,
Verbs : [ ] string { "request-serviceaccounts-token-audience" } ,
ResourceNames : [ ] string { } , // resource name empty means all service accounts
} } ,
}
rb := & rbacv1 . RoleBinding {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-node1" , Namespace : "ns" } ,
Subjects : [ ] rbacv1 . Subject { { Kind : "User" , Name : "system:node:node1" } } ,
RoleRef : rbacv1 . RoleRef { APIGroup : rbacv1 . GroupName , Kind : "Role" , Name : "audience-access-for-node1" } ,
}
createServiceAccount ( t , superuserClient , "ns" , "custom-sa" )
pod := createPod ( t , superuserClient , nil , podWithServiceAccountName ( "custom-sa" ) )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) , tokenRequestWithName ( "custom-sa" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience2" ) , tokenRequestWithName ( "custom-sa" ) ) , ` audience "audience2" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
createRBACRole ( t , role , superuserClient )
createRBACRoleBinding ( t , rb , superuserClient )
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) , tokenRequestWithName ( "custom-sa" ) ) )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience2" ) , tokenRequestWithName ( "custom-sa" ) ) , ` audience "audience2" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
deleteRBACRole ( t , role , superuserClient )
deleteRBACRoleBinding ( t , rb , superuserClient )
// After the delete use a expectedForbiddenMessage to wait for the RBAC authorizer to catch up.
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) , tokenRequestWithName ( "custom-sa" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
deletePod ( t , superuserClient , "pod1" )
deleteServiceAccount ( t , superuserClient , "ns" , "custom-sa" )
} )
t . Run ( "clusterrole and rolebinding in a namespace allowing nodes to request any audience, wildcard for audiences" , func ( t * testing . T ) {
role := & rbacv1 . Role {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-nodes" , Namespace : "ns" } ,
Rules : [ ] rbacv1 . PolicyRule { {
APIGroups : [ ] string { "" } ,
Resources : [ ] string { "*" } , // wildcard for audiences
Verbs : [ ] string { "request-serviceaccounts-token-audience" } ,
ResourceNames : [ ] string { "default" } ,
} } ,
}
rb := & rbacv1 . RoleBinding {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-node1" , Namespace : "ns" } ,
Subjects : [ ] rbacv1 . Subject { { Kind : "Group" , Name : "system:nodes" } } ,
RoleRef : rbacv1 . RoleRef { APIGroup : rbacv1 . GroupName , Kind : "Role" , Name : "audience-access-for-nodes" } ,
}
pod := createPod ( t , superuserClient , nil )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience2" ) ) , ` audience "audience2" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
randomAudience := rand . String ( 10 )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( randomAudience ) ) , ` audience " ` + randomAudience + ` " not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
createRBACRole ( t , role , superuserClient )
createRBACRoleBinding ( t , rb , superuserClient )
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) )
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience2" ) ) )
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "" ) ) )
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( randomAudience ) ) )
deleteRBACRole ( t , role , superuserClient )
deleteRBACRoleBinding ( t , rb , superuserClient )
// After the delete use a expectedForbiddenMessage to wait for the RBAC authorizer to catch up.
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
deletePod ( t , superuserClient , "pod1" )
} )
t . Run ( "clusterrole and rolebinding in a namespace allowing nodes to request any audience, any service account, wildcard for audiences" , func ( t * testing . T ) {
role := & rbacv1 . Role {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-nodes" , Namespace : "ns" } ,
Rules : [ ] rbacv1 . PolicyRule { {
APIGroups : [ ] string { "" } ,
Resources : [ ] string { "*" } , // wildcard for audiences
Verbs : [ ] string { "request-serviceaccounts-token-audience" } ,
ResourceNames : [ ] string { } , // empty resource name means all service accounts
} } ,
}
rb := & rbacv1 . RoleBinding {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-node1" , Namespace : "ns" } ,
Subjects : [ ] rbacv1 . Subject { { Kind : "Group" , Name : "system:nodes" } } ,
RoleRef : rbacv1 . RoleRef { APIGroup : rbacv1 . GroupName , Kind : "Role" , Name : "audience-access-for-nodes" } ,
}
pod := createPod ( t , superuserClient , nil )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience2" ) ) , ` audience "audience2" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
createRBACRole ( t , role , superuserClient )
createRBACRoleBinding ( t , rb , superuserClient )
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) )
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience2" ) ) )
deleteRBACRole ( t , role , superuserClient )
deleteRBACRoleBinding ( t , rb , superuserClient )
// After the delete use a expectedForbiddenMessage to wait for the RBAC authorizer to catch up.
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
deletePod ( t , superuserClient , "pod1" )
} )
t . Run ( "api server audience configured with rbac role" , func ( t * testing . T ) {
role := & rbacv1 . Role {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-nodes" , Namespace : "ns" } ,
Rules : [ ] rbacv1 . PolicyRule { {
APIGroups : [ ] string { "" } ,
Resources : [ ] string { "" } ,
Verbs : [ ] string { "request-serviceaccounts-token-audience" } ,
ResourceNames : [ ] string { } , // empty resource name means all service accounts
} } ,
}
rb := & rbacv1 . RoleBinding {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-node1" , Namespace : "ns" } ,
Subjects : [ ] rbacv1 . Subject { { Kind : "Group" , Name : "system:nodes" } } ,
RoleRef : rbacv1 . RoleRef { APIGroup : rbacv1 . GroupName , Kind : "Role" , Name : "audience-access-for-nodes" } ,
}
pod := createPod ( t , superuserClient , nil , podWithAutoMountServiceAccountToken ( false ) )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "" ) ) , ` audience "" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
createRBACRole ( t , role , superuserClient )
createRBACRoleBinding ( t , rb , superuserClient )
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "" ) ) )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
deleteRBACRole ( t , role , superuserClient )
deleteRBACRoleBinding ( t , rb , superuserClient )
// After the delete use a expectedForbiddenMessage to wait for the RBAC authorizer to catch up.
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "" ) ) , ` audience "" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
deletePod ( t , superuserClient , "pod1" )
} )
t . Run ( "audience with : and '/' configured with rbac role" , func ( t * testing . T ) {
role := & rbacv1 . Role {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-nodes" , Namespace : "ns" } ,
Rules : [ ] rbacv1 . PolicyRule { {
APIGroups : [ ] string { "" } ,
Resources : [ ] string { "myaud://audience1/audience2.com" } ,
Verbs : [ ] string { "request-serviceaccounts-token-audience" } ,
ResourceNames : [ ] string { } , // empty resource name means all service accounts
} } ,
}
rb := & rbacv1 . RoleBinding {
ObjectMeta : metav1 . ObjectMeta { Name : "audience-access-for-node1" , Namespace : "ns" } ,
Subjects : [ ] rbacv1 . Subject { { Kind : "Group" , Name : "system:nodes" } } ,
RoleRef : rbacv1 . RoleRef { APIGroup : rbacv1 . GroupName , Kind : "Role" , Name : "audience-access-for-nodes" } ,
}
pod := createPod ( t , superuserClient , nil , podWithAutoMountServiceAccountToken ( false ) )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "myaud://audience1/audience2.com" ) ) , ` audience "myaud://audience1/audience2.com" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
createRBACRole ( t , role , superuserClient )
createRBACRoleBinding ( t , rb , superuserClient )
expectAllowed ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "myaud://audience1/audience2.com" ) ) )
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "audience1" ) ) , ` audience "audience1" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
deleteRBACRole ( t , role , superuserClient )
deleteRBACRoleBinding ( t , rb , superuserClient )
// After the delete use a expectedForbiddenMessage to wait for the RBAC authorizer to catch up.
expectedForbiddenMessage ( t , createTokenRequest ( node1Client , pod . UID , tokenRequestWithAudiences ( "myaud://audience1/audience2.com" ) ) , ` audience "myaud://audience1/audience2.com" not found in pod spec volume, system:node:node1 is not authorized to request tokens for this audience ` )
2024-10-15 02:30:51 -04:00
deletePod ( t , superuserClient , "pod1" )
} )
}
func createKubeConfigFileForRestConfig ( t * testing . T , restConfig * rest . Config ) string {
t . Helper ( )
clientConfig := kubeconfig . CreateKubeConfig ( restConfig )
kubeConfigFile := filepath . Join ( t . TempDir ( ) , "kubeconfig.yaml" )
if err := clientcmd . WriteToFile ( * clientConfig , kubeConfigFile ) ; err != nil {
t . Fatal ( err )
}
return kubeConfigFile
}
2025-02-27 14:52:51 -05:00
type podOptions struct {
serviceAccountName string
autoMountServiceAccountToken * bool
}
type podOption func ( * podOptions )
func podWithServiceAccountName ( name string ) podOption {
return func ( opts * podOptions ) {
opts . serviceAccountName = name
}
}
func podWithAutoMountServiceAccountToken ( autoMount bool ) podOption {
return func ( opts * podOptions ) {
opts . autoMountServiceAccountToken = ptr . To ( autoMount )
}
}
func createPod ( t * testing . T , client clientset . Interface , volumes [ ] corev1 . Volume , opts ... podOption ) * corev1 . Pod {
2024-10-15 02:30:51 -04:00
t . Helper ( )
2025-02-27 14:52:51 -05:00
options := & podOptions {
serviceAccountName : "default" ,
autoMountServiceAccountToken : ptr . To ( true ) ,
}
for _ , opt := range opts {
opt ( options )
}
2024-10-15 02:30:51 -04:00
pod , err := client . CoreV1 ( ) . Pods ( "ns" ) . Create ( context . TODO ( ) , & corev1 . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "pod1" ,
} ,
Spec : corev1 . PodSpec {
2025-02-27 14:52:51 -05:00
NodeName : "node1" ,
Containers : [ ] corev1 . Container { { Name : "image" , Image : "busybox" } } ,
ServiceAccountName : options . serviceAccountName ,
Volumes : volumes ,
AutomountServiceAccountToken : options . autoMountServiceAccountToken ,
2024-10-15 02:30:51 -04:00
} ,
} , metav1 . CreateOptions { } )
checkNilError ( t , err )
return pod
}
func deletePod ( t * testing . T , client clientset . Interface , podName string ) {
t . Helper ( )
checkNilError ( t , client . CoreV1 ( ) . Pods ( "ns" ) . Delete ( context . TODO ( ) , podName , metav1 . DeleteOptions { GracePeriodSeconds : ptr . To [ int64 ] ( 0 ) } ) )
}
func createNode ( t * testing . T , client clientset . Interface , nodeName string ) {
t . Helper ( )
_ , err := client . CoreV1 ( ) . Nodes ( ) . Create ( context . TODO ( ) , & corev1 . Node { ObjectMeta : metav1 . ObjectMeta { Name : nodeName } } , metav1 . CreateOptions { } )
checkNilError ( t , err )
}
2025-02-27 14:52:51 -05:00
type tokenRequestOptions struct {
name string
audiences [ ] string
}
type tokenRequestOption func ( * tokenRequestOptions )
func tokenRequestWithName ( name string ) tokenRequestOption {
return func ( opts * tokenRequestOptions ) {
opts . name = name
}
}
func tokenRequestWithAudiences ( audiences ... string ) tokenRequestOption {
return func ( opts * tokenRequestOptions ) {
opts . audiences = audiences
}
}
func createTokenRequest ( client clientset . Interface , uid types . UID , opts ... tokenRequestOption ) func ( ) error {
options := & tokenRequestOptions {
name : "default" ,
audiences : [ ] string { } ,
}
for _ , opt := range opts {
opt ( options )
}
2024-10-15 02:30:51 -04:00
return func ( ) error {
2025-02-27 14:52:51 -05:00
_ , err := client . CoreV1 ( ) . ServiceAccounts ( "ns" ) . CreateToken ( context . TODO ( ) , options . name , & authenticationv1 . TokenRequest {
2024-10-15 02:30:51 -04:00
Spec : authenticationv1 . TokenRequestSpec {
BoundObjectRef : & authenticationv1 . BoundObjectReference {
Kind : "Pod" ,
Name : "pod1" ,
APIVersion : "v1" ,
UID : uid ,
} ,
2025-02-27 14:52:51 -05:00
Audiences : options . audiences ,
2024-10-15 02:30:51 -04:00
} ,
} , metav1 . CreateOptions { } )
return err
}
}
2025-02-05 15:06:04 -05:00
func createCSIDriver ( t * testing . T , client clientset . Interface , audience , driverName string ) {
2024-10-15 02:30:51 -04:00
t . Helper ( )
_ , err := client . StorageV1 ( ) . CSIDrivers ( ) . Create ( context . TODO ( ) , & storagev1 . CSIDriver {
2025-02-05 15:06:04 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : driverName } ,
2024-10-15 02:30:51 -04:00
Spec : storagev1 . CSIDriverSpec {
TokenRequests : [ ] storagev1 . TokenRequest { { Audience : audience } } ,
} ,
} , metav1 . CreateOptions { } )
checkNilError ( t , err )
}
2025-02-05 15:06:04 -05:00
func deleteCSIDriver ( t * testing . T , client clientset . Interface , driverName string ) {
2024-10-15 02:30:51 -04:00
t . Helper ( )
2025-02-05 15:06:04 -05:00
checkNilError ( t , client . StorageV1 ( ) . CSIDrivers ( ) . Delete ( context . TODO ( ) , driverName , metav1 . DeleteOptions { GracePeriodSeconds : ptr . To [ int64 ] ( 0 ) } ) )
2024-10-15 02:30:51 -04:00
}
2025-02-27 14:52:51 -05:00
func createServiceAccount ( t * testing . T , client clientset . Interface , namespace , name string ) {
2024-10-15 02:30:51 -04:00
t . Helper ( )
2025-02-27 14:52:51 -05:00
_ , err := client . CoreV1 ( ) . ServiceAccounts ( namespace ) . Create ( context . TODO ( ) , & corev1 . ServiceAccount {
ObjectMeta : metav1 . ObjectMeta { Name : name } ,
2024-10-15 02:30:51 -04:00
} , metav1 . CreateOptions { } )
checkNilError ( t , err )
}
2025-02-27 14:52:51 -05:00
func deleteServiceAccount ( t * testing . T , client clientset . Interface , namespace , name string ) {
t . Helper ( )
checkNilError ( t , client . CoreV1 ( ) . ServiceAccounts ( namespace ) . Delete ( context . TODO ( ) , name , metav1 . DeleteOptions { GracePeriodSeconds : ptr . To [ int64 ] ( 0 ) } ) )
}
2024-10-15 02:30:51 -04:00
func configureRBACForServiceAccountToken ( t * testing . T , client clientset . Interface ) {
t . Helper ( )
_ , err := client . RbacV1 ( ) . ClusterRoles ( ) . Update ( context . TODO ( ) , & rbacv1 . ClusterRole {
ObjectMeta : metav1 . ObjectMeta { Name : "system:node:node1" } ,
Rules : [ ] rbacv1 . PolicyRule { {
APIGroups : [ ] string { "" } ,
Resources : [ ] string { "serviceaccounts/token" } ,
Verbs : [ ] string { "create" } ,
} } ,
} , metav1 . UpdateOptions { } )
checkNilError ( t , err )
_ , err = client . RbacV1 ( ) . ClusterRoleBindings ( ) . Update ( context . TODO ( ) , & rbacv1 . ClusterRoleBinding {
ObjectMeta : metav1 . ObjectMeta { Name : "system:node" } ,
Subjects : [ ] rbacv1 . Subject { {
APIGroup : rbacv1 . GroupName ,
Kind : "Group" ,
Name : "system:nodes" ,
} } ,
RoleRef : rbacv1 . RoleRef {
APIGroup : rbacv1 . GroupName ,
Kind : "ClusterRole" ,
Name : "system:node" ,
} ,
} , metav1 . UpdateOptions { } )
checkNilError ( t , err )
}
2025-02-27 14:52:51 -05:00
func createRBACClusterRole ( t * testing . T , clusterrole * rbacv1 . ClusterRole , client clientset . Interface ) {
t . Helper ( )
_ , err := client . RbacV1 ( ) . ClusterRoles ( ) . Create ( context . TODO ( ) , clusterrole , metav1 . CreateOptions { } )
checkNilError ( t , err )
}
func createRBACClusterRoleBinding ( t * testing . T , clusterrolebinding * rbacv1 . ClusterRoleBinding , client clientset . Interface ) {
t . Helper ( )
_ , err := client . RbacV1 ( ) . ClusterRoleBindings ( ) . Create ( context . TODO ( ) , clusterrolebinding , metav1 . CreateOptions { } )
checkNilError ( t , err )
}
func createRBACRole ( t * testing . T , role * rbacv1 . Role , client clientset . Interface ) {
t . Helper ( )
_ , err := client . RbacV1 ( ) . Roles ( role . Namespace ) . Create ( context . TODO ( ) , role , metav1 . CreateOptions { } )
checkNilError ( t , err )
}
func createRBACRoleBinding ( t * testing . T , rolebinding * rbacv1 . RoleBinding , client clientset . Interface ) {
t . Helper ( )
_ , err := client . RbacV1 ( ) . RoleBindings ( rolebinding . Namespace ) . Create ( context . TODO ( ) , rolebinding , metav1 . CreateOptions { } )
checkNilError ( t , err )
}
func deleteRBACClusterRole ( t * testing . T , clusterrole * rbacv1 . ClusterRole , client clientset . Interface ) {
t . Helper ( )
checkNilError ( t , client . RbacV1 ( ) . ClusterRoles ( ) . Delete ( context . TODO ( ) , clusterrole . Name , metav1 . DeleteOptions { } ) )
}
func deleteRBACClusterRoleBinding ( t * testing . T , clusterrolebinding * rbacv1 . ClusterRoleBinding , client clientset . Interface ) {
t . Helper ( )
checkNilError ( t , client . RbacV1 ( ) . ClusterRoleBindings ( ) . Delete ( context . TODO ( ) , clusterrolebinding . Name , metav1 . DeleteOptions { } ) )
}
func deleteRBACRole ( t * testing . T , role * rbacv1 . Role , client clientset . Interface ) {
t . Helper ( )
checkNilError ( t , client . RbacV1 ( ) . Roles ( role . Namespace ) . Delete ( context . TODO ( ) , role . Name , metav1 . DeleteOptions { } ) )
}
func deleteRBACRoleBinding ( t * testing . T , rolebinding * rbacv1 . RoleBinding , client clientset . Interface ) {
t . Helper ( )
checkNilError ( t , client . RbacV1 ( ) . RoleBindings ( rolebinding . Namespace ) . Delete ( context . TODO ( ) , rolebinding . Name , metav1 . DeleteOptions { } ) )
}