feat(infisicalsecret-crd): added configmap support

This commit is contained in:
Daniel Hougaard
2025-03-13 20:06:48 +04:00
parent de7e92ccfc
commit ec363a5ad4
11 changed files with 367 additions and 189 deletions

View File

@@ -107,3 +107,25 @@ type ManagedKubeSecretConfig struct {
// +kubebuilder:validation:Optional
Template *InfisicalSecretTemplate `json:"template,omitempty"`
}
type ManagedKubeConfigMapConfig struct {
// The name of the Kubernetes ConfigMap
// +kubebuilder:validation:Required
ConfigMapName string `json:"configMapName"`
// The Kubernetes ConfigMap creation policy.
// Enum with values: 'Owner', 'Orphan'.
// Owner creates the config map and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it.
// Orphan will not set the config map owner. This will result in the config map being orphaned and not deleted when the resource is deleted.
// +kubebuilder:validation:Optional
// +kubebuilder:default:=Orphan
CreationPolicy string `json:"creationPolicy"`
// The namespace where the Kubernetes ConfigMap is located
// +kubebuilder:validation:Required
ConfigMapNamespace string `json:"configMapNamespace"`
// The template to transform the secret data
// +kubebuilder:validation:Optional
Template *InfisicalSecretTemplate `json:"template,omitempty"`
}

View File

@@ -139,6 +139,7 @@ type InfisicalSecretSpec struct {
// +kubebuilder:validation:Optional
ManagedKubeSecretReferences []ManagedKubeSecretConfig `json:"managedKubeSecretReferences"`
ManagedKubeConfigMapReferences []ManagedKubeConfigMapConfig `json:"managedKubeConfigMapReferences"`
// +kubebuilder:default:=60
ResyncInterval int `json:"resyncInterval"`

View File

@@ -572,6 +572,13 @@ func (in *InfisicalSecretSpec) DeepCopyInto(out *InfisicalSecretSpec) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.ManagedKubeConfigMapReferences != nil {
in, out := &in.ManagedKubeConfigMapReferences, &out.ManagedKubeConfigMapReferences
*out = make([]ManagedKubeConfigMapConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
out.TLS = in.TLS
}
@@ -691,6 +698,26 @@ func (in *MachineIdentityScopeInWorkspace) DeepCopy() *MachineIdentityScopeInWor
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ManagedKubeConfigMapConfig) DeepCopyInto(out *ManagedKubeConfigMapConfig) {
*out = *in
if in.Template != nil {
in, out := &in.Template, &out.Template
*out = new(InfisicalSecretTemplate)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagedKubeConfigMapConfig.
func (in *ManagedKubeConfigMapConfig) DeepCopy() *ManagedKubeConfigMapConfig {
if in == nil {
return nil
}
out := new(ManagedKubeConfigMapConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ManagedKubeSecretConfig) DeepCopyInto(out *ManagedKubeSecretConfig) {
*out = *in

View File

@@ -261,6 +261,44 @@ spec:
hostAPI:
description: Infisical host to pull secrets from
type: string
managedKubeConfigMapReferences:
items:
properties:
configMapName:
description: The name of the Kubernetes ConfigMap
type: string
configMapNamespace:
description: The namespace where the Kubernetes ConfigMap is
located
type: string
creationPolicy:
default: Orphan
description: 'The Kubernetes ConfigMap creation policy. Enum
with values: ''Owner'', ''Orphan''. Owner creates the config
map and sets .metadata.ownerReferences of the InfisicalSecret
CRD that created it. Orphan will not set the config map owner.
This will result in the config map being orphaned and not
deleted when the resource is deleted.'
type: string
template:
description: The template to transform the secret data
properties:
data:
additionalProperties:
type: string
description: The template key values
type: object
includeAllSecrets:
description: This injects all retrieved secrets into the
top level of your template. Secrets defined in the template
will take precedence over the injected ones.
type: boolean
type: object
required:
- configMapName
- configMapNamespace
type: object
type: array
managedKubeSecretReferences:
items:
properties:
@@ -381,6 +419,7 @@ spec:
- secretNamespace
type: object
required:
- managedKubeConfigMapReferences
- resyncInterval
type: object
status:

View File

@@ -11,7 +11,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func (r *InfisicalSecretReconciler) SetReadyToSyncSecretsConditions(ctx context.Context, infisicalSecret *v1alpha1.InfisicalSecret, secretsCount int, errorToConditionOn error) error {
func (r *InfisicalSecretReconciler) SetReadyToSyncSecretsConditions(ctx context.Context, infisicalSecret *v1alpha1.InfisicalSecret, secretsCount int, errorToConditionOn error) {
if infisicalSecret.Status.Conditions == nil {
infisicalSecret.Status.Conditions = []metav1.Condition{}
}
@@ -38,8 +38,6 @@ func (r *InfisicalSecretReconciler) SetReadyToSyncSecretsConditions(ctx context.
Message: fmt.Sprintf("Infisical controller has started syncing your secrets. Last reconcile synced %d secrets", secretsCount),
})
}
return r.Client.Status().Update(ctx, infisicalSecret)
}
func (r *InfisicalSecretReconciler) SetInfisicalTokenLoadCondition(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, authStrategy util.AuthStrategyType, errorToConditionOn error) {
@@ -62,11 +60,6 @@ func (r *InfisicalSecretReconciler) SetInfisicalTokenLoadCondition(ctx context.C
Message: fmt.Sprintf("Failed to load Infisical Token from the provided Kubernetes secret because: %v", errorToConditionOn),
})
}
err := r.Client.Status().Update(ctx, infisicalSecret)
if err != nil {
logger.Error(err, "Could not set condition for LoadedInfisicalToken")
}
}
func (r *InfisicalSecretReconciler) SetInfisicalAutoRedeploymentReady(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, numDeployments int, errorToConditionOn error) {
@@ -89,9 +82,4 @@ func (r *InfisicalSecretReconciler) SetInfisicalAutoRedeploymentReady(ctx contex
Message: fmt.Sprintf("Failed reconcile deployments because: %v", errorToConditionOn),
})
}
err := r.Client.Status().Update(ctx, infisicalSecret)
if err != nil {
logger.Error(err, "Could not set condition for AutoRedeployReady")
}
}

