chore(secrets): Warn if settings look like secrets but use invalid characters (#14706)
This commit is contained in:
parent
ae7fbc5082
commit
5f6772e869
|
|
@ -2,6 +2,7 @@ package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
@ -20,10 +21,12 @@ var unlinkedSecrets = make([]*Secret, 0)
|
||||||
// secretStorePattern is a regex to validate secret-store IDs
|
// secretStorePattern is a regex to validate secret-store IDs
|
||||||
var secretStorePattern = regexp.MustCompile(`^\w+$`)
|
var secretStorePattern = regexp.MustCompile(`^\w+$`)
|
||||||
|
|
||||||
// secretPattern is a regex to extract references to secrets stored
|
// secretPattern is a regex to extract references to secrets store in a secret-store
|
||||||
// in a secret-store.
|
|
||||||
var secretPattern = regexp.MustCompile(`@\{(\w+:\w+)\}`)
|
var secretPattern = regexp.MustCompile(`@\{(\w+:\w+)\}`)
|
||||||
|
|
||||||
|
// secretCandidatePattern is a regex to find secret candidates to warn users on invalid characters in references
|
||||||
|
var secretCandidatePattern = regexp.MustCompile(`@\{.+?:.+?}`)
|
||||||
|
|
||||||
// secretCount is the number of secrets use in Telegraf
|
// secretCount is the number of secrets use in Telegraf
|
||||||
var secretCount atomic.Int64
|
var secretCount atomic.Int64
|
||||||
|
|
||||||
|
|
@ -125,8 +128,18 @@ func (s *Secret) init(secret []byte) {
|
||||||
// Remember if the secret is completely empty
|
// Remember if the secret is completely empty
|
||||||
s.notempty = len(secret) != 0
|
s.notempty = len(secret) != 0
|
||||||
|
|
||||||
// Find all parts that need to be resolved and return them
|
// Find all secret candidates and check if they are really a valid
|
||||||
s.unlinked = secretPattern.FindAllString(string(secret), -1)
|
// reference. Otherwise issue a warning to let the user know that there is
|
||||||
|
// a potential issue with their secret instead of silently ignoring it.
|
||||||
|
candidates := secretCandidatePattern.FindAllString(string(secret), -1)
|
||||||
|
s.unlinked = make([]string, 0, len(candidates))
|
||||||
|
for _, c := range candidates {
|
||||||
|
if secretPattern.MatchString(c) {
|
||||||
|
s.unlinked = append(s.unlinked, c)
|
||||||
|
} else {
|
||||||
|
log.Printf("W! Secret %q contains invalid character(s), only letters, digits and underscores are allowed.", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
s.resolvers = nil
|
s.resolvers = nil
|
||||||
|
|
||||||
// Setup the container implementation
|
// Setup the container implementation
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/awnumar/memguard"
|
"github.com/awnumar/memguard"
|
||||||
|
|
@ -716,6 +718,27 @@ func (tsuite *SecretImplTestSuite) TestSecretSetResolveInvalid() {
|
||||||
require.ErrorContains(t, err, `linking new secrets failed: unlinked part "@{mock:another_secret}"`)
|
require.ErrorContains(t, err, `linking new secrets failed: unlinked part "@{mock:another_secret}"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tsuite *SecretImplTestSuite) TestSecretInvalidWarn() {
|
||||||
|
t := tsuite.T()
|
||||||
|
|
||||||
|
// Intercept the log output
|
||||||
|
var buf bytes.Buffer
|
||||||
|
backup := log.Writer()
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer log.SetOutput(backup)
|
||||||
|
|
||||||
|
cfg := []byte(`
|
||||||
|
[[inputs.mockup]]
|
||||||
|
secret = "server=a user=@{mock:secret-with-invalid-chars} pass=@{mock:secret_pass}"
|
||||||
|
`)
|
||||||
|
c := NewConfig()
|
||||||
|
require.NoError(t, c.LoadConfigData(cfg))
|
||||||
|
require.Len(t, c.Inputs, 1)
|
||||||
|
|
||||||
|
require.Contains(t, buf.String(), `W! Secret "@{mock:secret-with-invalid-chars}" contains invalid character(s)`)
|
||||||
|
require.NotContains(t, buf.String(), "@{mock:secret_pass}")
|
||||||
|
}
|
||||||
|
|
||||||
func TestSecretImplUnprotected(t *testing.T) {
|
func TestSecretImplUnprotected(t *testing.T) {
|
||||||
impl := &unprotectedSecretImpl{}
|
impl := &unprotectedSecretImpl{}
|
||||||
container := impl.Container([]byte("foobar"))
|
container := impl.Container([]byte("foobar"))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue