From 631a5205009ff9967318bf73e1a947eae2a01cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6berl?= Date: Sat, 13 Aug 2022 20:46:59 +0000 Subject: [PATCH 1/3] Add generic OIDC authentication Adds a way to provide the OIDC token directly to the provider. This enables to use the provider without tokens/passwords in GitLab CI See: https://docs.gitlab.com/ee/ci/cloud_services/ Closes #16901 --- go.mod | 2 +- go.sum | 4 +- internal/provider/provider.go | 12 ++++- .../authentication/auth_method_oidc.go | 44 ++++++++++++------ .../authentication/builder.go | 1 + vendor/modules.txt | 2 +- .../service_principal_oidc.html.markdown | 45 ++++++++++++++----- website/docs/index.html.markdown | 2 + 8 files changed, 82 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 1d3dab5dc69f..fb0ffa7ff7d4 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/gofrs/uuid v4.0.0+incompatible github.com/google/go-cmp v0.5.8 github.com/google/uuid v1.1.2 - github.com/hashicorp/go-azure-helpers v0.39.1 + github.com/hashicorp/go-azure-helpers v0.40.0 github.com/hashicorp/go-azure-sdk v0.20220824.1090858 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-uuid v1.0.3 diff --git a/go.sum b/go.sum index d1f6befbbbe9..d0e6ef68add6 100644 --- a/go.sum +++ b/go.sum @@ -214,8 +214,8 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-azure-helpers v0.12.0/go.mod h1:Zc3v4DNeX6PDdy7NljlYpnrdac1++qNW0I4U+ofGwpg= -github.com/hashicorp/go-azure-helpers v0.39.1 h1:f5zkbJccz9Zbgx1qiI1ssMh8jpPubT3cyPRpKuM9WfQ= -github.com/hashicorp/go-azure-helpers v0.39.1/go.mod h1:gcutZ/Hf/O7YN9M3UIvyZ9l0Rxv7Yrc9x5sSfM9cuSw= +github.com/hashicorp/go-azure-helpers v0.40.0 h1:NjiyF+jN+0mRdFBU894yzZSxu1SNrbvj8l4rEDpCB0A= +github.com/hashicorp/go-azure-helpers v0.40.0/go.mod h1:gcutZ/Hf/O7YN9M3UIvyZ9l0Rxv7Yrc9x5sSfM9cuSw= github.com/hashicorp/go-azure-sdk v0.20220824.1090858 h1:OPdyEfc24JtC4fhYChE6vC7meCAMbPkgyWZ5ZYPC1W8= github.com/hashicorp/go-azure-sdk v0.20220824.1090858/go.mod h1:jOhjVttoXh2We/glz4BC/0t0Lo8+M9WQBA4sbAPQPMY= github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 1e6faa4798b6..1521627304e6 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -171,13 +171,20 @@ func azureProvider(supportLegacyTestSuite bool) *schema.Provider { Type: schema.TypeString, Optional: true, DefaultFunc: schema.MultiEnvDefaultFunc([]string{"ARM_OIDC_REQUEST_TOKEN", "ACTIONS_ID_TOKEN_REQUEST_TOKEN"}, ""), - Description: "The bearer token for the request to the OIDC provider. For use When authenticating as a Service Principal using OpenID Connect.", + Description: "The bearer token for the request to the OIDC provider. For use when authenticating as a Service Principal using OpenID Connect.", }, "oidc_request_url": { Type: schema.TypeString, Optional: true, DefaultFunc: schema.MultiEnvDefaultFunc([]string{"ARM_OIDC_REQUEST_URL", "ACTIONS_ID_TOKEN_REQUEST_URL"}, ""), - Description: "The URL for the OIDC provider from which to request an ID token. For use When authenticating as a Service Principal using OpenID Connect.", + Description: "The URL for the OIDC provider from which to request an ID token. For use when authenticating as a Service Principal using OpenID Connect.", + }, + + "oidc_token": { + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{"ARM_OIDC_TOKEN"}, ""), + Description: "The OIDC ID token for use when authenticating as a Service Principal using OpenID Connect.", }, "use_oidc": { @@ -279,6 +286,7 @@ func providerConfigure(p *schema.Provider) schema.ConfigureContextFunc { ClientCertPath: d.Get("client_certificate_path").(string), IDTokenRequestToken: d.Get("oidc_request_token").(string), IDTokenRequestURL: d.Get("oidc_request_url").(string), + IDToken: d.Get("oidc_token").(string), // Feature Toggles SupportsClientCertAuth: true, diff --git a/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_oidc.go b/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_oidc.go index 2bc30910742e..c0e87b0af87a 100644 --- a/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_oidc.go +++ b/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_oidc.go @@ -3,6 +3,7 @@ package authentication import ( "context" "fmt" + "github.com/Azure/go-autorest/autorest" "github.com/hashicorp/go-multierror" authWrapper "github.com/manicminer/hamilton-autorest/auth" @@ -14,6 +15,7 @@ type oidcAuth struct { auxiliaryTenantIds []string clientId string environment string + idToken string idTokenRequestToken string idTokenRequestUrl string tenantId string @@ -24,6 +26,7 @@ func (a oidcAuth) build(b Builder) (authMethod, error) { auxiliaryTenantIds: b.AuxiliaryTenantIDs, clientId: b.ClientID, environment: b.Environment, + idToken: b.IDToken, idTokenRequestUrl: b.IDTokenRequestURL, idTokenRequestToken: b.IDTokenRequestToken, tenantId: b.TenantID, @@ -32,7 +35,7 @@ func (a oidcAuth) build(b Builder) (authMethod, error) { } func (a oidcAuth) isApplicable(b Builder) bool { - return b.SupportsOIDCAuth && b.IDTokenRequestURL != "" && b.IDTokenRequestToken != "" && b.UseMicrosoftGraph + return b.SupportsOIDCAuth && b.UseMicrosoftGraph && (b.IDToken != "" || (b.IDTokenRequestURL != "" && b.IDTokenRequestToken != "")) } func (a oidcAuth) name() string { @@ -49,17 +52,30 @@ func (a oidcAuth) getMSALToken(ctx context.Context, api environments.Api, _ auto return nil, fmt.Errorf("environment config error: %v", err) } - conf := auth.GitHubOIDCConfig{ - Environment: environment, - TenantID: a.tenantId, - AuxiliaryTenantIDs: a.auxiliaryTenantIds, - ClientID: a.clientId, - IDTokenRequestURL: a.idTokenRequestUrl, - IDTokenRequestToken: a.idTokenRequestToken, - Scopes: []string{api.DefaultScope()}, + if a.idToken == "" { + conf := auth.GitHubOIDCConfig{ + Environment: environment, + TenantID: a.tenantId, + AuxiliaryTenantIDs: a.auxiliaryTenantIds, + ClientID: a.clientId, + IDTokenRequestURL: a.idTokenRequestUrl, + IDTokenRequestToken: a.idTokenRequestToken, + Scopes: []string{api.DefaultScope()}, + } + return &authWrapper.Authorizer{Authorizer: conf.TokenSource(ctx)}, nil + } + + conf := auth.ClientCredentialsConfig{ + Environment: environment, + TenantID: a.tenantId, + AuxiliaryTenantIDs: a.auxiliaryTenantIds, + ClientID: a.clientId, + FederatedAssertion: a.idToken, + Scopes: []string{api.DefaultScope()}, + TokenVersion: auth.TokenVersion2, } - return &authWrapper.Authorizer{Authorizer: conf.TokenSource(ctx)}, nil + return &authWrapper.Authorizer{Authorizer: conf.TokenSource(ctx, auth.ClientCredentialsAssertionType)}, nil } func (a oidcAuth) populateConfig(c *Config) error { @@ -82,12 +98,12 @@ func (a oidcAuth) validate() error { err = multierror.Append(err, fmt.Errorf(fmtErrorMessage, "Client ID")) } - if a.idTokenRequestUrl == "" { - err = multierror.Append(err, fmt.Errorf(fmtErrorMessage, "ID Token Request URL")) + if a.idTokenRequestUrl == "" && a.idToken == "" { + err = multierror.Append(err, fmt.Errorf(fmtErrorMessage, "ID Token or ID Token Request URL")) } - if a.idTokenRequestToken == "" { - err = multierror.Append(err, fmt.Errorf(fmtErrorMessage, "ID Token Request Token")) + if a.idTokenRequestToken == "" && a.idToken == "" { + err = multierror.Append(err, fmt.Errorf(fmtErrorMessage, "ID Token or ID Token Request Token")) } return err.ErrorOrNil() diff --git a/vendor/github.com/hashicorp/go-azure-helpers/authentication/builder.go b/vendor/github.com/hashicorp/go-azure-helpers/authentication/builder.go index 3f0d299a500a..3d7d7a567936 100644 --- a/vendor/github.com/hashicorp/go-azure-helpers/authentication/builder.go +++ b/vendor/github.com/hashicorp/go-azure-helpers/authentication/builder.go @@ -46,6 +46,7 @@ type Builder struct { // OIDC Auth SupportsOIDCAuth bool + IDToken string IDTokenRequestURL string IDTokenRequestToken string diff --git a/vendor/modules.txt b/vendor/modules.txt index 7cf0ac172b2a..d7abfe5ba5f4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -166,7 +166,7 @@ github.com/google/uuid # github.com/hashicorp/errwrap v1.1.0 ## explicit github.com/hashicorp/errwrap -# github.com/hashicorp/go-azure-helpers v0.39.1 +# github.com/hashicorp/go-azure-helpers v0.40.0 ## explicit; go 1.17 github.com/hashicorp/go-azure-helpers/authentication github.com/hashicorp/go-azure-helpers/lang/dates diff --git a/website/docs/guides/service_principal_oidc.html.markdown b/website/docs/guides/service_principal_oidc.html.markdown index 438d990ebef9..698ccd8d17f5 100644 --- a/website/docs/guides/service_principal_oidc.html.markdown +++ b/website/docs/guides/service_principal_oidc.html.markdown @@ -19,9 +19,7 @@ Terraform supports a number of different methods for authenticating to Azure: We recommend using either a Service Principal or Managed Service Identity when running Terraform non-interactively (such as when running Terraform in a CI server) - and authenticating using the Azure CLI when running Terraform locally. -~> **Note:** WARNING: the current implementation of OIDC authentication only works in GitHub actions. A generic implementation where a token can be provided is being tracked in [this issue](https://github.com/hashicorp/terraform-provider-azurerm/issues/16901) - -## Setting up an Application and Service Principal +## Setting up an Application and Service Principal in Azure A Service Principal is a security principal within Azure Active Directory which can be granted access to resources within Azure Subscriptions. To authenticate with a Service Principal, you will need to create an Application object within Azure Active Directory, which you will use as a means of authentication, either [using a Client Secret](service_principal_client_secret.html), [a Client Certificate](service_principal_client_certificate.html), or OpenID Connect (which is documented in this guide). This can be done using the Azure Portal. @@ -90,7 +88,21 @@ Firstly, specify a Role which grants the appropriate permissions needed for the Secondly, search for and select the name of the Service Principal created in Azure Active Directory to assign it this role - then press **Save**. -### Configuring the Service Principal in Terraform +### Configure Azure Active Directory Application to Trust a Generic Issuer + +On the Azure Active Directory application page, go to **Certificates and secrets**. + +In the Federated credentials tab, select Add credential. The Add a credential blade opens. In the **Federated credential scenario** drop-down box select **Other issuer**. + +Specify the **Issuer URL** and **Subject Identifier** for your OIDC provider. For GitLab use your GitLab instance URL and the project/branch specifier as defined in the [GitLab OIDC documentation](https://docs.gitlab.com/ee/ci/cloud_services/) (e.g. `"project_path:user/my-repo:ref_type:branch:ref:main"`). + +Add a **Name** for the federated credential. + +For GitLab you will need to set the value for **Audience** to your GitLab instance ("https://gitlab.com" or "https://gitlab.example.com"). + +Click **Add** to configure the federated credential. + +## Configuring the Service Principal in Terraform ~> **Note:** If using the AzureRM Backend you may also need to configure OIDC there too, see [the documentation for the AzureRM Backend](https://www.terraform.io/language/settings/backends/azurerm) for more information. @@ -104,7 +116,16 @@ $ export ARM_SUBSCRIPTION_ID="00000000-0000-0000-0000-000000000000" $ export ARM_TENANT_ID="00000000-0000-0000-0000-000000000000" ``` -The provider will detect the `ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN` environment variables set by GitHub. You can also specify the `ARM_OIDC_REQUEST_TOKEN` and `ARM_OIDC_REQUEST_URL` environment variables. +The provider will use the `ARM_OIDC_TOKEN` environment variable as an OIDC token. + +GitLab CI provides a valid OIDC token as an environment variable with the name `CI_JOB_JWT_V2` - you will have to set the ARM_OIDC_TOKEN in the GitLab CI variables: + +```yaml +variables: + ARM_OIDC_TOKEN: $CI_JOB_JWT_V2 +``` + +For GitHub provider will detect the `ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN` environment variables set by the GitHub Actions runtim. You can also specify the `ARM_OIDC_REQUEST_TOKEN` and `ARM_OIDC_REQUEST_URL` environment variables. For GitHub Actions workflows, you'll need to ensure the workflow has `write` permissions for the `id-token`. @@ -147,11 +168,12 @@ At this point running either `terraform plan` or `terraform apply` should allow --- -It's also possible to configure these variables either in-line or from using variables in Terraform (as the `oidc_request_token` and `oidc_request_url` are in this example), like so: +It's also possible to configure these variables either in-line or from using variables in Terraform (as the `oidc_token` or `oidc_request_token` and `oidc_request_url` are in this example), like so: ~> **NOTE:** We'd recommend not defining these variables in-line since they could easily be checked into Source Control. ```hcl +variable "oidc_token" {} variable "oidc_request_token" {} variable "oidc_request_url" {} @@ -170,12 +192,15 @@ terraform { provider "azurerm" { features {} - subscription_id = "00000000-0000-0000-0000-000000000000" - client_id = "00000000-0000-0000-0000-000000000000" - use_oidc = true + subscription_id = "00000000-0000-0000-0000-000000000000" + client_id = "00000000-0000-0000-0000-000000000000" + use_oidc = true + # for GitHub actions oidc_request_token = var.oidc_request_token oidc_request_url = var.oidc_request_url - tenant_id = "00000000-0000-0000-0000-000000000000" + # for GitLab or generic OIDC + oidc_token = var.oidc_token + tenant_id = "00000000-0000-0000-0000-000000000000" } ``` diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index 61a77a16a545..60a7c63d7cc6 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -136,6 +136,8 @@ When authenticating as a Service Principal using Open ID Connect, the following * `oidc_request_url` - (Optional) The URL for the OIDC provider from which to request an ID token. This can also be sourced from the `ARM_OIDC_REQUEST_URL` or `ACTIONS_ID_TOKEN_REQUEST_URL` Environment Variables. +* `oidc_token` - (Optional) The OIDC ID token. This can also be sourced from the `ARM_OIDC_TOKEN` environment Variable. + * `use_oidc` - (Optional) Should OIDC be used for Authentication? This can also be sourced from the `ARM_USE_OIDC` Environment Variable. Defaults to `false`. More information on [how to configure a Service Principal using OpenID Connect can be found in this guide](guides/service_principal_oidc.html). From 69619efc611de2b1485435d8b30bd61bcfbb1db6 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Thu, 25 Aug 2022 14:12:33 +0100 Subject: [PATCH 2/3] Documentation wording for OIDC authentication --- .../service_principal_oidc.html.markdown | 27 +++++-------------- website/docs/index.html.markdown | 2 +- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/website/docs/guides/service_principal_oidc.html.markdown b/website/docs/guides/service_principal_oidc.html.markdown index 698ccd8d17f5..c4aed2b89c8a 100644 --- a/website/docs/guides/service_principal_oidc.html.markdown +++ b/website/docs/guides/service_principal_oidc.html.markdown @@ -92,15 +92,7 @@ Secondly, search for and select the name of the Service Principal created in Azu On the Azure Active Directory application page, go to **Certificates and secrets**. -In the Federated credentials tab, select Add credential. The Add a credential blade opens. In the **Federated credential scenario** drop-down box select **Other issuer**. - -Specify the **Issuer URL** and **Subject Identifier** for your OIDC provider. For GitLab use your GitLab instance URL and the project/branch specifier as defined in the [GitLab OIDC documentation](https://docs.gitlab.com/ee/ci/cloud_services/) (e.g. `"project_path:user/my-repo:ref_type:branch:ref:main"`). - -Add a **Name** for the federated credential. - -For GitLab you will need to set the value for **Audience** to your GitLab instance ("https://gitlab.com" or "https://gitlab.example.com"). - -Click **Add** to configure the federated credential. +In the Federated credentials tab, select **Add credential**. The 'Add a credential' blade opens. Refer to the instructions from your OIDC provider for completing the form, before choosing a **Name** for the federated credential and clicking the **Add** button. ## Configuring the Service Principal in Terraform @@ -116,16 +108,9 @@ $ export ARM_SUBSCRIPTION_ID="00000000-0000-0000-0000-000000000000" $ export ARM_TENANT_ID="00000000-0000-0000-0000-000000000000" ``` -The provider will use the `ARM_OIDC_TOKEN` environment variable as an OIDC token. +The provider will use the `ARM_OIDC_TOKEN` environment variable as an OIDC token. You can use this variable to specify the token provided by your OIDC provider. -GitLab CI provides a valid OIDC token as an environment variable with the name `CI_JOB_JWT_V2` - you will have to set the ARM_OIDC_TOKEN in the GitLab CI variables: - -```yaml -variables: - ARM_OIDC_TOKEN: $CI_JOB_JWT_V2 -``` - -For GitHub provider will detect the `ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN` environment variables set by the GitHub Actions runtim. You can also specify the `ARM_OIDC_REQUEST_TOKEN` and `ARM_OIDC_REQUEST_URL` environment variables. +When running Terraform in GitHub Actions, the provider will detect the `ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN` environment variables set by the GitHub Actions runtime. You can also specify the `ARM_OIDC_REQUEST_TOKEN` and `ARM_OIDC_REQUEST_URL` environment variables. For GitHub Actions workflows, you'll need to ensure the workflow has `write` permissions for the `id-token`. @@ -195,10 +180,12 @@ provider "azurerm" { subscription_id = "00000000-0000-0000-0000-000000000000" client_id = "00000000-0000-0000-0000-000000000000" use_oidc = true - # for GitHub actions + + # for GitHub Actions oidc_request_token = var.oidc_request_token oidc_request_url = var.oidc_request_url - # for GitLab or generic OIDC + + # for other generic OIDC providers oidc_token = var.oidc_token tenant_id = "00000000-0000-0000-0000-000000000000" } diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index 60a7c63d7cc6..6450026b927c 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -136,7 +136,7 @@ When authenticating as a Service Principal using Open ID Connect, the following * `oidc_request_url` - (Optional) The URL for the OIDC provider from which to request an ID token. This can also be sourced from the `ARM_OIDC_REQUEST_URL` or `ACTIONS_ID_TOKEN_REQUEST_URL` Environment Variables. -* `oidc_token` - (Optional) The OIDC ID token. This can also be sourced from the `ARM_OIDC_TOKEN` environment Variable. +* `oidc_token` - (Optional) The ID token when authenticating using OpenID Connect (OIDC). This can also be sourced from the `ARM_OIDC_TOKEN` environment Variable. * `use_oidc` - (Optional) Should OIDC be used for Authentication? This can also be sourced from the `ARM_USE_OIDC` Environment Variable. Defaults to `false`. From 66f7850fc7ece6e443ddb36649c3d49c9f415851 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Thu, 25 Aug 2022 14:12:58 +0100 Subject: [PATCH 3/3] Use EnvDefaultFUnc for single variable --- internal/provider/provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 1521627304e6..42c166a441f2 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -183,7 +183,7 @@ func azureProvider(supportLegacyTestSuite bool) *schema.Provider { "oidc_token": { Type: schema.TypeString, Optional: true, - DefaultFunc: schema.MultiEnvDefaultFunc([]string{"ARM_OIDC_TOKEN"}, ""), + DefaultFunc: schema.EnvDefaultFunc("ARM_OIDC_TOKEN", ""), Description: "The OIDC ID token for use when authenticating as a Service Principal using OpenID Connect.", },