add substitution into k8 and fix loading token

This commit is contained in:
Maidul Islam
2022-12-15 22:25:44 -05:00
parent 72bf160f2e
commit 7fe2e15a98
6 changed files with 122 additions and 13 deletions
@@ -11,7 +11,7 @@ type KubeSecretReference struct {
// The name space where the Kubernetes Secret is located // The name space where the Kubernetes Secret is located
// +kubebuilder:validation:Required // +kubebuilder:validation:Required
SecretNamespace string `json:"secretNamespace,omitempty"` SecretNamespace string `json:"secretNamespace"`
} }
// InfisicalSecretSpec defines the desired state of InfisicalSecret // InfisicalSecretSpec defines the desired state of InfisicalSecret
@@ -21,11 +21,11 @@ type InfisicalSecretSpec struct {
// The Infisical project id // The Infisical project id
// +kubebuilder:validation:Required // +kubebuilder:validation:Required
ProjectId string `json:"projectId,omitempty"` ProjectId string `json:"projectId"`
// The Infisical environment such as dev, prod, testing // The Infisical environment such as dev, prod, testing
// +kubebuilder:validation:Required // +kubebuilder:validation:Required
Environment string `json:"environment,omitempty"` Environment string `json:"environment"`
} }
// InfisicalSecretStatus defines the observed state of InfisicalSecret // InfisicalSecretStatus defines the observed state of InfisicalSecret
@@ -48,6 +48,7 @@ spec:
type: string type: string
required: required:
- secretName - secretName
- secretNamespace
type: object type: object
managedSecret: managedSecret:
properties: properties:
@@ -59,10 +60,14 @@ spec:
type: string type: string
required: required:
- secretName - secretName
- secretNamespace
type: object type: object
projectId: projectId:
description: The Infisical project id description: The Infisical project id
type: string type: string
required:
- environment
- projectId
type: object type: object
status: status:
description: InfisicalSecretStatus defines the observed state of InfisicalSecret description: InfisicalSecretStatus defines the observed state of InfisicalSecret
@@ -10,6 +10,7 @@ metadata:
name: infisicalsecret-sample name: infisicalsecret-sample
spec: spec:
projectId: 62faf98ae0b05e8529b5da46 projectId: 62faf98ae0b05e8529b5da46
environment: dev
infisicalToken: infisicalToken:
secretName: service-token secretName: service-token
secretNamespace: default secretNamespace: default
@@ -71,6 +71,6 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
// SetupWithManager sets up the controller with the Manager. // SetupWithManager sets up the controller with the Manager.
func (r *InfisicalSecretReconciler) SetupWithManager(mgr ctrl.Manager) error { func (r *InfisicalSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr). return ctrl.NewControllerManagedBy(mgr).
For(&secretsv1alpha1.InfisicalSecret{}). For(&secretsv1alpha1.InfisicalSecret{}). // TODO we should also be watching secrets with the name specifed
Complete(r) Complete(r)
} }
@@ -3,6 +3,7 @@ package controllers
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"github.com/Infisical/infisical/k8-operator/api/v1alpha1" "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
api "github.com/Infisical/infisical/k8-operator/packages/api" api "github.com/Infisical/infisical/k8-operator/packages/api"
@@ -28,12 +29,12 @@ func (r *InfisicalSecretReconciler) GetKubeSecretByNamespacedName(ctx context.Co
func (r *InfisicalSecretReconciler) GetInfisicalToken(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (string, error) { func (r *InfisicalSecretReconciler) GetInfisicalToken(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (string, error) {
tokenSecret, err := r.GetKubeSecretByNamespacedName(ctx, types.NamespacedName{ tokenSecret, err := r.GetKubeSecretByNamespacedName(ctx, types.NamespacedName{
Namespace: infisicalSecret.Spec.ManagedSecret.SecretNamespace, Namespace: infisicalSecret.Spec.InfisicalToken.SecretNamespace,
Name: infisicalSecret.Spec.ManagedSecret.SecretName, Name: infisicalSecret.Spec.InfisicalToken.SecretName,
}) })
if err != nil { if err != nil {
return "", fmt.Errorf("failed to read infisical token secret from secret named [%s] in namespace [%s]: with error [%w]", infisicalSecret.Spec.ManagedSecret.SecretName, infisicalSecret.Spec.ManagedSecret.SecretNamespace, err) return "", fmt.Errorf("failed to read Infisical token secret from secret named [%s] in namespace [%s]: with error [%w]", infisicalSecret.Spec.ManagedSecret.SecretName, infisicalSecret.Spec.ManagedSecret.SecretNamespace, err)
} }
infisicalServiceToken := tokenSecret.Data[INFISICAL_TOKEN_SECRET_KEY_NAME] infisicalServiceToken := tokenSecret.Data[INFISICAL_TOKEN_SECRET_KEY_NAME]
@@ -41,7 +42,7 @@ func (r *InfisicalSecretReconciler) GetInfisicalToken(ctx context.Context, infis
return "", fmt.Errorf("the Infisical token is not set in the Kubernetes secret. Please add the key [%s] with the corresponding token value", INFISICAL_TOKEN_SECRET_KEY_NAME) return "", fmt.Errorf("the Infisical token is not set in the Kubernetes secret. Please add the key [%s] with the corresponding token value", INFISICAL_TOKEN_SECRET_KEY_NAME)
} }
return string(infisicalServiceToken), nil return strings.Replace(string(infisicalServiceToken), " ", "", -1), nil
} }
func (r *InfisicalSecretReconciler) CreateInfisicalManagedKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret, secretsFromAPI []models.SingleEnvironmentVariable) error { func (r *InfisicalSecretReconciler) CreateInfisicalManagedKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret, secretsFromAPI []models.SingleEnvironmentVariable) error {
@@ -87,6 +88,7 @@ func (r *InfisicalSecretReconciler) UpdateInfisicalManagedKubeSecret(ctx context
func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) error { func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) error {
infisicalToken, err := r.GetInfisicalToken(ctx, infisicalSecret) infisicalToken, err := r.GetInfisicalToken(ctx, infisicalSecret)
r.SetInfisicalTokenLoadCondition(ctx, &infisicalSecret, err)
if err != nil { if err != nil {
return fmt.Errorf("unable to load Infisical Token from the specified Kubernetes secret with error [%w]", err) return fmt.Errorf("unable to load Infisical Token from the specified Kubernetes secret with error [%w]", err)
} }
@@ -101,6 +103,7 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context
} }
secretsFromApi, err := api.GetAllEnvironmentVariables(infisicalSecret.Spec.ProjectId, infisicalSecret.Spec.Environment, infisicalToken) secretsFromApi, err := api.GetAllEnvironmentVariables(infisicalSecret.Spec.ProjectId, infisicalSecret.Spec.Environment, infisicalToken)
if err != nil { if err != nil {
return err return err
} }
@@ -113,12 +116,14 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context
} }
func (r *InfisicalSecretReconciler) SetReadyToSyncSecretsConditions(ctx context.Context, infisicalSecret *v1alpha1.InfisicalSecret, maybeSecretsSyncError error) { // Conditions
func (r *InfisicalSecretReconciler) SetReadyToSyncSecretsConditions(ctx context.Context, infisicalSecret *v1alpha1.InfisicalSecret, errorToConditionOn error) {
if infisicalSecret.Status.Conditions == nil { if infisicalSecret.Status.Conditions == nil {
infisicalSecret.Status.Conditions = []metav1.Condition{} infisicalSecret.Status.Conditions = []metav1.Condition{}
} }
if maybeSecretsSyncError == nil { if errorToConditionOn == nil {
meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{ meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{
Type: "secrets.infisical.com/ReadyToSyncSecrets", Type: "secrets.infisical.com/ReadyToSyncSecrets",
Status: metav1.ConditionTrue, Status: metav1.ConditionTrue,
@@ -130,12 +135,39 @@ func (r *InfisicalSecretReconciler) SetReadyToSyncSecretsConditions(ctx context.
Type: "secrets.infisical.com/ReadyToSyncSecrets", Type: "secrets.infisical.com/ReadyToSyncSecrets",
Status: metav1.ConditionFalse, Status: metav1.ConditionFalse,
Reason: "Error", Reason: "Error",
Message: fmt.Sprintf("Failed to update secret because: %v", maybeSecretsSyncError), Message: fmt.Sprintf("Failed to update secret because: %v", errorToConditionOn),
}) })
} }
err := r.Client.Status().Update(ctx, infisicalSecret) err := r.Client.Status().Update(ctx, infisicalSecret)
if err != nil { if err != nil {
fmt.Println("Could not set condition") fmt.Println("Could not set condition", err)
}
}
func (r *InfisicalSecretReconciler) SetInfisicalTokenLoadCondition(ctx context.Context, infisicalSecret *v1alpha1.InfisicalSecret, errorToConditionOn error) {
if infisicalSecret.Status.Conditions == nil {
infisicalSecret.Status.Conditions = []metav1.Condition{}
}
if errorToConditionOn == nil {
meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{
Type: "secrets.infisical.com/LoadedInfisicalToken",
Status: metav1.ConditionTrue,
Reason: "OK",
Message: "Infisical controller has located the Infisical token in provided Kubernetes secret",
})
} else {
meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{
Type: "secrets.infisical.com/LoadedInfisicalToken",
Status: metav1.ConditionFalse,
Reason: "Error",
Message: fmt.Sprintf("Failed to load Infisical Token because: %v", errorToConditionOn),
})
}
err := r.Client.Status().Update(ctx, infisicalSecret)
if err != nil {
fmt.Println("Could not set condition for LoadedInfisicalToken")
} }
} }
+72 -1
View File
@@ -4,6 +4,7 @@ import (
"encoding/base64" "encoding/base64"
"errors" "errors"
"fmt" "fmt"
"regexp"
"strings" "strings"
"github.com/Infisical/infisical/k8-operator/packages/crypto" "github.com/Infisical/infisical/k8-operator/packages/crypto"
@@ -20,7 +21,7 @@ func GetAllEnvironmentVariables(projectId string, envName string, infisicalToken
return nil, err return nil, err
} }
return envsFromApi, nil return SubstituteSecrets(envsFromApi), nil
} }
func GetSecretsFromAPIUsingInfisicalToken(infisicalToken string, envName string, projectId string) ([]models.SingleEnvironmentVariable, error) { func GetSecretsFromAPIUsingInfisicalToken(infisicalToken string, envName string, projectId string) ([]models.SingleEnvironmentVariable, error) {
@@ -106,3 +107,73 @@ func GetSecretsFromAPIUsingInfisicalToken(infisicalToken string, envName string,
return listOfEnv, nil return listOfEnv, nil
} }
func getExpandedEnvVariable(secrets []models.SingleEnvironmentVariable, variableWeAreLookingFor string, hashMapOfCompleteVariables map[string]string, hashMapOfSelfRefs map[string]string) string {
if value, found := hashMapOfCompleteVariables[variableWeAreLookingFor]; found {
return value
}
for _, secret := range secrets {
if secret.Key == variableWeAreLookingFor {
regex := regexp.MustCompile(`\${([^\}]*)}`)
variablesToPopulate := regex.FindAllString(secret.Value, -1)
// case: variable is a constant so return its value
if len(variablesToPopulate) == 0 {
return secret.Value
}
valueToEdit := secret.Value
for _, variableWithSign := range variablesToPopulate {
variableWithoutSign := strings.Trim(variableWithSign, "}")
variableWithoutSign = strings.Trim(variableWithoutSign, "${")
// case: reference to self
if variableWithoutSign == secret.Key {
hashMapOfSelfRefs[variableWithoutSign] = variableWithoutSign
continue
} else {
var expandedVariableValue string
if preComputedVariable, found := hashMapOfCompleteVariables[variableWithoutSign]; found {
expandedVariableValue = preComputedVariable
} else {
expandedVariableValue = getExpandedEnvVariable(secrets, variableWithoutSign, hashMapOfCompleteVariables, hashMapOfSelfRefs)
hashMapOfCompleteVariables[variableWithoutSign] = expandedVariableValue
}
// If after expanding all the vars above, is the current var a self ref? if so no replacement needed for it
if _, found := hashMapOfSelfRefs[variableWithoutSign]; found {
continue
} else {
valueToEdit = strings.ReplaceAll(valueToEdit, variableWithSign, expandedVariableValue)
}
}
}
return valueToEdit
} else {
continue
}
}
return "${" + variableWeAreLookingFor + "}"
}
func SubstituteSecrets(secrets []models.SingleEnvironmentVariable) []models.SingleEnvironmentVariable {
hashMapOfCompleteVariables := make(map[string]string)
hashMapOfSelfRefs := make(map[string]string)
expandedSecrets := []models.SingleEnvironmentVariable{}
for _, secret := range secrets {
expandedVariable := getExpandedEnvVariable(secrets, secret.Key, hashMapOfCompleteVariables, hashMapOfSelfRefs)
expandedSecrets = append(expandedSecrets, models.SingleEnvironmentVariable{
Key: secret.Key,
Value: expandedVariable,
})
}
return expandedSecrets
}