diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d9fa0633d..bf56ceed3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ Main (unreleased) ### Features +- Add `azuread.oauth` to `prometheus.remote_write` to support Azure AD authentication applicaion. (@callumau) + - Add `add_cloudwatch_timestamp` to `prometheus.exporter.cloudwatch` metrics. (@captncraig) - Add support to `prometheus.operator.servicemonitors` to allow `endpointslice` role. (@yoyosir) diff --git a/docs/sources/reference/components/prometheus/prometheus.remote_write.md b/docs/sources/reference/components/prometheus/prometheus.remote_write.md index d2614a2e52..cf0ab0e86e 100644 --- a/docs/sources/reference/components/prometheus/prometheus.remote_write.md +++ b/docs/sources/reference/components/prometheus/prometheus.remote_write.md @@ -54,7 +54,8 @@ endpoint > oauth2 | [oauth2][] | Configure OAuth2 for authenticating to the endp 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 > azuread > managed_identity | [managed_identity][] | Configure Azure user-assigned managed identity. | no +endpoint > azuread > oauth | [oauth][] | Configure Azure application authenication. | no 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 @@ -72,6 +73,7 @@ basic_auth` refers to a `basic_auth` block defined inside an [sigv4]: #sigv4-block [azuread]: #azuread-block [managed_identity]: #managed_identity-block +[oauth]: #oauth-block [tls_config]: #tls_config-block [queue_config]: #queue_config-block [metadata_config]: #metadata_config-block @@ -151,6 +153,10 @@ metrics fails. {{< docs/shared lookup="reference/components/managed_identity-block.md" source="alloy" version="" >}} +### oauth block + +{{< docs/shared lookup="reference/components/oauth-block.md" source="alloy" version="" >}} + ### tls_config block {{< docs/shared lookup="reference/components/tls-config-block.md" source="alloy" version="" >}} diff --git a/docs/sources/shared/reference/components/oauth-block.md b/docs/sources/shared/reference/components/oauth-block.md new file mode 100644 index 0000000000..3e2e951831 --- /dev/null +++ b/docs/sources/shared/reference/components/oauth-block.md @@ -0,0 +1,25 @@ +--- +canonical: https://grafana.com/docs/alloy/latest/shared/reference/components/oauth-block/ +description: Shared content, oauth block +headless: true +--- + +Name | Type | Description | Default | Required +----------------|----------|---------------------------------------------------------------------------------|---------|--------- +`client_id` | `string` | Client ID of the Microsoft authentication application used to authenticate. | | yes +`client_secret` | `string` | Client secret of the Microsoft authentication application used to authenticate. | | yes +`tenant_id` | `string` | Tenant ID of the Microsoft authentication application 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` + +`tenant_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 diff --git a/internal/component/prometheus/remotewrite/types.go b/internal/component/prometheus/remotewrite/types.go index 74dcf835d0..aac4313617 100644 --- a/internal/component/prometheus/remotewrite/types.go +++ b/internal/component/prometheus/remotewrite/types.go @@ -274,15 +274,41 @@ type ManagedIdentityConfig struct { ClientID string `alloy:"client_id,attr"` } -func (m ManagedIdentityConfig) toPrometheusType() azuread.ManagedIdentityConfig { - return azuread.ManagedIdentityConfig{ +func (m ManagedIdentityConfig) toPrometheusType() *azuread.ManagedIdentityConfig { + if m.ClientID == "" { + return nil + } + + return &azuread.ManagedIdentityConfig{ ClientID: m.ClientID, } } +// Azure AD oauth +type AzureOAuthConfig struct { + // AzureADOAuth is the OAuth configuration that is being used to authenticate. + ClientID string `alloy:"client_id,attr"` + ClientSecret string `alloy:"client_secret,attr"` + TenantID string `alloy:"tenant_id,attr"` +} + +func (m AzureOAuthConfig) toPrometheusType() *azuread.OAuthConfig { + if m.ClientID == "" && m.ClientSecret == "" && m.TenantID == "" { + return nil + } + + return &azuread.OAuthConfig{ + ClientID: m.ClientID, + ClientSecret: m.ClientSecret, + TenantID: m.TenantID, + } +} + type AzureADConfig struct { // ManagedIdentity is the managed identity that is being used to authenticate. - ManagedIdentity ManagedIdentityConfig `alloy:"managed_identity,block"` + ManagedIdentity ManagedIdentityConfig `alloy:"managed_identity,block,optional"` + // OAuth is the OAuth configuration that is being used to authenticate. + OAuth AzureOAuthConfig `alloy:"oauth,block,optional"` // Cloud is the Azure cloud in which the service is running. Example: AzurePublic/AzureGovernment/AzureChina. Cloud string `alloy:"cloud,attr,optional"` @@ -293,9 +319,25 @@ func (a *AzureADConfig) Validate() error { 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") + // Ensure both Managed Identity and OAuth are not provided + if a.ManagedIdentity != (ManagedIdentityConfig{}) && a.OAuth != (AzureOAuthConfig{}) { + return fmt.Errorf("at most oauth or managed identity must be configured for azuread") + } + + // Validate Managed Identity if it is provided + if (a.ManagedIdentity != ManagedIdentityConfig{}) { + _, err := uuid.Parse(a.ManagedIdentity.ClientID) + if err != nil { + return fmt.Errorf("the provided Azure Managed Identity client_id provided is invalid") + } + } + + // Validate OAuth if it is provided + if (a.OAuth != AzureOAuthConfig{}) { + _, err := uuid.Parse(a.OAuth.ClientID) + if err != nil { + return fmt.Errorf("the provided Azure Application Identity client_id provided is invalid") + } } return nil @@ -314,8 +356,10 @@ func (a *AzureADConfig) toPrometheusType() *azuread.AzureADConfig { } mangedIdentity := a.ManagedIdentity.toPrometheusType() + oauth := a.OAuth.toPrometheusType() return &azuread.AzureADConfig{ - ManagedIdentity: &mangedIdentity, + OAuth: oauth, + ManagedIdentity: mangedIdentity, Cloud: a.Cloud, } } diff --git a/internal/component/prometheus/remotewrite/types_test.go b/internal/component/prometheus/remotewrite/types_test.go index 839141c38f..5f2885067e 100644 --- a/internal/component/prometheus/remotewrite/types_test.go +++ b/internal/component/prometheus/remotewrite/types_test.go @@ -161,6 +161,32 @@ func TestAlloyConfig(t *testing.T) { } }), }, + { + testName: "AzureAD_Oauth", + cfg: ` + endpoint { + url = "http://0.0.0.0:11111/api/v1/write" + + azuread { + cloud = "AzureChina" + oauth { + client_id = "00000000-0000-0000-0000-000000000000" + tenant_id = "00000000-0000-0000-0000-000000000001" + client_secret = "00000000-0000-0000-0000-000000000002" + } + } + }`, + expectedCfg: expectedCfg(func(c *config.Config) { + c.RemoteWriteConfigs[0].AzureADConfig = &azuread.AzureADConfig{ + Cloud: "AzureChina", + OAuth: &azuread.OAuthConfig{ + ClientID: "00000000-0000-0000-0000-000000000000", + ClientSecret: "00000000-0000-0000-0000-000000000002", + TenantID: "00000000-0000-0000-0000-000000000001", + }, + } + }), + }, { testName: "SigV4_Defaults", cfg: ` @@ -223,6 +249,26 @@ func TestAlloyConfig(t *testing.T) { }`, errorMsg: "at most one of sigv4, azuread, basic_auth, oauth2, bearer_token & bearer_token_file must be configured", }, + { + testName: "TooManyAuthAzureAD", + cfg: ` + endpoint { + url = "http://0.0.0.0:11111/api/v1/write" + + sigv4 {} + azuread { + managed_identity { + client_id = "00000000-0000-0000-0000-000000000000" + } + oauth { + client_id = "00000000-0000-0000-0000-000000000000" + tenant_id = "00000000-0000-0000-0000-000000000001" + client_secret = "00000000-0000-0000-0000-000000000002" + } + } + }`, + errorMsg: "at most oauth or managed identity must be configured for azuread", + }, { testName: "BadAzureClientId", cfg: ` @@ -237,6 +283,37 @@ func TestAlloyConfig(t *testing.T) { }`, errorMsg: "the provided Azure Managed Identity client_id provided is invalid", }, + { + testName: "BadAzureOAuthClientId", + cfg: ` + endpoint { + url = "http://0.0.0.0:11111/api/v1/write" + + azuread { + oauth { + client_id = "bad_client_id" + tenant_id = "00000000-0000-0000-0000-000000000001" + client_secret = "00000000-0000-0000-0000-000000000002" + } + } + }`, + errorMsg: "the provided Azure Application Identity client_id provided is invalid", + }, + { + testName: "MissingAzureOAuthTenantId", + cfg: ` + endpoint { + url = "http://0.0.0.0:11111/api/v1/write" + + azuread { + oauth { + client_id = "bad_client_id" + client_secret = "00000000-0000-0000-0000-000000000002" + } + } + }`, + errorMsg: "missing required attribute \"tenant_id\"", + }, { // Make sure the squashed HTTPClientConfig Validate function is being utilized correctly testName: "BadBearerConfig", diff --git a/internal/converter/internal/prometheusconvert/component/remote_write.go b/internal/converter/internal/prometheusconvert/component/remote_write.go index ca634423cd..691ec70c63 100644 --- a/internal/converter/internal/prometheusconvert/component/remote_write.go +++ b/internal/converter/internal/prometheusconvert/component/remote_write.go @@ -129,10 +129,26 @@ func toAzureAD(azureADConfig *azuread.AzureADConfig) *remotewrite.AzureADConfig return nil } - return &remotewrite.AzureADConfig{ - Cloud: azureADConfig.Cloud, - ManagedIdentity: remotewrite.ManagedIdentityConfig{ + var oauth remotewrite.AzureOAuthConfig + var managedIdentity remotewrite.ManagedIdentityConfig + + if azureADConfig.OAuth != nil { + oauth = remotewrite.AzureOAuthConfig{ + ClientID: azureADConfig.OAuth.ClientID, + ClientSecret: azureADConfig.OAuth.ClientSecret, + TenantID: azureADConfig.OAuth.TenantID, + } + } + + if azureADConfig.ManagedIdentity != nil { + managedIdentity = remotewrite.ManagedIdentityConfig{ ClientID: azureADConfig.ManagedIdentity.ClientID, - }, + } + } + + return &remotewrite.AzureADConfig{ + Cloud: azureADConfig.Cloud, + ManagedIdentity: managedIdentity, + OAuth: oauth, } } diff --git a/internal/converter/internal/staticconvert/testdata/prom_remote_write.alloy b/internal/converter/internal/staticconvert/testdata/prom_remote_write.alloy index 2d341fed6a..08f0269909 100644 --- a/internal/converter/internal/staticconvert/testdata/prom_remote_write.alloy +++ b/internal/converter/internal/staticconvert/testdata/prom_remote_write.alloy @@ -109,3 +109,23 @@ prometheus.remote_write "metrics_test7_azuread_explicit" { } } } + +prometheus.remote_write "metrics_test8_azuread_appauth" { + endpoint { + name = "test8_azuread_appauth-1654c7" + url = "http://localhost:9012/api/prom/push" + + queue_config { } + + metadata_config { } + + azuread { + oauth { + client_id = "00000000-0000-0000-0000-000000000000" + client_secret = "fake_client_secret" + tenant_id = "00000000-0000-0000-0000-000000000000" + } + cloud = "AzureGovernment" + } + } +} diff --git a/internal/converter/internal/staticconvert/testdata/prom_remote_write.yaml b/internal/converter/internal/staticconvert/testdata/prom_remote_write.yaml index ef548902cc..2e521f31e0 100644 --- a/internal/converter/internal/staticconvert/testdata/prom_remote_write.yaml +++ b/internal/converter/internal/staticconvert/testdata/prom_remote_write.yaml @@ -42,3 +42,13 @@ metrics: cloud: AzureGovernment managed_identity: client_id: 00000000-0000-0000-0000-000000000000 + - name: "test8_azuread_appauth" + remote_write: + - url: http://localhost:9012/api/prom/push + azuread: + cloud: AzureGovernment + oauth: + client_id: 00000000-0000-0000-0000-000000000000 + client_secret: fake_client_secret + tenant_id: 00000000-0000-0000-0000-000000000000 +