Skip to content
This repository was archived by the owner on Apr 25, 2023. It is now read-only.

Commit 7946858

Browse files
authored
Merge pull request #1515 from mesosphere/join-secret-creation
fix: Use specific service account token secret for join
2 parents a402d2e + d878436 commit 7946858

File tree

2 files changed

+84
-34
lines changed

2 files changed

+84
-34
lines changed

pkg/kubefedctl/join.go

+78-34
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
kubeclient "k8s.io/client-go/kubernetes"
3838
"k8s.io/client-go/rest"
3939
"k8s.io/klog/v2"
40+
"k8s.io/utils/pointer"
4041
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
4142

4243
fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1"
@@ -93,15 +94,15 @@ type joinFederation struct {
9394
}
9495

9596
type joinFederationOptions struct {
96-
secretName string
97-
scope apiextv1.ResourceScope
98-
errorOnExisting bool
97+
hostClusterSecretName string
98+
scope apiextv1.ResourceScope
99+
errorOnExisting bool
99100
}
100101

101102
// Bind adds the join specific arguments to the flagset passed in as an
102103
// argument.
103104
func (o *joinFederationOptions) Bind(flags *pflag.FlagSet) {
104-
flags.StringVar(&o.secretName, "secret-name", "",
105+
flags.StringVar(&o.hostClusterSecretName, "secret-name", "",
105106
"Name of the secret where the cluster's credentials will be stored in the host cluster. This name should be a valid RFC 1035 label. If unspecified, defaults to a generated name containing the cluster name.")
106107
flags.BoolVar(&o.errorOnExisting, "error-on-existing", true,
107108
"Whether the join operation will throw an error if it encounters existing artifacts with the same name as those it's trying to create. If false, the join operation will update existing artifacts to match its own specification.")
@@ -160,7 +161,7 @@ func (j *joinFederation) Complete(args []string) error {
160161

161162
klog.V(2).Infof("Args and flags: name %s, host: %s, host-system-namespace: %s, kubeconfig: %s, cluster-context: %s, secret-name: %s, dry-run: %v",
162163
j.ClusterName, j.HostClusterContext, j.KubeFedNamespace, j.Kubeconfig, j.ClusterContext,
163-
j.secretName, j.DryRun)
164+
j.hostClusterSecretName, j.DryRun)
164165

165166
return nil
166167
}
@@ -197,7 +198,7 @@ func (j *joinFederation) Run(cmdOut io.Writer, config util.FedConfig) error {
197198
}
198199

199200
_, err = JoinCluster(hostConfig, clusterConfig, j.KubeFedNamespace,
200-
hostClusterName, j.ClusterName, j.secretName, j.joinFederationOptions.scope, j.DryRun, j.errorOnExisting)
201+
hostClusterName, j.ClusterName, j.hostClusterSecretName, j.joinFederationOptions.scope, j.DryRun, j.errorOnExisting)
201202

