From 15a22ae00a42890d58859426de8513af55ee31ac Mon Sep 17 00:00:00 2001 From: Manolo Mancebo <32719210+lckz@users.noreply.github.com> Date: Thu, 30 Jan 2025 18:00:02 +0000 Subject: [PATCH] FEATURE: Enable OIDC support for Azure DNS provider (#3398) Co-authored-by: PE Team Co-authored-by: Jeffrey Cafferata --- documentation/provider/azure_dns.md | 94 +++++++++++++++++++++++++- providers/azuredns/azureDnsProvider.go | 53 ++++++++++++--- 2 files changed, 133 insertions(+), 14 deletions(-) diff --git a/documentation/provider/azure_dns.md b/documentation/provider/azure_dns.md index cde9e9c89..0b8315719 100644 --- a/documentation/provider/azure_dns.md +++ b/documentation/provider/azure_dns.md @@ -1,7 +1,56 @@ ## Configuration -To use this provider, add an entry to `creds.json` with `TYPE` set to `AZURE_DNS` -along with the API credentials. +To use this provider, add an entry to `creds.json` with `TYPE` set to `AZURE_DNS`, along with the necessary credentials. The provider supports three authentication methods: + +1. **DefaultAzureCredential (Recommended)**: Simplifies authentication by leveraging Azure's credential chain (e.g., environment variables, managed identities, Azure CLI, etc.). +2. **Client ID and Secret**: Provides backward compatibility for users who prefer this method. +3. **OIDC (InteractiveBrowserCredential)**: Allows interactive login via the browser for specific scenarios. + +### Example Configurations + +#### **DefaultAzureCredential (Recommended)** + +This method does not require explicit credentials in `creds.json` and leverages Azure's default authentication chain: +- Managed Identity (if running in Azure) +- Environment variables +- Azure CLI credentials + +No additional setup is required in `creds.json`: + +{% code title="creds.json" %} +```json +{ + "azuredns_main": { + "TYPE": "AZURE_DNS", + "SubscriptionID": "AZURE_SUBSCRIPTION_ID", + "ResourceGroup": "AZURE_RESOURCE_GROUP" + } +} +``` +{% endcode %} + +You can also use environment variables: + +```shell +export AZURE_SUBSCRIPTION_ID=XXXXXXXXX +export AZURE_RESOURCE_GROUP=YYYYYYYYY +``` + +{% code title="creds.json" %} +```json +{ + "azuredns_main": { + "TYPE": "AZURE_DNS", + "SubscriptionID": "$AZURE_SUBSCRIPTION_ID", + "ResourceGroup": "$AZURE_RESOURCE_GROUP" + } +} +``` +{% endcode %} + +#### **Client ID and Secret (Backward Compatibility)** + +To use the client ID and secret-based authentication: Example: @@ -45,7 +94,46 @@ export AZURE_CLIENT_SECRET=BBBBBBBBB ``` {% endcode %} -NOTE: The ResourceGroup is case sensitive. +#### **OIDC (Interactive Browser Authentication)** + +To enable OIDC for interactive login: + +{% code title="creds.json" %} +```json +{ + "azuredns_main": { + "TYPE": "AZURE_DNS", + "SubscriptionID": "AZURE_SUBSCRIPTION_ID", + "ResourceGroup": "AZURE_RESOURCE_GROUP", + "TenantID": "AZURE_TENANT_ID", + "UseOIDC": "true" + } +} +``` +{% endcode %} + ++You can also use environment variables: +```shell +export AZURE_SUBSCRIPTION_ID=XXXXXXXXX +export AZURE_RESOURCE_GROUP=YYYYYYYYY +export AZURE_TENANT_ID=ZZZZZZZZ +export UseOIDC=true +``` + +{% code title="creds.json" %} +```json +{ + "azuredns_main": { + "TYPE": "AZURE_DNS", + "SubscriptionID": "$AZURE_SUBSCRIPTION_ID", + "ResourceGroup": "$AZURE_RESOURCE_GROUP", + "TenantID": "$AZURE_TENANT_ID", + "UseOIDC": "$UseOIDC" + } +} +``` +{% endcode %} + ## Metadata This provider does not recognize any special metadata fields unique to Azure DNS. diff --git a/providers/azuredns/azureDnsProvider.go b/providers/azuredns/azureDnsProvider.go index d243a4ab6..8b0548b94 100644 --- a/providers/azuredns/azureDnsProvider.go +++ b/providers/azuredns/azureDnsProvider.go @@ -26,24 +26,53 @@ type azurednsProvider struct { subscriptionID *string } +// Modified `newAzureDNSDsp` to maintain backward compatibility with the new OIDC support. func newAzureDNSDsp(conf map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) { return newAzureDNS(conf, metadata) } +// Updated function to prioritize DefaultAzureCredential and fallback to OIDC or client-secret-based methods. func newAzureDNS(m map[string]string, _ json.RawMessage) (*azurednsProvider, error) { subID, rg := m["SubscriptionID"], m["ResourceGroup"] clientID, clientSecret, tenantID := m["ClientID"], m["ClientSecret"], m["TenantID"] - credential, authErr := aauth.NewClientSecretCredential(tenantID, clientID, clientSecret, nil) - if authErr != nil { - return nil, authErr + useOIDC := m["UseOIDC"] == "true" + + var credential azcore.TokenCredential + var authErr error + + // Authentication Logic + if useOIDC { + // OIDC Authentication with `InteractiveBrowserCredential` + oidcCredentialOpts := aauth.InteractiveBrowserCredentialOptions{ + TenantID: tenantID, + } + credential, authErr = aauth.NewInteractiveBrowserCredential(&oidcCredentialOpts) + if authErr != nil { + return nil, fmt.Errorf("failed to create OIDC credential: %w", authErr) + } + } else if clientID != "" && clientSecret != "" { + // Client ID and Secret-based Authentication + credential, authErr = aauth.NewClientSecretCredential(tenantID, clientID, clientSecret, nil) + if authErr != nil { + return nil, fmt.Errorf("failed to create Client Secret credential: %w", authErr) + } + } else { + // Default Azure Credential as the default mechanism + credential, authErr = aauth.NewDefaultAzureCredential(nil) + if authErr != nil { + return nil, fmt.Errorf("failed to create Default Azure credential: %w", authErr) + } } - zonesClient, zoneErr := adns.NewZonesClient(subID, credential, nil) - if zoneErr != nil { - return nil, zoneErr + + // Create DNS clients using the selected credential + zonesClient, err := adns.NewZonesClient(subID, credential, nil) + if err != nil { + return nil, fmt.Errorf("failed to create zones client: %w", err) } - recordsClient, recordErr := adns.NewRecordSetsClient(subID, credential, nil) - if recordErr != nil { - return nil, recordErr + + recordsClient, err := adns.NewRecordSetsClient(subID, credential, nil) + if err != nil { + return nil, fmt.Errorf("failed to create records client: %w", err) } api := &azurednsProvider{ @@ -52,10 +81,12 @@ func newAzureDNS(m map[string]string, _ json.RawMessage) (*azurednsProvider, err resourceGroup: to.StringPtr(rg), subscriptionID: to.StringPtr(subID), } - err := api.getZones() - if err != nil { + + // Initialize zones + if err := api.getZones(); err != nil { return nil, err } + return api, nil }