...

Source file src/edge-infra.dev/pkg/edge/iam/ctl/providerctl/provider_secret_controller.go

Documentation: edge-infra.dev/pkg/edge/iam/ctl/providerctl

     1  package providerctl
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/google/uuid"
     9  	"github.com/ory/x/randx"
    10  	apiv1 "k8s.io/api/core/v1"
    11  	"k8s.io/apimachinery/pkg/types"
    12  	ctrl "sigs.k8s.io/controller-runtime"
    13  	"sigs.k8s.io/controller-runtime/pkg/client"
    14  
    15  	apierrs "k8s.io/apimachinery/pkg/api/errors"
    16  
    17  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    18  	logger "sigs.k8s.io/controller-runtime/pkg/log"
    19  
    20  	api "edge-infra.dev/pkg/edge/iam/api/v1alpha1"
    21  	"edge-infra.dev/pkg/edge/iam/crypto"
    22  )
    23  
    24  const (
    25  	PrivateKeysSecretName = "private-key-secret"
    26  	ChallengeSecretName   = "challenge-secret"
    27  )
    28  
    29  func (r *ProviderReconciler) reconcilePrivateKeysSecret(ctx context.Context, req ctrl.Request, provider api.Provider) (api.Provider, *apiv1.Secret, error) {
    30  	log := logger.FromContext(ctx)
    31  	secret, err := secretExists(ctx, req, r.Client, PrivateKeysSecretName)
    32  	if err != nil {
    33  		log.Info("failed to check if secret exists. will retry soon", "ns", req.Namespace, "secret", PrivateKeysSecretName)
    34  		return api.NotReady(provider, SecretExistCheckFailure, err.Error()), nil, err
    35  	}
    36  
    37  	// private keys secret exists, return it
    38  	if secret != nil {
    39  		return provider, secret, nil
    40  	}
    41  
    42  	// private keys secret does not exist, let's create it
    43  	secret, err = r.createPrivateKeysSecret(ctx, provider)
    44  	if err != nil {
    45  		log.Info("failed to check if secret exists. will retry soon", "ns", req.Namespace, "secret", PrivateKeysSecretName)
    46  		return api.NotReady(provider, SecretCreationFailure, err.Error()), nil, err
    47  	}
    48  
    49  	log.Info("successfully created secret", "ns", req.Namespace, "name", PrivateKeysSecretName)
    50  	return provider, secret, nil
    51  }
    52  
    53  func (r *ProviderReconciler) createPrivateKeysSecret(ctx context.Context, provider api.Provider) (*apiv1.Secret, error) {
    54  	secret := apiv1.Secret{}
    55  	secret.ObjectMeta = metav1.ObjectMeta{
    56  		Name:      PrivateKeysSecretName,
    57  		Namespace: provider.Namespace,
    58  	}
    59  
    60  	pk := crypto.CreatePrivateKey()
    61  	serializedPrivateKey := crypto.Serialize(pk)
    62  	secret.Data = map[string][]byte{
    63  		"private_key":    []byte(serializedPrivateKey),
    64  		"private_key_id": []byte(uuid.New().String()),
    65  	}
    66  
    67  	err := r.Create(ctx, &secret)
    68  	if err != nil {
    69  		return nil, fmt.Errorf("failed to create private keys secret name '%v'", PrivateKeysSecretName)
    70  	}
    71  
    72  	return &secret, nil
    73  }
    74  
    75  func (r *ProviderReconciler) validatePrivateKeysSecret(provider api.Provider, secret *apiv1.Secret) (api.Provider, error) {
    76  	_, found := secret.Data["private_key"]
    77  	if !found {
    78  		e := errors.New(`"private_key property missing"`)
    79  		return api.NotReady(provider, MissingSecretData, e.Error()), e
    80  	}
    81  
    82  	_, found = secret.Data["private_key_id"]
    83  	if !found {
    84  		e := errors.New(`"private_key_id property missing"`)
    85  		return api.NotReady(provider, MissingSecretData, e.Error()), e
    86  	}
    87  
    88  	return provider, nil
    89  }
    90  
    91  func (r *ProviderReconciler) validateChallengeSecret(provider api.Provider, secret *apiv1.Secret) (api.Provider, error) {
    92  	_, found := secret.Data["secret"]
    93  	if !found {
    94  		e := errors.New(`"secret property missing"`)
    95  		return api.NotReady(provider, MissingSecretData, e.Error()), e
    96  	}
    97  
    98  	return provider, nil
    99  }
   100  
   101  func secretExists(ctx context.Context, req ctrl.Request, c client.Client, secretName string) (*apiv1.Secret, error) {
   102  	secret := &apiv1.Secret{}
   103  	err := c.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: secretName}, secret)
   104  	switch {
   105  	case err == nil:
   106  		return secret, nil
   107  	case apierrs.IsNotFound(err):
   108  		return nil, nil
   109  	default:
   110  		return nil, fmt.Errorf("failed to check if secret exists: %w", err)
   111  	}
   112  }
   113  
   114  func (r *ProviderReconciler) reconcileChallengeSecret(ctx context.Context, req ctrl.Request, provider api.Provider) (api.Provider, *apiv1.Secret, error) {
   115  	log := logger.FromContext(ctx)
   116  
   117  	secret, err := secretExists(ctx, req, r.Client, ChallengeSecretName)
   118  	if err != nil {
   119  		log.Info("failed to check if secret exists. will retry soon", "ns", req.Namespace, "secret", ChallengeSecretName)
   120  		return api.NotReady(provider, SecretExistCheckFailure, err.Error()), nil, err
   121  	}
   122  
   123  	// challenge secret exists, return it
   124  	if secret != nil {
   125  		return provider, secret, nil
   126  	}
   127  
   128  	// challenge secret does not exist, let's create it
   129  	secret, err = r.createChallengeSecret(ctx, provider)
   130  	if err != nil {
   131  		log.Info("failed to create secret. will retry soon", "ns", req.Namespace, "secret", ChallengeSecretName)
   132  		return api.NotReady(provider, SecretCreationFailure, err.Error()), nil, err
   133  	}
   134  
   135  	log.Info("successfully created secret", "ns", req.Namespace, "name", ChallengeSecretName)
   136  	return provider, secret, nil
   137  }
   138  
   139  func (r *ProviderReconciler) createChallengeSecret(ctx context.Context, provider api.Provider) (*apiv1.Secret, error) {
   140  	secret := apiv1.Secret{}
   141  	secret.ObjectMeta = metav1.ObjectMeta{
   142  		Name:      ChallengeSecretName,
   143  		Namespace: provider.Namespace,
   144  	}
   145  
   146  	s, _ := generateSecret(32)
   147  	secret.Data = map[string][]byte{
   148  		"secret": s,
   149  	}
   150  
   151  	err := r.Create(ctx, &secret)
   152  	if err != nil {
   153  		return nil, fmt.Errorf("failed to create challenge secret name '%v'", ChallengeSecretName)
   154  	}
   155  
   156  	return &secret, nil
   157  }
   158  
   159  var secretCharSet = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
   160  
   161  func generateSecret(length int) ([]byte, error) {
   162  	secret, err := randx.RuneSequence(length, secretCharSet)
   163  	if err != nil {
   164  		return []byte{}, err
   165  	}
   166  	return []byte(string(secret)), nil
   167  }
   168  

View as plain text