202203
return err
203204
}
@@ -206,18 +207,18 @@ func (j *joinFederation) Run(cmdOut io.Writer, config util.FedConfig) error {
206207
// KubeFed namespace in the joining cluster will be the same as in the
207208
// host cluster.
208209
func JoinCluster(hostConfig, clusterConfig *rest.Config, kubefedNamespace,
209-
hostClusterName, joiningClusterName, secretName string,
210+
hostClusterName, joiningClusterName, hostClusterSecretName string,
210211
scope apiextv1.ResourceScope, dryRun, errorOnExisting bool) (*fedv1b1.KubeFedCluster, error) {
211212
return joinClusterForNamespace(hostConfig, clusterConfig, kubefedNamespace,
212-
kubefedNamespace, hostClusterName, joiningClusterName, secretName,
213+
kubefedNamespace, hostClusterName, joiningClusterName, hostClusterSecretName,
213214
scope, dryRun, errorOnExisting)
214215
}
215216

216217
// joinClusterForNamespace registers a cluster with a KubeFed control
217218
// plane. The KubeFed namespace in the joining cluster is provided by
218219
// the joiningNamespace parameter.
219220
func joinClusterForNamespace(hostConfig, clusterConfig *rest.Config, kubefedNamespace,
220-
joiningNamespace, hostClusterName, joiningClusterName, secretName string,
221+
joiningNamespace, hostClusterName, joiningClusterName, hostClusterSecretName string,
221222
scope apiextv1.ResourceScope, dryRun, errorOnExisting bool) (*fedv1b1.KubeFedCluster, error) {
222223
start := time.Now()
223224

@@ -254,15 +255,16 @@ func joinClusterForNamespace(hostConfig, clusterConfig *rest.Config, kubefedName
254255
}
255256
klog.V(2).Infof("Created %s namespace in joining cluster", joiningNamespace)
256257

257-
saName, err := createAuthorizedServiceAccount(clusterClientset,
258+
joiningClusterSATokenSecretName, err := createAuthorizedServiceAccount(clusterClientset,
258259
joiningNamespace, joiningClusterName, hostClusterName,
259260
scope, dryRun, errorOnExisting)
260261
if err != nil {
261262
return nil, err
262263
}
263264

264265
secret, caBundle, err := populateSecretInHostCluster(clusterClientset, hostClientset,
265-
saName, kubefedNamespace, joiningNamespace, joiningClusterName, secretName, dryRun, errorOnExisting)
266+
joiningClusterSATokenSecretName, kubefedNamespace, joiningNamespace, joiningClusterName,
267+
hostClusterSecretName, dryRun, errorOnExisting)
266268
if err != nil {
267269
klog.V(2).Infof("Error creating secret in host cluster: %s due to: %v", hostClusterName, err)
268270
return nil, err
@@ -417,13 +419,13 @@ func createKubeFedNamespace(clusterClientset kubeclient.Interface, kubefedNamesp
417419
return fedNamespace, nil
418420
}
419421

420-
// createAuthorizedServiceAccount creates a service account and grants
421-
// the privileges required by the KubeFed control plane to manage
422+
// createAuthorizedServiceAccount creates a service account and service account token secret
423+
// and grants the privileges required by the KubeFed control plane to manage
422424
// resources in the joining cluster. The name of the created service
423425
// account is returned on success.
424426
func createAuthorizedServiceAccount(joiningClusterClientset kubeclient.Interface,
425427
namespace, joiningClusterName, hostClusterName string,
426-
scope apiextv1.ResourceScope, dryRun, errorOnExisting bool) (string, error) {
428+
scope apiextv1.ResourceScope, dryRun, errorOnExisting bool) (saTokenSecretName string, err error) {
427429
klog.V(2).Infof("Creating service account in joining cluster: %s", joiningClusterName)
428430

429431
saName, err := createServiceAccount(joiningClusterClientset, namespace,
@@ -436,6 +438,16 @@ func createAuthorizedServiceAccount(joiningClusterClientset kubeclient.Interface
436438

437439
klog.V(2).Infof("Created service account: %s in joining cluster: %s", saName, joiningClusterName)
438440

441+
saTokenSecretName, err = createServiceAccountTokenSecret(saName, joiningClusterClientset, namespace,
442+
joiningClusterName, hostClusterName, dryRun, errorOnExisting)
443+
if err != nil {
444+
klog.V(2).Infof("Error creating service account token secret: %s in joining cluster: %s due to: %v",
445+
saName, joiningClusterName, err)
446+
return "", err
447+
}
448+
449+
klog.V(2).Infof("Created service account token secret: %s in joining cluster: %s", saTokenSecretName, joiningClusterName)
450+
439451
if scope == apiextv1.NamespaceScoped {
440452
klog.V(2).Infof("Creating role and binding for service account: %s in joining cluster: %s", saName, joiningClusterName)
441453

@@ -474,7 +486,7 @@ func createAuthorizedServiceAccount(joiningClusterClientset kubeclient.Interface
474486
saName, joiningClusterName)
475487
}
476488

477-
return saName, nil
489+
return saTokenSecretName, nil
478490
}
479491

480492
// createServiceAccount creates a service account in the cluster associated
@@ -487,7 +499,11 @@ func createServiceAccount(clusterClientset kubeclient.Interface, namespace,
487499
ObjectMeta: metav1.ObjectMeta{
488500
Name: saName,
489501
Namespace: namespace,
502+
Annotations: map[string]string{
503+
"kubernetes.io/enforce-mountable-secrets": "true",
504+
},
490505
},
506+
AutomountServiceAccountToken: pointer.Bool(false),
491507
}
492508

493509
if dryRun {
@@ -510,6 +526,45 @@ func createServiceAccount(clusterClientset kubeclient.Interface, namespace,
510526
}
511527
}
512528

529+
// createServiceAccountTokenSecret creates a service account token secret in the cluster associated
530+
// with clusterClientset with credentials that will be used by the host cluster
531+
// to access its API server.
532+
func createServiceAccountTokenSecret(saName string, clusterClientset kubeclient.Interface, namespace,
533+
joiningClusterName, hostClusterName string, dryRun, errorOnExisting bool) (string, error) {
534+
saTokenSecretName := util.ClusterServiceAccountTokenSecretName(joiningClusterName, hostClusterName)
535+
saTokenSecret := &corev1.Secret{
536+
ObjectMeta: metav1.ObjectMeta{
537+
Name: saTokenSecretName,
538+
Namespace: namespace,
539+
Annotations: map[string]string{
540+
"kubernetes.io/service-account.name": saName,
541+
},
542+
},
543+
Type: corev1.SecretTypeServiceAccountToken,
544+
}
545+
546+
if dryRun {
547+
return saName, nil
548+
}
549+
550+
// Create a new service account.
551+
_, err := clusterClientset.CoreV1().Secrets(namespace).Create(
552+
context.Background(), saTokenSecret, metav1.CreateOptions{},
553+
)
554+
switch {
555+
case apierrors.IsAlreadyExists(err) && errorOnExisting:
556+
klog.V(2).Infof("Service account token secret %s/%s already exists in target cluster %s",
557+
namespace, saName, joiningClusterName)
558+
return "", err
559+
case err != nil && !apierrors.IsAlreadyExists(err):
560+
klog.V(2).Infof("Could not create service account token secret %s/%s in target cluster %s due to: %v",
561+
namespace, saName, joiningClusterName, err)
562+
return "", err
563+
default:
564+
return saTokenSecretName, nil
565+
}
566+
}
567+
513568
func bindingSubjects(saName, namespace string) []rbacv1.Subject {
514569
return []rbacv1.Subject{
515570
{
@@ -828,7 +883,7 @@ func createHealthCheckClusterRoleAndBinding(clientset kubeclient.Interface, saNa
828883
// hostClientset, putting it in a secret named secretName in the provided
829884
// namespace.
830885
func populateSecretInHostCluster(clusterClientset, hostClientset kubeclient.Interface,
831-
saName, hostNamespace, joiningNamespace, joiningClusterName, secretName string,
886+
saTokenSecretName, hostNamespace, joiningNamespace, joiningClusterName, secretName string,
832887
dryRun bool, errorOnExisting bool) (*corev1.Secret, []byte, error) {
833888
klog.V(2).Infof("Creating cluster credentials secret in host cluster")
834889

@@ -840,33 +895,22 @@ func populateSecretInHostCluster(clusterClientset, hostClientset kubeclient.Inte
840895

841896
// Get the secret from the joining cluster.
842897
var secret *corev1.Secret
898+
843899
err := wait.PollImmediate(1*time.Second, serviceAccountSecretTimeout, func() (bool, error) {
844-
sa, err := clusterClientset.CoreV1().ServiceAccounts(joiningNamespace).Get(
845-
context.Background(), saName, metav1.GetOptions{},
900+
joiningClusterSASecret, err := clusterClientset.CoreV1().Secrets(joiningNamespace).Get(
901+
context.Background(), saTokenSecretName, metav1.GetOptions{},
846902
)
847903
if err != nil {
848904
return false, nil
849905
}
850906

851-
for _, objReference := range sa.Secrets {
852-
saSecretName := objReference.Name
853-
var err error
854-
secret, err = clusterClientset.CoreV1().Secrets(joiningNamespace).Get(
855-
context.Background(), saSecretName, metav1.GetOptions{},
856-
)
857-
if err != nil {
858-
return false, nil
859-
}
860-
if secret.Type == corev1.SecretTypeServiceAccountToken {
861-
klog.V(2).Infof("Using secret named: %s", secret.Name)
862-
return true, nil
863-
}
864-
}
865-
return false, nil
907+
secret = joiningClusterSASecret
908+
909+
return true, nil
866910
})
867911

868912
if err != nil {
869-
klog.V(2).Infof("Could not get service account secret from joining cluster: %v", err)
913+
klog.V(2).Infof("Could not get service account token secret from joining cluster: %v", err)
870914
return nil, nil, err
871915
}
872916

pkg/kubefedctl/util/util.go

+6
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ func ClusterServiceAccountName(joiningClusterName, hostClusterName string) strin
107107
return fmt.Sprintf("%s-%s", joiningClusterName, hostClusterName)
108108
}
109109

110+
// ClusterServiceAccountTokenSecretName returns the name of a service account token secret whose
111+
// credentials are used by the host cluster to access the client cluster.
112+
func ClusterServiceAccountTokenSecretName(joiningClusterName, hostClusterName string) string {
113+
return fmt.Sprintf("%s-%s", joiningClusterName, hostClusterName)
114+
}
115+
110116
// RoleName returns the name of a Role or ClusterRole and its
111117
// associated RoleBinding or ClusterRoleBinding that are used to allow
112118
// the service account to access necessary resources on the cluster.

0 commit comments

Comments
 (0)