mirror of
https://github.com/grafana/grafana.git
synced 2026-03-13 15:29:48 +08:00
Plugins: Forward AWS SDK credential chain env vars to external AWS plugins (#119772)
Plugins: Forward AWS SDK credential chain env vars to external plugins
Fixes broken AWS authentication (assume role, SigV4) for external plugins
(Redshift, Athena, Amazon Prometheus, OpenSearch) in Grafana 12.4.0.
In v12.4.0, commit f041563df3 changed plugin env var handling so external
plugins no longer receive host environment variables by default. This breaks
the AWS SDK credential chain in container environments (EKS IRSA, ECS Fargate)
which rely on vars like AWS_ROLE_ARN, AWS_WEB_IDENTITY_TOKEN_FILE, and
AWS_CONTAINER_CREDENTIALS_RELATIVE_URI.
Forward 8 critical AWS SDK credential chain env vars from the host to plugins
already in [aws] forward_settings_to_plugins. This is scoped to trusted AWS
plugins only, more targeted than PR #118870's global approach.
Includes test coverage for forwarding, non-forwarding, and selective env var
filtering.
Fixes #119235, #119603, opensearch-datasource#1012
This commit is contained in:
@@ -1986,7 +1986,7 @@ public_key_retrieval_disabled = false
|
||||
public_key_retrieval_on_startup = false
|
||||
# Enter a comma-separated list of plugin identifiers to avoid loading (including core plugins). These plugins will be hidden in the catalog.
|
||||
disable_plugins =
|
||||
# Comma separated list of plugin ids for which environment variables should be forwarded. Used only when feature flag pluginsSkipHostEnvVars is enabled.
|
||||
# Comma separated list of plugin ids for which all host environment variables should be forwarded to the plugin process.
|
||||
forward_host_env_vars =
|
||||
# Comma separated list of plugin ids to install as part of the startup process.
|
||||
# These will be installed, by default, asynchronously (in the background) while starting Grafana.
|
||||
|
||||
@@ -125,9 +125,34 @@ func (p *EnvVarsProvider) awsEnvVars(pluginID string) []string {
|
||||
variables = append(variables, p.envVar(awsds.ListMetricsPageLimitKeyName, p.cfg.AWSListMetricsPageLimit))
|
||||
}
|
||||
|
||||
// Forward AWS SDK credential chain env vars from the host so that plugins can use
|
||||
// EKS IRSA, ECS task roles, and other environment-based credential providers.
|
||||
for _, envVarName := range awsHostEnvVarNames {
|
||||
if v, ok := os.LookupEnv(envVarName); ok {
|
||||
variables = append(variables, p.envVar(envVarName, v))
|
||||
}
|
||||
}
|
||||
|
||||
return variables
|
||||
}
|
||||
|
||||
// awsHostEnvVarNames are the host environment variables forwarded to AWS plugins.
|
||||
// These are needed for the AWS SDK default credential chain to resolve credentials
|
||||
// in container environments (EKS with IRSA, ECS Fargate).
|
||||
var awsHostEnvVarNames = []string{
|
||||
// EKS IRSA
|
||||
"AWS_ROLE_ARN",
|
||||
"AWS_WEB_IDENTITY_TOKEN_FILE",
|
||||
// ECS (Fargate / EC2)
|
||||
"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI",
|
||||
"AWS_CONTAINER_CREDENTIALS_FULL_URI",
|
||||
"AWS_CONTAINER_AUTHORIZATION_TOKEN",
|
||||
"AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE",
|
||||
// Region
|
||||
"AWS_REGION",
|
||||
"AWS_DEFAULT_REGION",
|
||||
}
|
||||
|
||||
func (p *EnvVarsProvider) secureSocksProxyEnvVars() []string {
|
||||
if p.cfg.ProxySettings.Enabled {
|
||||
return []string{
|
||||
|
||||
@@ -484,28 +484,80 @@ func TestPluginEnvVarsProvider_authEnvVars(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPluginEnvVarsProvider_awsEnvVars(t *testing.T) {
|
||||
t.Run("backend datasource with aws settings", func(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
pluginID string
|
||||
forwardToPlugins []string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "Will generate AWS env vars for plugin as long as is in the forwardToPlugins list",
|
||||
forwardToPlugins: []string{"foobar-datasource", "cloudwatch", "prometheus"},
|
||||
pluginID: "cloudwatch",
|
||||
expected: []string{"GF_VERSION=", "AWS_AUTH_AssumeRoleEnabled=false", "AWS_AUTH_AllowedAuthProviders=grafana_assume_role,keys", "AWS_AUTH_EXTERNAL_ID=mock_external_id", "AWS_AUTH_SESSION_DURATION=10m", "AWS_CW_LIST_METRICS_PAGE_LIMIT=100"},
|
||||
tcs := []struct {
|
||||
name string
|
||||
pluginID string
|
||||
forwardToPlugins []string
|
||||
hostEnvVars map[string]string
|
||||
expected []string
|
||||
unexpectedKeys []string
|
||||
}{
|
||||
{
|
||||
name: "generates AWS auth settings for whitelisted plugin",
|
||||
forwardToPlugins: []string{"foobar-datasource", "cloudwatch", "prometheus"},
|
||||
pluginID: "cloudwatch",
|
||||
expected: []string{"GF_VERSION=", "AWS_AUTH_AssumeRoleEnabled=false", "AWS_AUTH_AllowedAuthProviders=grafana_assume_role,keys", "AWS_AUTH_EXTERNAL_ID=mock_external_id", "AWS_AUTH_SESSION_DURATION=10m", "AWS_CW_LIST_METRICS_PAGE_LIMIT=100"},
|
||||
},
|
||||
{
|
||||
name: "does not generate AWS env vars for non-whitelisted plugin",
|
||||
forwardToPlugins: []string{"cloudwatch", "foobar-datasource"},
|
||||
pluginID: "prometheus",
|
||||
expected: []string{"GF_VERSION="},
|
||||
},
|
||||
{
|
||||
name: "forwards AWS SDK credential chain env vars for whitelisted plugin",
|
||||
forwardToPlugins: []string{"cloudwatch"},
|
||||
pluginID: "cloudwatch",
|
||||
hostEnvVars: map[string]string{
|
||||
"AWS_ROLE_ARN": "arn:aws:iam::123456789012:role/test-role",
|
||||
"AWS_WEB_IDENTITY_TOKEN_FILE": "/var/run/secrets/token",
|
||||
"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI": "/v2/credentials/uuid",
|
||||
"AWS_REGION": "us-east-1",
|
||||
},
|
||||
{
|
||||
name: "Will not generate AWS env vars for plugin as long as is in not the forwardToPlugins list",
|
||||
forwardToPlugins: []string{"cloudwatch", "foobar-datasource"},
|
||||
pluginID: "prometheus",
|
||||
expected: []string{"GF_VERSION="},
|
||||
expected: []string{
|
||||
"GF_VERSION=",
|
||||
"AWS_AUTH_AssumeRoleEnabled=false", "AWS_AUTH_AllowedAuthProviders=grafana_assume_role,keys",
|
||||
"AWS_AUTH_EXTERNAL_ID=mock_external_id", "AWS_AUTH_SESSION_DURATION=10m", "AWS_CW_LIST_METRICS_PAGE_LIMIT=100",
|
||||
"AWS_ROLE_ARN=arn:aws:iam::123456789012:role/test-role",
|
||||
"AWS_WEB_IDENTITY_TOKEN_FILE=/var/run/secrets/token",
|
||||
"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=/v2/credentials/uuid",
|
||||
"AWS_REGION=us-east-1",
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "does not forward AWS SDK credential chain env vars for non-whitelisted plugin",
|
||||
forwardToPlugins: []string{"cloudwatch"},
|
||||
pluginID: "some-other-plugin",
|
||||
hostEnvVars: map[string]string{
|
||||
"AWS_ROLE_ARN": "arn:aws:iam::123456789012:role/test-role",
|
||||
"AWS_REGION": "us-east-1",
|
||||
},
|
||||
expected: []string{"GF_VERSION="},
|
||||
unexpectedKeys: []string{"AWS_ROLE_ARN", "AWS_REGION"},
|
||||
},
|
||||
{
|
||||
name: "only forwards AWS SDK env vars that are set in the host environment",
|
||||
forwardToPlugins: []string{"cloudwatch"},
|
||||
pluginID: "cloudwatch",
|
||||
hostEnvVars: map[string]string{
|
||||
"AWS_REGION": "eu-west-1",
|
||||
},
|
||||
expected: []string{
|
||||
"GF_VERSION=",
|
||||
"AWS_AUTH_AssumeRoleEnabled=false", "AWS_AUTH_AllowedAuthProviders=grafana_assume_role,keys",
|
||||
"AWS_AUTH_EXTERNAL_ID=mock_external_id", "AWS_AUTH_SESSION_DURATION=10m", "AWS_CW_LIST_METRICS_PAGE_LIMIT=100",
|
||||
"AWS_REGION=eu-west-1",
|
||||
},
|
||||
unexpectedKeys: []string{"AWS_ROLE_ARN", "AWS_WEB_IDENTITY_TOKEN_FILE", "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for k, v := range tc.hostEnvVars {
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
p := &plugins.Plugin{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: tc.pluginID,
|
||||
@@ -524,8 +576,13 @@ func TestPluginEnvVarsProvider_awsEnvVars(t *testing.T) {
|
||||
provider := NewEnvVarsProvider(cfg, nil, &fakeSSOSettingsProvider{})
|
||||
envVars := provider.PluginEnvVars(context.Background(), p)
|
||||
assert.ElementsMatch(t, tc.expected, envVars)
|
||||
}
|
||||
})
|
||||
|
||||
for _, key := range tc.unexpectedKeys {
|
||||
_, ok := getEnvVarWithExists(envVars, key)
|
||||
assert.False(t, ok, "env var %s should not be present", key)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginEnvVarsProvider_featureToggleEnvVar(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user