func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { // HostedZone represents a CloudFlare DNS zone type HostedZone struct { ID string `json:"id"` Name string `json:"name"` } result, err := c.makeRequest("GET", "/zones?per_page=1000", nil) if err != nil { return "", err } var zones []HostedZone err = json.Unmarshal(result, &zones) if err != nil { return "", err } var hostedZone HostedZone for _, zone := range zones { name := acme.ToFqdn(zone.Name) if strings.HasSuffix(fqdn, name) { if len(zone.Name) > len(hostedZone.Name) { hostedZone = zone } } } if hostedZone.ID == "" { return "", fmt.Errorf("No matching CloudFlare zone found for %s", fqdn) } return hostedZone.ID, nil }
func (c *DNSProvider) getHostedZone(domain string) (string, string, error) { zones, _, err := c.client.Domains.List() if err != nil { return "", "", fmt.Errorf("dnspod API call failed: %v", err) } authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return "", "", err } var hostedZone dnspod.Domain for _, zone := range zones { if zone.Name == acme.UnFqdn(authZone) { hostedZone = zone } } if hostedZone.ID == 0 { return "", "", fmt.Errorf("Zone %s not found in dnspod for domain %s", authZone, domain) } return fmt.Sprintf("%v", hostedZone.ID), hostedZone.Name, nil }
// CleanUp removes a given record that was generated by Present func (provider *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) provider.recordIDsMu.Lock() recordID, ok := provider.recordIDs[fqdn] provider.recordIDsMu.Unlock() if !ok { return fmt.Errorf("Unknown recordID for '%s'", fqdn) } authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) } authZone = acme.UnFqdn(authZone) zoneRecord, err := provider.getZoneInformationByName(authZone) if err != nil { return err } _, err = provider.client.RemoveRecord(zoneRecord.ID, recordID) if err != nil { return err } provider.recordIDsMu.Lock() delete(provider.recordIDs, fqdn) provider.recordIDsMu.Unlock() return nil }
// Present creates a TXT record to fulfil the dns-01 challenge func (c *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zone, err := c.getHostedZoneID(fqdn) if err != nil { return err } rsc := dns.NewRecordSetsClient(c.subscriptionId) rsc.Authorizer, err = c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) relative := toRelativeRecord(fqdn, acme.ToFqdn(zone)) rec := dns.RecordSet{ Name: &relative, Properties: &dns.RecordSetProperties{ TTL: to.Int64Ptr(60), TXTRecords: &[]dns.TxtRecord{dns.TxtRecord{Value: &[]string{value}}}, }, } _, err = rsc.CreateOrUpdate(c.resourceGroup, zone, relative, dns.TXT, rec, "", "") if err != nil { return err } return nil }
// CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) // get the record's unique ID from when we created it d.recordIDsMu.Lock() recordID, ok := d.recordIDs[fqdn] d.recordIDsMu.Unlock() if !ok { return fmt.Errorf("unknown record ID for '%s'", fqdn) } authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) } authZone = acme.UnFqdn(authZone) reqURL := fmt.Sprintf("/domain/zone/%s/record/%d", authZone, recordID) err = d.client.Delete(reqURL, nil) if err != nil { fmt.Printf("Error when call OVH api to delete challenge record : %q \n", err) return err } // Delete record ID from map d.recordIDsMu.Lock() delete(d.recordIDs, fqdn) d.recordIDsMu.Unlock() return nil }
// Present creates a TXT record to fulfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { // txtRecordRequest represents the request body to DO's API to make a TXT record type txtRecordRequest struct { FieldType string `json:"fieldType"` SubDomain string `json:"subDomain"` Target string `json:"target"` TTL int `json:"ttl"` } // txtRecordResponse represents a response from DO's API after making a TXT record type txtRecordResponse struct { ID int `json:"id"` FieldType string `json:"fieldType"` SubDomain string `json:"subDomain"` Target string `json:"target"` TTL int `json:"ttl"` Zone string `json:"zone"` } fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) // Parse domain name authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) } authZone = acme.UnFqdn(authZone) subDomain := d.extractRecordName(fqdn, authZone) reqURL := fmt.Sprintf("/domain/zone/%s/record", authZone) reqData := txtRecordRequest{FieldType: "TXT", SubDomain: subDomain, Target: value, TTL: ttl} var respData txtRecordResponse // Create TXT record err = d.client.Post(reqURL, reqData, &respData) if err != nil { fmt.Printf("Error when call OVH api to add record : %q \n", err) return err } // Apply the change reqURL = fmt.Sprintf("/domain/zone/%s/refresh", authZone) err = d.client.Post(reqURL, nil, nil) if err != nil { fmt.Printf("Error when call OVH api to refresh zone : %q \n", err) return err } d.recordIDsMu.Lock() d.recordIDs[fqdn] = respData.ID d.recordIDsMu.Unlock() return nil }
// Extract DNS zone and DNS entry name func (c *DNSProvider) FindZoneAndRecordName(fqdn, domain string) (string, string, error) { zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return "", "", err } zone = acme.UnFqdn(zone) name := acme.UnFqdn(fqdn) name = name[:len(name)-len("."+zone)] return zone, name, nil }
// CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) // get the record's unique ID from when we created it d.recordIDsMu.Lock() recordID, ok := d.recordIDs[fqdn] d.recordIDsMu.Unlock() if !ok { return fmt.Errorf("unknown record ID for '%s'", fqdn) } authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) } authZone = acme.UnFqdn(authZone) reqURL := fmt.Sprintf("%s/v2/domains/%s/records/%d", digitalOceanBaseURL, authZone, recordID) req, err := http.NewRequest("DELETE", reqURL, nil) if err != nil { return err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiAuthToken)) client := http.Client{Timeout: 30 * time.Second} resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode >= 400 { var errInfo digitalOceanAPIError json.NewDecoder(resp.Body).Decode(&errInfo) return fmt.Errorf("HTTP %d: %s: %s", resp.StatusCode, errInfo.ID, errInfo.Message) } // Delete record ID from map d.recordIDsMu.Lock() delete(d.recordIDs, fqdn) d.recordIDsMu.Unlock() return nil }
// CleanUp removes the TXT record matching the specified parameters func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) zone, err := c.getHostedZoneID(fqdn) if err != nil { return err } relative := toRelativeRecord(fqdn, acme.ToFqdn(zone)) rsc := dns.NewRecordSetsClient(c.subscriptionId) rsc.Authorizer, err = c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) _, err = rsc.Delete(c.resourceGroup, zone, relative, dns.TXT, "", "") if err != nil { return err } return nil }
// getHostedZone returns the managed-zone func (c *DNSProvider) getHostedZone(domain string) (string, error) { authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return "", err } zones, err := c.client.ManagedZones. List(c.project). DnsName(authZone). Do() if err != nil { return "", fmt.Errorf("GoogleCloud API call failed: %v", err) } if len(zones.ManagedZones) == 0 { return "", fmt.Errorf("No matching GoogleCloud domain found for domain %s", authZone) } return zones.ManagedZones[0].Name, nil }
// Present creates a record with a secret func (provider *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) } // 1. Aurora will happily create the TXT record when it is provided a fqdn, // but it will only appear in the control panel and will not be // propagated to DNS servers. Extract and use subdomain instead. // 2. A trailing dot in the fqdn will cause Aurora to add a trailing dot to // the subdomain, resulting in _acme-challenge..<domain> rather // than _acme-challenge.<domain> subdomain := fqdn[0 : len(fqdn)-len(authZone)-1] authZone = acme.UnFqdn(authZone) zoneRecord, err := provider.getZoneInformationByName(authZone) reqData := records.CreateRecordRequest{ RecordType: "TXT", Name: subdomain, Content: value, TTL: 300, } respData, err := provider.client.CreateRecord(zoneRecord.ID, reqData) if err != nil { return fmt.Errorf("Could not create record: '%s'.", err) } provider.recordIDsMu.Lock() provider.recordIDs[fqdn] = respData.ID provider.recordIDsMu.Unlock() return nil }
// Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { // txtRecordRequest represents the request body to DO's API to make a TXT record type txtRecordRequest struct { RecordType string `json:"type"` Name string `json:"name"` Data string `json:"data"` } // txtRecordResponse represents a response from DO's API after making a TXT record type txtRecordResponse struct { DomainRecord struct { ID int `json:"id"` Type string `json:"type"` Name string `json:"name"` Data string `json:"data"` } `json:"domain_record"` } fqdn, value, _ := acme.DNS01Record(domain, keyAuth) authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) } authZone = acme.UnFqdn(authZone) reqURL := fmt.Sprintf("%s/v2/domains/%s/records", digitalOceanBaseURL, authZone) reqData := txtRecordRequest{RecordType: "TXT", Name: fqdn, Data: value} body, err := json.Marshal(reqData) if err != nil { return err } req, err := http.NewRequest("POST", reqURL, bytes.NewReader(body)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiAuthToken)) client := http.Client{Timeout: 30 * time.Second} resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode >= 400 { var errInfo digitalOceanAPIError json.NewDecoder(resp.Body).Decode(&errInfo) return fmt.Errorf("HTTP %d: %s: %s", resp.StatusCode, errInfo.ID, errInfo.Message) } // Everything looks good; but we'll need the ID later to delete the record var respData txtRecordResponse err = json.NewDecoder(resp.Body).Decode(&respData) if err != nil { return err } d.recordIDsMu.Lock() d.recordIDs[fqdn] = respData.DomainRecord.ID d.recordIDsMu.Unlock() return nil }