1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View file

@ -0,0 +1,125 @@
# OS Secret-store Plugin
The `os` plugin allows to manage and store secrets using the native Operating
System keyring. For Windows this plugin uses the credential manager, on Linux
the kernel keyring is used and on MacOS we use the Keychain implementation.
To manage your secrets you can either use Telegraf or the tools that natively
comes with your operating system. Run
```shell
telegraf secrets help
```
to get more information on how to do this with Telegraf.
## Usage <!-- @/docs/includes/secret_usage.md -->
Secrets defined by a store are referenced with `@{<store-id>:<secret_key>}`
the Telegraf configuration. Only certain Telegraf plugins and options of
support secret stores. To see which plugins and options support
secrets, see their respective documentation (e.g.
`plugins/outputs/influxdb/README.md`). If the plugin's README has the
`Secret-store support` section, it will detail which options support secret
store usage.
## Configuration
The configuration differs slightly depending on the Operating System. We first
describe the common options here and the refer to the individual interpretation
or options in the following sections.
All secret-store implementations require an `id` to be able to reference the
store when specifying the secret. The `id` needs to be unique in the
configuration.
For all operating systems, the keyring name can be chosen using the `keyring`
parameter. However, the interpretation is slightly different on the individual
implementations.
The `dynamic` flag allows to indicate secrets that change during the runtime of
Telegraf. I.e. when set to `true`, the secret will be read from the secret-store
on every access by a plugin. If set to `false`, all secrets in the secret store
are assumed to be static and are only read once at startup of Telegraf.
```toml @sample.conf
# Operating System native secret-store
[[secretstores.os]]
## Unique identifier for the secret-store.
## This id can later be used in plugins to reference the secrets
## in this secret-store via @{<id>:<secret_key>} (mandatory)
id = "secretstore"
## Keyring Name & Collection
## * Linux: keyring name used for the secrets, collection is unused
## * macOS: keyring specifies the macOS' Keychain name and collection is an
## optional Keychain service name
## * Windows: keys follow a fixed pattern in the form
## `<collection>:<keyring>:<key_name>`. Please keep this in mind when
## creating secrets with the Windows credential tool.
# keyring = "telegraf"
# collection = ""
## macOS Keychain password
## If no password is specified here, Telegraf will prompt for it at startup
## time.
# password = ""
## Allow dynamic secrets that are updated during runtime of telegraf
# dynamic = false
```
### Linux
On Linux the kernel keyring in the `user` scope is used to store the
secrets. The `collection` setting is ignored on Linux.
### MacOS
On MacOS the Keychain implementation is used. Here the `keyring` parameter
corresponds to the Keychain name and the `collection` to the optional Keychain
service name. Additionally a password is required to access the Keychain.
The `password` itself is also a secret and can be a string, an environment
variable or a reference to a secret stored in another secret-store.
If `password` is omitted, you will be prompted for the password on startup.
### Windows
On Windows you can use the Credential Manager in the Control Panel or
[Telegraf](../../../cmd/telegraf/README.md) to manage your secrets.
If using the Credential Manager, click "Windows Credentials" and then
"Add a generic credential" with the following:
* _Internet or network address_: Enter the secret name in the format of:
`<collection>:<keyring>:<key_name>`
* _User name_: Use `telegraf`. This field is not used, but needs something
entered.
* _Password_: The actual secret value
If using Telegraf, see the help output of `telegraf secrets set` to add
secrets. Again use the `<collection>:<keyring>:<key_name>` format of the secret
key name.
### Docker
Access to the kernel keyring is __disabled by default__ in docker containers
(see [documentation](https://docs.docker.com/engine/security/seccomp/)).
In this case you will get an
`opening keyring failed: Specified keyring backend not available` error!
You can enable access to the kernel keyring, but as the keyring is __not__
namespaced, you should be aware of the security implication! One implication
is for example that keys added in one container are accessible by __all__
other containers running on the same host, not only within the same container.
### systemd-nspawn
The memguard dependency that Telegraf uses to secure memory for secret storage
requires the `CAP_IPC_LOCK` capability to correctly lock memory. Without this
capability Telegraf will panic. Users will need to start a container with the
`--capability=CAP_IPC_LOCK` flag for telegraf to correctly work.
See [github.com/awnumar/memguard#144][memguard-issue] for more information.
[memguard-issue]: https://github.com/awnumar/memguard/issues/144

View file

@ -0,0 +1,101 @@
//go:build darwin || linux || windows
//go:generate ../../../tools/readme_config_includer/generator
package os
import (
_ "embed"
"errors"
"fmt"
"github.com/99designs/keyring"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/plugins/secretstores"
)
//go:embed sample.conf
var sampleConfig string
type OS struct {
ID string `toml:"id"`
Keyring string `toml:"keyring"`
Collection string `toml:"collection"`
Dynamic bool `toml:"dynamic"`
Password config.Secret `toml:"password"`
ring keyring.Keyring
}
func (*OS) SampleConfig() string {
return sampleConfig
}
// Init initializes all internals of the secret-store
func (o *OS) Init() error {
defer o.Password.Destroy()
if o.ID == "" {
return errors.New("id missing")
}
// Set defaults
if o.Keyring == "" {
o.Keyring = "telegraf"
}
// Setup the actual keyring
cfg, err := o.createKeyringConfig()
if err != nil {
return fmt.Errorf("getting keyring config failed: %w", err)
}
kr, err := keyring.Open(cfg)
if err != nil {
return fmt.Errorf("opening keyring failed: %w", err)
}
o.ring = kr
return nil
}
// Get searches for the given key and return the secret
func (o *OS) Get(key string) ([]byte, error) {
item, err := o.ring.Get(key)
if err != nil {
return nil, err
}
return item.Data, nil
}
// Set sets the given secret for the given key
func (o *OS) Set(key, value string) error {
item := keyring.Item{
Key: key,
Data: []byte(value),
}
return o.ring.Set(item)
}
// List lists all known secret keys
func (o *OS) List() ([]string, error) {
return o.ring.Keys()
}
// GetResolver returns a function to resolve the given key.
func (o *OS) GetResolver(key string) (telegraf.ResolveFunc, error) {
resolver := func() ([]byte, bool, error) {
s, err := o.Get(key)
return s, o.Dynamic, err
}
return resolver, nil
}
// Register the secret-store on load.
func init() {
secretstores.Add("os", func(id string) telegraf.SecretStore {
return &OS{ID: id}
})
}

View file

@ -0,0 +1,29 @@
//go:build darwin
package os
import (
"fmt"
"github.com/99designs/keyring"
)
func (o *OS) createKeyringConfig() (keyring.Config, error) {
// Create the prompt-function in case we need it
promptFunc := keyring.TerminalPrompt
if !o.Password.Empty() {
passwd, err := o.Password.Get()
if err != nil {
return keyring.Config{}, fmt.Errorf("getting password failed: %w", err)
}
promptFunc = keyring.FixedStringPrompt(passwd.String())
passwd.Destroy()
}
return keyring.Config{
ServiceName: o.Collection,
AllowedBackends: []keyring.BackendType{keyring.KeychainBackend},
KeychainName: o.Keyring,
KeychainPasswordFunc: promptFunc,
}, nil
}

View file

@ -0,0 +1,19 @@
//go:build linux
package os
import (
"github.com/99designs/keyring"
)
func (o *OS) createKeyringConfig() (keyring.Config, error) {
if o.Keyring == "" {
o.Keyring = "telegraf"
}
return keyring.Config{
ServiceName: o.Keyring,
AllowedBackends: []keyring.BackendType{keyring.KeyCtlBackend},
KeyCtlScope: "user",
KeyCtlPerm: 0x3f3f0000, // "alswrvalswrv------------"
}, nil
}

View file

@ -0,0 +1,89 @@
//go:build darwin || linux || windows
package os
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf/internal/choice"
)
// In docker, access to the keyring is disabled by default see
// https://docs.docker.com/engine/security/seccomp/.
// You will see the following error then.
const dockerErr = "opening keyring failed: Specified keyring backend not available"
func TestSampleConfig(t *testing.T) {
plugin := &OS{}
require.NotEmpty(t, plugin.SampleConfig())
}
func TestInitFail(t *testing.T) {
tests := []struct {
name string
plugin *OS
expected string
}{
{
name: "invalid id",
plugin: &OS{},
expected: "id missing",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.plugin.Init()
require.ErrorContains(t, err, tt.expected)
})
}
}
func TestResolverInvalid(t *testing.T) {
plugin := &OS{ID: "test"}
// In docker, access to the keyring is disabled by default
// see https://docs.docker.com/engine/security/seccomp/.
err := plugin.Init()
if err != nil && err.Error() == dockerErr {
t.Skip("Kernel keyring not available!")
}
require.NoError(t, err)
// Make sure the key does not exist and try to read that key
testKey := "foobar secret key"
keys, err := plugin.List()
require.NoError(t, err)
for choice.Contains(testKey, keys) {
testKey += "x"
}
// Get the resolver
resolver, err := plugin.GetResolver(testKey)
require.NoError(t, err)
require.NotNil(t, resolver)
_, _, err = resolver()
require.Error(t, err)
}
func TestGetNonExisting(t *testing.T) {
plugin := &OS{ID: "test"}
// In docker, access to the keyring is disabled by default
// see https://docs.docker.com/engine/security/seccomp/.
err := plugin.Init()
if err != nil && err.Error() == dockerErr {
t.Skip("Kernel keyring not available!")
}
require.NoError(t, err)
// Make sure the key does not exist and try to read that key
testKey := "foobar secret key"
keys, err := plugin.List()
require.NoError(t, err)
for choice.Contains(testKey, keys) {
testKey += "x"
}
_, err = plugin.Get(testKey)
require.EqualError(t, err, "The specified item could not be found in the keyring")
}

View file

@ -0,0 +1 @@
package os

View file

@ -0,0 +1,15 @@
//go:build windows
package os
import (
"github.com/99designs/keyring"
)
func (o *OS) createKeyringConfig() (keyring.Config, error) {
return keyring.Config{
ServiceName: o.Keyring,
AllowedBackends: []keyring.BackendType{keyring.WinCredBackend},
WinCredPrefix: o.Collection,
}, nil
}

View file

@ -0,0 +1,24 @@
# Operating System native secret-store
[[secretstores.os]]
## Unique identifier for the secret-store.
## This id can later be used in plugins to reference the secrets
## in this secret-store via @{<id>:<secret_key>} (mandatory)
id = "secretstore"
## Keyring Name & Collection
## * Linux: keyring name used for the secrets, collection is unused
## * macOS: keyring specifies the macOS' Keychain name and collection is an
## optional Keychain service name
## * Windows: keys follow a fixed pattern in the form
## `<collection>:<keyring>:<key_name>`. Please keep this in mind when
## creating secrets with the Windows credential tool.
# keyring = "telegraf"
# collection = ""
## macOS Keychain password
## If no password is specified here, Telegraf will prompt for it at startup
## time.
# password = ""
## Allow dynamic secrets that are updated during runtime of telegraf
# dynamic = false