mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 20:27:43 +00:00
add substitution into k8 and fix loading token
This commit is contained in:
@@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user