Cloudflare dynamic dns not working
-
This post is deleted! -
You should be able to split the domain out (the "z" parameter) from the host with a bit of separating-the-dots code. That will provide that parameter from the variable you already have.
The tricky part looks to be the "id" parameter - do you have to do a "rec_load_all call" to get all your records back, loop through it and find the "id" of the record you want to update?
Don't they have a simpler way? It seems to me that record type, record name, content, user name, password, other record parameters (ttl…) should be enough for them to match and update a record. -
Im going to work on getting a correct URL tonight with reference to the new API and see if I can get this updating with at least a custom Dynamic DNS entry.
Will see how I go.
EDIT:
After attempting this I am receiving the following back from the API:
You must include an `a' parameter, with a value of wl|chl|nul|ban|comm_news|devmode|sec_lvl|ipv46|ob|cache_lvl|fpurge_ts|async|mirage2|img.q|minify|stats|zone_check|zone_ips|zone_ss|vote_ss|app|ip_lkup|set|app_req|app_req_list|app_version|custom_cert_set|custom_cert_purge|custom_cert_load_multi|custom_cert_load|ersubmit|zoneupload|user_notification_remove|pref_set|zone_file_purge|zone_file_refresh|zone_settings
Additionally, email and tkn parameters are required.It's as if the paremeters for "a" are not being recognised by the API, these may have changed from what is documented in the API documentation?
Either thaat or I am doing something wrong.
-
There seems to be some confusion on what the valid parameters are for the cloudflare client api. So just to help get this ball moving in the right direction here are the relevant parts of the api.
Link to full api - https://www.cloudflare.com/docs/client-api.html#s5.22 - Client Operations
All GET/POST requests should be directed at the client gateway interface, located at:https://www.cloudflare.com/api_json.html
2.1 - Basic Parameters
Every GET/POST request must include at the following basic parameter(s):"tkn" - This is the API key made available on your Account page.
"email" - The e-mail address associated with the API key.
"a" - To define which request is being made, the client should POST an "a" parameter. The "a" specifies which action you'd like to perform. Specific actions are described in Section 3 below.5.2 - "rec_edit" – Edit a DNS record
Edit a DNS record for a zone. The record will be updated to the data passed through arguments here.Input:
Requires the basic parameters described in Section 2.1 of this document. In addition you must pass the following parameters:"z" - The target domain
"type" - Type of DNS record. Values include: [A/CNAME/MX/TXT/SPF/AAAA/NS/SRV/LOC]
"id" - DNS Record ID. Available by using the rec_load_all call.
"name" - Name of the DNS record.
"content" - The content of the DNS record, will depend on the the type of record being added
"ttl" - TTL of record in seconds. 1 = Automatic, otherwise, value must in between 120 and 86400 seconds.
"service_mode"[applies to A/AAAA/CNAME] - Status of CloudFlare Proxy, 1 = orange cloud, 0 = grey cloud.The minimum amount of information that you would need to provide an update for foo.bar.com with the current IPv4 address is:
tkn=CLOUDFLAREAPI
email=me@bar.com
a=rec_edit
z=bar.com
type=A
name=foo
content=%IP%To manually update the IP for foo.bar.com you should be able to run the following -
curl https://www.cloudflare.com/api_json.html?a=rec_edit&tkn=CLOUDFLAREAPI&email=me@bar.com&a=rec_edit&z=bar.com&type=A&name=foo&content=%IP% -
You must include an `a' parameter, with a value of wl|chl|nul|ban|comm_news|devmode|sec_lvl|ipv46|ob|cache_lvl|fpurge_ts|async|mirage2|img.q|minify|stats|zone_check|zone_ips|zone_ss|vote_ss|app|ip_lkup|set|app_req|app_req_list|app_version|custom_cert_set|custom_cert_purge|custom_cert_load_multi|custom_cert_load|ersubmit|zoneupload|user_notification_remove|pref_set|zone_file_purge|zone_file_refresh|zone_settings Additionally, email and tkn parameters are required.
That message that comes back interactively doesn ot mention "rec_edit" at all. Someone who uses Cloudflare should tell them that the message is old/out-of-date - because that causes confusion to people trying to get their client working!
curl https://www.cloudflare.com/api_json.html?a=rec_edit&tkn=CLOUDFLAREAPI&email=me@bar.com&a=rec_edit&z=bar.com&type=A&name=foo&content=%IP%
"a=rec_edit" got repeated twice in there - I guess it will not break? But should not be necessary.
Someone post an actual successful update URL string (with their password "tkn" and domain name obfuscated) and then we can easily make the pfSense code do the correct thing.
-
For me it wasn't working either, because you need to add the record_id, which you need to request with the rec_load_all API function before.
I extended the dyndns.class in /etc/inc/, so now it's working for me.
I can now use the Web-Interface, albeit MX and Wildcards are not working.
It's a rather dirty fix imo, but it's working, for me ;D.Edit:
I'm running
2.2-RELEASE (amd64)
built on Thu Jan 22 14:03:54 CST 2015
FreeBSD 10.1-RELEASE-p4 -
Hello,
… So we have some new update for this planned... ? :-\
ps : for people like me who cannot wait for pfs cloudflare, try this => http://www.ubuntugeek.com/how-to-use-cloudflare-as-a-ddclient-provider-under-ubuntu.html ... It works! :D
-
Thank you. Great first post & welcome to the forums! Works for me too, hopefully this will get rolled into pfSense soon.
For me it wasn't working either, because you need to add the record_id, which you need to request with the rec_load_all API function before.
I extended the dyndns.class in /etc/inc/, so now it's working for me.
I can now use the Web-Interface, albeit MX and Wildcards are not working.
It's a rather dirty fix imo, but it's working, for me ;D.Edit:
I'm running
2.2-RELEASE (amd64)
built on Thu Jan 22 14:03:54 CST 2015
FreeBSD 10.1-RELEASE-p4 -
I didn't like how much was hardcoded, so here is what I did to get it working on mine. I am currently running 2.2 but the fork/commit is based on the current master. The majority of this was from brainlesscurls: https://github.com/brainlesscurls/pfsense/commit/4f3ce17b98919e464044cbc983e31549b595bdc3
Here is the code that I created, it does not correctly handle the response coming back or check if cached differs at all:
https://github.com/pivconcto/pfsense/commit/40400bd172ae1097b5c0db540cf4dde20e9f78d7
-
I have sorted this not after a lot of research.
I stumbled across this website: http://blog.t-m-s.co.uk/ with a blog post with exactly our issue and solution with a very good explanation on the use of the CloudFlare API.
I recommend reading through this completely to understand what is going on.
After following the steps in the website and tweaking a few things to cater for my needs it's up and running beautifully.
-
I actually worked out a fix at the dyndns.class file:
CloudFlare now requires a "record ID" in order to update an address, for such I did another curl call before pfsense change of the IP in order to get the record ID from the target.
Also the CloudFlare URL is quite different from the one in the original file of pfSense.
if you open the /etc/inc/dyndns.class, line 607, the original case looks like:
case 'cloudflare':
$needsIP = TRUE;
$dnsServer ='www.cloudflare.com';
$dnsHost = str_replace(' ','', $this->_dnsHost);
$URL = "https://{$dnsServer}/api.html?a=DIUP&email={$this->_dnsUser}&tkn={$this->_dnsPass}&ip={$this->_dnsIP}&hosts={$dnsHost}";
curl_setopt($ch, CURLOPT_URL, $URL);
break;I change it to:
case 'cloudflare':
$needsIP = TRUE;
$dnsServer ='www.cloudflare.com';
list ($host,$zone) = split('.', $this->_dnsHost, 2);
$getid = "https://$dnsServer/api_json.html?a=rec_load_all&tkn={$this->_dnsPass}&email={$this->_dnsUser}&z=$zone";
$crl = curl_init();
$timeout = 5;
curl_setopt ($crl, CURLOPT_URL,$getid);
curl_setopt ($crl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt ($crl, CURLOPT_CONNECTTIMEOUT, $timeout);
$ids = curl_exec($crl);
curl_close($crl);
$json_ids = json_decode($ids, true);
$jsonIterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($json_ids),RecursiveIteratorIterator::SELF_FIRST);
$recid=0;
foreach ($jsonIterator as $key => $val) { if(!is_array($val)) { if ($key == "rec_id") { $recid=$val; } if ($key == "name" and $val == $this->_dnsHost) { break; } } }
$dnsHost = str_replace(' ','', $this->_dnsHost);
$URL = "https://{$dnsServer}/api_json.html?a=rec_edit&tkn={$this->_dnsPass}&email={$this->_dnsUser}&id=$recid&z=$zone&type=A&name=$host&content={$this->_dnsIP}&service_mode=1&ttl=1";
curl_setopt($ch, CURLOPT_URL, $URL);
break;In the Dynamic DNS interface all the information is pretty much the same, except the password must be the Token ID found at https://www.cloudflare.com/my-account ("Your API key is: ")
With this change he is automatically updating CloudFlare but the cached IP address still 0.0.0.0, something else must change to make this value also correct.
I hope this help to fix the problem for other people as well.
-
I actually worked out a fix at the dyndns.class file: CloudFlare now requires a "record ID" in order to update an address, for such I did another curl call before pfsense change of the IP in order to get the record ID from the target. Also the CloudFlare URL is quite different from the one in the original file of pfSense. if you open the /etc/inc/dyndns.class, line 607, the original case looks like: case 'cloudflare': $needsIP = TRUE; $dnsServer ='www.cloudflare.com'; $dnsHost = str_replace(' ','', $this->_dnsHost); $URL = "https://{$dnsServer}/api.html?a=DIUP&email={$this->_dnsUser}&tkn={$this->_dnsPass}&ip={$this->_dnsIP}&hosts={$dnsHost}"; curl_setopt($ch, CURLOPT_URL, $URL); break; I change it to: case 'cloudflare': $needsIP = TRUE; $dnsServer ='www.cloudflare.com'; list ($host,$zone) = split('\.', $this->_dnsHost, 2); $getid = "https://$dnsServer/api_json.html?a=rec_load_all&tkn={$this->_dnsPass}&email={$this->_dnsUser}&z=$zone"; $crl = curl_init(); $timeout = 5; curl_setopt ($crl, CURLOPT_URL,$getid); curl_setopt ($crl, CURLOPT_RETURNTRANSFER, 1); curl_setopt ($crl, CURLOPT_CONNECTTIMEOUT, $timeout); $ids = curl_exec($crl); curl_close($crl); $json_ids = json_decode($ids, true); $jsonIterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($json_ids),RecursiveIteratorIterator::SELF_FIRST); $recid=0; foreach ($jsonIterator as $key => $val) { if(!is_array($val)) { if ($key == "rec_id") { $recid=$val; } if ($key == "name" and $val == $this->_dnsHost) { break; } } } $dnsHost = str_replace(' ','', $this->_dnsHost); $URL = "https://{$dnsServer}/api_json.html?a=rec_edit&tkn={$this->_dnsPass}&email={$this->_dnsUser}&id=$recid&z=$zone&type=A&name=$host&content={$this->_dnsIP}&service_mode=1&ttl=1"; curl_setopt($ch, CURLOPT_URL, $URL); break; In the Dynamic DNS interface all the information is pretty much the same, except the password must be the Token ID found at https://www.cloudflare.com/my-account ("Your API key is: ") With this change he is automatically updating CloudFlare but the cached IP address still 0.0.0.0, something else must change to make this value also correct. I hope this help to fix the problem for other people as well.
I tried your fix but it just makes pfsense spit out crash reports and it still don't appear to be updating.I didn't delete my old cloudflare entry and start over so I assume that is what caused it to not load the changes. -
So after spending some time at this I came up with a working solution that uses CloudFlare's latest API. Be sure to use your account API key for the password field instead of your account password.
It updates the cached IP also and handles some basic errors.
Tested on 2.2.3-RELEASE 32 bit and 64 bit systems without errors.
If you choose "Diagnostics -> Edit File" then open the "/etc/inc/dyndns.class" file and edit the lines to the ones below.
Original
case 'cloudflare': $needsIP = TRUE; $dnsServer ='www.cloudflare.com'; $dnsHost = str_replace(' ','', $this->_dnsHost); $URL = "https://{$dnsServer}/api.html?a=DIUP&email={$this->_dnsUser}&tkn={$this->_dnsPass}&ip={$this->_dnsIP}&hosts={$dnsHost}"; curl_setopt($ch, CURLOPT_URL, $URL); break;
Patch
case 'cloudflare': $needsIP = TRUE; $dnsServer ='api.cloudflare.com'; $dnsHost = str_replace(' ','', $this->_dnsHost); $host_names = explode(".", $dnsHost); $bottom_host_name = $host_names[count($host_names)-2] . "." . $host_names[count($host_names)-1]; curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'X-Auth-Email: '.$this->_dnsUser.'', 'X-Auth-Key: '.$this->_dnsPass.'', 'Content-Type: application/json' )); // Get zone ID $getZoneId = "https://{$dnsServer}/client/v4/zones/?name={$bottom_host_name}"; curl_setopt($ch, CURLOPT_URL, $getZoneId); $output = json_decode(curl_exec($ch)); $zone = $output->result[0]->id; if ($zone){ // If zone ID was found get host ID $getHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records?name={$this->_dnsHost}"; curl_setopt($ch, CURLOPT_URL, $getHostId); $output = json_decode(curl_exec($ch)); $host = $output->result[0]->id; if ($host){ // If host ID was found update host $hostData = array( "content" => "{$this->_dnsIP}", "type" => "A", "name" => "{$this->_dnsHost}", "proxiable" => false, "proxied" => false ); $data_json = json_encode($hostData); $updateHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records/{$host}"; curl_setopt($ch, CURLOPT_URL, $updateHostId); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); curl_setopt($ch, CURLOPT_POSTFIELDS,$data_json); } } break;
Original
case 'cloudflare': // recieve multipe results $data = explode("\n",$data); $lines = count($data)-1; // loop over the lines for ($pos=0; ($successful_update || $pos == 0) && $pos < $lines; $pos++){ $resp = $data[$pos]; if (preg_match('/UAUTH/i', $resp)) { $status = "DynDNS: The username specified is not authorized to update this hostname and domain."; } else if (preg_match('/NOHOST/i', $resp)) { $status = "DynDNS: No valid FQDN (fully qualified domain name) was specified"; } else if (preg_match('/INVLDHST/i', $resp)) { $status = "DynDNS: An invalid hostname was specified. This may be due to the fact the hostname has not been created in the system. Creating new host names via clients is not supported."; } else if (preg_match('/INVLDIP/i', $resp)) { $status = "DynDNS: The IP address given is not valid."; } else if (preg_match('/DUPHST/i', $resp)) { $status = "DynDNS: Duplicate values exist for a record. Only single values for records are supported currently."; } else if (preg_match('/NOUPDATE/i', $resp)) { $status = "DynDNS: No changes made to the hostname (".strtok($resp,' ')."). Continual updates with no changes lead to blocked clients."; $successful_update = true; //success if it is the same so that it saves } else if (preg_match('/OK/i', $resp)) { $status = "DynDNS: (Success) (".strtok($resp,' ').") IP Address for Changed Successfully!"; $successful_update = true; } else { $status = "DynDNS: (Unknown Response)"; log_error("DynDNS: PAYLOAD: {$resp}"); $this->_debug($resp); } log_error($status); } break;
Patch
case 'cloudflare': $output = json_decode($data); if ($output->result->content === $this->_dnsIP){ $status = "DynDNS: (Success) {$this->_dnsHost} updated to {$this->_dnsIP}"; $successful_update = true; } elseif ($output->errors[0]->code === 9103){ $status = "DynDNS ({$this->_dnsHost}): ERROR - Invalid Credentials! Don't forget to use API Key for password field with CloudFlare."; } elseif (($output->success) && (!$output->result[0]->id)) { $status = "DynDNS ({$this->_dnsHost}): ERROR - Zone or Host ID was not found, check your hostname."; } else { $status = "DynDNS ({$this->_dnsHost}): UNKNOWN ERROR - {$output->errors[0]->message}"; log_error("DynDNS ({$this->_dnsHost}): PAYLOAD: {$data}"); } break;
-
Thank you det0nat3.
That patch pretty much covers all that I was looking for.
-
wonders what's wrong with diff …
-
Is this still broken in 2.2.3?
Shouldn't it be possible to use a "custom" dyndns entry instead of the api?
Edit:
I used @__det0nat3__ solution, thanks! -
Thank you det0nat3!!!
n1!
I'm on 2.2.3-RELEASE (amd64) and still not working as is but your fix did the trick!
Would be great if it was in 2.2.4 ;)
-
Just updated to 2.2.4 and now getting this error:
/rc.dyndns.update: DynDNS: PAYLOAD: E_INVLDINPUT
DynDNS was previously working flawlessly after following this thread, any suggestions? Do I just redo the edit?
-
Just updated to 2.2.4 and now getting this error:
/rc.dyndns.update: DynDNS: PAYLOAD: E_INVLDINPUT
DynDNS was previously working flawlessly after following this thread, any suggestions? Do I just redo the edit?
Yeah, just happend to me too, seems like the update did an overwrite of the file.
Is there an ETA of a fix for this? -
Any manual patches need to be re-applied after upgrading the OS. https://github.com/pfsense/pfsense/pull/1916
You can use this URL with System Patches package: https://github.com/doktornotor/pfsense/commit/9aa75e0d5e6b75c0e138408a80ce9615b4136391.patch