View File

@@ -73,6 +73,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
// It's important we don't directly modify the CRD object, so we create a copy of it and move existing data into it.
managedKubeSecretReferences := infisicalSecretCRD.Spec.ManagedKubeSecretReferences
managedKubeConfigMapReferences := infisicalSecretCRD.Spec.ManagedKubeConfigMapReferences
if infisicalSecretCRD.Spec.ManagedSecretReference.SecretName != "" && managedKubeSecretReferences != nil && len(managedKubeSecretReferences) > 0 {
errMessage := "InfisicalSecret CRD cannot have both managedSecretReference and managedKubeSecretReferences"
@@ -89,8 +90,8 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
managedKubeSecretReferences = append(managedKubeSecretReferences, infisicalSecretCRD.Spec.ManagedSecretReference)
}
if len(managedKubeSecretReferences) == 0 {
errMessage := "InfisicalSecret CRD must have at least one managed secret reference set in the `managedKubeSecretReferences` field"
if len(managedKubeSecretReferences) == 0 && len(managedKubeConfigMapReferences) == 0 {
errMessage := "InfisicalSecret CRD must have at least one managed secret reference set in the `managedKubeSecretReferences` or `managedKubeConfigMapReferences` field"
logger.Error(defaultErrors.New(errMessage), errMessage)
return ctrl.Result{}, defaultErrors.New(errMessage)
}
@@ -151,7 +152,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
api.API_CA_CERTIFICATE = ""
}
secretsCount, err := r.ReconcileInfisicalSecret(ctx, logger, infisicalSecretCRD, managedKubeSecretReferences)
secretsCount, err := r.ReconcileInfisicalSecret(ctx, logger, &infisicalSecretCRD, managedKubeSecretReferences, managedKubeConfigMapReferences)
r.SetReadyToSyncSecretsConditions(ctx, &infisicalSecretCRD, secretsCount, err)
if err != nil {
@@ -163,6 +164,12 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
numDeployments, err := controllerhelpers.ReconcileDeploymentsWithMultipleManagedSecrets(ctx, r.Client, logger, managedKubeSecretReferences)
r.SetInfisicalAutoRedeploymentReady(ctx, logger, &infisicalSecretCRD, numDeployments, err)
err = r.Client.Status().Update(ctx, &infisicalSecretCRD)
if err != nil {
logger.Error(err, "Could not set condition for AutoRedeployReady")
}
if err != nil {
logger.Error(err, fmt.Sprintf("unable to reconcile auto redeployment. Will requeue after [requeueTime=%v]", requeueTime))
return ctrl.Result{

View File

@@ -12,6 +12,7 @@ import (
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
"github.com/Infisical/infisical/k8-operator/packages/api"
"github.com/Infisical/infisical/k8-operator/packages/constants"
"github.com/Infisical/infisical/k8-operator/packages/crypto"
"github.com/Infisical/infisical/k8-operator/packages/model"
"github.com/Infisical/infisical/k8-operator/packages/util"
"github.com/go-logr/logr"
@@ -165,10 +166,24 @@ var infisicalSecretTemplateFunctions = template.FuncMap{
},
}
func (r *InfisicalSecretReconciler) createInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret, managedSecretReference v1alpha1.ManagedKubeSecretConfig, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error {
func convertBinaryToStringMap(binaryMap map[string][]byte) map[string]string {
stringMap := make(map[string]string)
for k, v := range binaryMap {
stringMap[k] = string(v)
}
return stringMap
}
func (r *InfisicalSecretReconciler) createInfisicalManagedKubeResource(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret, managedSecretReferenceInterface interface{}, secretsFromAPI []model.SingleEnvironmentVariable, ETag string, resourceType constants.ManagedKubeResourceType) error {
plainProcessedSecrets := make(map[string][]byte)
secretType := managedSecretReference.SecretType
managedTemplateData := managedSecretReference.Template
var managedTemplateData *v1alpha1.InfisicalSecretTemplate
if resourceType == constants.MANAGED_KUBE_RESOURCE_TYPE_SECRET {
managedTemplateData = managedSecretReferenceInterface.(v1alpha1.ManagedKubeSecretConfig).Template
} else if resourceType == constants.MANAGED_KUBE_RESOURCE_TYPE_CONFIG_MAP {
managedTemplateData = managedSecretReferenceInterface.(v1alpha1.ManagedKubeConfigMapConfig).Template
}
if managedTemplateData == nil || managedTemplateData.IncludeAllSecrets {
for _, secret := range secretsFromAPI {
@@ -221,6 +236,10 @@ func (r *InfisicalSecretReconciler) createInfisicalManagedKubeSecret(ctx context
}
}
if resourceType == constants.MANAGED_KUBE_RESOURCE_TYPE_SECRET {
managedSecretReference := managedSecretReferenceInterface.(v1alpha1.ManagedKubeSecretConfig)
annotations[constants.SECRET_VERSION_ANNOTATION] = ETag
// create a new secret as specified by the managed secret spec of CRD
newKubeSecretInstance := &corev1.Secret{
@@ -230,7 +249,7 @@ func (r *InfisicalSecretReconciler) createInfisicalManagedKubeSecret(ctx context
Annotations: annotations,
Labels: labels,
},
Type: corev1.SecretType(secretType),
Type: corev1.SecretType(managedSecretReference.SecretType),
Data: plainProcessedSecrets,
}
@@ -246,9 +265,41 @@ func (r *InfisicalSecretReconciler) createInfisicalManagedKubeSecret(ctx context
if err != nil {
return fmt.Errorf("unable to create the managed Kubernetes secret : %w", err)
}
logger.Info(fmt.Sprintf("Successfully created a managed Kubernetes secret with your Infisical secrets. Type: %s", secretType))
logger.Info(fmt.Sprintf("Successfully created a managed Kubernetes secret with your Infisical secrets. Type: %s", managedSecretReference.SecretType))
return nil
} else if resourceType == constants.MANAGED_KUBE_RESOURCE_TYPE_CONFIG_MAP {
managedSecretReference := managedSecretReferenceInterface.(v1alpha1.ManagedKubeConfigMapConfig)
// create a new config map as specified by the managed secret spec of CRD
newKubeConfigMapInstance := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: managedSecretReference.ConfigMapName,
Namespace: managedSecretReference.ConfigMapNamespace,
Annotations: annotations,
Labels: labels,
},
Data: convertBinaryToStringMap(plainProcessedSecrets),
}
if managedSecretReference.CreationPolicy == "Owner" {
// Set InfisicalSecret instance as the owner and controller of the managed config map
err := ctrl.SetControllerReference(&infisicalSecret, newKubeConfigMapInstance, r.Scheme)
if err != nil {
return err
}
}
err := r.Client.Create(ctx, newKubeConfigMapInstance)
if err != nil {
return fmt.Errorf("unable to create the managed Kubernetes config map : %w", err)
}
logger.Info(fmt.Sprintf("Successfully created a managed Kubernetes config map with your Infisical secrets. Type: %s", managedSecretReference.ConfigMapName))
return nil
}
return fmt.Errorf("invalid resource type")
}
func (r *InfisicalSecretReconciler) updateInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, managedSecretReference v1alpha1.ManagedKubeSecretConfig, managedKubeSecret corev1.Secret, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error {
@@ -302,6 +353,109 @@ func (r *InfisicalSecretReconciler) updateInfisicalManagedKubeSecret(ctx context
return nil
}
func (r *InfisicalSecretReconciler) updateInfisicalManagedConfigMap(ctx context.Context, logger logr.Logger, managedConfigMapReference v1alpha1.ManagedKubeConfigMapConfig, managedConfigMap corev1.ConfigMap, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error {
managedTemplateData := managedConfigMapReference.Template
plainProcessedSecrets := make(map[string][]byte)
if managedTemplateData == nil || managedTemplateData.IncludeAllSecrets {
for _, secret := range secretsFromAPI {
plainProcessedSecrets[secret.Key] = []byte(secret.Value)
}
}
if managedTemplateData != nil {
secretKeyValue := make(map[string]model.SecretTemplateOptions)
for _, secret := range secretsFromAPI {
secretKeyValue[secret.Key] = model.SecretTemplateOptions{
Value: secret.Value,
SecretPath: secret.SecretPath,
}
}
for templateKey, userTemplate := range managedTemplateData.Data {
tmpl, err := template.New("secret-templates").Funcs(infisicalSecretTemplateFunctions).Parse(userTemplate)
if err != nil {
return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err)
}
buf := bytes.NewBuffer(nil)
err = tmpl.Execute(buf, secretKeyValue)
if err != nil {
return fmt.Errorf("unable to execute template: %s [err=%v]", templateKey, err)
}
plainProcessedSecrets[templateKey] = buf.Bytes()
}
}
// Initialize the Annotations map if it's nil
if managedConfigMap.ObjectMeta.Annotations == nil {
managedConfigMap.ObjectMeta.Annotations = make(map[string]string)
}
managedConfigMap.Data = convertBinaryToStringMap(plainProcessedSecrets)
managedConfigMap.ObjectMeta.Annotations[constants.SECRET_VERSION_ANNOTATION] = ETag
err := r.Client.Update(ctx, &managedConfigMap)
if err != nil {
return fmt.Errorf("unable to update Kubernetes config map because [%w]", err)
}
logger.Info("successfully updated managed Kubernetes config map")
return nil
}
func (r *InfisicalSecretReconciler) fetchSecretsFromAPI(ctx context.Context, logger logr.Logger, authDetails util.AuthenticationDetails, infisicalClient infisicalSdk.InfisicalClientInterface, infisicalSecret v1alpha1.InfisicalSecret) ([]model.SingleEnvironmentVariable, error) {
if authDetails.AuthStrategy == util.AuthStrategy.SERVICE_ACCOUNT { // Service Account // ! Legacy auth method
serviceAccountCreds, err := r.getInfisicalServiceAccountCredentialsFromKubeSecret(ctx, infisicalSecret)
if err != nil {
return nil, fmt.Errorf("ReconcileInfisicalSecret: unable to get service account creds from kube secret [err=%s]", err)
}
plainTextSecretsFromApi, err := util.GetPlainTextSecretsViaServiceAccount(infisicalClient, serviceAccountCreds, infisicalSecret.Spec.Authentication.ServiceAccount.ProjectId, infisicalSecret.Spec.Authentication.ServiceAccount.EnvironmentName)
if err != nil {
return nil, fmt.Errorf("\nfailed to get secrets because [err=%v]", err)
}
logger.Info("ReconcileInfisicalSecret: Fetched secrets via service account")
return plainTextSecretsFromApi, nil
} else if authDetails.AuthStrategy == util.AuthStrategy.SERVICE_TOKEN { // Service Tokens // ! Legacy / Deprecated auth method
infisicalToken, err := r.getInfisicalTokenFromKubeSecret(ctx, infisicalSecret)
if err != nil {
return nil, fmt.Errorf("ReconcileInfisicalSecret: unable to get service token from kube secret [err=%s]", err)
}
envSlug := infisicalSecret.Spec.Authentication.ServiceToken.SecretsScope.EnvSlug
secretsPath := infisicalSecret.Spec.Authentication.ServiceToken.SecretsScope.SecretsPath
recursive := infisicalSecret.Spec.Authentication.ServiceToken.SecretsScope.Recursive
plainTextSecretsFromApi, err := util.GetPlainTextSecretsViaServiceToken(infisicalClient, infisicalToken, envSlug, secretsPath, recursive)
if err != nil {
return nil, fmt.Errorf("\nfailed to get secrets because [err=%v]", err)
}
logger.Info("ReconcileInfisicalSecret: Fetched secrets via [type=SERVICE_TOKEN]")
return plainTextSecretsFromApi, nil
} else if authDetails.IsMachineIdentityAuth { // * Machine Identity authentication, the SDK will be authenticated at this point
plainTextSecretsFromApi, err := util.GetPlainTextSecretsViaMachineIdentity(infisicalClient, authDetails.MachineIdentityScope)
if err != nil {
return nil, fmt.Errorf("\nfailed to get secrets because [err=%v]", err)
}
logger.Info(fmt.Sprintf("ReconcileInfisicalSecret: Fetched secrets via machine identity [type=%v]", authDetails.AuthStrategy))
return plainTextSecretsFromApi, nil
} else {
return nil, errors.New("no authentication method provided. Please configure a authentication method then try again")
}
}
func (r *InfisicalSecretReconciler) getResourceVariables(infisicalSecret v1alpha1.InfisicalSecret) util.ResourceVariables {
var resourceVariables util.ResourceVariables
@@ -336,9 +490,13 @@ func (r *InfisicalSecretReconciler) updateResourceVariables(infisicalSecret v1al
infisicalSecretResourceVariablesMap[string(infisicalSecret.UID)] = resourceVariables
}
func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret, managedKubeSecretReferences []v1alpha1.ManagedKubeSecretConfig) (int, error) {
func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, managedKubeSecretReferences []v1alpha1.ManagedKubeSecretConfig, managedKubeConfigMapReferences []v1alpha1.ManagedKubeConfigMapConfig) (int, error) {
resourceVariables := r.getResourceVariables(infisicalSecret)
if infisicalSecret == nil {
return 0, fmt.Errorf("infisicalSecret is nil")
}
resourceVariables := r.getResourceVariables(*infisicalSecret)
infisicalClient := resourceVariables.InfisicalClient
cancelCtx := resourceVariables.CancelCtx
authDetails := resourceVariables.AuthDetails
@@ -346,22 +504,28 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context
if authDetails.AuthStrategy == "" {
logger.Info("No authentication strategy found. Attempting to authenticate")
authDetails, err = r.handleAuthentication(ctx, infisicalSecret, infisicalClient)
r.SetInfisicalTokenLoadCondition(ctx, logger, &infisicalSecret, authDetails.AuthStrategy, err)
authDetails, err = r.handleAuthentication(ctx, *infisicalSecret, infisicalClient)
r.SetInfisicalTokenLoadCondition(ctx, logger, infisicalSecret, authDetails.AuthStrategy, err)
if err != nil {
return 0, fmt.Errorf("unable to authenticate [err=%s]", err)
}
r.updateResourceVariables(infisicalSecret, util.ResourceVariables{
r.updateResourceVariables(*infisicalSecret, util.ResourceVariables{
InfisicalClient: infisicalClient,
CancelCtx: cancelCtx,
AuthDetails: authDetails,
})
}
secretsCount := 0
plainTextSecretsFromApi, err := r.fetchSecretsFromAPI(ctx, logger, authDetails, infisicalClient, *infisicalSecret)
if err != nil {
return 0, fmt.Errorf("failed to fetch secrets from API for managed secrets [err=%s]", err)
}
secretsCount := len(plainTextSecretsFromApi)
if len(managedKubeSecretReferences) > 0 {
for _, managedSecretReference := range managedKubeSecretReferences {
// Look for managed secret by name and namespace
managedKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
@@ -373,70 +537,43 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context
return 0, fmt.Errorf("something went wrong when fetching the managed Kubernetes secret [%w]", err)
}
// Get exiting Etag if exists
secretVersionBasedOnETag := ""
if managedKubeSecret != nil {
secretVersionBasedOnETag = managedKubeSecret.Annotations[constants.SECRET_VERSION_ANNOTATION]
}
var plainTextSecretsFromApi []model.SingleEnvironmentVariable
var updateDetails model.RequestUpdateUpdateDetails
if authDetails.AuthStrategy == util.AuthStrategy.SERVICE_ACCOUNT { // Service Account // ! Legacy auth method
serviceAccountCreds, err := r.getInfisicalServiceAccountCredentialsFromKubeSecret(ctx, infisicalSecret)
if err != nil {
return 0, fmt.Errorf("ReconcileInfisicalSecret: unable to get service account creds from kube secret [err=%s]", err)
}
plainTextSecretsFromApi, updateDetails, err = util.GetPlainTextSecretsViaServiceAccount(infisicalClient, serviceAccountCreds, infisicalSecret.Spec.Authentication.ServiceAccount.ProjectId, infisicalSecret.Spec.Authentication.ServiceAccount.EnvironmentName, secretVersionBasedOnETag)
if err != nil {
return 0, fmt.Errorf("\nfailed to get secrets because [err=%v]", err)
}
logger.Info("ReconcileInfisicalSecret: Fetched secrets via service account")
} else if authDetails.AuthStrategy == util.AuthStrategy.SERVICE_TOKEN { // Service Tokens // ! Legacy / Deprecated auth method
infisicalToken, err := r.getInfisicalTokenFromKubeSecret(ctx, infisicalSecret)
if err != nil {
return 0, fmt.Errorf("ReconcileInfisicalSecret: unable to get service token from kube secret [err=%s]", err)
}
envSlug := infisicalSecret.Spec.Authentication.ServiceToken.SecretsScope.EnvSlug
secretsPath := infisicalSecret.Spec.Authentication.ServiceToken.SecretsScope.SecretsPath
recursive := infisicalSecret.Spec.Authentication.ServiceToken.SecretsScope.Recursive
plainTextSecretsFromApi, updateDetails, err = util.GetPlainTextSecretsViaServiceToken(infisicalClient, infisicalToken, secretVersionBasedOnETag, envSlug, secretsPath, recursive)
if err != nil {
return 0, fmt.Errorf("\nfailed to get secrets because [err=%v]", err)
}
logger.Info("ReconcileInfisicalSecret: Fetched secrets via [type=SERVICE_TOKEN]")
} else if authDetails.IsMachineIdentityAuth { // * Machine Identity authentication, the SDK will be authenticated at this point
plainTextSecretsFromApi, updateDetails, err = util.GetPlainTextSecretsViaMachineIdentity(infisicalClient, secretVersionBasedOnETag, authDetails.MachineIdentityScope)
if err != nil {
return 0, fmt.Errorf("\nfailed to get secrets because [err=%v]", err)
}
logger.Info(fmt.Sprintf("ReconcileInfisicalSecret: Fetched secrets via machine identity [type=%v]", authDetails.AuthStrategy))
} else {
return 0, errors.New("no authentication method provided. Please configure a authentication method then try again")
}
secretsCount = len(plainTextSecretsFromApi)
newEtag := crypto.ComputeEtag([]byte(fmt.Sprintf("%v", plainTextSecretsFromApi)))
if managedKubeSecret == nil {
if err := r.createInfisicalManagedKubeSecret(ctx, logger, infisicalSecret, managedSecretReference, plainTextSecretsFromApi, updateDetails.ETag); err != nil {
if err := r.createInfisicalManagedKubeResource(ctx, logger, *infisicalSecret, managedSecretReference, plainTextSecretsFromApi, newEtag, constants.MANAGED_KUBE_RESOURCE_TYPE_SECRET); err != nil {
return 0, fmt.Errorf("failed to create managed secret [err=%s]", err)
}
} else {
if err := r.updateInfisicalManagedKubeSecret(ctx, logger, managedSecretReference, *managedKubeSecret, plainTextSecretsFromApi, updateDetails.ETag); err != nil {
if err := r.updateInfisicalManagedKubeSecret(ctx, logger, managedSecretReference, *managedKubeSecret, plainTextSecretsFromApi, newEtag); err != nil {
return 0, fmt.Errorf("failed to update managed secret [err=%s]", err)
}
}
}
}
if len(managedKubeConfigMapReferences) > 0 {
for _, managedConfigMapReference := range managedKubeConfigMapReferences {
managedKubeConfigMap, err := util.GetKubeConfigMapByNamespacedName(ctx, r.Client, types.NamespacedName{
Name: managedConfigMapReference.ConfigMapName,
Namespace: managedConfigMapReference.ConfigMapNamespace,
})
if err != nil && !k8Errors.IsNotFound(err) {
return 0, fmt.Errorf("something went wrong when fetching the managed Kubernetes config map [%w]", err)
}
newEtag := crypto.ComputeEtag([]byte(fmt.Sprintf("%v", plainTextSecretsFromApi)))
if managedKubeConfigMap == nil {
if err := r.createInfisicalManagedKubeResource(ctx, logger, *infisicalSecret, managedConfigMapReference, plainTextSecretsFromApi, newEtag, constants.MANAGED_KUBE_RESOURCE_TYPE_CONFIG_MAP); err != nil {
return 0, fmt.Errorf("failed to create managed config map [err=%s]", err)
}
} else {
if err := r.updateInfisicalManagedConfigMap(ctx, logger, managedConfigMapReference, *managedKubeConfigMap, plainTextSecretsFromApi, newEtag); err != nil {
return 0, fmt.Errorf("failed to update managed config map [err=%s]", err)
}
}
}
}
return secretsCount, nil
}

View File

@@ -26,6 +26,13 @@ const (
PUSH_SECRET_DELETE_POLICY_ENABLED PushSecretDeletionPolicy = "Delete"
)
type ManagedKubeResourceType string
const (
MANAGED_KUBE_RESOURCE_TYPE_SECRET ManagedKubeResourceType = "Secret"
MANAGED_KUBE_RESOURCE_TYPE_CONFIG_MAP ManagedKubeResourceType = "ConfigMap"
)
type DynamicSecretLeaseRevocationPolicy string
const (

View File

@@ -11,11 +11,6 @@ type MachineIdentityDetails struct {
ClientSecret string
}
type RequestUpdateUpdateDetails struct {
Modified bool
ETag string
}
type SingleEnvironmentVariable struct {
Key string `json:"key"`
Value string `json:"value"`

View File

@@ -25,6 +25,16 @@ func GetKubeSecretByNamespacedName(ctx context.Context, reconcilerClient client.
return kubeSecret, err
}
func GetKubeConfigMapByNamespacedName(ctx context.Context, reconcilerClient client.Client, namespacedName types.NamespacedName) (*corev1.ConfigMap, error) {
kubeConfigMap := &corev1.ConfigMap{}
err := reconcilerClient.Get(ctx, namespacedName, kubeConfigMap)
if err != nil {
kubeConfigMap = nil
}
return kubeConfigMap, err
}
func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, universalAuthRef v1alpha1.KubeSecretReference) (machineIdentityDetails model.MachineIdentityDetails, err error) {
universalAuthCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{

View File

@@ -6,7 +6,6 @@ import (
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
"github.com/Infisical/infisical/k8-operator/packages/api"
"github.com/Infisical/infisical/k8-operator/packages/crypto"
"github.com/Infisical/infisical/k8-operator/packages/model"
"github.com/go-resty/resty/v2"
infisical "github.com/infisical/go-sdk"
@@ -49,7 +48,7 @@ func GetServiceTokenDetails(infisicalToken string) (api.GetServiceTokenDetailsRe
return serviceTokenDetails, nil
}
func GetPlainTextSecretsViaMachineIdentity(infisicalClient infisical.InfisicalClientInterface, etag string, secretScope v1alpha1.MachineIdentityScopeInWorkspace) ([]model.SingleEnvironmentVariable, model.RequestUpdateUpdateDetails, error) {
func GetPlainTextSecretsViaMachineIdentity(infisicalClient infisical.InfisicalClientInterface, secretScope v1alpha1.MachineIdentityScopeInWorkspace) ([]model.SingleEnvironmentVariable, error) {
secrets, err := infisicalClient.Secrets().List(infisical.ListSecretsOptions{
ProjectSlug: secretScope.ProjectSlug,
@@ -61,7 +60,7 @@ func GetPlainTextSecretsViaMachineIdentity(infisicalClient infisical.InfisicalCl
})
if err != nil {
return nil, model.RequestUpdateUpdateDetails{}, err
return nil, fmt.Errorf("unable to get secrets. [err=%v]", err)
}
var environmentVariables []model.SingleEnvironmentVariable
@@ -77,18 +76,13 @@ func GetPlainTextSecretsViaMachineIdentity(infisicalClient infisical.InfisicalCl
})
}
newEtag := crypto.ComputeEtag([]byte(fmt.Sprintf("%v", environmentVariables)))
return environmentVariables, model.RequestUpdateUpdateDetails{
Modified: etag != newEtag,
ETag: newEtag,
}, nil
return environmentVariables, nil
}
func GetPlainTextSecretsViaServiceToken(infisicalClient infisical.InfisicalClientInterface, fullServiceToken string, etag string, envSlug string, secretPath string, recursive bool) ([]model.SingleEnvironmentVariable, model.RequestUpdateUpdateDetails, error) {
func GetPlainTextSecretsViaServiceToken(infisicalClient infisical.InfisicalClientInterface, fullServiceToken string, envSlug string, secretPath string, recursive bool) ([]model.SingleEnvironmentVariable, error) {
serviceTokenParts := strings.SplitN(fullServiceToken, ".", 4)
if len(serviceTokenParts) < 4 {
return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("invalid service token entered. Please double check your service token and try again")
return nil, fmt.Errorf("invalid service token entered. Please double check your service token and try again")
}
serviceToken := fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2])
@@ -100,7 +94,7 @@ func GetPlainTextSecretsViaServiceToken(infisicalClient infisical.InfisicalClien
serviceTokenDetails, err := api.CallGetServiceTokenDetailsV2(httpClient)
if err != nil {
return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("unable to get service token details. [err=%v]", err)
return nil, fmt.Errorf("unable to get service token details. [err=%v]", err)
}
secrets, err := infisicalClient.Secrets().List(infisical.ListSecretsOptions{
@@ -113,7 +107,7 @@ func GetPlainTextSecretsViaServiceToken(infisicalClient infisical.InfisicalClien
})
if err != nil {
return nil, model.RequestUpdateUpdateDetails{}, err
return nil, err
}
var environmentVariables []model.SingleEnvironmentVariable
@@ -129,31 +123,26 @@ func GetPlainTextSecretsViaServiceToken(infisicalClient infisical.InfisicalClien
})
}
newEtag := crypto.ComputeEtag([]byte(fmt.Sprintf("%v", environmentVariables)))
return environmentVariables, model.RequestUpdateUpdateDetails{
Modified: etag != newEtag,
ETag: newEtag,
}, nil
return environmentVariables, nil
}
// Fetches plaintext secrets from an API endpoint using a service account.
// The function fetches the service account details and keys, decrypts the workspace key, fetches the encrypted secrets for the specified project and environment, and decrypts the secrets using the decrypted workspace key.
// Returns the plaintext secrets, encrypted secrets response, and any errors that occurred during the process.
func GetPlainTextSecretsViaServiceAccount(infisicalClient infisical.InfisicalClientInterface, serviceAccountCreds model.ServiceAccountDetails, projectId string, environmentName string, etag string) ([]model.SingleEnvironmentVariable, model.RequestUpdateUpdateDetails, error) {
func GetPlainTextSecretsViaServiceAccount(infisicalClient infisical.InfisicalClientInterface, serviceAccountCreds model.ServiceAccountDetails, projectId string, environmentName string) ([]model.SingleEnvironmentVariable, error) {
httpClient := resty.New()
httpClient.SetAuthToken(serviceAccountCreds.AccessKey).
SetHeader("Accept", "application/json")
serviceAccountDetails, err := api.CallGetServiceTokenAccountDetailsV2(httpClient)
if err != nil {
return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("GetPlainTextSecretsViaServiceAccount: unable to get service account details. [err=%v]", err)
return nil, fmt.Errorf("GetPlainTextSecretsViaServiceAccount: unable to get service account details. [err=%v]", err)
}
serviceAccountKeys, err := api.CallGetServiceAccountKeysV2(httpClient, api.GetServiceAccountKeysRequest{ServiceAccountId: serviceAccountDetails.ServiceAccount.ID})
if err != nil {
return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("GetPlainTextSecretsViaServiceAccount: unable to get service account key details. [err=%v]", err)
return nil, fmt.Errorf("GetPlainTextSecretsViaServiceAccount: unable to get service account key details. [err=%v]", err)
}
// find key for requested project
@@ -165,7 +154,7 @@ func GetPlainTextSecretsViaServiceAccount(infisicalClient infisical.InfisicalCli
}
if workspaceServiceAccountKey.ID == "" || workspaceServiceAccountKey.EncryptedKey == "" || workspaceServiceAccountKey.Nonce == "" || serviceAccountCreds.PublicKey == "" || serviceAccountCreds.PrivateKey == "" {
return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("unable to find key for [projectId=%s] [err=%v]. Ensure that the given service account has access to given projectId", projectId, err)
return nil, fmt.Errorf("unable to find key for [projectId=%s] [err=%v]. Ensure that the given service account has access to given projectId", projectId, err)
}
secrets, err := infisicalClient.Secrets().List(infisical.ListSecretsOptions{
@@ -178,7 +167,7 @@ func GetPlainTextSecretsViaServiceAccount(infisicalClient infisical.InfisicalCli
})
if err != nil {
return nil, model.RequestUpdateUpdateDetails{}, err
return nil, err
}
var environmentVariables []model.SingleEnvironmentVariable
@@ -193,49 +182,5 @@ func GetPlainTextSecretsViaServiceAccount(infisicalClient infisical.InfisicalCli
})
}
newEtag := crypto.ComputeEtag([]byte(fmt.Sprintf("%v", environmentVariables)))
return environmentVariables, model.RequestUpdateUpdateDetails{
Modified: etag != newEtag,
ETag: newEtag,
}, nil
}
func getSecretsByKeys(secrets []model.SingleEnvironmentVariable) map[string]model.SingleEnvironmentVariable {
secretMapByName := make(map[string]model.SingleEnvironmentVariable, len(secrets))
for _, secret := range secrets {
secretMapByName[secret.Key] = secret
}
return secretMapByName
}
func MergeRawImportedSecrets(secrets []model.SingleEnvironmentVariable, importedSecrets []api.ImportedRawSecretV3) []model.SingleEnvironmentVariable {
if importedSecrets == nil {
return secrets
}
hasOverriden := make(map[string]bool)
for _, sec := range secrets {
hasOverriden[sec.Key] = true
}
for i := len(importedSecrets) - 1; i >= 0; i-- {
importSec := importedSecrets[i]
for _, sec := range importSec.Secrets {
if _, ok := hasOverriden[sec.SecretKey]; !ok {
secrets = append(secrets, model.SingleEnvironmentVariable{
Key: sec.SecretKey,
Value: sec.SecretValue,
Type: sec.Type,
ID: sec.ID,
})
hasOverriden[sec.SecretKey] = true
}
}
}
return secrets
return environmentVariables, nil
}