Token random generation (#136)

Signed-off-by: galal-hussein <hussein.galal.ahmed.11@gmail.com>
This commit is contained in:
Hussein Galal
2024-11-01 21:27:03 +02:00
committed by GitHub
parent 26a7fa023f
commit 84f921641b
14 changed files with 225 additions and 64 deletions

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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{

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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"

View File

@@ -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,
}
}

View 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")
}