@ -3,6 +3,8 @@ package util
import (
"encoding/base64"
"fmt"
"path"
"regexp"
"strings"
"github.com/Infisical/infisical/k8-operator/packages/api"
@ -48,7 +50,7 @@ func GetServiceTokenDetails(infisicalToken string) (api.GetServiceTokenDetailsRe
return serviceTokenDetails , nil
}
func GetPlainTextSecretsViaServiceToken ( fullServiceToken string , etag string ) ( [ ] model . SingleEnvironmentVariable , api . GetEncryptedSecretsV3Response , error ) {
func GetPlainTextSecretsViaServiceToken ( fullServiceToken string , etag string , envSlug string , secretPath string ) ( [ ] model . SingleEnvironmentVariable , api . GetEncryptedSecretsV3Response , error ) {
serviceTokenParts := strings . SplitN ( fullServiceToken , "." , 4 )
if len ( serviceTokenParts ) < 4 {
return nil , api . GetEncryptedSecretsV3Response { } , fmt . Errorf ( "invalid service token entered. Please double check your service token and try again" )
@ -68,9 +70,9 @@ func GetPlainTextSecretsViaServiceToken(fullServiceToken string, etag string) ([
encryptedSecretsResponse , err := api . CallGetSecretsV3 ( httpClient , api . GetEncryptedSecretsV3Request {
WorkspaceId : serviceTokenDetails . Workspace ,
Environment : serviceTokenDetails. Environment ,
Environment : envSlug ,
ETag : etag ,
SecretPath : se rviceTokenDetails. Se cretPath,
SecretPath : se cretPath,
} )
if err != nil {
@ -92,7 +94,10 @@ func GetPlainTextSecretsViaServiceToken(fullServiceToken string, etag string) ([
return nil , api . GetEncryptedSecretsV3Response { } , fmt . Errorf ( "unable to decrypt your secrets [err=%v]" , err )
}
return plainTextSecrets , encryptedSecretsResponse , nil
// expand secrets that are referenced
expandedSecrets := ExpandSecrets ( plainTextSecrets , fullServiceToken )
return expandedSecrets , encryptedSecretsResponse , nil
}
// Fetches plaintext secrets from an API endpoint using a service account.
@ -252,3 +257,104 @@ func GetPlainTextSecrets(key []byte, encryptedSecretsResponse api.GetEncryptedSe
return plainTextSecrets , nil
}
var secRefRegex = regexp . MustCompile ( ` \$ { ([^\}]*)} ` )
func recursivelyExpandSecret ( expandedSecs map [ string ] string , interpolatedSecs map [ string ] string , crossSecRefFetch func ( env string , path [ ] string , key string ) string , key string ) string {
if v , ok := expandedSecs [ key ] ; ok {
return v
}
interpolatedVal , ok := interpolatedSecs [ key ]
if ! ok {
return ""
// panic(fmt.Errorf("Could not find referred secret with key name %s", key), "Please check it refers a")
}
refs := secRefRegex . FindAllStringSubmatch ( interpolatedVal , - 1 )
for _ , val := range refs {
// key: "${something}" val: [${something},something]
interpolatedExp , interpolationKey := val [ 0 ] , val [ 1 ]
ref := strings . Split ( interpolationKey , "." )
// ${KEY1} => [key1]
if len ( ref ) == 1 {
val := recursivelyExpandSecret ( expandedSecs , interpolatedSecs , crossSecRefFetch , interpolationKey )
interpolatedVal = strings . ReplaceAll ( interpolatedVal , interpolatedExp , val )
continue
}
// cross board reference ${env.folder.key1} => [env folder key1]
if len ( ref ) > 1 {
secEnv , tmpSecPath , secKey := ref [ 0 ] , ref [ 1 : len ( ref ) - 1 ] , ref [ len ( ref ) - 1 ]
interpolatedSecs [ interpolationKey ] = crossSecRefFetch ( secEnv , tmpSecPath , secKey ) // get the reference value
val := recursivelyExpandSecret ( expandedSecs , interpolatedSecs , crossSecRefFetch , interpolationKey )
interpolatedVal = strings . ReplaceAll ( interpolatedVal , interpolatedExp , val )
}
}
expandedSecs [ key ] = interpolatedVal
return interpolatedVal
}
func ExpandSecrets ( secrets [ ] model . SingleEnvironmentVariable , infisicalToken string ) [ ] model . SingleEnvironmentVariable {
expandedSecs := make ( map [ string ] string )
interpolatedSecs := make ( map [ string ] string )
// map[env.secret-path][keyname]Secret
crossEnvRefSecs := make ( map [ string ] map [ string ] model . SingleEnvironmentVariable ) // a cache to hold all cross board reference secrets
for _ , sec := range secrets {
// get all references in a secret
refs := secRefRegex . FindAllStringSubmatch ( sec . Value , - 1 )
// nil means its a secret without reference
if refs == nil {
expandedSecs [ sec . Key ] = sec . Value // atomic secrets without any interpolation
} else {
interpolatedSecs [ sec . Key ] = sec . Value
}
}
for i , sec := range secrets {
// already present pick that up
if expandedVal , ok := expandedSecs [ sec . Key ] ; ok {
secrets [ i ] . Value = expandedVal
continue
}
expandedVal := recursivelyExpandSecret ( expandedSecs , interpolatedSecs , func ( env string , secPaths [ ] string , secKey string ) string {
secPaths = append ( [ ] string { "/" } , secPaths ... )
secPath := path . Join ( secPaths ... )
secPathDot := strings . Join ( secPaths , "." )
uniqKey := fmt . Sprintf ( "%s.%s" , env , secPathDot )
if crossRefSec , ok := crossEnvRefSecs [ uniqKey ] ; ! ok {
// if not in cross reference cache, fetch it from server
refSecs , _ , err := GetPlainTextSecretsViaServiceToken ( infisicalToken , "" , env , secPath )
if err != nil {
fmt . Println ( "HELLO===>" , "MOO" , err )
// HandleError(err, fmt.Sprintf("Could not fetch secrets in environment: %s secret-path: %s", env, secPath), "If you are using a service token to fetch secrets, please ensure it is valid")
}
refSecsByKey := getSecretsByKeys ( refSecs )
// save it to avoid calling api again for same environment and folder path
crossEnvRefSecs [ uniqKey ] = refSecsByKey
return refSecsByKey [ secKey ] . Value
} else {
return crossRefSec [ secKey ] . Value
}
} , sec . Key )
secrets [ i ] . Value = expandedVal
}
return secrets
}
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
}