Skip to content

Commit

Permalink
create a bootstrap token only if doesn't exist (#213)
Browse files Browse the repository at this point in the history
* create a bootstrap token only if doesn't exists
  • Loading branch information
p0lyn0mial authored May 25, 2018
1 parent 7d6a02f commit e7c65fd
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 33 deletions.
2 changes: 1 addition & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 7 additions & 5 deletions cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ type controllerRunOptions struct {
// nodeLister holds a lister that knows how to list Nodes from a cache
nodeLister listerscorev1.NodeLister

// configMapLister holds a lister that knows how to list ConfigMaps from a cache
configMapLister listerscorev1.ConfigMapLister
// secreSystemNstLister knows hot to list Secrects that are inside kube-system namespace from a cache
secretSystemNsLister listerscorev1.SecretLister

// machineInformer holds a shared informer for Machines
machineInformer cache.SharedIndexInformer
Expand Down Expand Up @@ -176,6 +176,7 @@ func main() {
machineInformerFactory := machineinformers.NewSharedInformerFactory(machineClient, time.Second*30)
kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, time.Second*30)
kubePublicKubeInformerFactory := kubeinformers.NewFilteredSharedInformerFactory(kubeClient, time.Second*30, metav1.NamespacePublic, nil)
kubeSystemInformerFactory := kubeinformers.NewFilteredSharedInformerFactory(kubeClient, time.Second*30, metav1.NamespaceSystem, nil)
defaultKubeInformerFactory := kubeinformers.NewFilteredSharedInformerFactory(kubeClient, time.Second*30, metav1.NamespaceDefault, nil)

kubeconfigProvider := clusterinfo.New(cfg, kubePublicKubeInformerFactory.Core().V1().ConfigMaps().Lister(), defaultKubeInformerFactory.Core().V1().Endpoints().Lister())
Expand All @@ -188,7 +189,7 @@ func main() {
leaderElectionClient: leaderElectionClient,
nodeInformer: kubeInformerFactory.Core().V1().Nodes().Informer(),
nodeLister: kubeInformerFactory.Core().V1().Nodes().Lister(),
configMapLister: kubePublicKubeInformerFactory.Core().V1().ConfigMaps().Lister(),
secretSystemNsLister: kubeSystemInformerFactory.Core().V1().Secrets().Lister(),
machineInformer: machineInformerFactory.Machine().V1alpha1().Machines().Informer(),
machineLister: machineInformerFactory.Machine().V1alpha1().Machines().Lister(),
kubeconfigProvider: kubeconfigProvider,
Expand All @@ -199,8 +200,9 @@ func main() {
kubePublicKubeInformerFactory.Start(stopCh)
defaultKubeInformerFactory.Start(stopCh)
machineInformerFactory.Start(stopCh)
kubeSystemInformerFactory.Start(stopCh)

for _, syncsMap := range []map[reflect.Type]bool{kubeInformerFactory.WaitForCacheSync(stopCh), kubePublicKubeInformerFactory.WaitForCacheSync(stopCh), machineInformerFactory.WaitForCacheSync(stopCh), defaultKubeInformerFactory.WaitForCacheSync(stopCh)} {
for _, syncsMap := range []map[reflect.Type]bool{kubeInformerFactory.WaitForCacheSync(stopCh), kubePublicKubeInformerFactory.WaitForCacheSync(stopCh), machineInformerFactory.WaitForCacheSync(stopCh), defaultKubeInformerFactory.WaitForCacheSync(stopCh), kubeSystemInformerFactory.WaitForCacheSync(stopCh)} {
for key, synced := range syncsMap {
if !synced {
glog.Fatalf("unable to sync %s", key)
Expand Down Expand Up @@ -279,9 +281,9 @@ func startControllerViaLeaderElection(runOptions controllerRunOptions) error {
runOptions.machineClient,
runOptions.nodeInformer,
runOptions.nodeLister,
runOptions.configMapLister,
runOptions.machineInformer,
runOptions.machineLister,
runOptions.secretSystemNsLister,
runOptions.clusterDNSIPs,
controller.MetricsCollection{
Machines: runOptions.metrics.Machines,
Expand Down
10 changes: 9 additions & 1 deletion examples/machine-controller.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,18 @@ rules:
- apiGroups:
- ""
resources:
- secrets
- events
verbs:
- create
- apiGroups:
- ""
resources:
- secrets
verbs:
- create
- update
- list
- watch
- apiGroups:
- ""
resources:
Expand Down
104 changes: 87 additions & 17 deletions pkg/controller/kubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,60 +6,130 @@ import (

"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apimachinery/pkg/util/rand"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

const (
secretTypeBootstrapToken v1.SecretType = "bootstrap.kubernetes.io/token"
machineNameLabelKey = "machine.k8s.io/machine.name"
tokenIDKey = "token-id"
tokenSecretKey = "token-secret"
expirationKey = "expiration"
tokenFormatter = "%s.%s"
)

func (c *Controller) createBootstrapKubeconfig(name string) (*clientcmdapi.Config, error) {
token, err := c.createBootstrapToken(name)
if err != nil {
return nil, err
}

infoKubeconfig, err := c.kubeconfigProvider.GetKubeconfig()
if err != nil {
return nil, err
}

outConfig := infoKubeconfig.DeepCopy()

outConfig.AuthInfos = map[string]*clientcmdapi.AuthInfo{
"": {
Token: token,
},
}

return outConfig, nil
}

func (c *Controller) createBootstrapToken(name string) (string, error) {
existingSecret, err := c.getSecretIfExists(name)
if err != nil {
return "", err
}
if existingSecret != nil {
return c.updateSecretExpirationAndGetToken(existingSecret)
}

tokenID := rand.String(6)
tokenSecret := rand.String(16)

secret := v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("bootstrap-token-%s", tokenID),
Name: fmt.Sprintf("bootstrap-token-%s", tokenID),
Labels: map[string]string{machineNameLabelKey: name},
},
Type: secretTypeBootstrapToken,
StringData: map[string]string{
"description": "bootstrap token for " + name,
"token-id": tokenID,
"token-secret": tokenSecret,
"expiration": metav1.Now().Add(24 * time.Hour).Format(time.RFC3339),
tokenIDKey: tokenID,
tokenSecretKey: tokenSecret,
expirationKey: metav1.Now().Add(24 * time.Hour).Format(time.RFC3339),
"usage-bootstrap-authentication": "true",
"usage-bootstrap-signing": "true",
"auth-extra-groups": "system:bootstrappers:machine-controller:default-node-token",
},
}

_, err := c.kubeClient.CoreV1().Secrets(metav1.NamespaceSystem).Create(&secret)
_, err = c.kubeClient.CoreV1().Secrets(metav1.NamespaceSystem).Create(&secret)
if err != nil {
return "", err
}

return fmt.Sprintf("%s.%s", tokenID, tokenSecret), nil
return fmt.Sprintf(tokenFormatter, tokenID, tokenSecret), nil
}

func (c *Controller) createBootstrapKubeconfig(name string) (*clientcmdapi.Config, error) {
token, err := c.createBootstrapToken(name)
func (c *Controller) updateSecretExpirationAndGetToken(secret *v1.Secret) (string, error) {
if secret.StringData == nil {
secret.StringData = map[string]string{}
}
secret.StringData[expirationKey] = metav1.Now().Add(1 * time.Hour).Format(time.RFC3339)
tokenID := secret.StringData[tokenIDKey]
tokenSecret := secret.StringData[tokenSecretKey]
token := fmt.Sprintf(tokenFormatter, tokenID, tokenSecret)

expBytes, ok := secret.Data["expiration"]
if !ok {
return "", fmt.Errorf("haven't found %s key in the secret's Data field", expirationKey)
}
expString := string(expBytes)
expVal := metav1.Now()
err := expVal.UnmarshalQueryParameter(expString)
if err != nil {
return nil, err
return "", err
}
now := metav1.Now()
now.Add(15 * time.Minute)
// expVal has to point to a time in the future otherwise we need to update expiration time
if now.Before(&expVal) {
return token, nil
}

infoKubeconfig, err := c.kubeconfigProvider.GetKubeconfig()
_, err = c.kubeClient.CoreV1().Secrets(metav1.NamespaceSystem).Update(secret)
if err != nil {
return nil, err
return "", err
}
return token, nil
}

outConfig := infoKubeconfig.DeepCopy()
func (c *Controller) getSecretIfExists(name string) (*v1.Secret, error) {
req, err := labels.NewRequirement(machineNameLabelKey, selection.Equals, []string{name})
if err != nil {
return nil, err
}

outConfig.AuthInfos = map[string]*clientcmdapi.AuthInfo{
"": {
Token: token,
},
selector := labels.NewSelector().Add(*req)
secrets, err := c.secretSystemNsLister.List(selector)
if err != nil {
return nil, err
}

return outConfig, nil
if len(secrets) == 0 {
return nil, nil
}
if len(secrets) > 1 {
return nil, fmt.Errorf("expected to find exactly one secret for the given machine name =%s but found %d", name, len(secrets))
}
return secrets[0], nil
}
18 changes: 9 additions & 9 deletions pkg/controller/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ type Controller struct {
kubeClient kubernetes.Interface
machineClient machineclientset.Interface

nodesLister listerscorev1.NodeLister
configMapLister listerscorev1.ConfigMapLister
machinesLister machinelistersv1alpha1.MachineLister
nodesLister listerscorev1.NodeLister
machinesLister machinelistersv1alpha1.MachineLister
secretSystemNsLister listerscorev1.SecretLister

workqueue workqueue.RateLimitingInterface
recorder record.EventRecorder
Expand Down Expand Up @@ -115,9 +115,9 @@ func NewMachineController(
machineClient machineclientset.Interface,
nodeInformer cache.SharedIndexInformer,
nodeLister listerscorev1.NodeLister,
configMapLister listerscorev1.ConfigMapLister,
machineInformer cache.SharedIndexInformer,
machineLister machinelistersv1alpha1.MachineLister,
secretSystemNsLister listerscorev1.SecretLister,
clusterDNSIPs []net.IP,
metrics MetricsCollection,
kubeconfigProvider KubeconfigProvider,
Expand All @@ -129,12 +129,12 @@ func NewMachineController(
eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeClient.CoreV1().Events("")})

controller := &Controller{
kubeClient: kubeClient,
nodesLister: nodeLister,
configMapLister: configMapLister,
kubeClient: kubeClient,
nodesLister: nodeLister,

machineClient: machineClient,
machinesLister: machineLister,
machineClient: machineClient,
machinesLister: machineLister,
secretSystemNsLister: secretSystemNsLister,

workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.NewItemFastSlowRateLimiter(2*time.Second, 10*time.Second, 5), "Machines"),
recorder: eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: "machine-controller"}),
Expand Down

0 comments on commit e7c65fd

Please sign in to comment.