From 6445e1f907865ec430671a71ccb31255a4024fa5 Mon Sep 17 00:00:00 2001 From: Andreas Christou Date: Fri, 29 Sep 2023 11:47:34 +0100 Subject: [PATCH] [v10.1.x] Azure: Settings for Azure AD Workload Identity (#75683) * Azure: Settings for Azure AD Workload Identity (#75283) * Settings for Azure AD Workload Identity * Update dependency on Grafana Azure SDK * Documentation * Fix JS code * Cleanup Prometheus backend code * Making prettier happy (cherry picked from commit 3ee40d3a5a168a850cb5d03cc1f50b5cdae32849) # Conflicts: # go.mod # go.sum # public/app/plugins/datasource/mssql/azureauth/AzureAuth.testMocks.ts * Fix go.sum --------- Co-authored-by: Sergey Kostrukov --- conf/defaults.ini | 18 +++++++++++++ conf/sample.ini | 18 +++++++++++++ .../setup-grafana/configure-grafana/_index.md | 26 +++++++++++++++++++ go.mod | 10 +++---- go.sum | 10 +++++++ packages/grafana-runtime/src/config.ts | 2 ++ pkg/api/dtos/frontend_settings.go | 7 ++--- pkg/api/frontendsettings.go | 7 ++--- pkg/setting/setting_azure.go | 18 +++++++++++++ pkg/tsdb/prometheus/azureauth/azure.go | 25 +----------------- 10 files changed, 106 insertions(+), 35 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index 99e00df1f21..e57a7a03a98 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -845,6 +845,24 @@ managed_identity_enabled = false # Should be set for user-assigned identity and should be empty for system-assigned identity managed_identity_client_id = +# Specifies whether Azure AD Workload Identity authentication should be enabled in datasources that support it +# For more documentation on Azure AD Workload Identity, review this documentation: +# https://azure.github.io/azure-workload-identity/docs/ +# Disabled by default, needs to be explicitly enabled +workload_identity_enabled = false + +# Tenant ID of the Azure AD Workload Identity +# Allows to override default tenant ID of the Azure AD identity associated with the Kubernetes service account +workload_identity_tenant_id = + +# Client ID of the Azure AD Workload Identity +# Allows to override default client ID of the Azure AD identity associated with the Kubernetes service account +workload_identity_client_id = + +# Custom path to token file for the Azure AD Workload Identity +# Allows to set a custom path to the projected service account token file +workload_identity_token_file = + # Specifies whether user identity authentication (on behalf of currently signed-in user) should be enabled in datasources # that support it (requires AAD authentication) # Disabled by default, needs to be explicitly enabled diff --git a/conf/sample.ini b/conf/sample.ini index 66aeb950e9c..acce65e3c37 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -797,6 +797,24 @@ # Should be set for user-assigned identity and should be empty for system-assigned identity ;managed_identity_client_id = +# Specifies whether Azure AD Workload Identity authentication should be enabled in datasources that support it +# For more documentation on Azure AD Workload Identity, review this documentation: +# https://azure.github.io/azure-workload-identity/docs/ +# Disabled by default, needs to be explicitly enabled +;workload_identity_enabled = false + +# Tenant ID of the Azure AD Workload Identity +# Allows to override default tenant ID of the Azure AD identity associated with the Kubernetes service account +;workload_identity_tenant_id = + +# Client ID of the Azure AD Workload Identity +# Allows to override default client ID of the Azure AD identity associated with the Kubernetes service account +;workload_identity_client_id = + +# Custom path to token file for the Azure AD Workload Identity +# Allows to set a custom path to the projected service account token file +;workload_identity_token_file = + # Specifies whether user identity authentication (on behalf of currently signed-in user) should be enabled in datasources # that support it (requires AAD authentication) # Disabled by default, needs to be explicitly enabled diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index 220d832e080..c1b304c33fc 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -1154,6 +1154,32 @@ The client ID to use for user-assigned managed identity. Should be set for user-assigned identity and should be empty for system-assigned identity. +### workload_identity_enabled + +Specifies whether Azure AD Workload Identity authentication should be enabled in datasources that support it. + +For more documentation on Azure AD Workload Identity, review [Azure AD Workload Identity](https://azure.github.io/azure-workload-identity/docs/) documentation. + +Disabled by default, needs to be explicitly enabled. + +### workload_identity_tenant_id + +Tenant ID of the Azure AD Workload Identity. + +Allows to override default tenant ID of the Azure AD identity associated with the Kubernetes service account. + +### workload_identity_client_id + +Client ID of the Azure AD Workload Identity. + +Allows to override default client ID of the Azure AD identity associated with the Kubernetes service account. + +### workload_identity_token_file + +Custom path to token file for the Azure AD Workload Identity. + +Allows to set a custom path to the projected service account token file. + ### user_identity_enabled Specifies whether user identity authentication (on behalf of currently signed-in user) should be enabled in datasources that support it (requires AAD authentication). diff --git a/go.mod b/go.mod index 80c8a50c6d9..9233d2b5462 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/grafana/alerting v0.0.0-20230606080147-55b8d71c7890 // @grafana/alerting-squad-backend github.com/grafana/cuetsy v0.1.10 // @grafana/grafana-as-code github.com/grafana/grafana-aws-sdk v0.16.1 // @grafana/aws-datasources - github.com/grafana/grafana-azure-sdk-go v1.7.0 // @grafana/backend-platform + github.com/grafana/grafana-azure-sdk-go v1.9.0 // @grafana/backend-platform github.com/grafana/grafana-plugin-sdk-go v0.172.0 // @grafana/plugins-platform-backend github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // @grafana/backend-platform github.com/hashicorp/go-hclog v1.5.0 // @grafana/plugins-platform-backend @@ -131,7 +131,7 @@ require ( ) require ( - github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect @@ -228,7 +228,7 @@ require ( require ( cloud.google.com/go/kms v1.10.1 // @grafana/backend-platform - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // @grafana/backend-platform + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // @grafana/backend-platform github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.9.0 // @grafana/backend-platform github.com/Azure/azure-storage-blob-go v0.15.0 // @grafana/backend-platform github.com/Azure/go-autorest/autorest/adal v0.9.22 // @grafana/backend-platform @@ -414,9 +414,9 @@ require ( cloud.google.com/go/compute v1.19.0 // indirect cloud.google.com/go/iam v0.13.0 // indirect filippo.io/age v1.1.1 // @grafana/grafana-authnz-team - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect github.com/Masterminds/sprig/v3 v3.2.2 // @grafana/backend-platform github.com/Microsoft/go-winio v0.6.0 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // @grafana/plugins-platform-backend diff --git a/go.sum b/go.sum index 5c975291299..62d42f93e82 100644 --- a/go.sum +++ b/go.sum @@ -558,10 +558,16 @@ github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9Eb github.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 h1:sVW/AFBTGyJxDaMYlq0ct3jUXTtj12tQ6zE2GZUgVQw= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1 h1:SEy2xmstIphdPwNBUi7uhvjyjhVKISfwjfOJmuy7kg4= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.9.0 h1:TOFrNxfjslms5nLLIMjW7N0+zSALX4KiGsptmpb16AA= github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.9.0/go.mod h1:EAyXOW1F6BTJPiK2pDvmnvxOHPxoTYWoqBeIlql+QhI= github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0 h1:Lg6BW0VPmCwcMlvOviL3ruHFO+H9tZNqscK0AeuFjGM= @@ -608,6 +614,8 @@ github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 h1:VgSJlZH5u0k2qxSpqyghcFQKmvYckj46uymKK5XzkBM= github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0/go.mod h1:BDJ5qMFKx9DugEg3+uQSDCdbYPr5s9vBTrL9P8TpqOU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -1789,6 +1797,8 @@ github.com/grafana/grafana-aws-sdk v0.16.1 h1:R/hMtQP7H0+8nWFoIOApaZj0qstmZM+5Pw github.com/grafana/grafana-aws-sdk v0.16.1/go.mod h1:rCXLYoMpPqF90U7XqgVJ1HIAopFVF0bB3SXBVEJIm3I= github.com/grafana/grafana-azure-sdk-go v1.7.0 h1:2EAPwNl/qsDMHwKjlzaHif+H+bHcF1W7sM8/jAcxVcI= github.com/grafana/grafana-azure-sdk-go v1.7.0/go.mod h1:X4PdEQIYgHfn0KTa2ZTKvufhNz6jbCEKUQPZIlcyOGw= +github.com/grafana/grafana-azure-sdk-go v1.9.0 h1:4JRwlqgUtPRAQSoiV4DFZDQ3lbNsauHqj9kC6SMR9Ak= +github.com/grafana/grafana-azure-sdk-go v1.9.0/go.mod h1:1vBa0KOl+/Kcm7V888OyMXDSFncmek14q7XhEkrcSaA= github.com/grafana/grafana-google-sdk-go v0.1.0 h1:LKGY8z2DSxKjYfr2flZsWgTRTZ6HGQbTqewE3JvRaNA= github.com/grafana/grafana-google-sdk-go v0.1.0/go.mod h1:Vo2TKWfDVmNTELBUM+3lkrZvFtBws0qSZdXhQxRdJrE= github.com/grafana/grafana-plugin-sdk-go v0.94.0/go.mod h1:3VXz4nCv6wH5SfgB3mlW39s+c+LetqSCjFj7xxPC5+M= diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts index 2762a6d5729..33441e9abc6 100644 --- a/packages/grafana-runtime/src/config.ts +++ b/packages/grafana-runtime/src/config.ts @@ -21,6 +21,7 @@ import { export interface AzureSettings { cloud?: string; managedIdentityEnabled: boolean; + workloadIdentityEnabled: boolean; userIdentityEnabled: boolean; } @@ -124,6 +125,7 @@ export class GrafanaBootConfig implements GrafanaConfig { awsAssumeRoleEnabled = false; azure: AzureSettings = { managedIdentityEnabled: false, + workloadIdentityEnabled: false, userIdentityEnabled: false, }; caching = { diff --git a/pkg/api/dtos/frontend_settings.go b/pkg/api/dtos/frontend_settings.go index b11869c9020..b7d39a249d6 100644 --- a/pkg/api/dtos/frontend_settings.go +++ b/pkg/api/dtos/frontend_settings.go @@ -45,9 +45,10 @@ type FrontendSettingsLicenseInfoDTO struct { } type FrontendSettingsAzureDTO struct { - Cloud string `json:"cloud"` - ManagedIdentityEnabled bool `json:"managedIdentityEnabled"` - UserIdentityEnabled bool `json:"userIdentityEnabled"` + Cloud string `json:"cloud"` + ManagedIdentityEnabled bool `json:"managedIdentityEnabled"` + WorkloadIdentityEnabled bool `json:"workloadIdentityEnabled"` + UserIdentityEnabled bool `json:"userIdentityEnabled"` } type FrontendSettingsCachingDTO struct { diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index a90ce8c6308..9c7a155d6ec 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -205,9 +205,10 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro SupportBundlesEnabled: isSupportBundlesEnabled(hs), Azure: dtos.FrontendSettingsAzureDTO{ - Cloud: hs.Cfg.Azure.Cloud, - ManagedIdentityEnabled: hs.Cfg.Azure.ManagedIdentityEnabled, - UserIdentityEnabled: hs.Cfg.Azure.UserIdentityEnabled, + Cloud: hs.Cfg.Azure.Cloud, + ManagedIdentityEnabled: hs.Cfg.Azure.ManagedIdentityEnabled, + WorkloadIdentityEnabled: hs.Cfg.Azure.WorkloadIdentityEnabled, + UserIdentityEnabled: hs.Cfg.Azure.UserIdentityEnabled, }, Caching: dtos.FrontendSettingsCachingDTO{ diff --git a/pkg/setting/setting_azure.go b/pkg/setting/setting_azure.go index 05fb415ad9f..ec1c079ba53 100644 --- a/pkg/setting/setting_azure.go +++ b/pkg/setting/setting_azure.go @@ -17,6 +17,24 @@ func (cfg *Cfg) readAzureSettings() { azureSettings.ManagedIdentityEnabled = azureSection.Key("managed_identity_enabled").MustBool(false) azureSettings.ManagedIdentityClientId = azureSection.Key("managed_identity_client_id").String() + // Workload Identity authentication + if azureSection.Key("workload_identity_enabled").MustBool(false) { + azureSettings.WorkloadIdentityEnabled = true + workloadIdentitySettings := &azsettings.WorkloadIdentitySettings{} + + if val := azureSection.Key("workload_identity_tenant_id").String(); val != "" { + workloadIdentitySettings.TenantId = val + } + if val := azureSection.Key("workload_identity_client_id").String(); val != "" { + workloadIdentitySettings.ClientId = val + } + if val := azureSection.Key("workload_identity_token_file").String(); val != "" { + workloadIdentitySettings.TokenFile = val + } + + azureSettings.WorkloadIdentitySettings = workloadIdentitySettings + } + // User Identity authentication if azureSection.Key("user_identity_enabled").MustBool(false) { azureSettings.UserIdentityEnabled = true diff --git a/pkg/tsdb/prometheus/azureauth/azure.go b/pkg/tsdb/prometheus/azureauth/azure.go index a61e1bf8f4e..1ce7ba534bf 100644 --- a/pkg/tsdb/prometheus/azureauth/azure.go +++ b/pkg/tsdb/prometheus/azureauth/azure.go @@ -77,7 +77,7 @@ func getOverriddenScopes(jsonData map[string]interface{}) ([]string, error) { func getPrometheusScopes(settings *azsettings.AzureSettings, credentials azcredentials.AzureCredentials) ([]string, error) { // Extract cloud from credentials - azureCloud, err := getAzureCloudFromCredentials(settings, credentials) + azureCloud, err := azcredentials.GetAzureCloud(settings, credentials) if err != nil { return nil, err } @@ -90,26 +90,3 @@ func getPrometheusScopes(settings *azsettings.AzureSettings, credentials azcrede return scopes, nil } } - -// To be part of grafana-azure-sdk-go -func getAzureCloudFromCredentials(settings *azsettings.AzureSettings, credentials azcredentials.AzureCredentials) (string, error) { - switch c := credentials.(type) { - case *azcredentials.AzureManagedIdentityCredentials: - // In case of managed identity, the cloud is always same as where Grafana is hosted - return getDefaultAzureCloud(settings), nil - case *azcredentials.AzureClientSecretCredentials: - return c.AzureCloud, nil - default: - err := fmt.Errorf("the Azure credentials of type '%s' not supported by Prometheus datasource", c.AzureAuthType()) - return "", err - } -} - -// To be part of grafana-azure-sdk-go -func getDefaultAzureCloud(settings *azsettings.AzureSettings) string { - cloudName := settings.Cloud - if cloudName == "" { - return azsettings.AzurePublic - } - return cloudName -}