diff --git a/config/config.go b/config/config.go index 364e7f1df..4f350b955 100644 --- a/config/config.go +++ b/config/config.go @@ -822,13 +822,19 @@ func (c *Config) addSecretStore(name string, table *ast.Table) error { var storeid string c.getFieldString(table, "id", &storeid) + if storeid == "" { + return fmt.Errorf("%q secret-store without ID", name) + } + if !secretStorePattern.MatchString(storeid) { + return fmt.Errorf("invalid secret-store ID %q, must only contain letters, numbers or underscore", storeid) + } creator, ok := secretstores.SecretStores[name] if !ok { // Handle removed, deprecated plugins if di, deprecated := secretstores.Deprecations[name]; deprecated { printHistoricPluginDeprecationNotice("secretstores", name, di) - return fmt.Errorf("plugin deprecated") + return errors.New("plugin deprecated") } return fmt.Errorf("undefined but requested secretstores: %s", name) } diff --git a/config/secret.go b/config/secret.go index 27914f7c1..b9ddd017d 100644 --- a/config/secret.go +++ b/config/secret.go @@ -18,6 +18,9 @@ import ( // list. var unlinkedSecrets = make([]*Secret, 0) +// secretStorePattern is a regex to validate secret-store IDs +var secretStorePattern = regexp.MustCompile(`^\w+$`) + // secretPattern is a regex to extract references to secrets stored // in a secret-store. var secretPattern = regexp.MustCompile(`@\{(\w+:\w+)\}`) diff --git a/config/secret_test.go b/config/secret_test.go index 50bf7739a..d253c9bc4 100644 --- a/config/secret_test.go +++ b/config/secret_test.go @@ -2,12 +2,14 @@ package config import ( "errors" + "fmt" "os" "testing" "github.com/awnumar/memguard" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/plugins/inputs" + "github.com/influxdata/telegraf/plugins/secretstores" "github.com/stretchr/testify/require" ) @@ -552,6 +554,46 @@ func TestSecretStoreDynamic(t *testing.T) { } } +func TestSecretStoreDeclarationMissingID(t *testing.T) { + cfg := []byte(`[[secretstores.mockup]]`) + + c := NewConfig() + err := c.LoadConfigData(cfg) + require.ErrorContains(t, err, `error parsing mockup, "mockup" secret-store without ID`) +} + +func TestSecretStoreDeclarationInvalidID(t *testing.T) { + invalidIDs := []string{"foo.bar", "dummy-123", "test!", "wohoo+"} + tmpl := ` + [[secretstores.mockup]] + id = %q +` + for _, id := range invalidIDs { + t.Run(id, func(t *testing.T) { + cfg := []byte(fmt.Sprintf(tmpl, id)) + c := NewConfig() + err := c.LoadConfigData(cfg) + require.ErrorContains(t, err, `error parsing mockup, invalid secret-store ID`) + }) + } +} + +func TestSecretStoreDeclarationValidID(t *testing.T) { + validIDs := []string{"foobar", "dummy123", "test_id", "W0Hoo_lala123"} + tmpl := ` + [[secretstores.mockup]] + id = %q +` + for _, id := range validIDs { + t.Run(id, func(t *testing.T) { + cfg := []byte(fmt.Sprintf(tmpl, id)) + c := NewConfig() + err := c.LoadConfigData(cfg) + require.NoError(t, err) + }) + } +} + /*** Mockup (input) plugin for testing to avoid cyclic dependencies ***/ type MockupSecretPlugin struct { Secret Secret `toml:"secret"` @@ -604,4 +646,7 @@ func (s *MockupSecretStore) GetResolver(key string) (telegraf.ResolveFunc, error func init() { // Register the mockup input plugin for the required names inputs.Add("mockup", func() telegraf.Input { return &MockupSecretPlugin{} }) + secretstores.Add("mockup", func(id string) telegraf.SecretStore { + return &MockupSecretStore{} + }) } diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index b6000b54f..cae623fb1 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -166,6 +166,8 @@ configurations. A reference to a secret is specified in form `@{:}`, where the `secret store id` is the unique ID you defined for your secret-store and `secret name` is the name of the secret to use. +**NOTE:** Both, the `secret store id` as well as the `secret name` can only +consist of letters (both upper- and lowercase), numbers and underscores. **Example**: