From e4fa3d6b19f6a7c1b0c41098f8760f48c1cc081b Mon Sep 17 00:00:00 2001 From: Paulin Todev Date: Wed, 4 Oct 2023 18:29:36 +0100 Subject: [PATCH] SigV4 and AzureAD authentication support for `prometheus.remote_write` (#5368) --- CHANGELOG.md | 2 + component/prometheus/remotewrite/types.go | 112 ++++++- .../prometheus/remotewrite/types_test.go | 305 +++++++++++++++--- .../prometheusconvert/remote_write.go | 38 ++- .../prometheusconvert/testdata/scrape.river | 61 +++- .../prometheusconvert/testdata/scrape.yaml | 26 +- .../testdata/unsupported.diags | 1 - .../testdata/unsupported.yaml | 2 - .../testdata/prom_remote_write.river | 67 ++++ .../testdata/prom_remote_write.yaml | 27 +- .../components/prometheus.remote_write.md | 20 ++ .../reference/components/azuread-block.md | 19 ++ .../components/managed_identity-block.md | 22 ++ .../flow/reference/components/sigv4-block.md | 24 ++ 14 files changed, 671 insertions(+), 55 deletions(-) create mode 100644 docs/sources/shared/flow/reference/components/azuread-block.md create mode 100644 docs/sources/shared/flow/reference/components/managed_identity-block.md create mode 100644 docs/sources/shared/flow/reference/components/sigv4-block.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d79f910d90a..24fbf76cf9bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,6 +91,8 @@ Main (unreleased) - Flow: the `prometheus.scrape` component can now configure the scraping of Prometheus native histograms. (@tpaschalis) +- Flow: the `prometheus.remote_write` component now supports SigV4 and AzureAD authentication. (@ptodev) + ### Enhancements - Clustering: allow advertise interfaces to be configurable, with the possibility to select all available interfaces. (@wildum) diff --git a/component/prometheus/remotewrite/types.go b/component/prometheus/remotewrite/types.go index 12665f02f3dd..39ef6a55a191 100644 --- a/component/prometheus/remotewrite/types.go +++ b/component/prometheus/remotewrite/types.go @@ -8,12 +8,16 @@ import ( types "github.com/grafana/agent/component/common/config" flow_relabel "github.com/grafana/agent/component/common/relabel" + "github.com/grafana/river/rivertypes" + "github.com/google/uuid" common "github.com/prometheus/common/config" "github.com/prometheus/common/model" + promsigv4 "github.com/prometheus/common/sigv4" "github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/storage" + "github.com/prometheus/prometheus/storage/remote/azuread" ) // Defaults for config blocks. @@ -72,6 +76,8 @@ type EndpointOptions struct { QueueOptions *QueueOptions `river:"queue_config,block,optional"` MetadataOptions *MetadataOptions `river:"metadata_config,block,optional"` WriteRelabelConfigs []*flow_relabel.Config `river:"write_relabel_config,block,optional"` + SigV4 *SigV4Config `river:"sigv4,block,optional"` + AzureAD *AzureADConfig `river:"azuread,block,optional"` } // SetToDefault implements river.Defaulter. @@ -83,6 +89,14 @@ func (r *EndpointOptions) SetToDefault() { } } +func isAuthSetInHttpClientConfig(cfg *types.HTTPClientConfig) bool { + return cfg.BasicAuth != nil || + cfg.OAuth2 != nil || + cfg.Authorization != nil || + len(cfg.BearerToken) > 0 || + len(cfg.BearerTokenFile) > 0 +} + // Validate implements river.Validator. func (r *EndpointOptions) Validate() error { // We must explicitly Validate because HTTPClientConfig is squashed and it won't run otherwise @@ -92,6 +106,20 @@ func (r *EndpointOptions) Validate() error { } } + const tooManyAuthErr = "at most one of sigv4, azuread, basic_auth, oauth2, bearer_token & bearer_token_file must be configured" + + if r.SigV4 != nil { + if r.AzureAD != nil || isAuthSetInHttpClientConfig(r.HTTPClientConfig) { + return fmt.Errorf(tooManyAuthErr) + } + } + + if r.AzureAD != nil { + if r.SigV4 != nil || isAuthSetInHttpClientConfig(r.HTTPClientConfig) { + return fmt.Errorf(tooManyAuthErr) + } + } + if r.WriteRelabelConfigs != nil { for _, relabelConfig := range r.WriteRelabelConfigs { if err := relabelConfig.Validate(); err != nil { @@ -216,7 +244,8 @@ func convertConfigs(cfg Arguments) (*config.Config, error) { HTTPClientConfig: *rw.HTTPClientConfig.Convert(), QueueConfig: rw.QueueOptions.toPrometheusType(), MetadataConfig: rw.MetadataOptions.toPrometheusType(), - // TODO(rfratto): SigV4Config + SigV4Config: rw.SigV4.toPrometheusType(), + AzureADConfig: rw.AzureAD.toPrometheusType(), }) } @@ -236,3 +265,84 @@ func toLabels(in map[string]string) labels.Labels { sort.Sort(res) return res } + +// ManagedIdentityConfig is used to store managed identity config values +type ManagedIdentityConfig struct { + // ClientID is the clientId of the managed identity that is being used to authenticate. + ClientID string `river:"client_id,attr"` +} + +func (m ManagedIdentityConfig) toPrometheusType() azuread.ManagedIdentityConfig { + return azuread.ManagedIdentityConfig{ + ClientID: m.ClientID, + } +} + +type AzureADConfig struct { + // ManagedIdentity is the managed identity that is being used to authenticate. + ManagedIdentity ManagedIdentityConfig `river:"managed_identity,block"` + + // Cloud is the Azure cloud in which the service is running. Example: AzurePublic/AzureGovernment/AzureChina. + Cloud string `river:"cloud,attr,optional"` +} + +func (a *AzureADConfig) Validate() error { + if a.Cloud != azuread.AzureChina && a.Cloud != azuread.AzureGovernment && a.Cloud != azuread.AzurePublic { + return fmt.Errorf("must provide a cloud in the Azure AD config") + } + + _, err := uuid.Parse(a.ManagedIdentity.ClientID) + if err != nil { + return fmt.Errorf("the provided Azure Managed Identity client_id provided is invalid") + } + + return nil +} + +// SetToDefault implements river.Defaulter. +func (a *AzureADConfig) SetToDefault() { + *a = AzureADConfig{ + Cloud: azuread.AzurePublic, + } +} + +func (a *AzureADConfig) toPrometheusType() *azuread.AzureADConfig { + if a == nil { + return nil + } + + mangedIdentity := a.ManagedIdentity.toPrometheusType() + return &azuread.AzureADConfig{ + ManagedIdentity: &mangedIdentity, + Cloud: a.Cloud, + } +} + +type SigV4Config struct { + Region string `river:"region,attr,optional"` + AccessKey string `river:"access_key,attr,optional"` + SecretKey rivertypes.Secret `river:"secret_key,attr,optional"` + Profile string `river:"profile,attr,optional"` + RoleARN string `river:"role_arn,attr,optional"` +} + +func (s *SigV4Config) Validate() error { + if (s.AccessKey == "") != (s.SecretKey == "") { + return fmt.Errorf("must provide an AWS SigV4 access key and secret key if credentials are specified in the SigV4 config") + } + return nil +} + +func (s *SigV4Config) toPrometheusType() *promsigv4.SigV4Config { + if s == nil { + return nil + } + + return &promsigv4.SigV4Config{ + Region: s.Region, + AccessKey: s.AccessKey, + SecretKey: common.Secret(s.SecretKey), + Profile: s.Profile, + RoleARN: s.RoleARN, + } +} diff --git a/component/prometheus/remotewrite/types_test.go b/component/prometheus/remotewrite/types_test.go index a8b721ac92f6..b9dc4970b990 100644 --- a/component/prometheus/remotewrite/types_test.go +++ b/component/prometheus/remotewrite/types_test.go @@ -1,61 +1,280 @@ package remotewrite import ( + "net/url" "testing" + "time" "github.com/grafana/river" + commonconfig "github.com/prometheus/common/config" + "github.com/prometheus/common/model" + "github.com/prometheus/common/sigv4" + "github.com/prometheus/prometheus/config" + "github.com/prometheus/prometheus/model/labels" + "github.com/prometheus/prometheus/model/relabel" + "github.com/prometheus/prometheus/storage/remote/azuread" "github.com/stretchr/testify/require" ) +func expectedCfg(transform func(c *config.Config)) *config.Config { + // Initialize this with the default expected config + res := &config.Config{ + GlobalConfig: config.GlobalConfig{ + ExternalLabels: labels.Labels{}, + }, + RemoteWriteConfigs: []*config.RemoteWriteConfig{ + { + URL: &commonconfig.URL{ + URL: &url.URL{ + Scheme: "http", + Host: "0.0.0.0:11111", + Path: `/api/v1/write`, + }, + }, + RemoteTimeout: model.Duration(30 * time.Second), + WriteRelabelConfigs: []*relabel.Config{}, + SendExemplars: true, + HTTPClientConfig: commonconfig.HTTPClientConfig{ + FollowRedirects: true, + EnableHTTP2: true, + }, + QueueConfig: config.QueueConfig{ + Capacity: 10000, + MaxShards: 50, + MinShards: 1, + MaxSamplesPerSend: 2000, + BatchSendDeadline: model.Duration(5 * time.Second), + MinBackoff: model.Duration(30 * time.Millisecond), + MaxBackoff: model.Duration(5 * time.Second), + RetryOnRateLimit: true, + }, + MetadataConfig: config.MetadataConfig{ + Send: true, + SendInterval: model.Duration(1 * time.Minute), + MaxSamplesPerSend: 2000, + }, + }, + }, + } + + if transform != nil { + transform(res) + } + return res +} + func TestRiverConfig(t *testing.T) { - var exampleRiverConfig = ` - external_labels = { - cluster = "local", - } - - endpoint { - name = "test-url" - url = "http://0.0.0.0:11111/api/v1/write" - remote_timeout = "100ms" - - queue_config { - batch_send_deadline = "100ms" + tests := []struct { + testName string + cfg string + expectedCfg *config.Config + errorMsg string + }{ + { + testName: "Endpoint_Defaults", + cfg: ` + endpoint { + url = "http://0.0.0.0:11111/api/v1/write" } + `, + expectedCfg: expectedCfg(nil), + }, + { + testName: "RelabelConfig", + cfg: ` + external_labels = { + cluster = "local", + } + + endpoint { + name = "test-url" + url = "http://0.0.0.0:11111/api/v1/write" + remote_timeout = "100ms" + + queue_config { + batch_send_deadline = "100ms" + } + + write_relabel_config { + source_labels = ["instance"] + target_label = "instance" + action = "lowercase" + } + }`, + expectedCfg: expectedCfg(func(c *config.Config) { + relabelCfg := &relabel.DefaultRelabelConfig + relabelCfg.SourceLabels = model.LabelNames{"instance"} + relabelCfg.TargetLabel = "instance" + relabelCfg.Action = "lowercase" + + c.GlobalConfig.ExternalLabels = labels.FromMap(map[string]string{ + "cluster": "local", + }) + c.RemoteWriteConfigs[0].Name = "test-url" + c.RemoteWriteConfigs[0].RemoteTimeout = model.Duration(100 * time.Millisecond) + c.RemoteWriteConfigs[0].QueueConfig.BatchSendDeadline = model.Duration(100 * time.Millisecond) + c.RemoteWriteConfigs[0].WriteRelabelConfigs = []*relabel.Config{ + relabelCfg, + } + }), + }, + { + testName: "AzureAD_Defaults", + cfg: ` + endpoint { + url = "http://0.0.0.0:11111/api/v1/write" + + azuread { + managed_identity { + client_id = "00000000-0000-0000-0000-000000000000" + } + } + }`, + expectedCfg: expectedCfg(func(c *config.Config) { + c.RemoteWriteConfigs[0].AzureADConfig = &azuread.AzureADConfig{ + Cloud: "AzurePublic", + ManagedIdentity: &azuread.ManagedIdentityConfig{ + ClientID: "00000000-0000-0000-0000-000000000000", + }, + } + }), + }, + { + testName: "AzureAD_Explicit", + cfg: ` + endpoint { + url = "http://0.0.0.0:11111/api/v1/write" - write_relabel_config { - source_labels = ["instance"] - target_label = "instance" - action = "lowercase" + azuread { + cloud = "AzureChina" + managed_identity { + client_id = "00000000-0000-0000-0000-000000000000" + } + } + }`, + expectedCfg: expectedCfg(func(c *config.Config) { + c.RemoteWriteConfigs[0].AzureADConfig = &azuread.AzureADConfig{ + Cloud: "AzureChina", + ManagedIdentity: &azuread.ManagedIdentityConfig{ + ClientID: "00000000-0000-0000-0000-000000000000", + }, + } + }), + }, + { + testName: "SigV4_Defaults", + cfg: ` + endpoint { + url = "http://0.0.0.0:11111/api/v1/write" + + sigv4 {} + }`, + expectedCfg: expectedCfg(func(c *config.Config) { + c.RemoteWriteConfigs[0].SigV4Config = &sigv4.SigV4Config{} + }), + }, + { + testName: "SigV4_Explicit", + cfg: ` + endpoint { + url = "http://0.0.0.0:11111/api/v1/write" + + sigv4 { + region = "us-east-1" + access_key = "example_access_key" + secret_key = "example_secret_key" + profile = "example_profile" + role_arn = "example_role_arn" + } + }`, + expectedCfg: expectedCfg(func(c *config.Config) { + c.RemoteWriteConfigs[0].SigV4Config = &sigv4.SigV4Config{ + Region: "us-east-1", + AccessKey: "example_access_key", + SecretKey: commonconfig.Secret("example_secret_key"), + Profile: "example_profile", + RoleARN: "example_role_arn", + } + }), + }, + { + testName: "TooManyAuth1", + cfg: ` + endpoint { + url = "http://0.0.0.0:11111/api/v1/write" + + sigv4 {} + bearer_token = "token" + }`, + errorMsg: "at most one of sigv4, azuread, basic_auth, oauth2, bearer_token & bearer_token_file must be configured", + }, + { + testName: "TooManyAuth2", + cfg: ` + endpoint { + url = "http://0.0.0.0:11111/api/v1/write" + + sigv4 {} + azuread { + managed_identity { + client_id = "00000000-0000-0000-0000-000000000000" + } + } + }`, + errorMsg: "at most one of sigv4, azuread, basic_auth, oauth2, bearer_token & bearer_token_file must be configured", + }, + { + testName: "BadAzureClientId", + cfg: ` + endpoint { + url = "http://0.0.0.0:11111/api/v1/write" + + azuread { + managed_identity { + client_id = "bad_client_id" + } + } + }`, + errorMsg: "the provided Azure Managed Identity client_id provided is invalid", + }, + { + // Make sure the squashed HTTPClientConfig Validate function is being utilized correctly + testName: "BadBearerConfig", + cfg: ` + external_labels = { + cluster = "local", } - } -` - var args Arguments - err := river.Unmarshal([]byte(exampleRiverConfig), &args) - require.NoError(t, err) -} + endpoint { + name = "test-url" + url = "http://0.0.0.0:11111/api/v1/write" + remote_timeout = "100ms" + bearer_token = "token" + bearer_token_file = "/path/to/file.token" + + queue_config { + batch_send_deadline = "100ms" + } + }`, + errorMsg: "at most one of bearer_token & bearer_token_file must be configured", + }, + } -func TestBadRiverConfig(t *testing.T) { - var exampleRiverConfig = ` - external_labels = { - cluster = "local", - } - - endpoint { - name = "test-url" - url = "http://0.0.0.0:11111/api/v1/write" - remote_timeout = "100ms" - bearer_token = "token" - bearer_token_file = "/path/to/file.token" - - queue_config { - batch_send_deadline = "100ms" + for _, tc := range tests { + t.Run(tc.testName, func(t *testing.T) { + var args Arguments + err := river.Unmarshal([]byte(tc.cfg), &args) + + if tc.errorMsg != "" { + require.ErrorContains(t, err, tc.errorMsg) + return } - } -` + require.NoError(t, err) + + promCfg, err := convertConfigs(args) + require.NoError(t, err) - // Make sure the squashed HTTPClientConfig Validate function is being utilized correctly - var args Arguments - err := river.Unmarshal([]byte(exampleRiverConfig), &args) - require.ErrorContains(t, err, "at most one of bearer_token & bearer_token_file must be configured") + require.Equal(t, tc.expectedCfg, promCfg) + }) + } } diff --git a/converter/internal/prometheusconvert/remote_write.go b/converter/internal/prometheusconvert/remote_write.go index 6115a7e9e50b..3ecd1e51d795 100644 --- a/converter/internal/prometheusconvert/remote_write.go +++ b/converter/internal/prometheusconvert/remote_write.go @@ -8,7 +8,10 @@ import ( "github.com/grafana/agent/component/prometheus/remotewrite" "github.com/grafana/agent/converter/diag" "github.com/grafana/agent/converter/internal/common" + "github.com/grafana/river/rivertypes" + "github.com/prometheus/common/sigv4" prom_config "github.com/prometheus/prometheus/config" + "github.com/prometheus/prometheus/storage/remote/azuread" ) func appendPrometheusRemoteWrite(pb *prometheusBlocks, globalConfig prom_config.GlobalConfig, remoteWriteConfigs []*prom_config.RemoteWriteConfig, label string) *remotewrite.Exports { @@ -40,10 +43,6 @@ func appendPrometheusRemoteWrite(pb *prometheusBlocks, globalConfig prom_config. func validateRemoteWriteConfig(remoteWriteConfig *prom_config.RemoteWriteConfig) diag.Diagnostics { var diags diag.Diagnostics - if remoteWriteConfig.SigV4Config != nil { - diags.Add(diag.SeverityLevelError, "unsupported remote_write sigv4 config was provided") - } - diags.AddAll(ValidateHttpClientConfig(&remoteWriteConfig.HTTPClientConfig)) return diags } @@ -76,6 +75,8 @@ func getEndpointOptions(remoteWriteConfigs []*prom_config.RemoteWriteConfig) []* QueueOptions: toQueueOptions(&remoteWriteConfig.QueueConfig), MetadataOptions: toMetadataOptions(&remoteWriteConfig.MetadataConfig), WriteRelabelConfigs: ToFlowRelabelConfigs(remoteWriteConfig.WriteRelabelConfigs), + SigV4: toSigV4(remoteWriteConfig.SigV4Config), + AzureAD: toAzureAD(remoteWriteConfig.AzureADConfig), } endpoints = append(endpoints, endpoint) @@ -104,3 +105,32 @@ func toMetadataOptions(metadataConfig *prom_config.MetadataConfig) *remotewrite. MaxSamplesPerSend: metadataConfig.MaxSamplesPerSend, } } + +// toSigV4 converts a Prometheus SigV4 config to a River SigV4 config. +func toSigV4(sigv4Config *sigv4.SigV4Config) *remotewrite.SigV4Config { + if sigv4Config == nil { + return nil + } + + return &remotewrite.SigV4Config{ + Region: sigv4Config.Region, + AccessKey: sigv4Config.AccessKey, + SecretKey: rivertypes.Secret(sigv4Config.SecretKey), + Profile: sigv4Config.Profile, + RoleARN: sigv4Config.RoleARN, + } +} + +// toAzureAD converts a Prometheus AzureAD config to a River AzureAD config. +func toAzureAD(azureADConfig *azuread.AzureADConfig) *remotewrite.AzureADConfig { + if azureADConfig == nil { + return nil + } + + return &remotewrite.AzureADConfig{ + Cloud: azureADConfig.Cloud, + ManagedIdentity: remotewrite.ManagedIdentityConfig{ + ClientID: azureADConfig.ManagedIdentity.ClientID, + }, + } +} diff --git a/converter/internal/prometheusconvert/testdata/scrape.river b/converter/internal/prometheusconvert/testdata/scrape.river index 2e1ef3665e15..0ca26d333fb9 100644 --- a/converter/internal/prometheusconvert/testdata/scrape.river +++ b/converter/internal/prometheusconvert/testdata/scrape.river @@ -43,7 +43,7 @@ prometheus.remote_write "default" { } endpoint { - name = "remote-1" + name = "remote1" url = "http://remote-write-url1" queue_config { } @@ -69,4 +69,63 @@ prometheus.remote_write "default" { metadata_config { } } + + endpoint { + name = "remote3_sigv4_defaults" + url = "http://localhost:9012/api/prom/push" + + queue_config { } + + metadata_config { } + + sigv4 { } + } + + endpoint { + name = "remote4_sigv4_explicit" + url = "http://localhost:9012/api/prom/push" + + queue_config { } + + metadata_config { } + + sigv4 { + region = "us-east-1" + access_key = "fake_access_key" + secret_key = "fake_secret_key" + profile = "fake_profile" + role_arn = "fake_role_arn" + } + } + + endpoint { + name = "remote5_azuread_defaults" + url = "http://localhost:9012/api/prom/push" + + queue_config { } + + metadata_config { } + + azuread { + managed_identity { + client_id = "00000000-0000-0000-0000-000000000000" + } + } + } + + endpoint { + name = "remote6_azuread_explicit" + url = "http://localhost:9012/api/prom/push" + + queue_config { } + + metadata_config { } + + azuread { + managed_identity { + client_id = "00000000-0000-0000-0000-000000000000" + } + cloud = "AzureGovernment" + } + } } diff --git a/converter/internal/prometheusconvert/testdata/scrape.yaml b/converter/internal/prometheusconvert/testdata/scrape.yaml index 196e0686108c..d4b1e7e203c7 100644 --- a/converter/internal/prometheusconvert/testdata/scrape.yaml +++ b/converter/internal/prometheusconvert/testdata/scrape.yaml @@ -22,7 +22,7 @@ scrape_configs: - targets: ["localhost:9093"] remote_write: - - name: "remote-1" + - name: "remote1" url: "http://remote-write-url1" write_relabel_configs: - source_labels: [__address1__] @@ -30,4 +30,26 @@ remote_write: - source_labels: [__address2__] target_label: __param_target2 - name: "remote2" - url: "http://remote-write-url2" \ No newline at end of file + url: "http://remote-write-url2" + - name: "remote3_sigv4_defaults" + url: http://localhost:9012/api/prom/push + sigv4: {} + - name: "remote4_sigv4_explicit" + url: http://localhost:9012/api/prom/push + sigv4: + region: us-east-1 + access_key: fake_access_key + secret_key: fake_secret_key + profile: fake_profile + role_arn: fake_role_arn + - name: "remote5_azuread_defaults" + url: http://localhost:9012/api/prom/push + azuread: + managed_identity: + client_id: 00000000-0000-0000-0000-000000000000 + - name: "remote6_azuread_explicit" + url: http://localhost:9012/api/prom/push + azuread: + cloud: AzureGovernment + managed_identity: + client_id: 00000000-0000-0000-0000-000000000000 diff --git a/converter/internal/prometheusconvert/testdata/unsupported.diags b/converter/internal/prometheusconvert/testdata/unsupported.diags index 3efc82ee60ab..6ea410da9bc5 100644 --- a/converter/internal/prometheusconvert/testdata/unsupported.diags +++ b/converter/internal/prometheusconvert/testdata/unsupported.diags @@ -7,7 +7,6 @@ (Error) unsupported native_histogram_bucket_limit for scrape_configs (Error) unsupported storage config was provided (Error) unsupported tracing config was provided -(Error) unsupported remote_write sigv4 config was provided (Error) unsupported HTTP Client config proxy_from_environment was provided (Error) unsupported HTTP Client config max_version was provided (Error) unsupported remote_read config was provided \ No newline at end of file diff --git a/converter/internal/prometheusconvert/testdata/unsupported.yaml b/converter/internal/prometheusconvert/testdata/unsupported.yaml index 5536f2d08d22..bf677c030a39 100644 --- a/converter/internal/prometheusconvert/testdata/unsupported.yaml +++ b/converter/internal/prometheusconvert/testdata/unsupported.yaml @@ -51,7 +51,5 @@ remote_write: proxy_from_environment: true tls_config: max_version: TLS13 - sigv4: - region: us-east-1 - name: "remote2" url: "http://remote-write-url2" \ No newline at end of file diff --git a/converter/internal/staticconvert/testdata/prom_remote_write.river b/converter/internal/staticconvert/testdata/prom_remote_write.river index 82f6d23e15cb..df5a9848a234 100644 --- a/converter/internal/staticconvert/testdata/prom_remote_write.river +++ b/converter/internal/staticconvert/testdata/prom_remote_write.river @@ -42,3 +42,70 @@ prometheus.remote_write "metrics_test3" { metadata_config { } } } + +prometheus.remote_write "metrics_test4_sigv4_defaults" { + endpoint { + name = "test4_sigv4_defaults-c42e88" + url = "http://localhost:9012/api/prom/push" + + queue_config { } + + metadata_config { } + + sigv4 { } + } +} + +prometheus.remote_write "metrics_test5_sigv4_explicit" { + endpoint { + name = "test5_sigv4_explicit-050ad5" + url = "http://localhost:9012/api/prom/push" + + queue_config { } + + metadata_config { } + + sigv4 { + region = "us-east-1" + access_key = "fake_access_key" + secret_key = "fake_secret_key" + profile = "fake_profile" + role_arn = "fake_role_arn" + } + } +} + +prometheus.remote_write "metrics_test6_azuread_defaults" { + endpoint { + name = "test6_azuread_defaults-50e17f" + url = "http://localhost:9012/api/prom/push" + + queue_config { } + + metadata_config { } + + azuread { + managed_identity { + client_id = "00000000-0000-0000-0000-000000000000" + } + } + } +} + +prometheus.remote_write "metrics_test7_azuread_explicit" { + endpoint { + name = "test7_azuread_explicit-0f55f1" + url = "http://localhost:9012/api/prom/push" + + queue_config { } + + metadata_config { } + + azuread { + managed_identity { + client_id = "00000000-0000-0000-0000-000000000000" + } + cloud = "AzureGovernment" + } + } +} diff --git a/converter/internal/staticconvert/testdata/prom_remote_write.yaml b/converter/internal/staticconvert/testdata/prom_remote_write.yaml index e3f302ebd997..ef548902ccb6 100644 --- a/converter/internal/staticconvert/testdata/prom_remote_write.yaml +++ b/converter/internal/staticconvert/testdata/prom_remote_write.yaml @@ -16,4 +16,29 @@ metrics: - url: http://localhost:9012/api/prom/push queue_config: retry_on_http_429: false - \ No newline at end of file + - name: "test4_sigv4_defaults" + remote_write: + - url: http://localhost:9012/api/prom/push + sigv4: {} + - name: "test5_sigv4_explicit" + remote_write: + - url: http://localhost:9012/api/prom/push + sigv4: + region: us-east-1 + access_key: fake_access_key + secret_key: fake_secret_key + profile: fake_profile + role_arn: fake_role_arn + - name: "test6_azuread_defaults" + remote_write: + - url: http://localhost:9012/api/prom/push + azuread: + managed_identity: + client_id: 00000000-0000-0000-0000-000000000000 + - name: "test7_azuread_explicit" + remote_write: + - url: http://localhost:9012/api/prom/push + azuread: + cloud: AzureGovernment + managed_identity: + client_id: 00000000-0000-0000-0000-000000000000 diff --git a/docs/sources/flow/reference/components/prometheus.remote_write.md b/docs/sources/flow/reference/components/prometheus.remote_write.md index 28b877538c52..c1bfb5619b66 100644 --- a/docs/sources/flow/reference/components/prometheus.remote_write.md +++ b/docs/sources/flow/reference/components/prometheus.remote_write.md @@ -54,6 +54,9 @@ endpoint > basic_auth | [basic_auth][] | Configure basic_auth for authenticating endpoint > authorization | [authorization][] | Configure generic authorization to the endpoint. | no endpoint > oauth2 | [oauth2][] | Configure OAuth2 for authenticating to the endpoint. | no endpoint > oauth2 > tls_config | [tls_config][] | Configure TLS settings for connecting to the endpoint. | no +endpoint > sigv4 | [sigv4][] | Configure AWS Signature Verification 4 for authenticating to the endpoint. | no +endpoint > azuread | [azuread][] | Configure AzureAD for authenticating to the endpoint. | no +endpoint > azuread > managed_identity | [managed_identity][] | Configure Azure user-assigned managed identity. | yes endpoint > tls_config | [tls_config][] | Configure TLS settings for connecting to the endpoint. | no endpoint > queue_config | [queue_config][] | Configuration for how metrics are batched before sending. | no endpoint > metadata_config | [metadata_config][] | Configuration for how metric metadata is sent. | no @@ -68,6 +71,9 @@ basic_auth` refers to a `basic_auth` block defined inside an [basic_auth]: #basic_auth-block [authorization]: #authorization-block [oauth2]: #oauth2-block +[sigv4]: #sigv4-block +[azuread]: #azuread-block +[managed_identity]: #managed_identity-block [tls_config]: #tls_config-block [queue_config]: #queue_config-block [metadata_config]: #metadata_config-block @@ -101,6 +107,8 @@ Name | Type | Description | Default | Required - [`basic_auth` block][basic_auth]. - [`authorization` block][authorization]. - [`oauth2` block][oauth2]. + - [`sigv4` block][sigv4]. + - [`azuread` block][azuread]. When multiple `endpoint` blocks are provided, metrics are concurrently sent to all configured locations. Each endpoint has a _queue_ which is used to read metrics @@ -128,6 +136,18 @@ metrics fails. {{< docs/shared lookup="flow/reference/components/oauth2-block.md" source="agent" version="" >}} +### sigv4 block + +{{< docs/shared lookup="flow/reference/components/sigv4-block.md" source="agent" version="" >}} + +### azuread block + +{{< docs/shared lookup="flow/reference/components/azuread-block.md" source="agent" version="" >}} + +### managed_identity block + +{{< docs/shared lookup="flow/reference/components/managed_identity-block.md" source="agent" version="" >}} + ### tls_config block {{< docs/shared lookup="flow/reference/components/tls-config-block.md" source="agent" version="" >}} diff --git a/docs/sources/shared/flow/reference/components/azuread-block.md b/docs/sources/shared/flow/reference/components/azuread-block.md new file mode 100644 index 000000000000..b1ecc1e03c3a --- /dev/null +++ b/docs/sources/shared/flow/reference/components/azuread-block.md @@ -0,0 +1,19 @@ +--- +aliases: +- /docs/agent/shared/flow/reference/components/azuread-block/ +- /docs/grafana-cloud/agent/shared/flow/reference/components/azuread-block/ +- /docs/grafana-cloud/monitor-infrastructure/agent/shared/flow/reference/components/azuread-block/ +- /docs/grafana-cloud/monitor-infrastructure/integrations/agent/shared/flow/reference/components/azuread-block/ +canonical: https://grafana.com/docs/agent/latest/shared/flow/reference/components/azuread-block/ +description: Shared content, azuread block +headless: true +--- + +Name | Type | Description | Default | Required +---- | ---- | ----------- | ------- | -------- +`cloud` | `string` | The Azure Cloud. | `"AzurePublic"` | no + +The supported values for `cloud` are: +* `"AzurePublic"` +* `"AzureChina"` +* `"AzureGovernment"` \ No newline at end of file diff --git a/docs/sources/shared/flow/reference/components/managed_identity-block.md b/docs/sources/shared/flow/reference/components/managed_identity-block.md new file mode 100644 index 000000000000..bf4dd4aa6831 --- /dev/null +++ b/docs/sources/shared/flow/reference/components/managed_identity-block.md @@ -0,0 +1,22 @@ +--- +aliases: +- /docs/agent/shared/flow/reference/components/managed_identity-block/ +- /docs/grafana-cloud/agent/shared/flow/reference/components/managed_identity-block/ +- /docs/grafana-cloud/monitor-infrastructure/agent/shared/flow/reference/components/managed_identity-block/ +- /docs/grafana-cloud/monitor-infrastructure/integrations/agent/shared/flow/reference/components/managed_identity-block/ +canonical: https://grafana.com/docs/agent/latest/shared/flow/reference/components/managed_identity-block/ +description: Shared content, managed_identity block +headless: true +--- + +Name | Type | Description | Default | Required +---- | ---- | ----------- | ------- | -------- +`client_id` | `string` | Client ID of the managed identity which is used to authenticate. | | yes + +`client_id` should be a valid [UUID][] in one of the supported formats: +* `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` +* `urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` +* Microsoft encoding: `{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}` +* Raw hex encoding: `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` + +[UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier \ No newline at end of file diff --git a/docs/sources/shared/flow/reference/components/sigv4-block.md b/docs/sources/shared/flow/reference/components/sigv4-block.md new file mode 100644 index 000000000000..65a67da4e3a5 --- /dev/null +++ b/docs/sources/shared/flow/reference/components/sigv4-block.md @@ -0,0 +1,24 @@ +--- +aliases: +- /docs/agent/shared/flow/reference/components/sigv4-block/ +- /docs/grafana-cloud/agent/shared/flow/reference/components/sigv4-block/ +- /docs/grafana-cloud/monitor-infrastructure/agent/shared/flow/reference/components/sigv4-block/ +- /docs/grafana-cloud/monitor-infrastructure/integrations/agent/shared/flow/reference/components/sigv4-block/ +canonical: https://grafana.com/docs/agent/latest/shared/flow/reference/components/sigv4-block/ +description: Shared content, sigv4 block +headless: true +--- + +Name | Type | Description | Default | Required +---- | ---- | ----------- | ------- | -------- +`region` | `string` | AWS region. | | no +`access_key` | `string` | AWS API access key. | | no +`secret_key` | `secret` | AWS API secret key.| | no +`profile` | `string` | Named AWS profile used to authenticate. | | no +`role_arn` | `string` | AWS Role ARN, an alternative to using AWS API keys. | | no + +If `region` is left blank, the region from the default credentials chain is used. + +If `access_key` is left blank, the environment variable `AWS_ACCESS_KEY_ID` is used. + +If `secret_key` is left blank, the environment variable `AWS_SECRET_ACCESS_KEY` is used.