package providerctl import ( "context" "errors" "fmt" "github.com/google/uuid" "github.com/ory/x/randx" apiv1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" apierrs "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" logger "sigs.k8s.io/controller-runtime/pkg/log" api "edge-infra.dev/pkg/edge/iam/api/v1alpha1" "edge-infra.dev/pkg/edge/iam/crypto" ) const ( PrivateKeysSecretName = "private-key-secret" ChallengeSecretName = "challenge-secret" ) func (r *ProviderReconciler) reconcilePrivateKeysSecret(ctx context.Context, req ctrl.Request, provider api.Provider) (api.Provider, *apiv1.Secret, error) { log := logger.FromContext(ctx) secret, err := secretExists(ctx, req, r.Client, PrivateKeysSecretName) if err != nil { log.Info("failed to check if secret exists. will retry soon", "ns", req.Namespace, "secret", PrivateKeysSecretName) return api.NotReady(provider, SecretExistCheckFailure, err.Error()), nil, err } // private keys secret exists, return it if secret != nil { return provider, secret, nil } // private keys secret does not exist, let's create it secret, err = r.createPrivateKeysSecret(ctx, provider) if err != nil { log.Info("failed to check if secret exists. will retry soon", "ns", req.Namespace, "secret", PrivateKeysSecretName) return api.NotReady(provider, SecretCreationFailure, err.Error()), nil, err } log.Info("successfully created secret", "ns", req.Namespace, "name", PrivateKeysSecretName) return provider, secret, nil } func (r *ProviderReconciler) createPrivateKeysSecret(ctx context.Context, provider api.Provider) (*apiv1.Secret, error) { secret := apiv1.Secret{} secret.ObjectMeta = metav1.ObjectMeta{ Name: PrivateKeysSecretName, Namespace: provider.Namespace, } pk := crypto.CreatePrivateKey() serializedPrivateKey := crypto.Serialize(pk) secret.Data = map[string][]byte{ "private_key": []byte(serializedPrivateKey), "private_key_id": []byte(uuid.New().String()), } err := r.Create(ctx, &secret) if err != nil { return nil, fmt.Errorf("failed to create private keys secret name '%v'", PrivateKeysSecretName) } return &secret, nil } func (r *ProviderReconciler) validatePrivateKeysSecret(provider api.Provider, secret *apiv1.Secret) (api.Provider, error) { _, found := secret.Data["private_key"] if !found { e := errors.New(`"private_key property missing"`) return api.NotReady(provider, MissingSecretData, e.Error()), e } _, found = secret.Data["private_key_id"] if !found { e := errors.New(`"private_key_id property missing"`) return api.NotReady(provider, MissingSecretData, e.Error()), e } return provider, nil } func (r *ProviderReconciler) validateChallengeSecret(provider api.Provider, secret *apiv1.Secret) (api.Provider, error) { _, found := secret.Data["secret"] if !found { e := errors.New(`"secret property missing"`) return api.NotReady(provider, MissingSecretData, e.Error()), e } return provider, nil } func secretExists(ctx context.Context, req ctrl.Request, c client.Client, secretName string) (*apiv1.Secret, error) { secret := &apiv1.Secret{} err := c.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: secretName}, secret) switch { case err == nil: return secret, nil case apierrs.IsNotFound(err): return nil, nil default: return nil, fmt.Errorf("failed to check if secret exists: %w", err) } } func (r *ProviderReconciler) reconcileChallengeSecret(ctx context.Context, req ctrl.Request, provider api.Provider) (api.Provider, *apiv1.Secret, error) { log := logger.FromContext(ctx) secret, err := secretExists(ctx, req, r.Client, ChallengeSecretName) if err != nil { log.Info("failed to check if secret exists. will retry soon", "ns", req.Namespace, "secret", ChallengeSecretName) return api.NotReady(provider, SecretExistCheckFailure, err.Error()), nil, err } // challenge secret exists, return it if secret != nil { return provider, secret, nil } // challenge secret does not exist, let's create it secret, err = r.createChallengeSecret(ctx, provider) if err != nil { log.Info("failed to create secret. will retry soon", "ns", req.Namespace, "secret", ChallengeSecretName) return api.NotReady(provider, SecretCreationFailure, err.Error()), nil, err } log.Info("successfully created secret", "ns", req.Namespace, "name", ChallengeSecretName) return provider, secret, nil } func (r *ProviderReconciler) createChallengeSecret(ctx context.Context, provider api.Provider) (*apiv1.Secret, error) { secret := apiv1.Secret{} secret.ObjectMeta = metav1.ObjectMeta{ Name: ChallengeSecretName, Namespace: provider.Namespace, } s, _ := generateSecret(32) secret.Data = map[string][]byte{ "secret": s, } err := r.Create(ctx, &secret) if err != nil { return nil, fmt.Errorf("failed to create challenge secret name '%v'", ChallengeSecretName) } return &secret, nil } var secretCharSet = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") func generateSecret(length int) ([]byte, error) { secret, err := randx.RuneSequence(length, secretCharSet) if err != nil { return []byte{}, err } return []byte(string(secret)), nil }