From fa034751dc33aa36b4b7dcaaba319f47b98e2060 Mon Sep 17 00:00:00 2001 From: gitirabassi Date: Tue, 10 Mar 2020 14:55:19 +0100 Subject: [PATCH] feat(azure): add usage of Managed Identities for azuredns provider for acme dns01 challenge Signed-off-by: gitirabassi --- deploy/crds/crd-challenges.yaml | 3 -- deploy/crds/crd-clusterissuers.yaml | 3 -- deploy/crds/crd-issuers.yaml | 3 -- pkg/apis/acme/v1alpha2/types_issuer.go | 6 ++-- pkg/apis/acme/v1alpha3/types_issuer.go | 6 ++-- .../apis/certmanager/validation/issuer.go | 23 ++++++++---- pkg/issuer/acme/dns/azuredns/azuredns.go | 36 +++++++++++++------ pkg/issuer/acme/dns/dns.go | 24 +++++++------ 8 files changed, 63 insertions(+), 41 deletions(-) diff --git a/deploy/crds/crd-challenges.yaml b/deploy/crds/crd-challenges.yaml index d6f6bb9bd..058e97ae7 100644 --- a/deploy/crds/crd-challenges.yaml +++ b/deploy/crds/crd-challenges.yaml @@ -201,11 +201,8 @@ spec: containing the configuration for Azure DNS type: object required: - - clientID - - clientSecretSecretRef - resourceGroupName - subscriptionID - - tenantID properties: clientID: type: string diff --git a/deploy/crds/crd-clusterissuers.yaml b/deploy/crds/crd-clusterissuers.yaml index e258da814..e8ff79e39 100644 --- a/deploy/crds/crd-clusterissuers.yaml +++ b/deploy/crds/crd-clusterissuers.yaml @@ -244,11 +244,8 @@ spec: containing the configuration for Azure DNS type: object required: - - clientID - - clientSecretSecretRef - resourceGroupName - subscriptionID - - tenantID properties: clientID: type: string diff --git a/deploy/crds/crd-issuers.yaml b/deploy/crds/crd-issuers.yaml index 7667fb11c..6d62939a7 100644 --- a/deploy/crds/crd-issuers.yaml +++ b/deploy/crds/crd-issuers.yaml @@ -244,11 +244,8 @@ spec: containing the configuration for Azure DNS type: object required: - - clientID - - clientSecretSecretRef - resourceGroupName - subscriptionID - - tenantID properties: clientID: type: string diff --git a/pkg/apis/acme/v1alpha2/types_issuer.go b/pkg/apis/acme/v1alpha2/types_issuer.go index 7ca32385b..ca7b79b92 100644 --- a/pkg/apis/acme/v1alpha2/types_issuer.go +++ b/pkg/apis/acme/v1alpha2/types_issuer.go @@ -343,13 +343,13 @@ type ACMEIssuerDNS01ProviderRoute53 struct { // ACMEIssuerDNS01ProviderAzureDNS is a structure containing the // configuration for Azure DNS type ACMEIssuerDNS01ProviderAzureDNS struct { - ClientID string `json:"clientID"` + ClientID string `json:"clientID,omitempty"` - ClientSecret cmmeta.SecretKeySelector `json:"clientSecretSecretRef"` + ClientSecret cmmeta.SecretKeySelector `json:"clientSecretSecretRef,omitempty"` SubscriptionID string `json:"subscriptionID"` - TenantID string `json:"tenantID"` + TenantID string `json:"tenantID,omitempty"` ResourceGroupName string `json:"resourceGroupName"` diff --git a/pkg/apis/acme/v1alpha3/types_issuer.go b/pkg/apis/acme/v1alpha3/types_issuer.go index f1025ffa1..d4dec8579 100644 --- a/pkg/apis/acme/v1alpha3/types_issuer.go +++ b/pkg/apis/acme/v1alpha3/types_issuer.go @@ -343,13 +343,13 @@ type ACMEIssuerDNS01ProviderRoute53 struct { // ACMEIssuerDNS01ProviderAzureDNS is a structure containing the // configuration for Azure DNS type ACMEIssuerDNS01ProviderAzureDNS struct { - ClientID string `json:"clientID"` + ClientID string `json:"clientID,omitempty"` - ClientSecret cmmeta.SecretKeySelector `json:"clientSecretSecretRef"` + ClientSecret cmmeta.SecretKeySelector `json:"clientSecretSecretRef,omitempty"` SubscriptionID string `json:"subscriptionID"` - TenantID string `json:"tenantID"` + TenantID string `json:"tenantID,omitempty"` ResourceGroupName string `json:"resourceGroupName"` diff --git a/pkg/internal/apis/certmanager/validation/issuer.go b/pkg/internal/apis/certmanager/validation/issuer.go index c77694cb2..1631f0b24 100644 --- a/pkg/internal/apis/certmanager/validation/issuer.go +++ b/pkg/internal/apis/certmanager/validation/issuer.go @@ -253,16 +253,27 @@ func ValidateACMEChallengeSolverDNS01(p *cmacme.ACMEChallengeSolverDNS01, fldPat el = append(el, field.Forbidden(fldPath.Child("azuredns"), "may not specify more than one provider type")) } else { numProviders++ - el = append(el, ValidateSecretKeySelector(&p.AzureDNS.ClientSecret, fldPath.Child("azuredns", "clientSecretSecretRef"))...) - if len(p.AzureDNS.ClientID) == 0 { - el = append(el, field.Required(fldPath.Child("azuredns", "clientID"), "")) + // if ClientID is defined then clientSecret and tenantID must be defined + if len(p.AzureDNS.ClientID) > 0 { + el = append(el, ValidateSecretKeySelector(&p.AzureDNS.ClientSecret, fldPath.Child("azuredns", "clientSecretSecretRef"))...) + if len(p.AzureDNS.TenantID) == 0 { + el = append(el, field.Required(fldPath.Child("azuredns", "tenantID"), "")) + } } + // if ClientSecret is defined then both ClientID and TenantID must be defined + if &p.AzureDNS.ClientSecret != nil { + if len(p.AzureDNS.ClientID) == 0 { + el = append(el, field.Required(fldPath.Child("azuredns", "clientID"), "")) + } + if len(p.AzureDNS.TenantID) == 0 { + el = append(el, field.Required(fldPath.Child("azuredns", "tenantID"), "")) + } + } + // SubscriptionID must always be defined if len(p.AzureDNS.SubscriptionID) == 0 { el = append(el, field.Required(fldPath.Child("azuredns", "subscriptionID"), "")) } - if len(p.AzureDNS.TenantID) == 0 { - el = append(el, field.Required(fldPath.Child("azuredns", "tenantID"), "")) - } + // ResourceGroupName must always be defined if len(p.AzureDNS.ResourceGroupName) == 0 { el = append(el, field.Required(fldPath.Child("azuredns", "resourceGroupName"), "")) } diff --git a/pkg/issuer/acme/dns/azuredns/azuredns.go b/pkg/issuer/acme/dns/azuredns/azuredns.go index da781879a..9d55fc299 100644 --- a/pkg/issuer/acme/dns/azuredns/azuredns.go +++ b/pkg/issuer/acme/dns/azuredns/azuredns.go @@ -48,12 +48,12 @@ func NewDNSProvider(dns01Nameservers []string) (*DNSProvider, error) { zoneName := ("AZURE_ZONE_NAME") environment := ("AZURE_ENVIRONMENT") - return NewDNSProviderCredentials(environment, clientID, clientSecret, subscriptionID, tenantID, resourceGroupName, zoneName, dns01Nameservers) + return NewDNSProviderCredentials(environment, clientID, clientSecret, subscriptionID, tenantID, resourceGroupName, zoneName, dns01Nameservers, true) } // NewDNSProviderCredentials returns a DNSProvider instance configured for the Azure // DNS service using static credentials from its parameters -func NewDNSProviderCredentials(environment, clientID, clientSecret, subscriptionID, tenantID, resourceGroupName, zoneName string, dns01Nameservers []string) (*DNSProvider, error) { +func NewDNSProviderCredentials(environment, clientID, clientSecret, subscriptionID, tenantID, resourceGroupName, zoneName string, dns01Nameservers []string, ambient bool) (*DNSProvider, error) { env := azure.PublicCloud if environment != "" { var err error @@ -62,15 +62,31 @@ func NewDNSProviderCredentials(environment, clientID, clientSecret, subscription return nil, err } } + spt := &adal.ServicePrincipalToken{} + if clientID != "" { + klog.Info("azuredns authenticating with clientID and secret key") + oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, tenantID) + if err != nil { + return nil, err + } + spt, err = adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, env.ResourceManagerEndpoint) + if err != nil { + return nil, err + } + } else { + klog.Info("azuredns authenticating with managed identity") + if !ambient { + return nil, fmt.Errorf("ClientID is not set but neither `--cluster-issuer-ambient-credentials` nor `--issuer-ambient-credentials` are set. These are necessary to enable Azure Managed Identities") + } + msiEndpoint, err := adal.GetMSIVMEndpoint() + if err != nil { + return nil, fmt.Errorf("failed to get the managed service identity endpoint: %v", err) + } - oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, tenantID) - if err != nil { - return nil, err - } - - spt, err := adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, env.ResourceManagerEndpoint) - if err != nil { - return nil, err + spt, err = adal.NewServicePrincipalTokenFromMSI(msiEndpoint, env.ServiceManagementEndpoint) + if err != nil { + return nil, fmt.Errorf("failed to create the managed service identity token: %v", err) + } } rc := dns.NewRecordSetsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID) diff --git a/pkg/issuer/acme/dns/dns.go b/pkg/issuer/acme/dns/dns.go index 546f1e753..13d0cb699 100644 --- a/pkg/issuer/acme/dns/dns.go +++ b/pkg/issuer/acme/dns/dns.go @@ -64,7 +64,7 @@ type dnsProviderConstructors struct { cloudDNS func(project string, serviceAccount []byte, dns01Nameservers []string, ambient bool) (*clouddns.DNSProvider, error) cloudFlare func(email, apikey, apiToken string, dns01Nameservers []string) (*cloudflare.DNSProvider, error) route53 func(accessKey, secretKey, hostedZoneID, region, role string, ambient bool, dns01Nameservers []string) (*route53.DNSProvider, error) - azureDNS func(environment, clientID, clientSecret, subscriptionID, tenantID, resourceGroupName, hostedZoneName string, dns01Nameservers []string) (*azuredns.DNSProvider, error) + azureDNS func(environment, clientID, clientSecret, subscriptionID, tenantID, resourceGroupName, hostedZoneName string, dns01Nameservers []string, ambient bool) (*azuredns.DNSProvider, error) acmeDNS func(host string, accountJson []byte, dns01Nameservers []string) (*acmedns.DNSProvider, error) digitalOcean func(token string, dns01Nameservers []string) (*digitalocean.DNSProvider, error) } @@ -330,25 +330,29 @@ func (s *Solver) solverForChallenge(ctx context.Context, issuer v1alpha2.Generic } case providerConfig.AzureDNS != nil: dbg.Info("preparing to create AzureDNS provider") - clientSecret, err := s.secretLister.Secrets(resourceNamespace).Get(providerConfig.AzureDNS.ClientSecret.Name) - if err != nil { - return nil, nil, fmt.Errorf("error getting azuredns client secret: %s", err) - } + secret := "" + if providerConfig.AzureDNS.ClientID != "" { + clientSecret, err := s.secretLister.Secrets(resourceNamespace).Get(providerConfig.AzureDNS.ClientSecret.Name) + if err != nil { + return nil, nil, fmt.Errorf("error getting azuredns client secret: %s", err) + } - clientSecretBytes, ok := clientSecret.Data[providerConfig.AzureDNS.ClientSecret.Key] - if !ok { - return nil, nil, fmt.Errorf("error getting azure dns client secret: key '%s' not found in secret", providerConfig.AzureDNS.ClientSecret.Key) + clientSecretBytes, ok := clientSecret.Data[providerConfig.AzureDNS.ClientSecret.Key] + if !ok { + return nil, nil, fmt.Errorf("error getting azure dns client secret: key '%s' not found in secret", providerConfig.AzureDNS.ClientSecret.Key) + } + secret = string(clientSecretBytes) } - impl, err = s.dnsProviderConstructors.azureDNS( string(providerConfig.AzureDNS.Environment), providerConfig.AzureDNS.ClientID, - string(clientSecretBytes), + secret, providerConfig.AzureDNS.SubscriptionID, providerConfig.AzureDNS.TenantID, providerConfig.AzureDNS.ResourceGroupName, providerConfig.AzureDNS.HostedZoneName, s.DNS01Nameservers, + canUseAmbientCredentials, ) if err != nil { return nil, nil, fmt.Errorf("error instantiating azuredns challenge solver: %s", err)