2016-08-22 18:31:50 -06:00
package cloudflare
import (
"encoding/json"
"fmt"
"net"
2023-05-26 10:48:30 +10:00
"os"
"strconv"
2016-08-22 18:31:50 -06:00
"strings"
2023-08-21 12:19:16 -04:00
"golang.org/x/net/idna"
2023-05-20 19:21:45 +02:00
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/pkg/transform"
"github.com/StackExchange/dnscontrol/v4/providers"
2022-08-14 20:46:56 -04:00
"github.com/cloudflare/cloudflare-go"
2023-02-28 01:25:09 -05:00
"github.com/fatih/color"
2016-08-22 18:31:50 -06:00
)
/ *
2017-04-10 20:26:11 -05:00
Cloudflare API DNS provider :
2016-08-22 18:31:50 -06:00
Info required in ` creds.json ` :
- apikey
- apiuser
2018-12-19 15:48:27 +01:00
- accountid ( optional )
2016-08-22 18:31:50 -06:00
2017-04-10 20:26:11 -05:00
Record level metadata available :
- cloudflare_proxy ( "on" , "off" , or "full" )
2016-08-22 18:31:50 -06:00
2017-04-10 20:26:11 -05:00
Domain level metadata available :
- cloudflare_proxy_default ( "on" , "off" , or "full" )
2016-08-22 18:31:50 -06:00
2017-04-10 20:26:11 -05:00
Provider level metadata available :
2016-08-22 18:31:50 -06:00
- ip_conversions
* /
2018-01-04 19:19:35 -05:00
var features = providers . DocumentationNotes {
2022-03-02 11:19:15 -05:00
providers . CanGetZones : providers . Can ( ) ,
2018-01-04 19:19:35 -05:00
providers . CanUseAlias : providers . Can ( "CF automatically flattens CNAME records into A records dynamically" ) ,
providers . CanUseCAA : providers . Can ( ) ,
2022-03-02 11:19:15 -05:00
providers . CanUseDSForChildren : providers . Can ( ) ,
2023-03-16 19:04:20 +01:00
providers . CanUseLOC : providers . Cannot ( ) ,
2023-09-07 13:31:34 -07:00
providers . CanUseNAPTR : providers . Can ( ) ,
2022-05-03 12:07:49 +02:00
providers . CanUsePTR : providers . Can ( ) ,
2018-01-04 19:19:35 -05:00
providers . CanUseSRV : providers . Can ( ) ,
2019-05-21 04:32:39 +02:00
providers . CanUseSSHFP : providers . Can ( ) ,
2022-03-02 11:19:15 -05:00
providers . CanUseTLSA : providers . Can ( ) ,
2017-09-14 16:13:17 -04:00
providers . DocCreateDomains : providers . Can ( ) ,
2018-01-04 19:19:35 -05:00
providers . DocDualHost : providers . Cannot ( "Cloudflare will not work well in situations where it is not the only DNS server" ) ,
2017-09-14 16:13:17 -04:00
providers . DocOfficiallySupported : providers . Can ( ) ,
}
2017-05-19 14:15:57 -04:00
func init ( ) {
2021-03-07 13:19:22 -05:00
fns := providers . DspFuncs {
2021-05-04 14:15:31 -04:00
Initializer : newCloudflare ,
2021-03-08 20:14:30 -05:00
RecordAuditor : AuditRecords ,
2021-03-07 13:19:22 -05:00
}
providers . RegisterDomainServiceProviderType ( "CLOUDFLAREAPI" , fns , features )
2017-05-19 14:15:57 -04:00
providers . RegisterCustomRecordType ( "CF_REDIRECT" , "CLOUDFLAREAPI" , "" )
providers . RegisterCustomRecordType ( "CF_TEMP_REDIRECT" , "CLOUDFLAREAPI" , "" )
2021-10-11 17:04:49 -03:00
providers . RegisterCustomRecordType ( "CF_WORKER_ROUTE" , "CLOUDFLAREAPI" , "" )
2017-05-19 14:15:57 -04:00
}
2020-10-26 09:25:30 -04:00
// cloudflareProvider is the handle for API calls.
type cloudflareProvider struct {
2022-08-08 13:00:58 -04:00
domainIndex map [ string ] string // Call c.fetchDomainList() to populate before use.
2017-05-19 14:15:57 -04:00
nameservers map [ string ] [ ] string
2020-06-18 09:37:57 -04:00
ipConversions [ ] transform . IPConversion
2017-05-19 14:15:57 -04:00
ignoredLabels [ ] string
manageRedirects bool
2021-10-11 17:04:49 -03:00
manageWorkers bool
2023-09-15 15:30:55 -04:00
accountID string
2021-09-30 05:09:42 -06:00
cfClient * cloudflare . API
2016-08-22 18:31:50 -06:00
}
2023-05-08 23:49:26 +03:00
// TODO(dlemenkov): remove this function after deleting all commented code referecing it
//func labelMatches(label string, matches []string) bool {
// printer.Debugf("DEBUG: labelMatches(%#v, %#v)\n", label, matches)
// for _, tst := range matches {
// if label == tst {
// return true
// }
// }
// return false
//}
2017-05-19 14:15:57 -04:00
2018-01-09 12:53:16 -05:00
// GetNameservers returns the nameservers for a domain.
2020-10-26 09:25:30 -04:00
func ( c * cloudflareProvider ) GetNameservers ( domain string ) ( [ ] * models . Nameserver , error ) {
2016-12-16 13:10:27 -07:00
if c . domainIndex == nil {
if err := c . fetchDomainList ( ) ; err != nil {
return nil , err
}
}
ns , ok := c . nameservers [ domain ]
if ! ok {
2020-08-30 19:52:37 -04:00
return nil , fmt . Errorf ( "nameservers for %s not found in cloudflare account" , domain )
2016-12-16 13:10:27 -07:00
}
2020-03-01 10:33:24 -05:00
return models . ToNameservers ( ns )
2016-12-16 13:10:27 -07:00
}
2016-08-22 18:31:50 -06:00
2020-06-18 09:37:57 -04:00
// ListZones returns a list of the DNS zones.
2020-10-26 09:25:30 -04:00
func ( c * cloudflareProvider ) ListZones ( ) ( [ ] string , error ) {
2020-02-18 08:59:18 -05:00
if err := c . fetchDomainList ( ) ; err != nil {
return nil , err
}
zones := make ( [ ] string , 0 , len ( c . domainIndex ) )
for d := range c . domainIndex {
zones = append ( zones , d )
}
return zones , nil
}
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
2023-05-02 13:04:59 -04:00
func ( c * cloudflareProvider ) GetZoneRecords ( domain string , meta map [ string ] string ) ( models . Records , error ) {
2023-04-14 15:22:23 -04:00
domainID , err := c . getDomainID ( domain )
2020-02-18 08:59:18 -05:00
if err != nil {
return nil , err
}
2023-04-14 15:22:23 -04:00
records , err := c . getRecordsForDomain ( domainID , domain )
2020-02-18 08:59:18 -05:00
if err != nil {
return nil , err
}
2023-04-14 15:22:23 -04:00
2020-02-18 08:59:18 -05:00
for _ , rec := range records {
2023-04-14 15:22:23 -04:00
if rec . TTL == 0 {
rec . TTL = 1
2020-02-18 08:59:18 -05:00
}
2020-11-24 10:30:21 -05:00
// Store the proxy status ("orange cloud") for use by get-zones:
m := getProxyMetadata ( rec )
if p , ok := m [ "proxy" ] ; ok {
if rec . Metadata == nil {
rec . Metadata = map [ string ] string { }
}
rec . Metadata [ "cloudflare_proxy" ] = p
}
2020-02-18 08:59:18 -05:00
}
2023-04-14 15:22:23 -04:00
// // FIXME(tlim) Why is this needed???
// // I don't know. Let's comment it out and see if anything breaks.
// for i := len(records) - 1; i >= 0; i-- {
// rec := records[i]
// // Delete ignore labels
// if labelMatches(dnsutil.TrimDomainName(rec.Original.(cloudflare.DNSRecord).Name, dc.Name), c.ignoredLabels) {
// printer.Debugf("ignored_label: %s\n", rec.Original.(cloudflare.DNSRecord).Name)
// records = append(records[:i], records[i+1:]...)
// }
// }
if c . manageRedirects {
prs , err := c . getPageRules ( domainID , domain )
if err != nil {
return nil , err
}
records = append ( records , prs ... )
}
if c . manageWorkers {
wrs , err := c . getWorkerRoutes ( domainID , domain )
if err != nil {
return nil , err
}
records = append ( records , wrs ... )
}
// Normalize
models . PostProcessRecords ( records )
2020-02-18 08:59:18 -05:00
return records , nil
}
2020-10-26 09:25:30 -04:00
func ( c * cloudflareProvider ) getDomainID ( name string ) ( string , error ) {
2016-08-22 18:31:50 -06:00
if c . domainIndex == nil {
if err := c . fetchDomainList ( ) ; err != nil {
2020-02-18 08:59:18 -05:00
return "" , err
2016-08-22 18:31:50 -06:00
}
}
2020-02-18 08:59:18 -05:00
id , ok := c . domainIndex [ name ]
2016-08-22 18:31:50 -06:00
if ! ok {
2020-02-18 08:59:18 -05:00
return "" , fmt . Errorf ( "'%s' not a zone in cloudflare account" , name )
2016-08-22 18:31:50 -06:00
}
2020-02-18 08:59:18 -05:00
return id , nil
}
2019-06-13 13:32:54 +02:00
2023-04-14 15:22:23 -04:00
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func ( c * cloudflareProvider ) GetZoneRecordsCorrections ( dc * models . DomainConfig , records models . Records ) ( [ ] * models . Correction , error ) {
2020-02-18 08:59:18 -05:00
if err := c . preprocessConfig ( dc ) ; err != nil {
return nil , err
}
2023-04-14 15:22:23 -04:00
// for i := len(records) - 1; i >= 0; i-- {
// rec := records[i]
// // Delete ignore labels
// if labelMatches(dnsutil.TrimDomainName(rec.Original.(cloudflare.DNSRecord).Name, dc.Name), c.ignoredLabels) {
// printer.Debugf("ignored_label: %s\n", rec.Original.(cloudflare.DNSRecord).Name)
// records = append(records[:i], records[i+1:]...)
// }
// }
2019-06-13 13:32:54 +02:00
2023-04-14 15:22:23 -04:00
checkNSModifications ( dc )
2019-06-13 13:32:54 +02:00
2023-04-14 15:22:23 -04:00
domainID , err := c . getDomainID ( dc . Name )
if err != nil {
return nil , err
2021-10-11 17:04:49 -03:00
}
2016-08-22 18:31:50 -06:00
for _ , rec := range dc . Records {
2017-04-19 13:13:28 -06:00
if rec . Type == "ALIAS" {
rec . Type = "CNAME"
}
2019-05-20 21:27:37 +02:00
// As per CF-API documentation proxied records are always forced to have a TTL of 1.
// When not forcing this property change here, dnscontrol tries each time to update
// the TTL of a record which simply cannot be changed anyway.
if rec . Metadata [ metaProxy ] != "off" {
rec . TTL = 1
}
2023-04-14 15:22:23 -04:00
// if labelMatches(rec.GetLabel(), c.ignoredLabels) {
// log.Fatalf("FATAL: dnsconfig contains label that matches ignored_labels: %#v is in %v)\n", rec.GetLabel(), c.ignoredLabels)
// }
2016-08-22 18:31:50 -06:00
}
2019-06-13 13:32:54 +02:00
2017-01-11 12:38:07 -07:00
checkNSModifications ( dc )
2017-11-07 14:12:17 -08:00
2022-12-11 15:02:58 -05:00
var corrections [ ] * models . Correction
2023-02-01 16:18:01 -05:00
if ! diff2 . EnableDiff2 {
2020-08-21 07:49:00 +12:00
2022-12-11 15:02:58 -05:00
differ := diff . New ( dc , getProxyMetadata )
_ , create , del , mod , err := differ . IncrementalDiff ( records )
if err != nil {
return nil , err
}
2016-08-22 18:31:50 -06:00
2022-12-11 15:02:58 -05:00
corrections := [ ] * models . Correction { }
for _ , d := range del {
ex := d . Existing
if ex . Type == "PAGE_RULE" {
corrections = append ( corrections , & models . Correction {
Msg : d . String ( ) ,
2023-02-01 16:18:01 -05:00
F : func ( ) error { return c . deletePageRule ( ex . Original . ( cloudflare . PageRule ) . ID , domainID ) } ,
2022-12-11 15:02:58 -05:00
} )
} else if ex . Type == "WORKER_ROUTE" {
corrections = append ( corrections , & models . Correction {
Msg : d . String ( ) ,
2023-02-01 16:18:01 -05:00
F : func ( ) error { return c . deleteWorkerRoute ( ex . Original . ( cloudflare . WorkerRoute ) . ID , domainID ) } ,
2022-12-11 15:02:58 -05:00
} )
2021-01-24 16:36:23 -05:00
} else {
2023-02-01 16:18:01 -05:00
corr := c . deleteRec ( ex . Original . ( cloudflare . DNSRecord ) , domainID )
2022-12-11 15:02:58 -05:00
// DS records must always have a corresponding NS record.
// Therefore, we remove DS records before any NS records.
if d . Existing . Type == "DS" {
corrections = append ( [ ] * models . Correction { corr } , corrections ... )
} else {
corrections = append ( corrections , corr )
}
2021-01-24 16:36:23 -05:00
}
2017-05-19 14:15:57 -04:00
}
2022-12-11 15:02:58 -05:00
for _ , d := range create {
des := d . Desired
if des . Type == "PAGE_RULE" {
corrections = append ( corrections , & models . Correction {
Msg : d . String ( ) ,
2023-02-01 16:18:01 -05:00
F : func ( ) error { return c . createPageRule ( domainID , des . GetTargetField ( ) ) } ,
2022-12-11 15:02:58 -05:00
} )
} else if des . Type == "WORKER_ROUTE" {
corrections = append ( corrections , & models . Correction {
Msg : d . String ( ) ,
2023-02-01 16:18:01 -05:00
F : func ( ) error { return c . createWorkerRoute ( domainID , des . GetTargetField ( ) ) } ,
2022-12-11 15:02:58 -05:00
} )
2021-01-24 16:36:23 -05:00
} else {
2023-02-01 16:18:01 -05:00
corr := c . createRec ( des , domainID )
2022-12-11 15:02:58 -05:00
// DS records must always have a corresponding NS record.
// Therefore, we create NS records before any DS records.
if d . Desired . Type == "NS" {
corrections = append ( corr , corrections ... )
} else {
corrections = append ( corrections , corr ... )
}
2021-01-24 16:36:23 -05:00
}
2017-05-19 14:15:57 -04:00
}
2016-08-22 18:31:50 -06:00
2022-12-11 15:02:58 -05:00
for _ , d := range mod {
rec := d . Desired
ex := d . Existing
if rec . Type == "PAGE_RULE" {
corrections = append ( corrections , & models . Correction {
Msg : d . String ( ) ,
2023-02-01 16:18:01 -05:00
F : func ( ) error {
return c . updatePageRule ( ex . Original . ( cloudflare . PageRule ) . ID , domainID , rec . GetTargetField ( ) )
} ,
2022-12-11 15:02:58 -05:00
} )
} else if rec . Type == "WORKER_ROUTE" {
corrections = append ( corrections , & models . Correction {
Msg : d . String ( ) ,
F : func ( ) error {
2023-02-01 16:18:01 -05:00
return c . updateWorkerRoute ( ex . Original . ( cloudflare . WorkerRoute ) . ID , domainID , rec . GetTargetField ( ) )
2022-12-11 15:02:58 -05:00
} ,
} )
} else {
e := ex . Original . ( cloudflare . DNSRecord )
proxy := e . Proxiable && rec . Metadata [ metaProxy ] != "off"
corrections = append ( corrections , & models . Correction {
Msg : d . String ( ) ,
2023-02-01 16:18:01 -05:00
F : func ( ) error { return c . modifyRecord ( domainID , e . ID , proxy , rec ) } ,
2022-12-11 15:02:58 -05:00
} )
}
}
// Add universalSSL change to corrections when needed
2023-02-01 16:18:01 -05:00
if changed , newState , err := c . checkUniversalSSL ( dc , domainID ) ; err == nil && changed {
2022-12-11 15:02:58 -05:00
var newStateString string
if newState {
newStateString = "enabled"
} else {
newStateString = "disabled"
}
2017-05-19 14:15:57 -04:00
corrections = append ( corrections , & models . Correction {
2022-12-11 15:02:58 -05:00
Msg : fmt . Sprintf ( "Universal SSL will be %s for this domain." , newStateString ) ,
2023-02-01 16:18:01 -05:00
F : func ( ) error { return c . changeUniversalSSL ( domainID , newState ) } ,
2017-05-19 14:15:57 -04:00
} )
}
2019-06-13 13:32:54 +02:00
2022-12-11 15:02:58 -05:00
return corrections , nil
2019-06-13 13:32:54 +02:00
}
2023-02-01 16:18:01 -05:00
// Cloudflare is a "ByRecord" API.
instructions , err := diff2 . ByRecord ( records , dc , genComparable )
if err != nil {
return nil , err
}
for _ , inst := range instructions {
addToFront := false
var corrs [ ] * models . Correction
domainID := domainID
msg := inst . Msgs [ 0 ]
switch inst . Type {
case diff2 . CREATE :
createRec := inst . New [ 0 ]
corrs = c . mkCreateCorrection ( createRec , domainID , msg )
// DS records must always have a corresponding NS record.
// Therefore, we create NS records before any DS records.
2023-04-14 15:22:23 -04:00
addToFront = ( createRec . Type == "NS" )
2023-02-01 16:18:01 -05:00
case diff2 . CHANGE :
newrec := inst . New [ 0 ]
oldrec := inst . Old [ 0 ]
corrs = c . mkChangeCorrection ( oldrec , newrec , domainID , msg )
case diff2 . DELETE :
deleteRec := inst . Old [ 0 ]
deleteRecType := deleteRec . Type
deleteRecOrig := deleteRec . Original
corrs = c . mkDeleteCorrection ( deleteRecType , deleteRecOrig , domainID , msg )
// DS records must always have a corresponding NS record.
// Therefore, we remove DS records before any NS records.
2023-04-14 15:22:23 -04:00
addToFront = ( deleteRecType == "DS" )
2023-02-01 16:18:01 -05:00
}
if addToFront {
corrections = append ( corrs , corrections ... )
} else {
corrections = append ( corrections , corrs ... )
}
}
// Add universalSSL change when needed
if changed , newState , err := c . checkUniversalSSL ( dc , domainID ) ; err == nil && changed {
var newStateString string
if newState {
newStateString = "enabled"
} else {
newStateString = "disabled"
}
corrections = append ( corrections , & models . Correction {
Msg : fmt . Sprintf ( "Universal SSL will be %s for this domain." , newStateString ) ,
F : func ( ) error { return c . changeUniversalSSL ( domainID , newState ) } ,
} )
}
2022-12-11 15:02:58 -05:00
2016-08-22 18:31:50 -06:00
return corrections , nil
2023-02-01 16:18:01 -05:00
}
func genComparable ( rec * models . RecordConfig ) string {
if rec . Type == "A" || rec . Type == "AAAA" || rec . Type == "CNAME" {
proxy := rec . Metadata [ metaProxy ]
if proxy != "" {
2023-08-21 12:19:16 -04:00
if proxy == "on" || proxy == "full" {
2023-04-14 15:22:23 -04:00
proxy = "true"
}
if proxy == "off" {
proxy = "false"
}
2023-02-01 16:18:01 -05:00
return "proxy=" + proxy
}
}
return ""
}
2022-12-11 17:28:58 -05:00
2023-02-01 16:18:01 -05:00
func ( c * cloudflareProvider ) mkCreateCorrection ( newrec * models . RecordConfig , domainID , msg string ) [ ] * models . Correction {
switch newrec . Type {
case "PAGE_RULE" :
return [ ] * models . Correction { {
Msg : msg ,
F : func ( ) error { return c . createPageRule ( domainID , newrec . GetTargetField ( ) ) } ,
} }
case "WORKER_ROUTE" :
return [ ] * models . Correction { {
Msg : msg ,
F : func ( ) error { return c . createWorkerRoute ( domainID , newrec . GetTargetField ( ) ) } ,
} }
default :
return c . createRecDiff2 ( newrec , domainID , msg )
}
}
func ( c * cloudflareProvider ) mkChangeCorrection ( oldrec , newrec * models . RecordConfig , domainID string , msg string ) [ ] * models . Correction {
2023-04-14 15:22:23 -04:00
2023-02-28 01:25:09 -05:00
var idTxt string
switch oldrec . Type {
case "PAGE_RULE" :
idTxt = oldrec . Original . ( cloudflare . PageRule ) . ID
case "WORKER_ROUTE" :
idTxt = oldrec . Original . ( cloudflare . WorkerRoute ) . ID
default :
idTxt = oldrec . Original . ( cloudflare . DNSRecord ) . ID
}
msg = msg + color . YellowString ( " id=%v" , idTxt )
2023-02-01 16:18:01 -05:00
switch newrec . Type {
case "PAGE_RULE" :
return [ ] * models . Correction { {
Msg : msg ,
F : func ( ) error {
2023-04-14 15:22:23 -04:00
return c . updatePageRule ( idTxt , domainID , newrec . GetTargetField ( ) )
2023-02-01 16:18:01 -05:00
} ,
} }
case "WORKER_ROUTE" :
return [ ] * models . Correction { {
Msg : msg ,
F : func ( ) error {
2023-04-14 15:22:23 -04:00
return c . updateWorkerRoute ( idTxt , domainID , newrec . GetTargetField ( ) )
2023-02-01 16:18:01 -05:00
} ,
} }
default :
e := oldrec . Original . ( cloudflare . DNSRecord )
proxy := e . Proxiable && newrec . Metadata [ metaProxy ] != "off"
2023-08-21 12:19:16 -04:00
//fmt.Fprintf(os.Stderr, "DEBUG: proxy := %v && %v != off is... %v\n", e.Proxiable, newrec.Metadata[metaProxy], proxy)
2023-02-01 16:18:01 -05:00
return [ ] * models . Correction { {
Msg : msg ,
F : func ( ) error { return c . modifyRecord ( domainID , e . ID , proxy , newrec ) } ,
} }
}
}
func ( c * cloudflareProvider ) mkDeleteCorrection ( recType string , origRec any , domainID string , msg string ) [ ] * models . Correction {
2023-02-28 01:25:09 -05:00
2023-02-01 16:18:01 -05:00
var idTxt string
switch recType {
case "PAGE_RULE" :
idTxt = origRec . ( cloudflare . PageRule ) . ID
case "WORKER_ROUTE" :
idTxt = origRec . ( cloudflare . WorkerRoute ) . ID
default :
idTxt = origRec . ( cloudflare . DNSRecord ) . ID
}
2023-02-28 01:25:09 -05:00
msg = msg + color . RedString ( " id=%v" , idTxt )
2023-02-01 16:18:01 -05:00
correction := & models . Correction {
Msg : msg ,
F : func ( ) error {
switch recType {
case "PAGE_RULE" :
return c . deletePageRule ( origRec . ( cloudflare . PageRule ) . ID , domainID )
case "WORKER_ROUTE" :
return c . deleteWorkerRoute ( origRec . ( cloudflare . WorkerRoute ) . ID , domainID )
default :
return c . deleteDNSRecord ( origRec . ( cloudflare . DNSRecord ) , domainID )
}
} ,
}
return [ ] * models . Correction { correction }
2016-08-22 18:31:50 -06:00
}
2017-01-11 12:38:07 -07:00
func checkNSModifications ( dc * models . DomainConfig ) {
newList := make ( [ ] * models . RecordConfig , 0 , len ( dc . Records ) )
2023-08-17 09:41:52 -06:00
punyRoot , err := idna . ToASCII ( dc . Name )
if err != nil {
punyRoot = dc . Name
}
2017-01-11 12:38:07 -07:00
for _ , rec := range dc . Records {
2023-08-17 09:41:52 -06:00
if rec . Type == "NS" && rec . GetLabelFQDN ( ) == punyRoot {
2018-02-15 12:02:50 -05:00
if ! strings . HasSuffix ( rec . GetTargetField ( ) , ".ns.cloudflare.com." ) {
2018-10-08 13:10:44 -07:00
printer . Warnf ( "cloudflare does not support modifying NS records on base domain. %s will not be added.\n" , rec . GetTargetField ( ) )
2017-01-11 12:38:07 -07:00
}
continue
}
newList = append ( newList , rec )
}
dc . Records = newList
}
2020-10-26 09:25:30 -04:00
func ( c * cloudflareProvider ) checkUniversalSSL ( dc * models . DomainConfig , id string ) ( changed bool , newState bool , err error ) {
2020-06-18 09:37:57 -04:00
expectedStr := dc . Metadata [ metaUniversalSSL ]
if expectedStr == "" {
return false , false , fmt . Errorf ( "metadata not set" )
2019-06-13 13:32:54 +02:00
}
if actual , err := c . getUniversalSSL ( id ) ; err == nil {
// convert str to bool
var expected bool
2020-06-18 09:37:57 -04:00
if expectedStr == "off" {
2019-06-13 13:32:54 +02:00
expected = false
} else {
expected = true
}
// did something change?
if actual != expected {
return true , expected , nil
}
return false , expected , nil
}
2020-06-18 09:37:57 -04:00
return false , false , fmt . Errorf ( "error receiving universal ssl state" )
2019-06-13 13:32:54 +02:00
}
2016-08-22 18:31:50 -06:00
const (
metaProxy = "cloudflare_proxy"
metaProxyDefault = metaProxy + "_default"
2019-06-13 13:32:54 +02:00
metaOriginalIP = "original_ip" // TODO(tlim): Unclear what this means.
metaUniversalSSL = "cloudflare_universalssl"
2016-08-22 18:31:50 -06:00
metaIPConversions = "ip_conversions" // TODO(tlim): Rename to obscure_rules.
)
func checkProxyVal ( v string ) ( string , error ) {
v = strings . ToLower ( v )
if v != "on" && v != "off" && v != "full" {
2020-08-30 19:52:37 -04:00
return "" , fmt . Errorf ( "bad metadata value for cloudflare_proxy: '%s'. Use on/off/full" , v )
2016-08-22 18:31:50 -06:00
}
return v , nil
}
2020-10-26 09:25:30 -04:00
func ( c * cloudflareProvider ) preprocessConfig ( dc * models . DomainConfig ) error {
2016-08-22 18:31:50 -06:00
// Determine the default proxy setting.
var defProxy string
var err error
if defProxy = dc . Metadata [ metaProxyDefault ] ; defProxy == "" {
defProxy = "off"
} else {
defProxy , err = checkProxyVal ( defProxy )
if err != nil {
return err
}
}
2019-06-13 13:32:54 +02:00
// Check UniversalSSL setting
if u := dc . Metadata [ metaUniversalSSL ] ; u != "" {
u = strings . ToLower ( u )
2019-06-27 01:21:23 -04:00
if u != "on" && u != "off" {
2020-06-18 09:37:57 -04:00
return fmt . Errorf ( "bad metadata value for %s: '%s'. Use on/off" , metaUniversalSSL , u )
2019-06-13 13:32:54 +02:00
}
}
2017-05-19 14:15:57 -04:00
2016-08-22 18:31:50 -06:00
// Normalize the proxy setting for each record.
// A and CNAMEs: Validate. If null, set to default.
// else: Make sure it wasn't set. Set to default.
2017-05-19 14:15:57 -04:00
// iterate backwards so first defined page rules have highest priority
2019-06-13 13:32:54 +02:00
currentPrPrio := 1
2017-05-19 14:15:57 -04:00
for i := len ( dc . Records ) - 1 ; i >= 0 ; i -- {
rec := dc . Records [ i ]
2017-04-19 13:13:28 -06:00
if rec . Metadata == nil {
rec . Metadata = map [ string ] string { }
}
2019-06-17 15:12:23 -04:00
// cloudflare uses "1" to mean "auto-ttl"
2019-06-27 01:21:23 -04:00
// if we get here and ttl is not specified (or is the dnscontrol default of 300),
2019-06-17 15:12:23 -04:00
// use automatic mode instead.
2019-06-27 01:21:23 -04:00
if rec . TTL == 0 || rec . TTL == 300 {
2017-01-11 12:38:07 -07:00
rec . TTL = 1
}
2023-01-27 05:15:22 -08:00
if rec . TTL != 1 && rec . TTL < 60 {
2023-01-28 11:10:02 -05:00
rec . TTL = 60
2017-04-19 13:13:28 -06:00
}
2019-06-13 13:32:54 +02:00
2017-04-20 07:13:21 -06:00
if rec . Type != "A" && rec . Type != "CNAME" && rec . Type != "AAAA" && rec . Type != "ALIAS" {
2016-08-22 18:31:50 -06:00
if rec . Metadata [ metaProxy ] != "" {
Switch to Go 1.13 error wrapping (#604)
* Replaced errors.Wrap with fmt.Errorf (#589)
* Find: errors\.Wrap\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Replaced errors.Wrapf with fmt.Errorf (#589)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])(,[^)]+)\)
* Replace: fmt.Errorf($2: %w$3$4, $1)
* Replaced errors.Errorf with fmt.Errorf (#589)
* Find: errors\.Errorf
Replace: fmt.Errorf
* Cleaned up remaining imports
* Cleanup
* Regenerate provider support matrix
This was broken by #533 ... and it's now the third time this has been missed.
2020-01-28 11:06:56 -05:00
return fmt . Errorf ( "cloudflare_proxy set on %v record: %#v cloudflare_proxy=%#v" , rec . Type , rec . GetLabel ( ) , rec . Metadata [ metaProxy ] )
2016-08-22 18:31:50 -06:00
}
// Force it to off.
rec . Metadata [ metaProxy ] = "off"
} else {
if val := rec . Metadata [ metaProxy ] ; val == "" {
rec . Metadata [ metaProxy ] = defProxy
} else {
val , err := checkProxyVal ( val )
if err != nil {
return err
}
rec . Metadata [ metaProxy ] = val
}
}
2019-06-13 13:32:54 +02:00
2017-05-19 14:15:57 -04:00
// CF_REDIRECT record types. Encode target as $FROM,$TO,$PRIO,$CODE
if rec . Type == "CF_REDIRECT" || rec . Type == "CF_TEMP_REDIRECT" {
if ! c . manageRedirects {
Switch to Go 1.13 error wrapping (#604)
* Replaced errors.Wrap with fmt.Errorf (#589)
* Find: errors\.Wrap\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Replaced errors.Wrapf with fmt.Errorf (#589)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])(,[^)]+)\)
* Replace: fmt.Errorf($2: %w$3$4, $1)
* Replaced errors.Errorf with fmt.Errorf (#589)
* Find: errors\.Errorf
Replace: fmt.Errorf
* Cleaned up remaining imports
* Cleanup
* Regenerate provider support matrix
This was broken by #533 ... and it's now the third time this has been missed.
2020-01-28 11:06:56 -05:00
return fmt . Errorf ( "you must add 'manage_redirects: true' metadata to cloudflare provider to use CF_REDIRECT records" )
2017-05-19 14:15:57 -04:00
}
2018-02-15 12:02:50 -05:00
parts := strings . Split ( rec . GetTargetField ( ) , "," )
2017-05-19 14:15:57 -04:00
if len ( parts ) != 2 {
2020-08-30 19:52:37 -04:00
return fmt . Errorf ( "invalid data specified for cloudflare redirect record" )
2017-05-19 14:15:57 -04:00
}
code := 301
if rec . Type == "CF_TEMP_REDIRECT" {
code = 302
}
2018-02-15 12:02:50 -05:00
rec . SetTarget ( fmt . Sprintf ( "%s,%d,%d" , rec . GetTargetField ( ) , currentPrPrio , code ) )
2017-05-19 14:15:57 -04:00
currentPrPrio ++
2020-08-27 22:45:58 +02:00
rec . TTL = 1
2017-05-19 14:15:57 -04:00
rec . Type = "PAGE_RULE"
}
2021-10-11 17:04:49 -03:00
// CF_WORKER_ROUTE record types. Encode target as $PATTERN,$SCRIPT
if rec . Type == "CF_WORKER_ROUTE" {
parts := strings . Split ( rec . GetTargetField ( ) , "," )
if len ( parts ) != 2 {
return fmt . Errorf ( "invalid data specified for cloudflare worker record" )
}
rec . TTL = 1
rec . Type = "WORKER_ROUTE"
}
2016-08-22 18:31:50 -06:00
}
// look for ip conversions and transform records
for _ , rec := range dc . Records {
2023-08-21 12:19:16 -04:00
// Only transform A records
2016-08-22 18:31:50 -06:00
if rec . Type != "A" {
continue
}
2018-01-09 12:53:16 -05:00
// only transform "full"
2016-08-22 18:31:50 -06:00
if rec . Metadata [ metaProxy ] != "full" {
continue
}
2018-02-15 12:02:50 -05:00
ip := net . ParseIP ( rec . GetTargetField ( ) )
2016-08-22 18:31:50 -06:00
if ip == nil {
Switch to Go 1.13 error wrapping (#604)
* Replaced errors.Wrap with fmt.Errorf (#589)
* Find: errors\.Wrap\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Replaced errors.Wrapf with fmt.Errorf (#589)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])(,[^)]+)\)
* Replace: fmt.Errorf($2: %w$3$4, $1)
* Replaced errors.Errorf with fmt.Errorf (#589)
* Find: errors\.Errorf
Replace: fmt.Errorf
* Cleaned up remaining imports
* Cleanup
* Regenerate provider support matrix
This was broken by #533 ... and it's now the third time this has been missed.
2020-01-28 11:06:56 -05:00
return fmt . Errorf ( "%s is not a valid ip address" , rec . GetTargetField ( ) )
2016-08-22 18:31:50 -06:00
}
2020-06-18 09:37:57 -04:00
newIP , err := transform . IP ( ip , c . ipConversions )
2016-08-22 18:31:50 -06:00
if err != nil {
return err
}
2018-02-15 12:02:50 -05:00
rec . Metadata [ metaOriginalIP ] = rec . GetTargetField ( )
rec . SetTarget ( newIP . String ( ) )
2016-08-22 18:31:50 -06:00
}
return nil
}
func newCloudflare ( m map [ string ] string , metadata json . RawMessage ) ( providers . DNSServiceProvider , error ) {
2020-10-26 09:25:30 -04:00
api := & cloudflareProvider { }
2016-08-22 18:31:50 -06:00
// check api keys from creds json file
2021-10-03 14:40:50 -06:00
if m [ "apitoken" ] == "" && ( m [ "apikey" ] == "" || m [ "apiuser" ] == "" ) {
Switch to Go 1.13 error wrapping (#604)
* Replaced errors.Wrap with fmt.Errorf (#589)
* Find: errors\.Wrap\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Replaced errors.Wrapf with fmt.Errorf (#589)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])(,[^)]+)\)
* Replace: fmt.Errorf($2: %w$3$4, $1)
* Replaced errors.Errorf with fmt.Errorf (#589)
* Find: errors\.Errorf
Replace: fmt.Errorf
* Cleaned up remaining imports
* Cleanup
* Regenerate provider support matrix
This was broken by #533 ... and it's now the third time this has been missed.
2020-01-28 11:06:56 -05:00
return nil , fmt . Errorf ( "if cloudflare apitoken is not set, apikey and apiuser must be provided" )
2019-10-23 17:48:00 +02:00
}
2021-10-03 14:40:50 -06:00
if m [ "apitoken" ] != "" && ( m [ "apikey" ] != "" || m [ "apiuser" ] != "" ) {
Switch to Go 1.13 error wrapping (#604)
* Replaced errors.Wrap with fmt.Errorf (#589)
* Find: errors\.Wrap\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Replaced errors.Wrapf with fmt.Errorf (#589)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])(,[^)]+)\)
* Replace: fmt.Errorf($2: %w$3$4, $1)
* Replaced errors.Errorf with fmt.Errorf (#589)
* Find: errors\.Errorf
Replace: fmt.Errorf
* Cleaned up remaining imports
* Cleanup
* Regenerate provider support matrix
This was broken by #533 ... and it's now the third time this has been missed.
2020-01-28 11:06:56 -05:00
return nil , fmt . Errorf ( "if cloudflare apitoken is set, apikey and apiuser should not be provided" )
2016-08-22 18:31:50 -06:00
}
2022-08-15 18:30:21 -04:00
optRP := cloudflare . UsingRetryPolicy ( 20 , 1 , 120 )
// UsingRetryPolicy is documented here:
// https://pkg.go.dev/github.com/cloudflare/cloudflare-go#UsingRetryPolicy
// The defaults are UsingRetryPolicy(3, 1, 30)
2021-09-30 05:09:42 -06:00
var err error
2021-10-03 14:40:50 -06:00
if m [ "apitoken" ] != "" {
2022-08-15 18:30:21 -04:00
api . cfClient , err = cloudflare . NewWithAPIToken ( m [ "apitoken" ] , optRP )
2021-09-30 05:09:42 -06:00
} else {
2022-08-15 18:30:21 -04:00
api . cfClient , err = cloudflare . New ( m [ "apikey" ] , m [ "apiuser" ] , optRP )
2021-09-30 05:09:42 -06:00
}
if err != nil {
return nil , fmt . Errorf ( "cloudflare credentials: %w" , err )
}
2018-12-19 15:48:27 +01:00
// Check account data if set
2021-10-03 14:40:50 -06:00
if m [ "accountid" ] != "" {
2023-09-15 15:30:55 -04:00
api . accountID = m [ "accountid" ]
2018-12-19 15:48:27 +01:00
}
2023-05-26 10:48:30 +10:00
debug , err := strconv . ParseBool ( os . Getenv ( "CLOUDFLAREAPI_DEBUG" ) )
if err == nil {
api . cfClient . Debug = debug
}
2016-08-22 18:31:50 -06:00
if len ( metadata ) > 0 {
parsedMeta := & struct {
2017-05-19 14:15:57 -04:00
IPConversions string ` json:"ip_conversions" `
IgnoredLabels [ ] string ` json:"ignored_labels" `
ManageRedirects bool ` json:"manage_redirects" `
2021-10-11 17:04:49 -03:00
ManageWorkers bool ` json:"manage_workers" `
2016-08-22 18:31:50 -06:00
} { }
err := json . Unmarshal ( [ ] byte ( metadata ) , parsedMeta )
if err != nil {
return nil , err
}
2017-05-19 14:15:57 -04:00
api . manageRedirects = parsedMeta . ManageRedirects
2021-10-11 17:04:49 -03:00
api . manageWorkers = parsedMeta . ManageWorkers
2016-08-22 18:31:50 -06:00
// ignored_labels:
2020-08-30 20:38:08 -04:00
api . ignoredLabels = append ( api . ignoredLabels , parsedMeta . IgnoredLabels ... )
2018-01-15 21:39:29 +01:00
if len ( api . ignoredLabels ) > 0 {
2018-10-08 13:10:44 -07:00
printer . Warnf ( "Cloudflare 'ignored_labels' configuration is deprecated and might be removed. Please use the IGNORE domain directive to achieve the same effect.\n" )
2018-01-15 21:39:29 +01:00
}
2016-08-22 18:31:50 -06:00
// parse provider level metadata
2017-05-19 14:15:57 -04:00
if len ( parsedMeta . IPConversions ) > 0 {
api . ipConversions , err = transform . DecodeTransformTable ( parsedMeta . IPConversions )
if err != nil {
return nil , err
}
2016-08-22 18:31:50 -06:00
}
}
return api , nil
}
// Used on the "existing" records.
2017-08-10 16:02:06 -04:00
type cfRecData struct {
2020-06-18 09:37:57 -04:00
Name string ` json:"name" `
Target cfTarget ` json:"target" `
Service string ` json:"service" ` // SRV
Proto string ` json:"proto" ` // SRV
Priority uint16 ` json:"priority" ` // SRV
Weight uint16 ` json:"weight" ` // SRV
Port uint16 ` json:"port" ` // SRV
Tag string ` json:"tag" ` // CAA
Flags uint8 ` json:"flags" ` // CAA
Value string ` json:"value" ` // CAA
Usage uint8 ` json:"usage" ` // TLSA
Selector uint8 ` json:"selector" ` // TLSA
MatchingType uint8 ` json:"matching_type" ` // TLSA
Certificate string ` json:"certificate" ` // TLSA
Algorithm uint8 ` json:"algorithm" ` // SSHFP/DS
HashType uint8 ` json:"type" ` // SSHFP
Fingerprint string ` json:"fingerprint" ` // SSHFP
KeyTag uint16 ` json:"key_tag" ` // DS
DigestType uint8 ` json:"digest_type" ` // DS
Digest string ` json:"digest" ` // DS
2019-11-14 11:25:20 -05:00
}
// cfTarget is a SRV target. A null target is represented by an empty string, but
// a dot is so acceptable.
type cfTarget string
// UnmarshalJSON decodes a SRV target from the Cloudflare API. A null target is
// represented by a false boolean or a dot. Domain names are FQDNs without a
// trailing period (as of 2019-11-05).
func ( c * cfTarget ) UnmarshalJSON ( data [ ] byte ) error {
var obj interface { }
if err := json . Unmarshal ( data , & obj ) ; err != nil {
return err
}
switch v := obj . ( type ) {
case string :
* c = cfTarget ( v )
case bool :
if v {
panic ( "unknown value for cfTarget bool: true" )
}
* c = "" // the "." is already added by nativeToRecord
}
return nil
}
// MarshalJSON encodes cfTarget for the Cloudflare API. Null targets are
// represented by a single period.
func ( c cfTarget ) MarshalJSON ( ) ( [ ] byte , error ) {
var obj string
switch c {
case "" , "." :
obj = "."
default :
obj = string ( c )
}
return json . Marshal ( obj )
}
// DNSControlString returns cfTarget normalized to be a FQDN. Null targets are
// represented by a single period.
func ( c cfTarget ) FQDN ( ) string {
return strings . TrimRight ( string ( c ) , "." ) + "."
2017-08-10 16:02:06 -04:00
}
2023-09-07 13:31:34 -07:00
type cfNaptrRecData struct {
Flags string ` json:"flags" `
Order uint16 ` json:"order" `
Preference uint16 ` json:"preference" `
Regex string ` json:"regex" `
Replacement string ` json:"replacement" `
Service string ` json:"service" `
}
2022-03-09 15:20:45 -05:00
// uint16Zero converts value to uint16 or returns 0.
func uint16Zero ( value interface { } ) uint16 {
switch v := value . ( type ) {
case float64 :
return uint16 ( v )
case uint16 :
return v
case nil :
}
return 0
}
// intZero converts value to int or returns 0.
func intZero ( value interface { } ) int {
switch v := value . ( type ) {
case float64 :
return int ( v )
case int :
return v
case nil :
}
return 0
}
// stringDefault returns the value as a string or returns the default value if nil.
func stringDefault ( value interface { } , def string ) string {
switch v := value . ( type ) {
case string :
return v
case nil :
}
return def
}
2022-01-27 15:58:56 -05:00
func ( c * cloudflareProvider ) nativeToRecord ( domain string , cr cloudflare . DNSRecord ) ( * models . RecordConfig , error ) {
2022-06-18 09:58:55 -04:00
2018-01-09 12:53:16 -05:00
// normalize cname,mx,ns records with dots to be consistent with our config format.
2022-05-03 12:07:49 +02:00
if cr . Type == "CNAME" || cr . Type == "MX" || cr . Type == "NS" || cr . Type == "PTR" {
2022-01-27 15:58:56 -05:00
if cr . Content != "." {
cr . Content = cr . Content + "."
2020-03-25 09:53:28 -04:00
}
2016-08-22 18:31:50 -06:00
}
2018-02-15 12:02:50 -05:00
2017-08-10 16:02:06 -04:00
rc := & models . RecordConfig {
2022-01-27 15:58:56 -05:00
TTL : uint32 ( cr . TTL ) ,
Original : cr ,
2023-02-01 16:18:01 -05:00
Metadata : map [ string ] string { } ,
2016-08-22 18:31:50 -06:00
}
2022-01-27 15:58:56 -05:00
rc . SetLabelFromFQDN ( cr . Name , domain )
2019-05-18 17:08:18 +02:00
2020-02-23 13:58:49 -05:00
// workaround for https://github.com/StackExchange/dnscontrol/issues/446
2022-01-27 15:58:56 -05:00
if cr . Type == "SPF" {
cr . Type = "TXT"
2019-05-18 17:08:18 +02:00
}
2023-02-01 16:18:01 -05:00
if cr . Type == "A" || cr . Type == "AAAA" || cr . Type == "CNAME" {
if cr . Proxied != nil {
if * ( cr . Proxied ) {
rc . Metadata [ metaProxy ] = "on"
} else {
rc . Metadata [ metaProxy ] = "off"
}
}
}
2022-01-27 15:58:56 -05:00
switch rType := cr . Type ; rType { // #rtype_variations
2017-12-20 10:25:23 -05:00
case "MX" :
2022-01-27 15:58:56 -05:00
if err := rc . SetTargetMX ( * cr . Priority , cr . Content ) ; err != nil {
2020-11-13 16:32:40 -05:00
return nil , fmt . Errorf ( "unparsable MX record received from cloudflare: %w" , err )
2018-02-15 12:02:50 -05:00
}
2017-12-20 10:25:23 -05:00
case "SRV" :
2022-01-27 15:58:56 -05:00
data := cr . Data . ( map [ string ] interface { } )
2022-03-03 16:53:54 -05:00
2022-03-09 15:20:45 -05:00
target := stringDefault ( data [ "target" ] , "MISSING.TARGET" )
2021-09-30 05:09:42 -06:00
if target != "." {
target += "."
}
2022-03-09 15:20:45 -05:00
if err := rc . SetTargetSRV ( uint16Zero ( data [ "priority" ] ) , uint16Zero ( data [ "weight" ] ) , uint16Zero ( data [ "port" ] ) ,
2021-09-30 05:09:42 -06:00
target ) ; err != nil {
2020-11-13 16:32:40 -05:00
return nil , fmt . Errorf ( "unparsable SRV record received from cloudflare: %w" , err )
2018-02-15 12:02:50 -05:00
}
2022-06-18 09:58:55 -04:00
case "TXT" :
err := rc . SetTargetTXT ( cr . Content )
return rc , err
default :
2022-01-27 15:58:56 -05:00
if err := rc . PopulateFromString ( rType , cr . Content , domain ) ; err != nil {
2020-11-13 16:32:40 -05:00
return nil , fmt . Errorf ( "unparsable record received from cloudflare: %w" , err )
2018-02-15 12:02:50 -05:00
}
2017-08-10 16:02:06 -04:00
}
2017-12-20 10:25:23 -05:00
2020-11-13 16:32:40 -05:00
return rc , nil
2016-08-22 18:31:50 -06:00
}
2017-01-11 12:38:07 -07:00
func getProxyMetadata ( r * models . RecordConfig ) map [ string ] string {
if r . Type != "A" && r . Type != "AAAA" && r . Type != "CNAME" {
return nil
2016-08-22 18:31:50 -06:00
}
2020-07-06 20:18:24 -04:00
var proxied bool
2017-01-11 12:38:07 -07:00
if r . Original != nil {
2021-09-30 05:09:42 -06:00
proxied = * r . Original . ( cloudflare . DNSRecord ) . Proxied
2017-01-11 12:38:07 -07:00
} else {
proxied = r . Metadata [ metaProxy ] != "off"
2016-08-22 18:31:50 -06:00
}
2017-01-11 12:38:07 -07:00
return map [ string ] string {
"proxy" : fmt . Sprint ( proxied ) ,
2016-08-22 18:31:50 -06:00
}
}
2017-05-05 22:20:43 +01:00
2023-02-07 17:52:49 +05:30
// EnsureZoneExists creates a zone if it does not exist
func ( c * cloudflareProvider ) EnsureZoneExists ( domain string ) error {
2022-08-08 13:00:58 -04:00
if c . domainIndex == nil {
if err := c . fetchDomainList ( ) ; err != nil {
return err
}
}
2017-05-05 22:20:43 +01:00
if _ , ok := c . domainIndex [ domain ] ; ok {
return nil
}
var id string
id , err := c . createZone ( domain )
2022-06-18 15:01:02 +02:00
printer . Printf ( "Added zone for %s to Cloudflare account: %s\n" , domain , id )
2017-05-05 22:20:43 +01:00
return err
}
2021-10-11 17:04:49 -03:00
2022-01-27 15:58:56 -05:00
// PrepareCloudflareTestWorkers creates Cloudflare Workers required for CF_WORKER_ROUTE tests.
2021-10-11 17:04:49 -03:00
func PrepareCloudflareTestWorkers ( prv providers . DNSServiceProvider ) error {
cf , ok := prv . ( * cloudflareProvider )
if ok {
err := cf . createTestWorker ( "dnscontrol_integrationtest_cnn" )
if err != nil {
return err
}
err = cf . createTestWorker ( "dnscontrol_integrationtest_msnbc" )
if err != nil {
return err
}
}
return nil
}