541 lines
20 KiB
Go
541 lines
20 KiB
Go
/*
|
|
Copyright 2020 The cert-manager Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package dns
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/cert-manager/cert-manager/pkg/acme/webhook"
|
|
whapi "github.com/cert-manager/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
|
|
cmacme "github.com/cert-manager/cert-manager/pkg/apis/acme/v1"
|
|
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
|
|
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
|
|
"github.com/cert-manager/cert-manager/pkg/controller"
|
|
"github.com/cert-manager/cert-manager/pkg/issuer/acme/dns/acmedns"
|
|
"github.com/cert-manager/cert-manager/pkg/issuer/acme/dns/akamai"
|
|
"github.com/cert-manager/cert-manager/pkg/issuer/acme/dns/azuredns"
|
|
"github.com/cert-manager/cert-manager/pkg/issuer/acme/dns/clouddns"
|
|
"github.com/cert-manager/cert-manager/pkg/issuer/acme/dns/cloudflare"
|
|
"github.com/cert-manager/cert-manager/pkg/issuer/acme/dns/digitalocean"
|
|
"github.com/cert-manager/cert-manager/pkg/issuer/acme/dns/rfc2136"
|
|
"github.com/cert-manager/cert-manager/pkg/issuer/acme/dns/route53"
|
|
"github.com/cert-manager/cert-manager/pkg/issuer/acme/dns/util"
|
|
webhookslv "github.com/cert-manager/cert-manager/pkg/issuer/acme/dns/webhook"
|
|
logf "github.com/cert-manager/cert-manager/pkg/logs"
|
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
|
)
|
|
|
|
// solver is the old solver type interface.
|
|
// All new solvers should be implemented using the new webhook.Solver interface.
|
|
type solver interface {
|
|
Present(domain, fqdn, value string) error
|
|
CleanUp(domain, fqdn, value string) error
|
|
}
|
|
|
|
// dnsProviderConstructors defines how each provider may be constructed.
|
|
// It is useful for mocking out a given provider since an alternate set of
|
|
// constructors may be set.
|
|
type dnsProviderConstructors struct {
|
|
cloudDNS func(project string, serviceAccount []byte, dns01Nameservers []string, ambient bool, hostedZoneName string) (*clouddns.DNSProvider, error)
|
|
cloudFlare func(email, apikey, apiToken string, dns01Nameservers []string, userAgent string) (*cloudflare.DNSProvider, error)
|
|
route53 func(accessKey, secretKey, hostedZoneID, region, role string, ambient bool, dns01Nameservers []string, userAgent string) (*route53.DNSProvider, error)
|
|
azureDNS func(environment, clientID, clientSecret, subscriptionID, tenantID, resourceGroupName, hostedZoneName string, dns01Nameservers []string, ambient bool, managedIdentity *cmacme.AzureManagedIdentity) (*azuredns.DNSProvider, error)
|
|
acmeDNS func(host string, accountJson []byte, dns01Nameservers []string) (*acmedns.DNSProvider, error)
|
|
digitalOcean func(token string, dns01Nameservers []string, userAgent string) (*digitalocean.DNSProvider, error)
|
|
}
|
|
|
|
// Solver is a solver for the acme dns01 challenge.
|
|
// Given a Certificate object, it determines the correct DNS provider based on
|
|
// the certificate, and configures it based on the referenced issuer.
|
|
type Solver struct {
|
|
*controller.Context
|
|
//secretLister internalinformers.SecretLister
|
|
dnsProviderConstructors dnsProviderConstructors
|
|
webhookSolvers map[string]webhook.Solver
|
|
}
|
|
|
|
// Present performs the work to configure DNS to resolve a DNS01 challenge.
|
|
func (s *Solver) Present(ctx context.Context, issuer v1.GenericIssuer, ch *cmacme.Challenge) error {
|
|
log := logf.WithResource(logf.FromContext(ctx, "Present"), ch).WithValues("domain", ch.Spec.DNSName)
|
|
ctx = logf.NewContext(ctx, log)
|
|
|
|
webhookSolver, req, err := s.prepareChallengeRequest(issuer, ch)
|
|
if err != nil && err != errNotFound {
|
|
return err
|
|
}
|
|
if err == nil {
|
|
log.V(logf.InfoLevel).Info("presenting DNS01 challenge for domain")
|
|
return webhookSolver.Present(req)
|
|
}
|
|
|
|
slv, providerConfig, err := s.solverForChallenge(ctx, issuer, ch)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fqdn, err := util.DNS01LookupFQDN(ch.Spec.DNSName, followCNAME(providerConfig.CNAMEStrategy), s.DNS01Nameservers...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.V(logf.DebugLevel).Info("presenting DNS01 challenge for domain")
|
|
|
|
return slv.Present(ch.Spec.DNSName, fqdn, ch.Spec.Key)
|
|
}
|
|
|
|
// Check verifies that the DNS records for the ACME challenge have propagated.
|
|
func (s *Solver) Check(ctx context.Context, issuer v1.GenericIssuer, ch *cmacme.Challenge) error {
|
|
log := logf.WithResource(logf.FromContext(ctx, "Check"), ch).WithValues("domain", ch.Spec.DNSName)
|
|
|
|
fqdn, err := util.DNS01LookupFQDN(ch.Spec.DNSName, false, s.DNS01Nameservers...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.V(logf.DebugLevel).Info("checking DNS propagation", "nameservers", s.Context.DNS01Nameservers)
|
|
|
|
ok, err := util.PreCheckDNS(fqdn, ch.Spec.Key, s.Context.DNS01Nameservers,
|
|
s.Context.DNS01CheckAuthoritative)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !ok {
|
|
return fmt.Errorf("DNS record for %q not yet propagated", ch.Spec.DNSName)
|
|
}
|
|
|
|
ttl := 60
|
|
log.V(logf.DebugLevel).Info("waiting DNS record TTL to allow the DNS01 record to propagate for domain", "ttl", ttl, "fqdn", fqdn)
|
|
time.Sleep(time.Second * time.Duration(ttl))
|
|
log.V(logf.DebugLevel).Info("ACME DNS01 validation record propagated", "fqdn", fqdn)
|
|
|
|
return nil
|
|
}
|
|
|
|
// CleanUp removes DNS records which are no longer needed after
|
|
// certificate issuance.
|
|
func (s *Solver) CleanUp(ctx context.Context, issuer v1.GenericIssuer, ch *cmacme.Challenge) error {
|
|
log := logf.WithResource(logf.FromContext(ctx, "CleanUp"), ch).WithValues("domain", ch.Spec.DNSName)
|
|
ctx = logf.NewContext(ctx, log)
|
|
|
|
webhookSolver, req, err := s.prepareChallengeRequest(issuer, ch)
|
|
if err != nil && err != errNotFound {
|
|
return err
|
|
}
|
|
if err == nil {
|
|
log.V(logf.DebugLevel).Info("cleaning up DNS01 challenge")
|
|
return webhookSolver.CleanUp(req)
|
|
}
|
|
|
|
slv, providerConfig, err := s.solverForChallenge(ctx, issuer, ch)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fqdn, err := util.DNS01LookupFQDN(ch.Spec.DNSName, followCNAME(providerConfig.CNAMEStrategy), s.DNS01Nameservers...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return slv.CleanUp(ch.Spec.DNSName, fqdn, ch.Spec.Key)
|
|
}
|
|
|
|
func followCNAME(strategy cmacme.CNAMEStrategy) bool {
|
|
return strategy == cmacme.FollowStrategy
|
|
}
|
|
|
|
func extractChallengeSolverConfig(ch *cmacme.Challenge) (*cmacme.ACMEChallengeSolverDNS01, error) {
|
|
if ch.Spec.Solver.DNS01 == nil {
|
|
return nil, fmt.Errorf("no dns01 challenge solver configuration found")
|
|
}
|
|
|
|
return ch.Spec.Solver.DNS01, nil
|
|
}
|
|
|
|
// solverForChallenge returns a Solver for the given providerName.
|
|
// The providerName is the name of an ACME DNS-01 challenge provider as
|
|
// specified on the Issuer resource for the Solver.
|
|
func (s *Solver) solverForChallenge(ctx context.Context, issuer v1.GenericIssuer, ch *cmacme.Challenge) (solver, *cmacme.ACMEChallengeSolverDNS01, error) {
|
|
log := logf.FromContext(ctx, "solverForChallenge")
|
|
dbg := log.V(logf.DebugLevel)
|
|
|
|
resourceNamespace := s.ResourceNamespace(issuer)
|
|
canUseAmbientCredentials := s.CanUseAmbientCredentials(issuer)
|
|
|
|
providerConfig, err := extractChallengeSolverConfig(ch)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
var impl solver
|
|
switch {
|
|
case providerConfig.Akamai != nil:
|
|
dbg.Info("preparing to create Akamai provider")
|
|
clientToken, err := s.loadSecretData(&providerConfig.Akamai.ClientToken, resourceNamespace)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error getting akamai client token: %w", err)
|
|
}
|
|
|
|
clientSecret, err := s.loadSecretData(&providerConfig.Akamai.ClientSecret, resourceNamespace)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error getting akamai client secret: %w", err)
|
|
}
|
|
|
|
accessToken, err := s.loadSecretData(&providerConfig.Akamai.AccessToken, resourceNamespace)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error getting akamai client token: %w", err)
|
|
}
|
|
|
|
impl, err = akamai.NewDNSProvider(
|
|
providerConfig.Akamai.ServiceConsumerDomain,
|
|
string(clientToken),
|
|
string(clientSecret),
|
|
string(accessToken),
|
|
s.DNS01Nameservers)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error instantiating akamai challenge solver: %w", err)
|
|
}
|
|
case providerConfig.CloudDNS != nil:
|
|
dbg.Info("preparing to create CloudDNS provider")
|
|
var keyData []byte
|
|
|
|
// if the serviceAccount field isn't nil we will load credentials from
|
|
// that secret. If it is nil we will attempt to instantiate the
|
|
// provider using ambient credentials (if enabled).
|
|
if providerConfig.CloudDNS.ServiceAccount != nil {
|
|
/*
|
|
saSecret, err := s.secretLister.Secrets(resourceNamespace).Get(providerConfig.CloudDNS.ServiceAccount.Name)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error getting clouddns service account: %s", err)
|
|
}
|
|
|
|
saKey := providerConfig.CloudDNS.ServiceAccount.Key
|
|
keyData = saSecret.Data[saKey]
|
|
if len(keyData) == 0 {
|
|
return nil, nil, fmt.Errorf("specified key %q not found in secret %s/%s", saKey, saSecret.Namespace, saSecret.Name)
|
|
}
|
|
*/
|
|
}
|
|
|
|
// attempt to construct the cloud dns provider
|
|
impl, err = s.dnsProviderConstructors.cloudDNS(providerConfig.CloudDNS.Project, keyData, s.DNS01Nameservers, s.CanUseAmbientCredentials(issuer), providerConfig.CloudDNS.HostedZoneName)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error instantiating google clouddns challenge solver: %s", err)
|
|
}
|
|
case providerConfig.Cloudflare != nil:
|
|
dbg.Info("preparing to create Cloudflare provider")
|
|
if providerConfig.Cloudflare.APIKey != nil && providerConfig.Cloudflare.APIToken != nil {
|
|
return nil, nil, fmt.Errorf("API key and API token secret references are both present")
|
|
}
|
|
|
|
/* var saSecretName, saSecretKey string
|
|
if providerConfig.Cloudflare.APIKey != nil {
|
|
saSecretName = providerConfig.Cloudflare.APIKey.Name
|
|
saSecretKey = providerConfig.Cloudflare.APIKey.Key
|
|
} else {
|
|
saSecretName = providerConfig.Cloudflare.APIToken.Name
|
|
saSecretKey = providerConfig.Cloudflare.APIToken.Key
|
|
}
|
|
|
|
saSecret, err := s.secretLister.Secrets(resourceNamespace).Get(saSecretName)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error getting cloudflare secret: %s", err)
|
|
}
|
|
|
|
keyData, ok := saSecret.Data[saSecretKey]
|
|
if !ok {
|
|
return nil, nil, fmt.Errorf("specified key %q not found in secret %s/%s", saSecretKey, saSecret.Namespace, saSecret.Name)
|
|
}
|
|
*/
|
|
var apiKey, apiToken string
|
|
if providerConfig.Cloudflare.APIKey != nil {
|
|
// apiKey = string(keyData)
|
|
} else {
|
|
// apiToken = string(keyData)
|
|
}
|
|
|
|
email := providerConfig.Cloudflare.Email
|
|
impl, err = s.dnsProviderConstructors.cloudFlare(email, apiKey, apiToken, s.DNS01Nameservers, s.RESTConfig.UserAgent)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error instantiating cloudflare challenge solver: %s", err)
|
|
}
|
|
case providerConfig.DigitalOcean != nil:
|
|
dbg.Info("preparing to create DigitalOcean provider")
|
|
// apiTokenSecret, err := s.secretLister.Secrets(resourceNamespace).Get(providerConfig.DigitalOcean.Token.Name)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error getting digitalocean token: %s", err)
|
|
}
|
|
|
|
// apiToken := string(apiTokenSecret.Data[providerConfig.DigitalOcean.Token.Key])
|
|
|
|
// impl, err = s.dnsProviderConstructors.digitalOcean(strings.TrimSpace(apiToken), s.DNS01Nameservers, s.RESTConfig.UserAgent)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error instantiating digitalocean challenge solver: %s", err.Error())
|
|
}
|
|
case providerConfig.Route53 != nil:
|
|
dbg.Info("preparing to create Route53 provider")
|
|
|
|
// Default to the AccessKeyID literal in the configuration
|
|
secretAccessKeyID := strings.TrimSpace(providerConfig.Route53.AccessKeyID)
|
|
|
|
// If the AccessKeyID secret reference option is defined, override the
|
|
// secretAccessKeyID variable.
|
|
if providerConfig.Route53.SecretAccessKeyID != nil {
|
|
// For route53, you must specify either an AccessKeyID or a secret
|
|
// reference to an AccessKeyID, but not both.
|
|
if len(providerConfig.Route53.AccessKeyID) > 0 {
|
|
return nil, nil, fmt.Errorf("route53 accessKeyID and accessKeyIDSecretRef cannot both be specified")
|
|
}
|
|
|
|
// Ensure Key specified.
|
|
if len(providerConfig.Route53.SecretAccessKeyID.Key) == 0 {
|
|
return nil, nil, fmt.Errorf("route53 accessKeyIDSecretRef requires a key field to be specified")
|
|
}
|
|
|
|
// Ensure Name specified.
|
|
if len(providerConfig.Route53.SecretAccessKeyID.Name) == 0 {
|
|
return nil, nil, fmt.Errorf("route53 accessKeyIDSecretRef requires a name field to be specified")
|
|
}
|
|
|
|
// secretAccessKeyIDSecret, err := s.secretLister.Secrets(resourceNamespace).Get(providerConfig.Route53.SecretAccessKeyID.Name)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error getting route53 secret access key id: %s", err)
|
|
}
|
|
|
|
/* secretAccessKeyIDBytes, ok := secretAccessKeyIDSecret.Data[providerConfig.Route53.SecretAccessKeyID.Key]
|
|
if !ok {
|
|
return nil, nil, fmt.Errorf("no data found in Secret %q at Key %q",
|
|
providerConfig.Route53.SecretAccessKeyID.Name,
|
|
providerConfig.Route53.SecretAccessKeyID.Key,
|
|
)
|
|
}
|
|
secretAccessKeyID = string(secretAccessKeyIDBytes)
|
|
*/
|
|
}
|
|
|
|
secretAccessKey := ""
|
|
if providerConfig.Route53.SecretAccessKey.Name != "" {
|
|
// secretAccessKeySecret, err := s.secretLister.Secrets(resourceNamespace).Get(providerConfig.Route53.SecretAccessKey.Name)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error getting route53 secret access key: %s", err)
|
|
}
|
|
|
|
// secretAccessKeyBytes, ok := secretAccessKeySecret.Data[providerConfig.Route53.SecretAccessKey.Key]
|
|
// if !ok {
|
|
// return nil, nil, fmt.Errorf("error getting route53 secret access key: key '%s' not found in secret", providerConfig.Route53.SecretAccessKey.Key)
|
|
// }
|
|
// secretAccessKey = string(secretAccessKeyBytes)
|
|
}
|
|
|
|
impl, err = s.dnsProviderConstructors.route53(
|
|
secretAccessKeyID,
|
|
strings.TrimSpace(secretAccessKey),
|
|
providerConfig.Route53.HostedZoneID,
|
|
providerConfig.Route53.Region,
|
|
providerConfig.Route53.Role,
|
|
canUseAmbientCredentials,
|
|
s.DNS01Nameservers,
|
|
s.RESTConfig.UserAgent,
|
|
)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error instantiating route53 challenge solver: %s", err)
|
|
}
|
|
case providerConfig.AzureDNS != nil:
|
|
dbg.Info("preparing to create AzureDNS provider")
|
|
secret := ""
|
|
// if ClientID is empty, then we try to use MSI (azure metadata API for credentials)
|
|
// if ClientID is empty we don't even try to get the ClientSecret because it would not be used
|
|
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)
|
|
// }
|
|
// secret = string(clientSecretBytes)
|
|
}
|
|
impl, err = s.dnsProviderConstructors.azureDNS(
|
|
string(providerConfig.AzureDNS.Environment),
|
|
providerConfig.AzureDNS.ClientID,
|
|
secret,
|
|
providerConfig.AzureDNS.SubscriptionID,
|
|
providerConfig.AzureDNS.TenantID,
|
|
providerConfig.AzureDNS.ResourceGroupName,
|
|
providerConfig.AzureDNS.HostedZoneName,
|
|
s.DNS01Nameservers,
|
|
canUseAmbientCredentials,
|
|
providerConfig.AzureDNS.ManagedIdentity,
|
|
)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error instantiating azuredns challenge solver: %s", err)
|
|
}
|
|
case providerConfig.AcmeDNS != nil:
|
|
dbg.Info("preparing to create ACMEDNS provider")
|
|
// accountSecret, err := s.secretLister.Secrets(resourceNamespace).Get(providerConfig.AcmeDNS.AccountSecret.Name)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("error getting acmedns accounts secret: %s", err)
|
|
}
|
|
/*
|
|
accountSecretBytes, ok := accountSecret.Data[providerConfig.AcmeDNS.AccountSecret.Key]
|
|
if !ok {
|
|
return nil, nil, fmt.Errorf("error getting acmedns accounts secret: key '%s' not found in secret", providerConfig.AcmeDNS.AccountSecret.Key)
|
|
}
|
|
|
|
impl, err = s.dnsProviderConstructors.acmeDNS(
|
|
providerConfig.AcmeDNS.Host,
|
|
accountSecretBytes,
|
|
s.DNS01Nameservers,
|
|
)
|
|
*/
|
|
if err != nil {
|
|
return nil, providerConfig, fmt.Errorf("error instantiating acmedns challenge solver: %s", err)
|
|
}
|
|
default:
|
|
return nil, providerConfig, fmt.Errorf("no dns provider config specified for challenge")
|
|
}
|
|
|
|
return impl, providerConfig, nil
|
|
}
|
|
|
|
func (s *Solver) prepareChallengeRequest(issuer v1.GenericIssuer, ch *cmacme.Challenge) (webhook.Solver, *whapi.ChallengeRequest, error) {
|
|
dns01Config, err := extractChallengeSolverConfig(ch)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
webhookSolver, cfg, err := s.dns01SolverForConfig(dns01Config)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
fqdn, err := util.DNS01LookupFQDN(ch.Spec.DNSName, followCNAME(dns01Config.CNAMEStrategy), s.DNS01Nameservers...)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
zone, err := util.FindZoneByFqdn(fqdn, s.DNS01Nameservers)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
resourceNamespace := s.ResourceNamespace(issuer)
|
|
canUseAmbientCredentials := s.CanUseAmbientCredentials(issuer)
|
|
|
|
// construct a ChallengeRequest which can be passed to DNS solvers.
|
|
// The provided config will be encoded to JSON in order to avoid a coupling
|
|
// between cert-manager and any particular DNS provider implementation.
|
|
b, err := json.Marshal(cfg)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
req := &whapi.ChallengeRequest{
|
|
Type: "dns-01",
|
|
ResolvedFQDN: fqdn,
|
|
ResolvedZone: zone,
|
|
AllowAmbientCredentials: canUseAmbientCredentials,
|
|
ResourceNamespace: resourceNamespace,
|
|
Key: ch.Spec.Key,
|
|
DNSName: ch.Spec.DNSName,
|
|
Config: &apiextensionsv1.JSON{Raw: b},
|
|
}
|
|
|
|
return webhookSolver, req, nil
|
|
}
|
|
|
|
var errNotFound = fmt.Errorf("failed to determine DNS01 solver type")
|
|
|
|
func (s *Solver) dns01SolverForConfig(config *cmacme.ACMEChallengeSolverDNS01) (webhook.Solver, interface{}, error) {
|
|
solverName := ""
|
|
var c interface{}
|
|
switch {
|
|
case config.Webhook != nil:
|
|
solverName = "webhook"
|
|
c = config.Webhook
|
|
case config.RFC2136 != nil:
|
|
solverName = "rfc2136"
|
|
c = config.RFC2136
|
|
}
|
|
if solverName == "" {
|
|
return nil, nil, errNotFound
|
|
}
|
|
p := s.webhookSolvers[solverName]
|
|
if p == nil {
|
|
return nil, c, fmt.Errorf("no solver provider configured for %q", solverName)
|
|
}
|
|
return p, c, nil
|
|
}
|
|
|
|
// NewSolver creates a Solver which can instantiate the appropriate DNS
|
|
// provider.
|
|
func NewSolver(ctx *controller.Context) (*Solver, error) {
|
|
secretsLister := ctx.KubeSharedInformerFactory.Secrets().Lister()
|
|
webhookSolvers := []webhook.Solver{
|
|
&webhookslv.Webhook{},
|
|
rfc2136.New(rfc2136.WithNamespace(ctx.Namespace), rfc2136.WithSecretsLister(secretsLister)),
|
|
}
|
|
|
|
initialized := make(map[string]webhook.Solver)
|
|
|
|
// the RESTConfig may be nil if we are running in a unit test environment,
|
|
// so don't initialize the webhook based solvers in this case.
|
|
if ctx.RESTConfig != nil {
|
|
// initialize all DNS providers
|
|
for _, s := range webhookSolvers {
|
|
err := s.Initialize(ctx.RESTConfig, ctx.StopCh)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error initializing DNS provider %q: %v", s.Name(), err)
|
|
}
|
|
initialized[s.Name()] = s
|
|
}
|
|
}
|
|
|
|
return &Solver{
|
|
Context: ctx,
|
|
//secretLister: ctx.KubeSharedInformerFactory.Secrets().Lister(),
|
|
dnsProviderConstructors: dnsProviderConstructors{
|
|
clouddns.NewDNSProvider,
|
|
cloudflare.NewDNSProviderCredentials,
|
|
route53.NewDNSProvider,
|
|
azuredns.NewDNSProviderCredentials,
|
|
acmedns.NewDNSProviderHostBytes,
|
|
digitalocean.NewDNSProviderCredentials,
|
|
},
|
|
webhookSolvers: initialized,
|
|
}, nil
|
|
}
|
|
|
|
func (s *Solver) loadSecretData(selector *cmmeta.SecretKeySelector, ns string) ([]byte, error) {
|
|
/*
|
|
secret, err := s.secretLister.Secrets(ns).Get(selector.Name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load secret %q: %w", ns+"/"+selector.Name, err)
|
|
}
|
|
|
|
if data, ok := secret.Data[selector.Key]; ok {
|
|
return data, nil
|
|
}
|
|
*/
|
|
return nil, fmt.Errorf("no key %q in secret %q", selector.Key, ns+"/"+selector.Name)
|
|
}
|