Skip to content

Instantly share code, notes, and snippets.

@emotality
Last active May 5, 2022 21:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emotality/bc5f699b553e51acbcebf25a14bcf2fd to your computer and use it in GitHub Desktop.
Save emotality/bc5f699b553e51acbcebf25a14bcf2fd to your computer and use it in GitHub Desktop.
CloudFlare Dynamic DNS (DDNS) for home server

CloudFlare Dynamic DNS (DDNS)

A CloudFlare Dynamic DNS (DDNS) cronjob for your home server!

Steps will be added soon!

<?php
/**
* Class CloudFlareAPI
*
* @author Jean-Pierre Fourie (emotality)
* @link https://github.com/emotality
* @link https://gist.github.com/emotality/bc5f699b553e51acbcebf25a14bcf2fd
*/
class CloudFlareAPI
{
/**
* The cURL handler.
*
* @var \CurlHandle $curl
*/
private $curl;
/**
* If this script output needs to be logged to .log file.
*
* @var bool $save_to_log
*/
private $save_to_log = true;
/**
* CloudFlare API base URL.
*
* @var string $cf_base_url
*/
private $cf_base_url = 'https://api.cloudflare.com/client/v4';
/**
* CloudFlare API auth token (40 characters).
*
* @var string $cf_auth_token
*/
private $cf_auth_token = 'XXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXX';
/**
* The CF DNS domain (32 characters).
*
* @var string $cf_dns_zone
*/
private $cf_dns_zone = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
/**
* The CF DNS record that should be updated (32 characters).
*
* @var string $cf_dns_record
*/
private $cf_dns_record = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
/**
* The domain/DNS record parameters.
*
* @var string $record_params
* @link https://api.cloudflare.com/#dns-records-for-a-zone-update-dns-record
*/
private $record_params = [
'type' => 'A',
'name' => 'home.example.com', // The domain you are updating.
'content' => null, // This will be filled with new IP address.
'ttl' => 1800, // Will be set to "Auto" if `proxied` is set to "true".
'proxied' => true, // Do NOT set to "true" if you want to SSH or get the real IP.
];
/**
* CloudFlareAPI constructor.
*/
public function __construct()
{
$url = sprintf('%s/zones/%s/dns_records/%s', $this->cf_base_url, $this->cf_dns_zone, $this->cf_dns_record);
$this->curl = curl_init($url);
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
}
/**
* Compare IP addresses then update to CloudFlare.
*
* @return bool
*/
public function updateCloudFlareIP()
{
$my_ip = $this->getMyCurrentIP();
$cf_ip = $this->getCloudFlareIP();
if (! $my_ip || ! $cf_ip) {
$this->echo('Could not get your current and/or CloudFlare IP address!', true, false);
return false;
}
if ($my_ip == $cf_ip) {
$this->echo('Your CloudFlare IP is already up to date!');
return true;
}
return $this->setCloudFlareIP($my_ip);
}
/**
* Get my current public IP address.
*
* @return string|null
*/
private function getMyCurrentIP() : ?string
{
return trim(file_get_contents('http://ipecho.net/plain')) ?? null;
}
/**
* Get the current IP on CloudFlare.
*
* @return string|null
* @link https://api.cloudflare.com/#dns-records-for-a-zone-dns-record-details
*/
private function getCloudFlareIP() : ?string
{
$header = [
'Accept: application/json',
sprintf('Authorization: Bearer %s', $this->cf_auth_token),
];
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($this->curl, CURLOPT_HTTPHEADER, $header);
$response = curl_exec($this->curl);
$error = curl_error($this->curl);
if (! empty($error)) {
$this->echo($error, true);
} elseif ($json = json_decode($response)) {
return $json->result->content ?? null;
}
return null;
}
/**
* Set the new IP on CloudFlare.
*
* @param string $new_ip
* @return bool
* @link https://api.cloudflare.com/#dns-records-for-a-zone-update-dns-record
*/
private function setCloudFlareIP(string $new_ip) : bool
{
$header = [
'Content-Type: application/json',
'Accept: application/json',
sprintf('Authorization: Bearer %s', $this->cf_auth_token),
];
// Set the new IP in the record parameters.
$this->record_params['content'] = $new_ip;
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($this->curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($this->curl, CURLOPT_POSTFIELDS, json_encode($this->record_params));
$response = curl_exec($this->curl);
$error = curl_error($this->curl);
if (! empty($error)) {
$this->echo($error, true);
} elseif ($json = json_decode($response)) {
$success = $json->success ?? false;
$message = $success ? 'DNS updated successfully!' : 'DNS update failed!';
$this->echo($message, ! $success);
return $success;
}
return false;
}
/**
* Echo out and append to log file.
*
* @param string $message
* @param bool $error
* @param false $exit
* @return void
*/
private function echo(string $message, bool $error = false, $exit = true)
{
echo $message = sprintf("[%s] %s\n", date('Y-m-d H:i:s'), $message);
if ($this->save_to_log) {
$log = sprintf('%s/%s.log', __DIR__, basename(__FILE__, '.php'));
file_put_contents($log, $message, FILE_APPEND);
}
if ($exit) {
exit($error ? 1 : 0);
}
}
/**
* CloudFlareAPI destructor.
*/
public function __destruct()
{
curl_close($this->curl);
}
}
(new CloudFlareAPI())->updateCloudFlareIP();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment