mirror of
https://github.com/rancher/k3k.git
synced 2026-03-03 02:00:38 +00:00
Token random generation (#136)
Signed-off-by: galal-hussein <hussein.galal.ahmed.11@gmail.com>
This commit is contained in:
@@ -16,11 +16,11 @@ type Agent interface {
|
||||
Resources() []ctrlruntimeclient.Object
|
||||
}
|
||||
|
||||
func New(cluster *v1alpha1.Cluster, serviceIP, sharedAgentImage string) Agent {
|
||||
func New(cluster *v1alpha1.Cluster, serviceIP, sharedAgentImage, token string) Agent {
|
||||
if cluster.Spec.Mode == VirtualNodeMode {
|
||||
return NewVirtualAgent(cluster, serviceIP)
|
||||
return NewVirtualAgent(cluster, serviceIP, token)
|
||||
}
|
||||
return NewSharedAgent(cluster, serviceIP, sharedAgentImage)
|
||||
return NewSharedAgent(cluster, serviceIP, sharedAgentImage, token)
|
||||
}
|
||||
|
||||
func configSecretName(clusterName string) string {
|
||||
|
||||
@@ -22,18 +22,20 @@ type SharedAgent struct {
|
||||
cluster *v1alpha1.Cluster
|
||||
serviceIP string
|
||||
sharedAgentImage string
|
||||
token string
|
||||
}
|
||||
|
||||
func NewSharedAgent(cluster *v1alpha1.Cluster, serviceIP, sharedAgentImage string) Agent {
|
||||
func NewSharedAgent(cluster *v1alpha1.Cluster, serviceIP, sharedAgentImage, token string) Agent {
|
||||
return &SharedAgent{
|
||||
cluster: cluster,
|
||||
serviceIP: serviceIP,
|
||||
sharedAgentImage: sharedAgentImage,
|
||||
token: token,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SharedAgent) Config() (ctrlruntimeclient.Object, error) {
|
||||
config := sharedAgentData(s.cluster)
|
||||
config := sharedAgentData(s.cluster, s.token, s.Name())
|
||||
|
||||
return &v1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
@@ -50,14 +52,13 @@ func (s *SharedAgent) Config() (ctrlruntimeclient.Object, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func sharedAgentData(cluster *v1alpha1.Cluster) string {
|
||||
nodeName := cluster.Name + "-" + "k3k-kubelet"
|
||||
func sharedAgentData(cluster *v1alpha1.Cluster, token, nodeName string) string {
|
||||
return fmt.Sprintf(`clusterName: %s
|
||||
clusterNamespace: %s
|
||||
nodeName: %s
|
||||
agentHostname: %s
|
||||
token: %s`,
|
||||
cluster.Name, cluster.Namespace, nodeName, nodeName, cluster.Spec.Token)
|
||||
cluster.Name, cluster.Namespace, nodeName, nodeName, token)
|
||||
}
|
||||
|
||||
func (s *SharedAgent) Resources() []ctrlruntimeclient.Object {
|
||||
|
||||
@@ -20,17 +20,19 @@ const (
|
||||
type VirtualAgent struct {
|
||||
cluster *v1alpha1.Cluster
|
||||
serviceIP string
|
||||
token string
|
||||
}
|
||||
|
||||
func NewVirtualAgent(cluster *v1alpha1.Cluster, serviceIP string) Agent {
|
||||
func NewVirtualAgent(cluster *v1alpha1.Cluster, serviceIP, token string) Agent {
|
||||
return &VirtualAgent{
|
||||
cluster: cluster,
|
||||
serviceIP: serviceIP,
|
||||
token: token,
|
||||
}
|
||||
}
|
||||
|
||||
func (v *VirtualAgent) Config() (ctrlruntimeclient.Object, error) {
|
||||
config := virtualAgentData(v.serviceIP, v.cluster.Spec.Token)
|
||||
config := virtualAgentData(v.serviceIP, v.token)
|
||||
|
||||
return &v1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
|
||||
@@ -116,7 +116,12 @@ func (c *ClusterReconciler) createCluster(ctx context.Context, cluster *v1alpha1
|
||||
log.Errorw("invalid change", zap.Error(err))
|
||||
return nil
|
||||
}
|
||||
s := server.New(cluster, c.Client)
|
||||
token, err := c.token(ctx, cluster)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s := server.New(cluster, c.Client, token)
|
||||
|
||||
if cluster.Spec.Persistence != nil {
|
||||
cluster.Status.Persistence = cluster.Spec.Persistence
|
||||
@@ -154,7 +159,7 @@ func (c *ClusterReconciler) createCluster(ctx context.Context, cluster *v1alpha1
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.agent(ctx, cluster, serviceIP); err != nil {
|
||||
if err := c.agent(ctx, cluster, serviceIP, token); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -173,7 +178,7 @@ func (c *ClusterReconciler) createCluster(ctx context.Context, cluster *v1alpha1
|
||||
}
|
||||
}
|
||||
|
||||
bootstrapSecret, err := bootstrap.Generate(ctx, cluster, serviceIP)
|
||||
bootstrapSecret, err := bootstrap.Generate(ctx, cluster, serviceIP, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -274,8 +279,8 @@ func (c *ClusterReconciler) server(ctx context.Context, cluster *v1alpha1.Cluste
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClusterReconciler) agent(ctx context.Context, cluster *v1alpha1.Cluster, serviceIP string) error {
|
||||
agent := agent.New(cluster, serviceIP, c.SharedAgentImage)
|
||||
func (c *ClusterReconciler) agent(ctx context.Context, cluster *v1alpha1.Cluster, serviceIP, token string) error {
|
||||
agent := agent.New(cluster, serviceIP, c.SharedAgentImage, token)
|
||||
agentsConfig, err := agent.Config()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -116,7 +116,7 @@ func (p *PodReconciler) handleServerPod(ctx context.Context, cluster v1alpha1.Cl
|
||||
}
|
||||
return nil
|
||||
}
|
||||
tlsConfig, err := p.getETCDTLS(&cluster, log)
|
||||
tlsConfig, err := p.getETCDTLS(ctx, &cluster, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -150,9 +150,12 @@ func (p *PodReconciler) handleServerPod(ctx context.Context, cluster v1alpha1.Cl
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PodReconciler) getETCDTLS(cluster *v1alpha1.Cluster, log *zap.SugaredLogger) (*tls.Config, error) {
|
||||
func (p *PodReconciler) getETCDTLS(ctx context.Context, cluster *v1alpha1.Cluster, log *zap.SugaredLogger) (*tls.Config, error) {
|
||||
log.Infow("generating etcd TLS client certificate", "Cluster", cluster.Name, "Namespace", cluster.Namespace)
|
||||
token := cluster.Spec.Token
|
||||
token, err := p.clusterToken(ctx, cluster)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
endpoint := server.ServiceName(cluster.Name) + "." + cluster.Namespace
|
||||
var b *bootstrap.ControlRuntimeBootstrap
|
||||
if err := retry.OnError(k3kcontroller.Backoff, func(err error) bool {
|
||||
@@ -218,3 +221,21 @@ func removePeer(ctx context.Context, client *clientv3.Client, name, address stri
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PodReconciler) clusterToken(ctx context.Context, cluster *v1alpha1.Cluster) (string, error) {
|
||||
var tokenSecret v1.Secret
|
||||
nn := types.NamespacedName{
|
||||
Name: TokenSecretName(cluster.Name),
|
||||
Namespace: cluster.Namespace,
|
||||
}
|
||||
if cluster.Spec.TokenSecretRef != nil {
|
||||
nn.Name = TokenSecretName(cluster.Name)
|
||||
}
|
||||
if err := p.Client.Get(ctx, nn, &tokenSecret); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, ok := tokenSecret.Data["token"]; !ok {
|
||||
return "", fmt.Errorf("no token field in secret %s/%s", nn.Namespace, nn.Name)
|
||||
}
|
||||
return string(tokenSecret.Data["token"]), nil
|
||||
}
|
||||
|
||||
@@ -32,9 +32,7 @@ type content struct {
|
||||
// Generate generates the bootstrap for the cluster:
|
||||
// 1- use the server token to get the bootstrap data from k3s
|
||||
// 2- save the bootstrap data as a secret
|
||||
func Generate(ctx context.Context, cluster *v1alpha1.Cluster, ip string) (*v1.Secret, error) {
|
||||
token := cluster.Spec.Token
|
||||
|
||||
func Generate(ctx context.Context, cluster *v1alpha1.Cluster, ip, token string) (*v1.Secret, error) {
|
||||
var bootstrap *ControlRuntimeBootstrap
|
||||
if err := retry.OnError(controller.Backoff, func(err error) bool {
|
||||
return true
|
||||
|
||||
@@ -18,9 +18,9 @@ func (s *Server) Config(init bool, serviceIP string) (*v1.Secret, error) {
|
||||
fmt.Sprintf("%s.%s", ServiceName(s.cluster.Name), s.cluster.Namespace),
|
||||
)
|
||||
|
||||
config := serverConfigData(serviceIP, s.cluster)
|
||||
config := serverConfigData(serviceIP, s.cluster, s.token)
|
||||
if init {
|
||||
config = initConfigData(s.cluster)
|
||||
config = initConfigData(s.cluster, s.token)
|
||||
}
|
||||
return &v1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
@@ -37,20 +37,20 @@ func (s *Server) Config(init bool, serviceIP string) (*v1.Secret, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func serverConfigData(serviceIP string, cluster *v1alpha1.Cluster) string {
|
||||
return "cluster-init: true\nserver: https://" + serviceIP + ":6443\n" + serverOptions(cluster)
|
||||
func serverConfigData(serviceIP string, cluster *v1alpha1.Cluster, token string) string {
|
||||
return "cluster-init: true\nserver: https://" + serviceIP + ":6443\n" + serverOptions(cluster, token)
|
||||
}
|
||||
|
||||
func initConfigData(cluster *v1alpha1.Cluster) string {
|
||||
return "cluster-init: true\n" + serverOptions(cluster)
|
||||
func initConfigData(cluster *v1alpha1.Cluster, token string) string {
|
||||
return "cluster-init: true\n" + serverOptions(cluster, token)
|
||||
}
|
||||
|
||||
func serverOptions(cluster *v1alpha1.Cluster) string {
|
||||
func serverOptions(cluster *v1alpha1.Cluster, token string) string {
|
||||
var opts string
|
||||
|
||||
// TODO: generate token if not found
|
||||
if cluster.Spec.Token != "" {
|
||||
opts = "token: " + cluster.Spec.Token + "\n"
|
||||
if token != "" {
|
||||
opts = "token: " + token + "\n"
|
||||
}
|
||||
if cluster.Status.ClusterCIDR != "" {
|
||||
opts = opts + "cluster-cidr: " + cluster.Status.ClusterCIDR + "\n"
|
||||
|
||||
@@ -31,12 +31,14 @@ const (
|
||||
type Server struct {
|
||||
cluster *v1alpha1.Cluster
|
||||
client client.Client
|
||||
token string
|
||||
}
|
||||
|
||||
func New(cluster *v1alpha1.Cluster, client client.Client) *Server {
|
||||
func New(cluster *v1alpha1.Cluster, client client.Client, token string) *Server {
|
||||
return &Server{
|
||||
cluster: cluster,
|
||||
client: client,
|
||||
token: token,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
98
pkg/controller/cluster/token.go
Normal file
98
pkg/controller/cluster/token.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/rancher/k3k/pkg/apis/k3k.io/v1alpha1"
|
||||
"github.com/rancher/k3k/pkg/controller"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
)
|
||||
|
||||
func (c *ClusterReconciler) token(ctx context.Context, cluster *v1alpha1.Cluster) (string, error) {
|
||||
if cluster.Spec.TokenSecretRef == nil {
|
||||
return c.ensureTokenSecret(ctx, cluster)
|
||||
}
|
||||
// get token data from secretRef
|
||||
nn := types.NamespacedName{
|
||||
Name: cluster.Spec.TokenSecretRef.Name,
|
||||
Namespace: cluster.Spec.TokenSecretRef.Namespace,
|
||||
}
|
||||
var tokenSecret v1.Secret
|
||||
if err := c.Client.Get(ctx, nn, &tokenSecret); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, ok := tokenSecret.Data["token"]; !ok {
|
||||
return "", fmt.Errorf("no token field in secret %s/%s", nn.Namespace, nn.Name)
|
||||
}
|
||||
return string(tokenSecret.Data["token"]), nil
|
||||
}
|
||||
|
||||
func (c *ClusterReconciler) ensureTokenSecret(ctx context.Context, cluster *v1alpha1.Cluster) (string, error) {
|
||||
// check if the secret is already created
|
||||
var (
|
||||
tokenSecret v1.Secret
|
||||
nn = types.NamespacedName{
|
||||
Name: TokenSecretName(cluster.Name),
|
||||
Namespace: cluster.Namespace,
|
||||
}
|
||||
)
|
||||
if err := c.Client.Get(ctx, nn, &tokenSecret); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if tokenSecret.Data != nil {
|
||||
return string(tokenSecret.Data["token"]), nil
|
||||
}
|
||||
c.logger.Info("Token secret is not specified, creating a random token")
|
||||
token, err := random(16)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tokenSecret = TokenSecretObj(token, cluster.Name, cluster.Namespace)
|
||||
if err := controllerutil.SetControllerReference(cluster, &tokenSecret, c.Scheme); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := c.ensure(ctx, &tokenSecret, false); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return token, nil
|
||||
|
||||
}
|
||||
|
||||
func random(size int) (string, error) {
|
||||
token := make([]byte, size)
|
||||
_, err := rand.Read(token)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(token), err
|
||||
}
|
||||
|
||||
func TokenSecretObj(token, name, namespace string) v1.Secret {
|
||||
return v1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Secret",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: TokenSecretName(name),
|
||||
Namespace: namespace,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"token": []byte(token),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TokenSecretName(clusterName string) string {
|
||||
return controller.SafeConcatNameWithPrefix(clusterName, "token")
|
||||
}
|
||||
Reference in New Issue
Block a user