Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
125
plugins/secretstores/os/README.md
Normal file
125
plugins/secretstores/os/README.md
Normal 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
|
101
plugins/secretstores/os/os.go
Normal file
101
plugins/secretstores/os/os.go
Normal 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}
|
||||
})
|
||||
}
|
29
plugins/secretstores/os/os_darwin.go
Normal file
29
plugins/secretstores/os/os_darwin.go
Normal 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
|
||||
}
|
19
plugins/secretstores/os/os_linux.go
Normal file
19
plugins/secretstores/os/os_linux.go
Normal 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
|
||||
}
|
89
plugins/secretstores/os/os_test.go
Normal file
89
plugins/secretstores/os/os_test.go
Normal 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")
|
||||
}
|
1
plugins/secretstores/os/os_unsupported.go
Normal file
1
plugins/secretstores/os/os_unsupported.go
Normal file
|
@ -0,0 +1 @@
|
|||
package os
|
15
plugins/secretstores/os/os_windows.go
Normal file
15
plugins/secretstores/os/os_windows.go
Normal 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
|
||||
}
|
24
plugins/secretstores/os/sample.conf
Normal file
24
plugins/secretstores/os/sample.conf
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue