diff --git a/docs/tutorials/azure.md b/docs/tutorials/azure.md index 202d7304b..8b186f9b7 100644 --- a/docs/tutorials/azure.md +++ b/docs/tutorials/azure.md @@ -58,6 +58,7 @@ The following fields are used: * `aadClientID` and `aadClientSecret` are associated with the Service Principal. This is only used with Service Principal method documented in the next section. * `useManagedIdentityExtension` - this is set to `true` if you use either AKS Kubelet Identity or AAD Pod Identities methods documented in the next section. * `userAssignedIdentityID` - this contains the client id from the Managed identitty when using the AAD Pod Identities method documented in the next setion. +* `activeDirectoryAuthorityHost` - this contains the uri to overwrite the default provided AAD Endpoint. This is useful for providing additional support where the endpoint is not available in the default cloud config from the [azure-sdk-for-go](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud#pkg-variables). * `useWorkloadIdentityExtension` - this is set to `true` if you use Workload Identity method documented in the next section. The Azure DNS provider expects, by default, that the configuration file is at `/etc/kubernetes/azure.json`. This can be overridden with the `--azure-config-file` option when starting ExternalDNS. diff --git a/main.go b/main.go index 22fefec95..eb6cd24d8 100644 --- a/main.go +++ b/main.go @@ -253,9 +253,9 @@ func main() { } p, err = awssd.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.DryRun, cfg.AWSSDServiceCleanup, cfg.TXTOwnerID, sd.New(awsSession)) case "azure-dns", "azure": - p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.DryRun) + p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.DryRun) case "azure-private-dns": - p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.DryRun) + p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.DryRun) case "bluecat": p, err = bluecat.NewBluecatProvider(cfg.BluecatConfigFile, cfg.BluecatDNSConfiguration, cfg.BluecatDNSServerName, cfg.BluecatDNSDeployType, cfg.BluecatDNSView, cfg.BluecatGatewayHost, cfg.BluecatRootZone, cfg.TXTPrefix, cfg.TXTSuffix, domainFilter, zoneIDFilter, cfg.DryRun, cfg.BluecatSkipTLSVerify) case "vinyldns": diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 0b25dcafe..26b579f82 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -101,6 +101,7 @@ type Config struct { AzureResourceGroup string AzureSubscriptionID string AzureUserAssignedIdentityClientID string + AzureActiveDirectoryAuthorityHost string BluecatDNSConfiguration string BluecatConfigFile string BluecatDNSView string diff --git a/provider/azure/azure.go b/provider/azure/azure.go index a7021d192..904fa6223 100644 --- a/provider/azure/azure.go +++ b/provider/azure/azure.go @@ -58,6 +58,7 @@ type AzureProvider struct { dryRun bool resourceGroup string userAssignedIdentityClientID string + activeDirectoryAuthorityHost string zonesClient ZonesClient recordSetsClient RecordSetsClient } @@ -65,8 +66,8 @@ type AzureProvider struct { // NewAzureProvider creates a new Azure provider. // // Returns the provider or an error if a provider could not be created. -func NewAzureProvider(configFile string, domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, subscriptionID string, resourceGroup string, userAssignedIdentityClientID string, dryRun bool) (*AzureProvider, error) { - cfg, err := getConfig(configFile, subscriptionID, resourceGroup, userAssignedIdentityClientID) +func NewAzureProvider(configFile string, domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, subscriptionID string, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, dryRun bool) (*AzureProvider, error) { + cfg, err := getConfig(configFile, subscriptionID, resourceGroup, userAssignedIdentityClientID, activeDirectoryAuthorityHost) if err != nil { return nil, fmt.Errorf("failed to read Azure config file '%s': %v", configFile, err) } @@ -90,6 +91,7 @@ func NewAzureProvider(configFile string, domainFilter endpoint.DomainFilter, zon dryRun: dryRun, resourceGroup: cfg.ResourceGroup, userAssignedIdentityClientID: cfg.UserAssignedIdentityID, + activeDirectoryAuthorityHost: cfg.ActiveDirectoryAuthorityHost, zonesClient: zonesClient, recordSetsClient: recordSetsClient, }, nil diff --git a/provider/azure/azure_private_dns.go b/provider/azure/azure_private_dns.go index 43e3bdc43..7b56cb6a0 100644 --- a/provider/azure/azure_private_dns.go +++ b/provider/azure/azure_private_dns.go @@ -52,6 +52,7 @@ type AzurePrivateDNSProvider struct { dryRun bool resourceGroup string userAssignedIdentityClientID string + activeDirectoryAuthorityHost string zonesClient PrivateZonesClient recordSetsClient PrivateRecordSetsClient } @@ -59,8 +60,8 @@ type AzurePrivateDNSProvider struct { // NewAzurePrivateDNSProvider creates a new Azure Private DNS provider. // // Returns the provider or an error if a provider could not be created. -func NewAzurePrivateDNSProvider(configFile string, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, subscriptionID string, resourceGroup string, userAssignedIdentityClientID string, dryRun bool) (*AzurePrivateDNSProvider, error) { - cfg, err := getConfig(configFile, subscriptionID, resourceGroup, userAssignedIdentityClientID) +func NewAzurePrivateDNSProvider(configFile string, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, subscriptionID string, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, dryRun bool) (*AzurePrivateDNSProvider, error) { + cfg, err := getConfig(configFile, subscriptionID, resourceGroup, userAssignedIdentityClientID, activeDirectoryAuthorityHost) if err != nil { return nil, fmt.Errorf("failed to read Azure config file '%s': %v", configFile, err) } @@ -83,6 +84,7 @@ func NewAzurePrivateDNSProvider(configFile string, domainFilter endpoint.DomainF dryRun: dryRun, resourceGroup: cfg.ResourceGroup, userAssignedIdentityClientID: cfg.UserAssignedIdentityID, + activeDirectoryAuthorityHost: cfg.ActiveDirectoryAuthorityHost, zonesClient: zonesClient, recordSetsClient: recordSetsClient, }, nil diff --git a/provider/azure/azure_test.go b/provider/azure/azure_test.go index 4fd9fa8fc..f2031d139 100644 --- a/provider/azure/azure_test.go +++ b/provider/azure/azure_test.go @@ -222,13 +222,13 @@ func createMockRecordSetMultiWithTTL(name, recordType string, ttl int64, values } // newMockedAzureProvider creates an AzureProvider comprising the mocked clients for zones and recordsets -func newMockedAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, userAssignedIdentityClientID string, zones []*dns.Zone, recordSets []*dns.RecordSet) (*AzureProvider, error) { +func newMockedAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, zones []*dns.Zone, recordSets []*dns.RecordSet) (*AzureProvider, error) { zonesClient := newMockZonesClient(zones) recordSetsClient := newMockRecordSetsClient(recordSets) - return newAzureProvider(domainFilter, zoneNameFilter, zoneIDFilter, dryRun, resourceGroup, userAssignedIdentityClientID, &zonesClient, &recordSetsClient), nil + return newAzureProvider(domainFilter, zoneNameFilter, zoneIDFilter, dryRun, resourceGroup, userAssignedIdentityClientID, activeDirectoryAuthorityHost, &zonesClient, &recordSetsClient), nil } -func newAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, userAssignedIdentityClientID string, zonesClient ZonesClient, recordsClient RecordSetsClient) *AzureProvider { +func newAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, zonesClient ZonesClient, recordsClient RecordSetsClient) *AzureProvider { return &AzureProvider{ domainFilter: domainFilter, zoneNameFilter: zoneNameFilter, @@ -236,6 +236,7 @@ func newAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoin dryRun: dryRun, resourceGroup: resourceGroup, userAssignedIdentityClientID: userAssignedIdentityClientID, + activeDirectoryAuthorityHost: activeDirectoryAuthorityHost, zonesClient: zonesClient, recordSetsClient: recordsClient, } @@ -246,7 +247,7 @@ func validateAzureEndpoints(t *testing.T, endpoints []*endpoint.Endpoint, expect } func TestAzureRecord(t *testing.T) { - provider, err := newMockedAzureProvider(endpoint.NewDomainFilter([]string{"example.com"}), endpoint.NewDomainFilter([]string{}), provider.NewZoneIDFilter([]string{""}), true, "k8s", "", + provider, err := newMockedAzureProvider(endpoint.NewDomainFilter([]string{"example.com"}), endpoint.NewDomainFilter([]string{}), provider.NewZoneIDFilter([]string{""}), true, "k8s", "", "", []*dns.Zone{ createMockZone("example.com", "/dnszones/example.com"), }, @@ -286,7 +287,7 @@ func TestAzureRecord(t *testing.T) { } func TestAzureMultiRecord(t *testing.T) { - provider, err := newMockedAzureProvider(endpoint.NewDomainFilter([]string{"example.com"}), endpoint.NewDomainFilter([]string{}), provider.NewZoneIDFilter([]string{""}), true, "k8s", "", + provider, err := newMockedAzureProvider(endpoint.NewDomainFilter([]string{"example.com"}), endpoint.NewDomainFilter([]string{}), provider.NewZoneIDFilter([]string{""}), true, "k8s", "", "", []*dns.Zone{ createMockZone("example.com", "/dnszones/example.com"), }, @@ -381,6 +382,7 @@ func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordSetsC dryRun, "group", "", + "", &zonesClient, client, ) @@ -440,7 +442,7 @@ func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordSetsC } func TestAzureNameFilter(t *testing.T) { - provider, err := newMockedAzureProvider(endpoint.NewDomainFilter([]string{"nginx.example.com"}), endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), true, "k8s", "", + provider, err := newMockedAzureProvider(endpoint.NewDomainFilter([]string{"nginx.example.com"}), endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), true, "k8s", "", "", []*dns.Zone{ createMockZone("example.com", "/dnszones/example.com"), }, @@ -506,6 +508,7 @@ func testAzureApplyChangesInternalZoneName(t *testing.T, dryRun bool, client Rec dryRun, "group", "", + "", &zonesClient, client, ) diff --git a/provider/azure/config.go b/provider/azure/config.go index 5bed03aed..eca4ae0d6 100644 --- a/provider/azure/config.go +++ b/provider/azure/config.go @@ -41,9 +41,10 @@ type config struct { UseManagedIdentityExtension bool `json:"useManagedIdentityExtension" yaml:"useManagedIdentityExtension"` UseWorkloadIdentityExtension bool `json:"useWorkloadIdentityExtension" yaml:"useWorkloadIdentityExtension"` UserAssignedIdentityID string `json:"userAssignedIdentityID" yaml:"userAssignedIdentityID"` + ActiveDirectoryAuthorityHost string `json:"activeDirectoryAuthorityHost" yaml:"activeDirectoryAuthorityHost"` } -func getConfig(configFile, subscriptionID, resourceGroup, userAssignedIdentityClientID string) (*config, error) { +func getConfig(configFile, subscriptionID, resourceGroup, userAssignedIdentityClientID, activeDirectoryAuthorityHost string) (*config, error) { contents, err := os.ReadFile(configFile) if err != nil { return nil, fmt.Errorf("failed to read Azure config file '%s': %v", configFile, err) @@ -65,6 +66,10 @@ func getConfig(configFile, subscriptionID, resourceGroup, userAssignedIdentityCl if userAssignedIdentityClientID != "" { cfg.UserAssignedIdentityID = userAssignedIdentityClientID } + // If activeDirectoryAuthorityHost is provided explicitly, override existing one in config file + if activeDirectoryAuthorityHost != "" { + cfg.ActiveDirectoryAuthorityHost = activeDirectoryAuthorityHost + } return cfg, nil } diff --git a/provider/azure/config_test.go b/provider/azure/config_test.go index 338184e32..1099515d3 100644 --- a/provider/azure/config_test.go +++ b/provider/azure/config_test.go @@ -51,10 +51,11 @@ func TestGetCloudConfiguration(t *testing.T) { func TestOverrideConfiguration(t *testing.T) { _, filename, _, _ := runtime.Caller(0) configFile := path.Join(path.Dir(filename), "config_test.json") - cfg, err := getConfig(configFile, "subscription-override", "rg-override", "") + cfg, err := getConfig(configFile, "subscription-override", "rg-override", "", "aad-endpoint-override") if err != nil { t.Errorf("got unexpected err %v", err) } assert.Equal(t, cfg.SubscriptionID, "subscription-override") assert.Equal(t, cfg.ResourceGroup, "rg-override") + assert.Equal(t, cfg.ActiveDirectoryAuthorityHost, "aad-endpoint-override") }