mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 09:26:47 +00:00
Feat: Agent improvements, Secrets state manager
This commit is contained in:
+52
-16
@@ -73,6 +73,19 @@ type Template struct {
|
|||||||
DestinationPath string `yaml:"destination-path"`
|
DestinationPath string `yaml:"destination-path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SecretsStateManager struct {
|
||||||
|
// etags should be stored in memory, and the key should be env-secretPath-projectID, and the value should be the actual etag
|
||||||
|
etags map[string]string
|
||||||
|
secretMutationChannel chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSecretsStateManager(secretMutationChannel chan bool) *SecretsStateManager {
|
||||||
|
return &SecretsStateManager{
|
||||||
|
etags: make(map[string]string),
|
||||||
|
secretMutationChannel: secretMutationChannel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ReadFile(filePath string) ([]byte, error) {
|
func ReadFile(filePath string) ([]byte, error) {
|
||||||
return ioutil.ReadFile(filePath)
|
return ioutil.ReadFile(filePath)
|
||||||
}
|
}
|
||||||
@@ -170,20 +183,29 @@ func ParseAgentConfig(configFile []byte) (*Config, error) {
|
|||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func secretTemplateFunction(accessToken string) func(string, string, string) ([]models.SingleEnvironmentVariable, error) {
|
func secretTemplateFunction(accessToken string, secretStateManager *SecretsStateManager) func(string, string, string) ([]models.SingleEnvironmentVariable, error) {
|
||||||
return func(projectID, envSlug, secretPath string) ([]models.SingleEnvironmentVariable, error) {
|
return func(projectID, envSlug, secretPath string) ([]models.SingleEnvironmentVariable, error) {
|
||||||
secrets, err := util.GetPlainTextSecretsViaMachineIdentity(accessToken, projectID, envSlug, secretPath, false)
|
res, err := util.GetPlainTextSecretsViaMachineIdentity(accessToken, projectID, envSlug, secretPath, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return secrets, nil
|
if secretStateManager != nil {
|
||||||
|
key := fmt.Sprintf("%s-%s-%s", envSlug, secretPath, projectID)
|
||||||
|
oldEtag, ok := secretStateManager.etags[key] // if there's no etag, it means it's the first time we are fetching this secret. we should only notify the secretMutationChannel if the etag has changed, not if it's the first time we are fetching the secret
|
||||||
|
if ok && oldEtag != res.Hash {
|
||||||
|
secretStateManager.secretMutationChannel <- true
|
||||||
|
}
|
||||||
|
secretStateManager.etags[key] = res.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Secrets, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProcessTemplate(templatePath string, data interface{}, accessToken string) (*bytes.Buffer, error) {
|
func ProcessTemplate(templatePath string, data interface{}, accessToken string, secretStateManager *SecretsStateManager) (*bytes.Buffer, error) {
|
||||||
// custom template function to fetch secrets from Infisical
|
// custom template function to fetch secrets from Infisical
|
||||||
secretFunction := secretTemplateFunction(accessToken)
|
secretFunction := secretTemplateFunction(accessToken, secretStateManager)
|
||||||
funcs := template.FuncMap{
|
funcs := template.FuncMap{
|
||||||
"secret": secretFunction,
|
"secret": secretFunction,
|
||||||
}
|
}
|
||||||
@@ -203,7 +225,7 @@ func ProcessTemplate(templatePath string, data interface{}, accessToken string)
|
|||||||
return &buf, nil
|
return &buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProcessBase64Template(encodedTemplate string, data interface{}, accessToken string) (*bytes.Buffer, error) {
|
func ProcessBase64Template(encodedTemplate string, data interface{}, accessToken string, secretStateManager *SecretsStateManager) (*bytes.Buffer, error) {
|
||||||
// custom template function to fetch secrets from Infisical
|
// custom template function to fetch secrets from Infisical
|
||||||
decoded, err := base64.StdEncoding.DecodeString(encodedTemplate)
|
decoded, err := base64.StdEncoding.DecodeString(encodedTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -212,7 +234,7 @@ func ProcessBase64Template(encodedTemplate string, data interface{}, accessToken
|
|||||||
|
|
||||||
templateString := string(decoded)
|
templateString := string(decoded)
|
||||||
|
|
||||||
secretFunction := secretTemplateFunction(accessToken)
|
secretFunction := secretTemplateFunction(accessToken, secretStateManager) // TODO: Fix this
|
||||||
funcs := template.FuncMap{
|
funcs := template.FuncMap{
|
||||||
"secret": secretFunction,
|
"secret": secretFunction,
|
||||||
}
|
}
|
||||||
@@ -244,13 +266,22 @@ type TokenManager struct {
|
|||||||
clientIdPath string
|
clientIdPath string
|
||||||
clientSecretPath string
|
clientSecretPath string
|
||||||
newAccessTokenNotificationChan chan bool
|
newAccessTokenNotificationChan chan bool
|
||||||
removeClientSecretOnRead bool
|
|
||||||
cachedClientSecret string
|
removeClientSecretOnRead bool
|
||||||
exitAfterAuth bool
|
cachedClientSecret string
|
||||||
|
exitAfterAuth bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTokenManager(fileDeposits []Sink, templates []Template, clientIdPath string, clientSecretPath string, newAccessTokenNotificationChan chan bool, removeClientSecretOnRead bool, exitAfterAuth bool) *TokenManager {
|
func NewTokenManager(fileDeposits []Sink, templates []Template, clientIdPath string, clientSecretPath string, newAccessTokenNotificationChan chan bool, removeClientSecretOnRead bool, exitAfterAuth bool) *TokenManager {
|
||||||
return &TokenManager{filePaths: fileDeposits, templates: templates, clientIdPath: clientIdPath, clientSecretPath: clientSecretPath, newAccessTokenNotificationChan: newAccessTokenNotificationChan, removeClientSecretOnRead: removeClientSecretOnRead, exitAfterAuth: exitAfterAuth}
|
return &TokenManager{
|
||||||
|
filePaths: fileDeposits,
|
||||||
|
templates: templates,
|
||||||
|
clientIdPath: clientIdPath,
|
||||||
|
clientSecretPath: clientSecretPath,
|
||||||
|
newAccessTokenNotificationChan: newAccessTokenNotificationChan,
|
||||||
|
removeClientSecretOnRead: removeClientSecretOnRead,
|
||||||
|
exitAfterAuth: exitAfterAuth,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *TokenManager) SetToken(token string, accessTokenTTL time.Duration, accessTokenMaxTTL time.Duration) {
|
func (tm *TokenManager) SetToken(token string, accessTokenTTL time.Duration, accessTokenMaxTTL time.Duration) {
|
||||||
@@ -428,7 +459,7 @@ func (tm *TokenManager) WriteTokenToFiles() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *TokenManager) FetchSecrets() {
|
func (tm *TokenManager) FetchSecrets(secretStateManager *SecretsStateManager) {
|
||||||
log.Info().Msgf("template engine started...")
|
log.Info().Msgf("template engine started...")
|
||||||
for {
|
for {
|
||||||
token := tm.GetToken()
|
token := tm.GetToken()
|
||||||
@@ -437,9 +468,9 @@ func (tm *TokenManager) FetchSecrets() {
|
|||||||
var processedTemplate *bytes.Buffer
|
var processedTemplate *bytes.Buffer
|
||||||
var err error
|
var err error
|
||||||
if secretTemplate.SourcePath != "" {
|
if secretTemplate.SourcePath != "" {
|
||||||
processedTemplate, err = ProcessTemplate(secretTemplate.SourcePath, nil, token)
|
processedTemplate, err = ProcessTemplate(secretTemplate.SourcePath, nil, token, secretStateManager)
|
||||||
} else {
|
} else {
|
||||||
processedTemplate, err = ProcessBase64Template(secretTemplate.Base64TemplateContent, nil, token)
|
processedTemplate, err = ProcessBase64Template(secretTemplate.Base64TemplateContent, nil, token, secretStateManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -458,7 +489,7 @@ func (tm *TokenManager) FetchSecrets() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fetch new secrets every 5 minutes (TODO: add PubSub in the future )
|
// fetch new secrets every 5 minutes (TODO: add PubSub in the future )
|
||||||
time.Sleep(5 * time.Minute)
|
time.Sleep(5 * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -537,19 +568,24 @@ var agentCmd = &cobra.Command{
|
|||||||
configUniversalAuthType := agentConfig.Auth.Config.(UniversalAuth)
|
configUniversalAuthType := agentConfig.Auth.Config.(UniversalAuth)
|
||||||
|
|
||||||
tokenRefreshNotifier := make(chan bool)
|
tokenRefreshNotifier := make(chan bool)
|
||||||
|
secretMutationNotifier := make(chan bool)
|
||||||
sigChan := make(chan os.Signal, 1)
|
sigChan := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
filePaths := agentConfig.Sinks
|
filePaths := agentConfig.Sinks
|
||||||
tm := NewTokenManager(filePaths, agentConfig.Templates, configUniversalAuthType.ClientIDPath, configUniversalAuthType.ClientSecretPath, tokenRefreshNotifier, configUniversalAuthType.RemoveClientSecretOnRead, agentConfig.Infisical.ExitAfterAuth)
|
tm := NewTokenManager(filePaths, agentConfig.Templates, configUniversalAuthType.ClientIDPath, configUniversalAuthType.ClientSecretPath, tokenRefreshNotifier, configUniversalAuthType.RemoveClientSecretOnRead, agentConfig.Infisical.ExitAfterAuth)
|
||||||
|
|
||||||
|
ssm := NewSecretsStateManager(secretMutationNotifier)
|
||||||
|
|
||||||
go tm.ManageTokenLifecycle()
|
go tm.ManageTokenLifecycle()
|
||||||
go tm.FetchSecrets()
|
go tm.FetchSecrets(ssm)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-tokenRefreshNotifier:
|
case <-tokenRefreshNotifier:
|
||||||
go tm.WriteTokenToFiles()
|
go tm.WriteTokenToFiles()
|
||||||
|
case <-secretMutationNotifier:
|
||||||
|
log.Info().Msgf("Mashallah, a mutation has occurred")
|
||||||
case <-sigChan:
|
case <-sigChan:
|
||||||
log.Info().Msg("agent is gracefully shutting...")
|
log.Info().Msg("agent is gracefully shutting...")
|
||||||
// TODO: check if we are in the middle of writing files to disk
|
// TODO: check if we are in the middle of writing files to disk
|
||||||
|
|||||||
Reference in New Issue
Block a user