mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-09-20 06:46:19 +08:00
CLOUDFLAREAPI: CF_SINGLE_REDIRECT improvements: fix bugs, log translated redirects (#3051)
This commit is contained in:
parent
1d348de91c
commit
0869052419
14
commands/types/dnscontrol.d.ts
vendored
14
commands/types/dnscontrol.d.ts
vendored
|
@ -472,6 +472,11 @@ declare function CAA(name: string, tag: "issue" | "issuewild" | "iodef", value:
|
|||
declare function CAA_BUILDER(opts: { label?: string; iodef: string; iodef_critical?: boolean; issue: string[]; issue_critical?: boolean; issuewild: string[]; issuewild_critical?: boolean; ttl?: Duration }): DomainModifier;
|
||||
|
||||
/**
|
||||
* WARNING: Cloudflare is removing this feature and replacing it with a new
|
||||
* feature called "Dynamic Single Redirect". DNSControl will automatically
|
||||
* generate "Dynamic Single Redirects" for a limited number of use cases. See
|
||||
* [`CLOUDFLAREAPI`](../provider/cloudflareapi.md) for details.
|
||||
*
|
||||
* `CF_REDIRECT` uses Cloudflare-specific features ("Forwarding URL" Page Rules) to
|
||||
* generate a HTTP 301 permanent redirect.
|
||||
*
|
||||
|
@ -533,6 +538,11 @@ declare function CF_REDIRECT(source: string, destination: string, ...modifiers:
|
|||
declare function CF_SINGLE_REDIRECT(name: string, code: number, when: string, then: string, ...modifiers: RecordModifier[]): DomainModifier;
|
||||
|
||||
/**
|
||||
* WARNING: Cloudflare is removing this feature and replacing it with a new
|
||||
* feature called "Dynamic Single Redirect". DNSControl will automatically
|
||||
* generate "Dynamic Single Redirects" for a limited number of use cases. See
|
||||
* [`CLOUDFLAREAPI`](../provider/cloudflareapi.md) for details.
|
||||
*
|
||||
* `CF_TEMP_REDIRECT` uses Cloudflare-specific features ("Forwarding URL" Page
|
||||
* Rules) to generate a HTTP 302 temporary redirect.
|
||||
*
|
||||
|
@ -1810,7 +1820,7 @@ declare function LOC_BUILDER_STR(opts: { label?: string; str: string; alt?: numb
|
|||
*
|
||||
* ```javascript
|
||||
* D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER),
|
||||
* M365_BUILDER({
|
||||
* M365_BUILDER("example.com", {
|
||||
* initialDomain: "example.onmicrosoft.com",
|
||||
* }),
|
||||
* END);
|
||||
|
@ -1822,7 +1832,7 @@ declare function LOC_BUILDER_STR(opts: { label?: string; str: string; alt?: numb
|
|||
*
|
||||
* ```javascript
|
||||
* D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER),
|
||||
* M365_BUILDER({
|
||||
* M365_BUILDER("example.com", {
|
||||
* label: "test",
|
||||
* mx: false,
|
||||
* autodiscover: false,
|
||||
|
|
|
@ -11,6 +11,13 @@ parameter_types:
|
|||
"modifiers...": RecordModifier[]
|
||||
---
|
||||
|
||||
{% hint style="warning" %}
|
||||
WARNING: Cloudflare is removing this feature and replacing it with a new
|
||||
feature called "Dynamic Single Redirect". DNSControl will automatically
|
||||
generate "Dynamic Single Redirects" for a limited number of use cases. See
|
||||
[`CLOUDFLAREAPI`](../provider/cloudflareapi.md) for details.
|
||||
{% endhint %}
|
||||
|
||||
`CF_REDIRECT` uses Cloudflare-specific features ("Forwarding URL" Page Rules) to
|
||||
generate a HTTP 301 permanent redirect.
|
||||
|
||||
|
|
|
@ -11,6 +11,13 @@ parameter_types:
|
|||
"modifiers...": RecordModifier[]
|
||||
---
|
||||
|
||||
{% hint style="warning" %}
|
||||
**WARNING**: Cloudflare is removing this feature and replacing it with a new
|
||||
feature called "Dynamic Single Redirect". DNSControl will automatically
|
||||
generate "Dynamic Single Redirects" for a limited number of use cases. See
|
||||
[`CLOUDFLAREAPI`](../provider/cloudflareapi.md) for details.
|
||||
{% endhint %}
|
||||
|
||||
`CF_TEMP_REDIRECT` uses Cloudflare-specific features ("Forwarding URL" Page
|
||||
Rules) to generate a HTTP 302 temporary redirect.
|
||||
|
||||
|
|
|
@ -211,7 +211,8 @@ Enable it using:
|
|||
|
||||
```javascript
|
||||
var DSP_CLOUDFLARE = NewDnsProvider("cloudflare", {
|
||||
"manage_redirects": true
|
||||
"manage_redirects": true,
|
||||
"transcode_log": "transcode.log",
|
||||
});
|
||||
```
|
||||
|
||||
|
@ -231,8 +232,7 @@ New-style redirects ("Single Redirect Rules") are a new feature of DNSControl
|
|||
as of v4.12.0 and may have bugs. Please test carefully.
|
||||
{% endhint %}
|
||||
|
||||
|
||||
Conversion mode:
|
||||
### Conversion mode:
|
||||
|
||||
DNSControl can convert from old-style redirects (Page Rules) to new-style
|
||||
redirect (Single Redirects). To enable this mode, set both `manage_redirects`
|
||||
|
@ -268,7 +268,7 @@ via the CloudFlare control panel or wait for Cloudflare to remove support for th
|
|||
|
||||
{% hint style="warning" %}
|
||||
Cloudflare's announcement says that they will convert old-style redirects (Page Rules) to new-style
|
||||
redirect (Single Redirects) but they do not give a date for when this will happen. DNSControl
|
||||
redirect (Single Redirects) but they do not give an exact date for when this will happen. DNSControl
|
||||
will probably see these new redirects as foreign and delete them.
|
||||
|
||||
Therefore it is probably safer to do the conversion ahead of them.
|
||||
|
@ -279,6 +279,54 @@ than DNSControl's. However there's no way for DNSControl to manage them since t
|
|||
If you have suggestions on how to handle this better please file a bug.
|
||||
{% endhint %}
|
||||
|
||||
### Converting to CF_SINGLE_REDIRECT permanently
|
||||
|
||||
DNSControl will help convert `CF_REDIRECT`/`CF_TEMP_REDIRECT` statements into
|
||||
`CF_SINGLE_REDIRECT` statements. You might choose to do this if you do not want
|
||||
to rely on the automatic translation, or if you want to edit the results of the
|
||||
translation.
|
||||
|
||||
DNSControl will generate a file of the translated statements if you specify
|
||||
a filename using the `transcode_log` meta option.
|
||||
|
||||
```javascript
|
||||
var DSP_CLOUDFLARE = NewDnsProvider("cloudflare", {
|
||||
"manage_single_redirects": true,
|
||||
"transcode_log": "transcode.log",
|
||||
});
|
||||
```
|
||||
|
||||
After running `dnscontrol preview` the contents will look something like this:
|
||||
|
||||
{% code title="transcode.log" %}
|
||||
```text
|
||||
D("example.com", ...
|
||||
CF_SINGLE_REDIRECT("1,302,https://example.com/*,https://replacement.example.com/$1",
|
||||
302,
|
||||
'http.host eq "example.com"',
|
||||
'concat("https://replacement.example.com", http.request.uri.path)'
|
||||
),
|
||||
CF_SINGLE_REDIRECT("2,302,https://img.example.com/*,https://replacement.example.com/$1",
|
||||
302,
|
||||
'http.host eq "img.example.com"',
|
||||
'concat("https://replacement.example.com", http.request.uri.path)'
|
||||
),
|
||||
CF_SINGLE_REDIRECT("3,302,https://i.example.com/*,https://replacement.example.com/$1",
|
||||
302,
|
||||
'http.host eq "i.example.com"',
|
||||
'concat("https://replacement.example.com", http.request.uri.path)'
|
||||
),
|
||||
D("otherdomain.com", ...
|
||||
CF_SINGLE_REDIRECT("1,301,https://one.otherdomain.com/,https://www.google.com/",
|
||||
301,
|
||||
'http.host eq "one.otherdomain.com" and http.request.uri.path eq "/"',
|
||||
'concat("https://www.google.com/", "")'
|
||||
),
|
||||
```
|
||||
{% endcode %}
|
||||
|
||||
Copying the statements to the proper place in `dnsconfig.js` is manual.
|
||||
|
||||
|
||||
## Redirects
|
||||
The Cloudflare provider can manage "Forwarding URL" Page Rules (redirects) for your domains. Simply use the `CF_REDIRECT` and `CF_TEMP_REDIRECT` functions to make redirects:
|
||||
|
|
|
@ -503,7 +503,7 @@ func cfSingleRedirectEnabled() bool {
|
|||
}
|
||||
|
||||
func cfSingleRedirect(name string, code any, when, then string) *models.RecordConfig {
|
||||
r := makeRec("@", name, "CLOUDFLAREAPI_SINGLE_REDIRECT")
|
||||
r := makeRec("@", name, cfsingleredirect.SINGLEREDIRECT)
|
||||
err := cfsingleredirect.FromRaw(r, []any{name, code, when, then})
|
||||
if err != nil {
|
||||
panic("Should not happen... cfSingleRedirect")
|
||||
|
@ -1947,6 +1947,7 @@ func makeTests() []*TestGroup {
|
|||
tc("changecode", cfSingleRedirect(`name1`, `302`, `http.host eq "cnn.slackoverflow.com"`, `concat("https://www.cnn.com", http.request.uri.path)`)),
|
||||
tc("changewhen", cfSingleRedirect(`name1`, `302`, `http.host eq "msnbc.slackoverflow.com"`, `concat("https://www.cnn.com", http.request.uri.path)`)),
|
||||
tc("changethen", cfSingleRedirect(`name1`, `302`, `http.host eq "msnbc.slackoverflow.com"`, `concat("https://www.msnbc.com", http.request.uri.path)`)),
|
||||
tc("changename", cfSingleRedirect(`name1bis`, `302`, `http.host eq "msnbc.slackoverflow.com"`, `concat("https://www.msnbc.com", http.request.uri.path)`)),
|
||||
),
|
||||
|
||||
// CLOUDFLAREAPI: PROXY
|
||||
|
|
|
@ -154,17 +154,18 @@ type CloudflareSingleRedirectConfig struct {
|
|||
//
|
||||
Code uint16 `json:"code,omitempty"` // 301 or 302
|
||||
// PR == PageRule
|
||||
PRDisplay string `json:"pr_display,omitempty"` // How is this displayed to the user
|
||||
PRWhen string `json:"pr_when,omitempty"`
|
||||
PRThen string `json:"pr_then,omitempty"`
|
||||
PRPriority int `json:"pr_priority,omitempty"` // Really an identifier for the rule.
|
||||
PRDisplay string `json:"pr_display,omitempty"` // How is this displayed to the user (SetTarget) for CF_REDIRECT/CF_TEMP_REDIRECT
|
||||
//
|
||||
// SR == SingleRedirect
|
||||
SRDisplay string `json:"sr_display,omitempty"` // How is this displayed to the user
|
||||
SRName string `json:"sr_name,omitempty"` // How is this displayed to the user
|
||||
SRWhen string `json:"sr_when,omitempty"`
|
||||
SRThen string `json:"sr_then,omitempty"`
|
||||
SRRRulesetID string `json:"sr_rulesetid,omitempty"`
|
||||
SRRRulesetRuleID string `json:"sr_rulesetruleid,omitempty"`
|
||||
SRDisplay string `json:"sr_display,omitempty"` // How is this displayed to the user (SetTarget) for CF_SINGLE_REDIRECT
|
||||
}
|
||||
|
||||
// MarshalJSON marshals RecordConfig.
|
||||
|
|
|
@ -38,6 +38,7 @@ func PostProcess(domains []*models.DomainConfig) error {
|
|||
|
||||
case "CLOUDFLAREAPI_SINGLE_REDIRECT":
|
||||
err = cfsingleredirect.FromRaw(rec, rawRec.Args)
|
||||
rec.SetLabel("@", dc.Name)
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("unknown rawrec type=%q", rawRec.Type)
|
||||
|
|
|
@ -87,23 +87,17 @@ type cloudflareProvider struct {
|
|||
cfClient *cloudflare.API
|
||||
//
|
||||
manageSingleRedirects bool // New "Single Redirects"-style redirects.
|
||||
//
|
||||
// Used by
|
||||
tcLogFilename string // Transcode Log file name
|
||||
tcLogFh *os.File // Transcode Log file handle
|
||||
tcZone string // Transcode Current zone
|
||||
|
||||
sync.Mutex // Protects all access to the following fields:
|
||||
domainIndex map[string]string // Cache of zone name to zone ID.
|
||||
nameservers map[string][]string // Cache of zone name to list of nameservers.
|
||||
}
|
||||
|
||||
// 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
|
||||
//}
|
||||
|
||||
// GetNameservers returns the nameservers for a domain.
|
||||
func (c *cloudflareProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
|
||||
|
||||
|
@ -162,17 +156,6 @@ func (c *cloudflareProvider) GetZoneRecords(domain string, meta map[string]strin
|
|||
}
|
||||
}
|
||||
|
||||
// // 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 { // if old
|
||||
prs, err := c.getPageRules(domainID, domain)
|
||||
if err != nil {
|
||||
|
@ -183,14 +166,11 @@ func (c *cloudflareProvider) GetZoneRecords(domain string, meta map[string]strin
|
|||
|
||||
if c.manageSingleRedirects { // if new xor old
|
||||
// Download the list of Single Redirects.
|
||||
// For each one, generate a CLOUDFLAREAPI_SINGLE_REDIRECT record
|
||||
// Append these records to `records`
|
||||
// For each one, generate a SINGLEREDIRECT record
|
||||
prs, err := c.getSingleRedirects(domainID, domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//printer.Printf("DEBUG: Single Redirects")
|
||||
//fmt.Fprintf(os.Stdout, "DEBUG: Single Redirects")
|
||||
records = append(records, prs...)
|
||||
}
|
||||
|
||||
|
@ -284,8 +264,6 @@ func (c *cloudflareProvider) GetZoneRecordsCorrections(dc *models.DomainConfig,
|
|||
case diff2.DELETE:
|
||||
deleteRec := inst.Old[0]
|
||||
deleteRecType := deleteRec.Type
|
||||
//deleteRecOrig := deleteRec.Original
|
||||
//corrs = c.mkDeleteCorrection(deleteRecType, deleteRecOrig, domainID, msg)
|
||||
corrs = c.mkDeleteCorrection(deleteRecType, deleteRec, domainID, msg)
|
||||
// DS records must always have a corresponding NS record.
|
||||
// Therefore, we remove DS records before any NS records.
|
||||
|
@ -344,10 +322,7 @@ func (c *cloudflareProvider) mkCreateCorrection(newrec *models.RecordConfig, dom
|
|||
Msg: msg,
|
||||
F: func() error { return c.createWorkerRoute(domainID, newrec.GetTargetField()) },
|
||||
}}
|
||||
case "CLOUDFLAREAPI_SINGLE_REDIRECT":
|
||||
//fmt.Printf("DEBUG: mkCreateSingleRedir: newrec=%+v\n", *newrec)
|
||||
//fmt.Printf("DEBUG: mkCreateSingleRedir: crn=%+v\n", (*newrec).CloudflareRedirect)
|
||||
//fmt.Printf("DEBUG: mkCreateSingleRedir: cr=%+v\n", (*newrec).CloudflareRedirect)
|
||||
case cfsingleredirect.SINGLEREDIRECT:
|
||||
return []*models.Correction{{
|
||||
Msg: msg,
|
||||
F: func() error {
|
||||
|
@ -367,8 +342,7 @@ func (c *cloudflareProvider) mkChangeCorrection(oldrec, newrec *models.RecordCon
|
|||
idTxt = oldrec.Original.(cloudflare.PageRule).ID
|
||||
case "WORKER_ROUTE":
|
||||
idTxt = oldrec.Original.(cloudflare.WorkerRoute).ID
|
||||
case "CLOUDFLAREAPI_SINGLE_REDIRECT":
|
||||
//idTxt = oldrec.Original.(cloudflare.RulesetRule).ID
|
||||
case cfsingleredirect.SINGLEREDIRECT:
|
||||
idTxt = oldrec.CloudflareRedirect.SRRRulesetID
|
||||
default:
|
||||
idTxt = oldrec.Original.(cloudflare.DNSRecord).ID
|
||||
|
@ -383,7 +357,7 @@ func (c *cloudflareProvider) mkChangeCorrection(oldrec, newrec *models.RecordCon
|
|||
return c.updatePageRule(idTxt, domainID, *newrec.CloudflareRedirect)
|
||||
},
|
||||
}}
|
||||
case "CLOUDFLAREAPI_SINGLE_REDIRECT":
|
||||
case cfsingleredirect.SINGLEREDIRECT:
|
||||
return []*models.Correction{{
|
||||
Msg: msg,
|
||||
F: func() error {
|
||||
|
@ -416,7 +390,7 @@ func (c *cloudflareProvider) mkDeleteCorrection(recType string, origRec *models.
|
|||
idTxt = origRec.Original.(cloudflare.PageRule).ID
|
||||
case "WORKER_ROUTE":
|
||||
idTxt = origRec.Original.(cloudflare.WorkerRoute).ID
|
||||
case "CLOUDFLAREAPI_SINGLE_REDIRECT":
|
||||
case cfsingleredirect.SINGLEREDIRECT:
|
||||
idTxt = origRec.Original.(cloudflare.RulesetRule).ID
|
||||
default:
|
||||
idTxt = origRec.Original.(cloudflare.DNSRecord).ID
|
||||
|
@ -431,12 +405,7 @@ func (c *cloudflareProvider) mkDeleteCorrection(recType string, origRec *models.
|
|||
return c.deletePageRule(origRec.Original.(cloudflare.PageRule).ID, domainID)
|
||||
case "WORKER_ROUTE":
|
||||
return c.deleteWorkerRoute(origRec.Original.(cloudflare.WorkerRoute).ID, domainID)
|
||||
case "CLOUDFLAREAPI_SINGLE_REDIRECT":
|
||||
//o := origRec.Original.(cloudflare.Ruleset)
|
||||
//printer.Printf("DEBUG: DELETE %+v\n", o)
|
||||
// printer.Printf("DEBUG: DELETE ID = %+v\n", o.ID)
|
||||
// printer.Printf("DEBUG: DELETE ACTION %+v\n", o.ActionParameters)
|
||||
// printer.Printf("DEBUG: DELETE FROMVALUE %+v\n", o.ActionParameters.FromValue)
|
||||
case cfsingleredirect.SINGLEREDIRECT:
|
||||
return c.deleteSingleRedirects(domainID, *origRec.CloudflareRedirect)
|
||||
default:
|
||||
return c.deleteDNSRecord(origRec.Original.(cloudflare.DNSRecord), domainID)
|
||||
|
@ -530,7 +499,7 @@ func (c *cloudflareProvider) preprocessConfig(dc *models.DomainConfig) error {
|
|||
// A and CNAMEs: Validate. If null, set to default.
|
||||
// else: Make sure it wasn't set. Set to default.
|
||||
// iterate backwards so first defined page rules have highest priority
|
||||
currentPrPrio := 1
|
||||
prPriority := 0
|
||||
for i := len(dc.Records) - 1; i >= 0; i-- {
|
||||
rec := dc.Records[i]
|
||||
if rec.Metadata == nil {
|
||||
|
@ -564,9 +533,7 @@ func (c *cloudflareProvider) preprocessConfig(dc *models.DomainConfig) error {
|
|||
}
|
||||
}
|
||||
|
||||
// CF_REDIRECT record types. Encode target as
|
||||
// $FROM,$TO,$PRIO,$CODE or build Cfsr struct for new-style
|
||||
// (Single Redirect) versions.
|
||||
// CF_REDIRECT record types:
|
||||
if rec.Type == "CF_REDIRECT" || rec.Type == "CF_TEMP_REDIRECT" {
|
||||
if !c.manageRedirects && !c.manageSingleRedirects {
|
||||
return fmt.Errorf("you must add 'manage_single_redirects: true' metadata to cloudflare provider to use CF_REDIRECT/CF_TEMP_REDIRECT records")
|
||||
|
@ -576,65 +543,49 @@ func (c *cloudflareProvider) preprocessConfig(dc *models.DomainConfig) error {
|
|||
code = 302
|
||||
}
|
||||
|
||||
if c.manageRedirects && !c.manageSingleRedirects {
|
||||
// Old-Style only. Convert this record to PAGE_RULE.
|
||||
//printer.Printf("DEBUG: prepro() target=%q\n", rec.GetTargetField())
|
||||
sr, err := cfsingleredirect.FromUserInput(rec.GetTargetField(), code, currentPrPrio)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fixPageRule(rec, sr)
|
||||
currentPrPrio++
|
||||
} else if !c.manageRedirects && c.manageSingleRedirects {
|
||||
// New-Style only. Convert this record to a CLOUDFLAREAPI_SINGLE_REDIRECT.
|
||||
sr, err := cfsingleredirect.FromUserInput(rec.GetTargetField(), code, currentPrPrio)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = fixSingleRedirect(rec, sr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Both! Convert this record to PAGE_RULE and append an additional CLOUDFLAREAPI_SINGLE_REDIRECT.
|
||||
part := strings.SplitN(rec.GetTargetField(), ",", 2)
|
||||
prWhen, prThen := part[0], part[1]
|
||||
prPriority++
|
||||
|
||||
target := rec.GetTargetField()
|
||||
// Convert this record to a PAGE_RULE.
|
||||
cfsingleredirect.MakePageRule(rec, prPriority, code, prWhen, prThen)
|
||||
rec.SetLabel("@", dc.Name)
|
||||
|
||||
if c.manageRedirects && !c.manageSingleRedirects {
|
||||
// Old-Style only. No additional work needed.
|
||||
|
||||
} else if !c.manageRedirects && c.manageSingleRedirects {
|
||||
// New-Style only. Convert PAGE_RULE to SINGLEREDIRECT.
|
||||
cfsingleredirect.TranscodePRtoSR(rec)
|
||||
if err := c.LogTranscode(dc.Name, rec.CloudflareRedirect); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
// Both old-style and new-style enabled!
|
||||
// Retain the PAGE_RULE and append an additional SINGLEREDIRECT.
|
||||
|
||||
// make a copy:
|
||||
newRec, err := rec.Copy()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The copy becomes the CF SingleRedirect
|
||||
sr, err := cfsingleredirect.FromUserInput(target, code, currentPrPrio)
|
||||
if err != nil {
|
||||
cfsingleredirect.TranscodePRtoSR(rec)
|
||||
if err := c.LogTranscode(dc.Name, rec.CloudflareRedirect); err != nil {
|
||||
return err
|
||||
}
|
||||
err = fixSingleRedirect(newRec, sr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Append the copy to the end of the list.
|
||||
dc.Records = append(dc.Records, newRec)
|
||||
|
||||
// The original becomes the PAGE_RULE:
|
||||
sr, err = cfsingleredirect.FromUserInput(target, code, currentPrPrio)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fixPageRule(rec, sr)
|
||||
currentPrPrio++
|
||||
|
||||
// The original PAGE_RULE remains untouched.
|
||||
}
|
||||
|
||||
} else if rec.Type == "CLOUDFLAREAPI_SINGLE_REDIRECT" {
|
||||
// CLOUDFLAREAPI_SINGLE_REDIRECT record types.
|
||||
} else if rec.Type == cfsingleredirect.SINGLEREDIRECT {
|
||||
// SINGLEREDIRECT record types. Verify they are enabled.
|
||||
if !c.manageSingleRedirects {
|
||||
return fmt.Errorf("you must add 'manage_single_redirects: true' metadata to cloudflare provider to use CF_SINGLE__REDIRECT records")
|
||||
}
|
||||
// Nothing needs to be done.
|
||||
|
||||
} else if rec.Type == "CF_WORKER_ROUTE" {
|
||||
// CF_WORKER_ROUTE record types. Encode target as $PATTERN,$SCRIPT
|
||||
|
@ -672,20 +623,35 @@ func (c *cloudflareProvider) preprocessConfig(dc *models.DomainConfig) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func fixPageRule(rc *models.RecordConfig, sr *models.CloudflareSingleRedirectConfig) {
|
||||
rc.Type = "PAGE_RULE"
|
||||
rc.TTL = 1
|
||||
rc.SetTarget(sr.PRDisplay)
|
||||
rc.CloudflareRedirect = sr
|
||||
}
|
||||
func (c *cloudflareProvider) LogTranscode(zone string, redirect *models.CloudflareSingleRedirectConfig) error {
|
||||
// No filename? Don't log anything.
|
||||
filename := c.tcLogFilename
|
||||
if filename == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
func fixSingleRedirect(rc *models.RecordConfig, sr *models.CloudflareSingleRedirectConfig) error {
|
||||
rc.Type = "CLOUDFLAREAPI_SINGLE_REDIRECT"
|
||||
rc.TTL = 1
|
||||
rc.SetTarget(sr.SRDisplay)
|
||||
rc.CloudflareRedirect = sr
|
||||
// File not opened already? Open it.
|
||||
if c.tcLogFh == nil {
|
||||
f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.tcLogFh = f
|
||||
}
|
||||
fh := c.tcLogFh
|
||||
|
||||
err := cfsingleredirect.AddNewStyleFields(sr)
|
||||
// Output "D(zone)" if needed.
|
||||
var text string
|
||||
if c.tcZone != zone {
|
||||
text = fmt.Sprintf("D(%q, ...\n", zone)
|
||||
}
|
||||
c.tcZone = zone
|
||||
|
||||
// Generate the new command and output.
|
||||
text = text + fmt.Sprintf(" CF_SINGLE_REDIRECT(%q,\n %03d,\n '%s',\n '%s'\n ),\n",
|
||||
redirect.SRName, redirect.Code,
|
||||
redirect.SRWhen, redirect.SRThen)
|
||||
_, err := fh.WriteString(text)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -733,6 +699,7 @@ func newCloudflare(m map[string]string, metadata json.RawMessage) (providers.DNS
|
|||
ManageWorkers bool `json:"manage_workers"`
|
||||
//
|
||||
ManageSingleRedirects bool `json:"manage_single_redirects"` // New-style Dynamic "Single Redirects"
|
||||
TranscodeLogFilename string `json:"transcode_log"` // Log the PAGE_RULE conversions.
|
||||
}{}
|
||||
err := json.Unmarshal([]byte(metadata), parsedMeta)
|
||||
if err != nil {
|
||||
|
@ -740,6 +707,7 @@ func newCloudflare(m map[string]string, metadata json.RawMessage) (providers.DNS
|
|||
}
|
||||
api.manageSingleRedirects = parsedMeta.ManageSingleRedirects
|
||||
api.manageRedirects = parsedMeta.ManageRedirects
|
||||
api.tcLogFilename = parsedMeta.TranscodeLogFilename
|
||||
api.manageWorkers = parsedMeta.ManageWorkers
|
||||
// ignored_labels:
|
||||
api.ignoredLabels = append(api.ignoredLabels, parsedMeta.IgnoredLabels...)
|
||||
|
|
|
@ -286,38 +286,28 @@ func (c *cloudflareProvider) getSingleRedirects(id string, domain string) ([]*mo
|
|||
}
|
||||
return nil, fmt.Errorf("failed fetching redirect rule list cloudflare: %s (%T)", err, err)
|
||||
}
|
||||
//var rulelist []cloudflare.RulesetRule
|
||||
//rulelist = rules.Rules
|
||||
//rulelist := rules.Rules
|
||||
|
||||
//printer.Printf("DEBUG: rules %+v\n", rules)
|
||||
recs := []*models.RecordConfig{}
|
||||
for _, pr := range rules.Rules {
|
||||
//printer.Printf("DEBUG: %+v\n", pr)
|
||||
|
||||
var thisPr = pr
|
||||
r := &models.RecordConfig{
|
||||
Type: "CLOUDFLAREAPI_SINGLE_REDIRECT",
|
||||
Original: thisPr,
|
||||
TTL: 1,
|
||||
}
|
||||
r.SetLabel("@", domain)
|
||||
|
||||
// Extract the valuables from the rule, use it to make the sr:
|
||||
srName := pr.Description
|
||||
srWhen := pr.Expression
|
||||
srThen := pr.ActionParameters.FromValue.TargetURL.Expression
|
||||
code := uint16(pr.ActionParameters.FromValue.StatusCode)
|
||||
sr := cfsingleredirect.FromAPIData(srWhen, srThen, code)
|
||||
//sr.SRRRuleList = rulelist
|
||||
//printer.Printf("DEBUG: DESCRIPTION = %v\n", pr.Description)
|
||||
sr.SRDisplay = pr.Description
|
||||
// printer.Printf("DEBUG: PR = %+v\n", pr)
|
||||
// printer.Printf("DEBUG: rules = %+v\n", rules)
|
||||
sr.SRRRulesetID = rules.ID
|
||||
sr.SRRRulesetRuleID = pr.ID //correct
|
||||
|
||||
r.CloudflareRedirect = sr
|
||||
r.SetTarget(pr.Description)
|
||||
cfsingleredirect.MakeSingleRedirectFromAPI(r, code, srName, srWhen, srThen)
|
||||
r.SetLabel("@", domain)
|
||||
|
||||
// Store the IDs
|
||||
sr := r.CloudflareRedirect
|
||||
sr.SRRRulesetID = rules.ID
|
||||
sr.SRRRulesetRuleID = pr.ID
|
||||
|
||||
recs = append(recs, r)
|
||||
}
|
||||
|
@ -327,9 +317,6 @@ func (c *cloudflareProvider) getSingleRedirects(id string, domain string) ([]*mo
|
|||
|
||||
func (c *cloudflareProvider) createSingleRedirect(domainID string, cfr models.CloudflareSingleRedirectConfig) error {
|
||||
|
||||
//printer.Printf("DEBUG: createSingleRedir: d=%v crf=%+v\n", domainID, cfr)
|
||||
// Asumption for target:
|
||||
|
||||
newSingleRedirectRulesActionParameters := cloudflare.RulesetRuleActionParameters{}
|
||||
newSingleRedirectRule := cloudflare.RulesetRule{}
|
||||
newSingleRedirectRules := []cloudflare.RulesetRule{}
|
||||
|
@ -347,7 +334,8 @@ func (c *cloudflareProvider) createSingleRedirect(domainID string, cfr models.Cl
|
|||
// Redirect expression
|
||||
newSingleRedirectRulesActionParameters.FromValue.TargetURL.Expression = cfr.SRThen
|
||||
// Redirect name
|
||||
newSingleRedirectRules[0].Description = cfr.SRDisplay
|
||||
newSingleRedirectRules[0].Description = cfr.SRName
|
||||
|
||||
// Rule action, should always be redirect in this case
|
||||
newSingleRedirectRules[0].Action = "redirect"
|
||||
// Phase should always be http_request_dynamic_redirect
|
||||
|
@ -401,7 +389,7 @@ func (c *cloudflareProvider) deleteSingleRedirects(domainID string, cfr models.C
|
|||
//printer.Printf("DEBUG: CALLING API DeleteRulesetRule: SRRRulesetID=%v, cfr.SRRRulesetRuleID=%v\n", cfr.SRRRulesetID, cfr.SRRRulesetRuleID)
|
||||
|
||||
err := c.cfClient.DeleteRulesetRule(context.Background(), cloudflare.ZoneIdentifier(domainID), cfr.SRRRulesetID, cfr.SRRRulesetRuleID)
|
||||
// TODO(tlim): This is terrible. It returns an error even when it is successful.
|
||||
// NB(tlim): Yuck. This returns an error even when it is successful. Dig into the JSON for the real status.
|
||||
if strings.Contains(err.Error(), `"success": true,`) {
|
||||
return nil
|
||||
}
|
||||
|
@ -410,13 +398,9 @@ func (c *cloudflareProvider) deleteSingleRedirects(domainID string, cfr models.C
|
|||
}
|
||||
|
||||
func (c *cloudflareProvider) updateSingleRedirect(domainID string, oldrec, newrec *models.RecordConfig) error {
|
||||
// rulesetID := cfr.SRRRulesetID
|
||||
// rulesetRuleID := cfr.SRRRulesetRuleID
|
||||
//printer.Printf("DEBUG: UPDATE-DEL domID=%v sr=%+v\n", domainID, cfr)
|
||||
if err := c.deleteSingleRedirects(domainID, *oldrec.CloudflareRedirect); err != nil {
|
||||
return err
|
||||
}
|
||||
//printer.Printf("DEBUG: UPDATE-CREATE domID=%v sr=%+v\n", domainID, newrec.CloudflareRedirect)
|
||||
return c.createSingleRedirect(domainID, *newrec.CloudflareRedirect)
|
||||
}
|
||||
|
||||
|
@ -437,24 +421,17 @@ func (c *cloudflareProvider) getPageRules(id string, domain string) ([]*models.R
|
|||
value := pr.Actions[0].Value.(map[string]interface{})
|
||||
var thisPr = pr
|
||||
r := &models.RecordConfig{
|
||||
Type: "PAGE_RULE",
|
||||
Original: thisPr,
|
||||
TTL: 1,
|
||||
}
|
||||
r.SetLabel("@", domain)
|
||||
code := intZero(value["status_code"])
|
||||
raw := fmt.Sprintf("%s,%s,%d,%d", // $FROM,$TO,$PRIO,$CODE
|
||||
pr.Targets[0].Constraint.Value,
|
||||
value["url"],
|
||||
pr.Priority,
|
||||
code)
|
||||
r.SetTarget(raw)
|
||||
|
||||
cr, err := cfsingleredirect.FromUserInput(raw, code, pr.Priority)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.CloudflareRedirect = cr
|
||||
code := intZero(value["status_code"])
|
||||
|
||||
when := pr.Targets[0].Constraint.Value
|
||||
then := value["url"].(string)
|
||||
currentPrPrio := pr.Priority
|
||||
|
||||
cfsingleredirect.MakePageRule(r, currentPrPrio, code, when, then)
|
||||
r.SetLabel("@", domain)
|
||||
|
||||
recs = append(recs, r)
|
||||
}
|
||||
|
@ -492,7 +469,6 @@ func (c *cloudflareProvider) createPageRule(domainID string, cfr models.Cloudfla
|
|||
}},
|
||||
},
|
||||
}
|
||||
//printer.Printf("DEBUG: createPageRule pr=%+v\n", pr)
|
||||
_, err := c.cfClient.CreatePageRule(context.Background(), domainID, pr)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -7,10 +7,14 @@ import (
|
|||
"github.com/StackExchange/dnscontrol/v4/pkg/rtypecontrol"
|
||||
)
|
||||
|
||||
// SINGLEREDIRECT is the string name for this rType.
|
||||
const SINGLEREDIRECT = "CLOUDFLAREAPI_SINGLE_REDIRECT"
|
||||
|
||||
func init() {
|
||||
rtypecontrol.Register("CLOUDFLAREAPI_SINGLE_REDIRECT")
|
||||
rtypecontrol.Register(SINGLEREDIRECT)
|
||||
}
|
||||
|
||||
// FromRaw convert RecordConfig using data from a RawRecordConfig's parameters.
|
||||
func FromRaw(rc *models.RecordConfig, items []any) error {
|
||||
|
||||
// Validate types.
|
||||
|
@ -23,17 +27,14 @@ func FromRaw(rc *models.RecordConfig, items []any) error {
|
|||
var code uint16
|
||||
|
||||
name = items[0].(string)
|
||||
|
||||
code = items[1].(uint16)
|
||||
if code != 301 && code != 302 {
|
||||
return fmt.Errorf("code (%03d) is not 301 or 302", code)
|
||||
}
|
||||
when = items[2].(string)
|
||||
then = items[3].(string)
|
||||
|
||||
when, then = items[2].(string), items[3].(string)
|
||||
|
||||
rc.Name = name
|
||||
rc.CloudflareRedirect = FromAPIData(when, then, code)
|
||||
rc.SetTarget(rc.CloudflareRedirect.SRDisplay)
|
||||
makeSingleRedirectFromRawRec(rc, code, name, when, then)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -9,51 +9,29 @@ import (
|
|||
"github.com/StackExchange/dnscontrol/v4/models"
|
||||
)
|
||||
|
||||
func FromUserInput(target string, code uint16, priority int) (*models.CloudflareSingleRedirectConfig, error) {
|
||||
// target: matcher,replacement,priority,code
|
||||
// target: cable.slackoverflow.com/*,https://change.cnn.com/$1,1,302
|
||||
|
||||
r := &models.CloudflareSingleRedirectConfig{}
|
||||
|
||||
// Break apart the 4-part string and store into the individual fields:
|
||||
parts := strings.Split(target, ",")
|
||||
//printer.Printf("DEBUG: cfsrFromOldStyle: parts=%v\n", parts)
|
||||
r.PRDisplay = fmt.Sprintf("%s,%d,%03d", target, priority, code)
|
||||
r.PRWhen = parts[0]
|
||||
r.PRThen = parts[1]
|
||||
r.PRPriority = priority
|
||||
r.Code = code
|
||||
|
||||
// Convert old-style to new-style:
|
||||
if err := AddNewStyleFields(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// AddNewStyleFields takes a PAGE_RULE-style target and populates the CFSRC.
|
||||
func AddNewStyleFields(sr *models.CloudflareSingleRedirectConfig) error {
|
||||
// TranscodePRtoSR takes a PAGE_RULE record, stores transcoded versions of the fields, and makes the record a CLOUDFLAREAPI_SINGLE_REDDIRECT.
|
||||
func TranscodePRtoSR(rec *models.RecordConfig) error {
|
||||
rec.Type = SINGLEREDIRECT // This record is now a CLOUDFLAREAPI_SINGLE_REDIRECT
|
||||
|
||||
// Extract the fields we're reading from:
|
||||
sr := rec.CloudflareRedirect
|
||||
code := sr.Code
|
||||
prWhen := sr.PRWhen
|
||||
prThen := sr.PRThen
|
||||
code := sr.Code
|
||||
srName := sr.PRDisplay
|
||||
|
||||
// Convert old-style patterns to new-style rules:
|
||||
srWhen, srThen, err := makeRuleFromPattern(prWhen, prThen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
display := fmt.Sprintf(`%s,%s,%d,%03d matcher=%s replacement=%s`,
|
||||
prWhen, prThen,
|
||||
sr.PRPriority, code,
|
||||
srWhen, srThen,
|
||||
)
|
||||
|
||||
// Store the results in the fields we're writing to:
|
||||
sr.SRWhen = srWhen
|
||||
sr.SRThen = srThen
|
||||
sr.SRDisplay = display
|
||||
// Fix the RecordConfig
|
||||
makeSingleRedirectFromConvert(rec,
|
||||
sr.PRPriority,
|
||||
prWhen, prThen,
|
||||
code,
|
||||
srName, srWhen, srThen)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -94,15 +94,6 @@ func Test_makeSingleDirectRule(t *testing.T) {
|
|||
wantExpr: `concat("https://survey.stackoverflow.co/2021", "")`,
|
||||
wantErr: false,
|
||||
},
|
||||
// {
|
||||
// name: "27",
|
||||
// pattern: "*www.stackoverflow.help/*",
|
||||
// replace: "https://stackoverflow.help/$1",
|
||||
/// FIXME(tlim): Should "$1" should be a "$2"? See dnsconfig.js:4344
|
||||
// wantMatch: `FIXME`,
|
||||
// wantExpr: `FIXME`,
|
||||
// wantErr: false,
|
||||
// },
|
||||
{
|
||||
name: "28",
|
||||
pattern: "*stackoverflow.help/support/solutions/articles/36000241656-write-an-article",
|
||||
|
|
122
providers/cloudflare/rtypes/cfsingleredirect/from.go
Normal file
122
providers/cloudflare/rtypes/cfsingleredirect/from.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
package cfsingleredirect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v4/models"
|
||||
)
|
||||
|
||||
// MakePageRule updates a RecordConfig to be a PAGE_RULE using PAGE_RULE data.
|
||||
func MakePageRule(rc *models.RecordConfig, priority int, code uint16, when, then string) {
|
||||
display := mkPageRuleBlob(priority, code, when, then)
|
||||
|
||||
rc.Type = "PAGE_RULE"
|
||||
rc.TTL = 1
|
||||
rc.CloudflareRedirect = &models.CloudflareSingleRedirectConfig{
|
||||
Code: code,
|
||||
//
|
||||
PRWhen: when,
|
||||
PRThen: then,
|
||||
PRPriority: priority,
|
||||
PRDisplay: display,
|
||||
}
|
||||
rc.SetTarget(display)
|
||||
}
|
||||
|
||||
// mkPageRuleBlob creates the 1,301,when,then string used in displays.
|
||||
func mkPageRuleBlob(priority int, code uint16, when, then string) string {
|
||||
return fmt.Sprintf("%d,%03d,%s,%s", priority, code, when, then)
|
||||
}
|
||||
|
||||
// makeSingleRedirectFromRawRec updates a RecordConfig to be a
|
||||
// SINGLEREDIRECT using the data from a RawRecord.
|
||||
func makeSingleRedirectFromRawRec(rc *models.RecordConfig, code uint16, name, when, then string) {
|
||||
target := targetFromRaw(name, code, when, then)
|
||||
|
||||
rc.Type = SINGLEREDIRECT
|
||||
rc.TTL = 1
|
||||
rc.CloudflareRedirect = &models.CloudflareSingleRedirectConfig{
|
||||
Code: code,
|
||||
//
|
||||
PRWhen: "UNKNOWABLE",
|
||||
PRThen: "UNKNOWABLE",
|
||||
PRPriority: 0,
|
||||
PRDisplay: "UNKNOWABLE",
|
||||
//
|
||||
SRName: name,
|
||||
SRWhen: when,
|
||||
SRThen: then,
|
||||
SRDisplay: target,
|
||||
}
|
||||
rc.SetTarget(rc.CloudflareRedirect.SRDisplay)
|
||||
}
|
||||
|
||||
// targetFromRaw create the display text used for a normal Redirect.
|
||||
func targetFromRaw(name string, code uint16, when, then string) string {
|
||||
return fmt.Sprintf("%s code=(%03d) when=(%s) then=(%s)",
|
||||
name,
|
||||
code,
|
||||
when,
|
||||
then,
|
||||
)
|
||||
}
|
||||
|
||||
// MakeSingleRedirectFromAPI updatese a RecordConfig to be a SINGLEREDIRECT using data downloaded via the API.
|
||||
func MakeSingleRedirectFromAPI(rc *models.RecordConfig, code uint16, name, when, then string) {
|
||||
// The target is the same as the name. It is the responsibility of the record creator to name it something diffable.
|
||||
target := targetFromAPIData(name, code, when, then)
|
||||
|
||||
rc.Type = SINGLEREDIRECT
|
||||
rc.TTL = 1
|
||||
rc.CloudflareRedirect = &models.CloudflareSingleRedirectConfig{
|
||||
Code: code,
|
||||
//
|
||||
PRWhen: "UNKNOWABLE",
|
||||
PRThen: "UNKNOWABLE",
|
||||
PRPriority: 0,
|
||||
PRDisplay: "UNKNOWABLE",
|
||||
//
|
||||
SRName: name,
|
||||
SRWhen: when,
|
||||
SRThen: then,
|
||||
SRDisplay: target,
|
||||
}
|
||||
rc.SetTarget(rc.CloudflareRedirect.SRDisplay)
|
||||
}
|
||||
|
||||
// targetFromAPIData creates the display text used for a Redirect as received from Cloudflare's API.
|
||||
func targetFromAPIData(name string, code uint16, when, then string) string {
|
||||
return fmt.Sprintf("%s code=(%03d) when=(%s) then=(%s)",
|
||||
name,
|
||||
code,
|
||||
when,
|
||||
then,
|
||||
)
|
||||
}
|
||||
|
||||
// makeSingleRedirectFromConvert updates a RecordConfig to be a SINGLEREDIRECT using data from a PAGE_RULE conversion.
|
||||
func makeSingleRedirectFromConvert(rc *models.RecordConfig,
|
||||
priority int,
|
||||
prWhen, prThen string,
|
||||
code uint16,
|
||||
srName, srWhen, srThen string) {
|
||||
|
||||
srDisplay := targetFromConverted(priority, code, prWhen, prThen, srWhen, srThen)
|
||||
|
||||
rc.Type = SINGLEREDIRECT
|
||||
rc.TTL = 1
|
||||
sr := rc.CloudflareRedirect
|
||||
sr.Code = code
|
||||
|
||||
sr.SRName = srName
|
||||
sr.SRWhen = srWhen
|
||||
sr.SRThen = srThen
|
||||
sr.SRDisplay = srDisplay
|
||||
|
||||
rc.SetTarget(rc.CloudflareRedirect.SRDisplay)
|
||||
}
|
||||
|
||||
// targetFromConverted makes the display text used when a redirect was the result of converting a PAGE_RULE.
|
||||
func targetFromConverted(prPriority int, code uint16, prWhen, prThen, srWhen, srThen string) string {
|
||||
return fmt.Sprintf("%d,%03d,%s,%s code=(%03d) when=(%s) then=(%s)", prPriority, code, prWhen, prThen, code, srWhen, srThen)
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package cfsingleredirect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v4/models"
|
||||
)
|
||||
|
||||
func FromAPIData(sm, sr string, code uint16) *models.CloudflareSingleRedirectConfig {
|
||||
r := &models.CloudflareSingleRedirectConfig{
|
||||
PRWhen: "UNKNOWABLE",
|
||||
PRThen: "UNKNOWABLE",
|
||||
Code: code,
|
||||
SRDisplay: fmt.Sprintf("code=%03d when=(%v) then=(%v)", code, sm, sr),
|
||||
SRWhen: sm,
|
||||
SRThen: sr,
|
||||
}
|
||||
return r
|
||||
}
|
Loading…
Reference in a new issue