├── INSTALL.md
├── LICENSE
├── README.md
├── da_cloudflare_dns_sync
├── composer.json
├── config_schema.json
├── dns_write_post_cloudflare.php
├── domains
│ └── default.json
├── verify_connection.php
└── verify_domain_config.php
└── dns_write_post.sh
/INSTALL.md:
--------------------------------------------------------------------------------
1 | # Installation Instructions (with helpful examples)
2 |
3 | You will need to have the following tools installed on your server to follow these instructions:
4 |
5 | * Composer Dependency Manager for PHP (see: https://getcomposer.org/)
6 | * git
7 |
8 |
9 | - SSH into your server (as admin)
10 | - Download the repository to a folder on your server (example as follows)
11 |
12 | - cd /home/admin
13 | - git clone https://github.com/pjjonesnz/directadmin_cloudflare_dns.git
14 |
15 |
16 | - Install the required composer packages
17 |
18 | - cd directadmin_cloudflare_dns/da_cloudflare_dns_sync
19 | - run 'php composer.phar install' (or possibly 'composer install' if you have composer set up already)
20 |
21 |
22 | - At this point the composer dependencies will be downloaded into the vendor subfolder by composer and everything is ready to move into place in the DirectAdmin custom scripts folder
23 | - Copy all the files in the main folder you created, including all subfolders, to /usr/local/directadmin/scripts/custom
24 |
25 | - Using the example folder structure given above, run the following
26 | - sudo cp -r /home/admin/directadmin_cloudflare_dns/* /usr/local/directadmin/scripts/custom/
27 |
28 |
29 |
30 |
31 | ## Cloudflare setup
32 |
33 |
34 | - Create a Cloudflare account if you haven't done so already at cloudflare.com
35 | - Login to your Cloudflare account
36 | - If you haven't used Cloudflare before, you will need to add one of your domains to Cloudflare to see the custom nameservers that have been assigned to your account. You're welcome to get a pro plan, but the free plan also works for this step.
37 | - Once you have added your domain, you'll see the two nameservers that have been assigned to you in the format name.ns.cloudflare.com - make note of these for the DirectAdmin setup below
38 | - Click on your profile icon at the top right of the website
39 | - Click on 'My Profile'
40 | - Select the 'API Tokens' tab
41 | - Under the API Keys section, click on 'View' next to 'Global API Key'
42 | - Type your password to view your API Key - make a safe note of this for the script setup below
43 |
44 |
45 | ## Script setup
46 |
47 |
48 | - Edit the dns_write_post.sh file to add your email and Cloudflare API Key
49 | - sudo vim /usr/local/directadmin/scripts/custom/dns_write_post.sh
50 | - Edit the script to add your Cloudflare registered email address: eg. $cloudflare_email = 'email@domain.com';
51 | - Edit the script to add your Cloudflare API Key: eg. $cloudflare_api_key='1234567890';
52 | - Save the script and exit the editor
53 | - Give dns_write_post.sh executable permissions: chmod 755 /usr/local/directadmin/scripts/custom/dns_write_post.sh
54 | - Run the following command to verify the connection to Cloudflare
55 |
56 | - /usr/local/directadmin/scripts/custom/dns_write_post.sh verify
57 | - You should see the following message, "Your user ID is: ......", with your user ID listed.
58 |
59 |
60 | - If so, CONGRATULATIONS, the script is installed and communicating with your Cloudflare account
61 |
62 |
63 | ## DirectAdmin setup
64 |
65 | - Login to your DirectAdmin reseller or admin account
66 | - Edit the DNS record for one of your websites. Change the nameserver records for the domain to your two new Cloudflare nameservers
67 | - Have a look at the DNS record on your Cloudflare account - if it is a different domain than you have previously added to your Cloudflare account, you will see that it has now been added, and any differences in the DNS record have been synchronized.
68 | - Obviously if you are moving your domain NS to Cloudflare for the first time, you will also need to update the nameserver records at your domain registrar.
69 |
70 |
71 | That's it!
72 |
73 | ## Troubleshooting (or just interested in watching your domain changes)
74 |
75 | Note: Any fatal errors encountered while adding/editing your domain will be displayed automatically displayed by DirectAdmin.
76 |
77 | But, if you want to see exactly what the script is doing you can enable and view all record deletions and additions by doing the following:
78 |
79 | * Edit the usr/local/directadmin/scripts/custom/dns_write_post.sh file
80 | * Set: ```$log_messages = true```
81 | * Save the file
82 | * Watch the output in the log file. eg. ```tail -f /tmp/cloudflare_dns_messages.log```
83 | * Edit your domain in DirectAdmin and watch the log file output
84 | * Remember to set: ```$log_messages = false``` when you have finished watching
85 |
86 |
87 |
88 | ### Notes:
89 |
90 | - The script is written in PHP and may need the first line changed to point to your PHP installation if your server has a different setup.
91 | - The TTL settings for your records are managed by Cloudflare by default. If you want to write the DirectAdmin defaults across to CloudFlare, set the $use_da_ttl variable to true. Note that custom TTL for individual records are not supported by DirectAdmin using dns_write_post scripts. Instead you can set the TTL for record types in the DirectAdmin named settings. See this document: https://www.directadmin.com/features.php?id=2084
92 | - At this stage script is only configurable for a single Cloudflare account per server
93 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 pjjonesnz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Feb 2021 Update - daflare plugin released
2 |
3 | **PLEASE NOTE:** This script is now deprecated. For my replacement DirectAdmin Plugin, daflare, see: https://daflare.com
4 |
5 | The benefits of moving to daflare are:
6 |
7 | * GUI configuration panel integrated into DirectAdmin
8 | * Easy plugin install/update/delete using DirectAdmin plugin manager
9 | * Uses Cloudflare API tokens for better security
10 | * Share a single Cloudflare account with all the user accounts you manage
11 |
12 | [Visit daflare.com](https://daflare.com) for all the details and to download
13 |
14 | ---
15 |
16 | # DEPRECATED: DirectAdmin to Cloudflare DNS Sync
17 |
18 | ## Deprecated script instructions
19 |
20 | Script to sync DirectAdmin dns changes *from* DirectAdmin *to* your Cloudflare account
21 |
22 |
23 | * **Keep your Cloudflare dns records in sync with your server** without having to log in to Cloudflare and manually change them
24 | * **Automatically add new domains** to your Cloudflare account when they are added to your server
25 | * **Set proxy on/off** using a config file **for the entire domain or for individual records**
26 | * Set proxy on/off for all of your domains, using a **default settings file**
27 |
28 | Cloudflare has a free dns service which is super fast. It is a great way to move nameserver hosting off your web server and on to a managed platform. And if you want to go even faster, enable their proxy!
29 |
30 | This handy script for DirectAdmin syncs the DNS records for your domains FROM DirectAdmin TO your Cloudflare account. Use this script when using Cloudflare as the nameserver host for your domain.
31 |
32 | I use this script in a production environment hosting multiple domains.
33 |
34 | ## Installation
35 |
36 | [Full Installation Instructions Here](./INSTALL.md)
37 |
38 | ## Usage
39 |
40 | 1. create/modify your domain in DirectAdmin
41 | 2. set your DirectAdmin domain's NS records to customname.ns.cloudflare.com AND customname2.ns.cloudflare.com (where customname/customname2 are the ns record names assigned to you by Cloudflare.
42 | 3. Check your Cloudflare account and see the domain settings automatically synchronized with your server's settings.
43 |
44 | **NOTE:** If you have already added your domain to Cloudflare and have changed your DNS settings manually there, and they are different from your server, your server will overwrite any settings on Cloudflare and delete any settings that don't exist on your server. The synchronization is one way, *from* your server *to* Cloudflare.
45 |
46 | ## Customize your proxy settings
47 |
48 | Proxy configuration is found in this folder: /usr/local/directadmin/scripts/custom/da_cloudflare_dns_sync/domains
49 |
50 | ### Proxy defaults
51 |
52 | The default settings for your server can be made in: 'default.json'
53 |
54 | | Setting | Description | Default |
55 | |---|---|---|
56 | | ```proxy_default``` | the default proxy setting for all valid records (A, CNAME and AAAA) | false |
57 | | ```proxy_record``` | an object that contains individual settings by record type and name | |
58 |
59 | Example to enable Cloudflare proxy on ALL DOMAINS, but disable it for ftp and mail A records:
60 |
61 | ```js
62 | {
63 | "proxy_default": true,
64 | "proxy_record": {
65 | "A": {
66 | "mail": false,
67 | "ftp": false
68 | },
69 | "CNAME": {
70 | },
71 | "AAAA": {
72 | }
73 | }
74 | }
75 | ```
76 |
77 | ## Testing your config files
78 |
79 | All config files in the domains folder can be tested by running the following command ```/usr/local/directadmin/scripts/custom/dns_write_post.sh test_config```
80 |
81 | Any errors while decoding the json file will be displayed. Any errors in the config options will also be displayed along with the json node tree that contains the error. If you have trouble finding what is causing the error feel free to open an issue.
82 |
83 | You can also test that your JSON file format is correct at [JSONLint](https://jsonlint.com/) - although this doesn't check that the config properties are correct.
84 |
85 | Note: Be careful that you don't have any trailing commas in your json file. Remember that json isn't the same as javascript object literal syntax.
86 |
87 | ## Settings for individual domains
88 |
89 | The proxy settings can also be enabled/disabled for particular domains
90 |
91 | * Copy the default.json file to **my_full_domain_name**.json (eg. ```cp default.json mydomainname.com.json```)
92 | * Change the settings in the newly create file for your domain as required
93 |
94 | Example to enable proxy on your domain record and www but disable it on everything else:
95 |
96 | ```js
97 | // Example domain name: mydomainname.com
98 | // Filename: mydomainname.com.json
99 |
100 | {
101 | "proxy_default": false,
102 | "proxy_record": {
103 | "A": { // Add A records here
104 | "mydomainname.com.": true,
105 | "www": true
106 | },
107 | "CNAME": { // Add CNAME records here
108 | },
109 | "AAAA": { // Add AAAA records here
110 | }
111 | }
112 | }
113 | ```
114 | **Please note:** comments in the above example must be removed as comments are not valid json.
115 |
116 | ## Setting proxy on/off for entire record type
117 |
118 | You can set the record type in the following two ways:
119 |
120 | 1. to an object (as per the example above), with a list of records you want to enable (or disable) the proxy for.
121 | 2. to a boolean, to set all records of that type to be proxied (or not). eg.
122 |
123 | ```js
124 | {
125 | "proxy_default": false,
126 | "proxy_record": {
127 | "A": true,
128 | "CNAME": {
129 | "my.record": true,
130 | },
131 | "AAAA": {
132 | }
133 | }
134 | }
135 | ```
136 |
137 |
138 |
139 | To cause your domain to update after changing any settings, edit one of the domain records and save your settings (even making it equal to the same settings as it was before will force an update of any changed records).
140 |
141 | I hope this script is helpful to you. PLEASE let me know if you have any troubles by creating an issue in Github.
142 |
--------------------------------------------------------------------------------
/da_cloudflare_dns_sync/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "cloudflare/sdk": "dev-pjjonesnz-fix-mx-priority-0",
4 | "opis/json-schema": "^1.0"
5 | },
6 | "repositories": [
7 | {
8 | "type": "vcs",
9 | "url": "https://github.com/pjjonesnz/cloudflare-php"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/da_cloudflare_dns_sync/config_schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-07/schema#",
3 | "type": "object",
4 | "properties": {
5 | "proxy_default": {
6 | "type": "boolean"
7 | },
8 | "proxy_record": {
9 | "type": "object",
10 | "additionalProperties": false,
11 | "properties": {
12 | "A": {
13 | "type": ["object","boolean"],
14 | "propertyNames": {
15 | "type": "string"
16 | },
17 | "patternProperties": {
18 | ".*": {
19 | "type": "boolean"
20 | }
21 | }
22 | },
23 | "CNAME": {
24 | "type": ["object","boolean"],
25 | "propertyNames": {
26 | "type": "string"
27 | },
28 | "patternProperties": {
29 | ".*": {
30 | "type": "boolean"
31 | }
32 | }
33 | },
34 | "AAAA": {
35 | "type": ["object","boolean"],
36 | "propertyNames": {
37 | "type": "string"
38 | },
39 | "patternProperties": {
40 | ".*": {
41 | "type": "boolean"
42 | }
43 | }
44 | }
45 | }
46 | }
47 | },
48 | "required": ["proxy_default"],
49 | "additionalProperties": false
50 | }
51 |
--------------------------------------------------------------------------------
/da_cloudflare_dns_sync/dns_write_post_cloudflare.php:
--------------------------------------------------------------------------------
1 | getZoneID($domain);
57 | } catch (\Exception $e) {
58 | if ($e->getMessage() == 'Could not find zones with specified name.') {
59 | // Create zone
60 | try {
61 | $result = $zones->addZone($domain);
62 | if ($result->name == $domain) {
63 | $zoneID = $result->id;
64 | } else {
65 | throw new Exception("Error Adding Domain", 1);
66 | }
67 | } catch (\Exception $e) {
68 | logMessage('Exception Caught: ' . $e->getMessage());
69 | exit;
70 | }
71 | } else {
72 | exit;
73 | }
74 | }
75 |
76 | logMessage('Zone ID for ' . $domain . ' - ' . $zoneID);
77 |
78 | /**
79 | * Load settings json for domain, or defaults if custom domain settings not available
80 | */
81 | $domain_settings = array('proxy_default' => false);
82 | $settings_filename = __DIR__ . '/domains/' . $domain . '.json';
83 |
84 | if (!file_exists($settings_filename)) {
85 | $settings_filename = __DIR__ . '/domains/default.json';
86 | if (!file_exists($settings_filename)) {
87 | logMessage('Error: default domain config file default.json missing from plugin.', true);
88 | $settings_filename = '';
89 | }
90 | }
91 | if ($settings_filename !== '') {
92 | $json_file = file_get_contents($settings_filename);
93 | if ($json_file !== false) {
94 | $load_settings = json_decode($json_file, true);
95 | if ($load_settings !== NULL) {
96 | $domain_settings = $load_settings;
97 | } else {
98 | logMessage('Error: Json error in default.json plugin config file.', true);
99 | }
100 | }
101 | }
102 |
103 | /**
104 | * Load existing DNS records for the domain
105 | */
106 | $dns = new \Cloudflare\API\Endpoints\DNS($adapter);
107 | $page = 0;
108 | $per_page = 500;
109 | $existingRecords = array();
110 |
111 | do {
112 | $page++;
113 | $listRecords = $dns->listRecords($zoneID, '', '', '', $page, $per_page);
114 | $existingRecords = array_merge($existingRecords, $listRecords->result);
115 | } while ($listRecords->result_info->total_pages > $page);
116 |
117 | /**
118 | * Array of dns records to add to Cloudflare
119 | */
120 | $recordsToAdd = array();
121 |
122 | /**
123 | * Array of dns records that already exist on Cloudflare
124 | */
125 | $recordsThatExist = array();
126 |
127 | // NOTE don't parse NS records
128 |
129 | // parse A records
130 | $output = parseInput($a);
131 | if (count($output) > 0) {
132 | foreach ($output as $value) {
133 | $record = (object) array(
134 | 'type' => 'A',
135 | 'name' => qualifyRecordName($value->key),
136 | 'content' => $value->value,
137 | 'ttl' => $aTTL,
138 | 'proxied' => is_proxied('A', $value->key),
139 | );
140 | if (doesRecordExist($existingRecords, $record) === false) {
141 | $recordsToAdd[] = $record;
142 | } else {
143 | $recordsThatExist[] = $record;
144 | }
145 | }
146 | }
147 |
148 | // parse TXT records
149 | $output = parseInput($txt);
150 | if (count($output) > 0) {
151 | foreach ($output as $value) {
152 | $record = (object) array(
153 | 'type' => 'TXT',
154 | 'name' => qualifyRecordName($value->key),
155 | 'content' => trim(preg_replace(array('/"\s+"/', '/"/'), array('', ''), trim($value->value, '()'))),
156 | 'ttl' => $txtTTL,
157 | 'proxied' => false
158 | );
159 | if (doesRecordExist($existingRecords, $record) === false) {
160 | $recordsToAdd[] = $record;
161 | } else {
162 | $recordsThatExist[] = $record;
163 | }
164 | }
165 | }
166 |
167 | // parse MX records
168 | $output = parseInput($mx_full);
169 | if (count($output) > 0) {
170 | foreach ($output as $value) {
171 | preg_match('/(\d+) (.*)/', $value->value, $parsedValue);
172 | $record = (object) array(
173 | 'type' => 'MX',
174 | 'name' => qualifyRecordName($value->key),
175 | 'priority' => $parsedValue[1],
176 | 'content' => qualifyRecordName($parsedValue[2]),
177 | 'ttl' => $mxTTL,
178 | 'proxied' => false,
179 | );
180 | if (doesRecordExist($existingRecords, $record) === false) {
181 | $recordsToAdd[] = $record;
182 | } else {
183 | $recordsThatExist[] = $record;
184 | }
185 | }
186 | }
187 |
188 | // parse CNAME records
189 | $output = parseInput($cname);
190 | if (count($output) > 0) {
191 | foreach ($output as $value) {
192 | $record = (object) array(
193 | 'type' => 'CNAME',
194 | 'name' => qualifyRecordName($value->key),
195 | 'content' => qualifyRecordName($value->value),
196 | 'ttl' => $cnameTTL,
197 | 'proxied' => is_proxied('CNAME', $value->key),
198 | );
199 | if (doesRecordExist($existingRecords, $record) === false) {
200 | $recordsToAdd[] = $record;
201 | } else {
202 | $recordsThatExist[] = $record;
203 | }
204 | }
205 | }
206 |
207 | // parse PTR records
208 | $output = parseInput($ptr);
209 | if (count($output) > 0) {
210 | foreach ($output as $value) {
211 | $record = (object) array(
212 | 'type' => 'PTR',
213 | 'name' => $value->key,
214 | 'content' => qualifyRecordName($value->value),
215 | 'ttl' => $ptrTTL,
216 | 'proxied' => false,
217 | );
218 | if (doesRecordExist($existingRecords, $record) === false) {
219 | $recordsToAdd[] = $record;
220 | } else {
221 | $recordsThatExist[] = $record;
222 | }
223 | }
224 | }
225 |
226 | // parse AAAA records
227 | $output = parseInput($aaaa);
228 | if (count($output) > 0) {
229 | foreach ($output as $value) {
230 | $record = (object) array(
231 | 'type' => 'AAAA',
232 | 'name' => qualifyRecordName($value->key),
233 | 'content' => $value->value,
234 | 'ttl' => $aaaaTTL,
235 | 'proxied' => is_proxied('AAAA', $value->key),
236 | );
237 | if (doesRecordExist($existingRecords, $record) === false) {
238 | $recordsToAdd[] = $record;
239 | } else {
240 | $recordsThatExist[] = $record;
241 | }
242 | }
243 | }
244 |
245 | // parse SRV records
246 | $output = parseInput($srv);
247 | if (count($output) > 0) {
248 |
249 | foreach ($output as $value) {
250 | preg_match('/(\d+) (\d+) (\d+) (.*)/', $value->value, $parsedValue);
251 |
252 | $fullSRVname = qualifyRecordName($value->key);
253 | preg_match('/^(.*)\._(tcp|udp|tls)\.(.*)$/', $fullSRVname, $srv_match);
254 |
255 | $record = (object) array(
256 | 'type' => 'SRV',
257 | 'name' => $fullSRVname,
258 | 'priority' => $parsedValue[1],
259 | 'content' => qualifyRecordName($parsedValue[4]),
260 | 'data' => array(
261 | 'name' => $srv_match[3],
262 | 'weight' => (int) $parsedValue[2],
263 | 'port' => (int) $parsedValue[3],
264 | 'target' => qualifyRecordName($parsedValue[4]),
265 | 'proto' => '_' . $srv_match[2],
266 | 'service' => $srv_match[1],
267 | 'priority' => (int) $parsedValue[1],
268 | ),
269 | 'ttl' => $srvTTL,
270 | 'proxied' => false,
271 | );
272 | if (doesRecordExist($existingRecords, $record) === false) {
273 | $recordsToAdd[] = $record;
274 | } else {
275 | $recordsThatExist[] = $record;
276 | }
277 | }
278 | }
279 |
280 | /**
281 | * Delete records from Cloudflare that don't exist in DirectAdmin
282 | *
283 | * While ignoring nameserver records, go through all existingRecords and see if there is a match in recordsToAdd or recordsThatExist - if not, delete the record.
284 | */
285 | $keysToDelete = array();
286 | foreach ($existingRecords as $record) {
287 | if ($record->type != 'NS') {
288 | if (!isRecordCurrent($recordsToAdd, $recordsThatExist, $record)) {
289 | array_push($keysToDelete, $record->id);
290 | logMessage('Queue Record to Delete: ' . $record->id . ' - ' . $record->name . "\t" . $record->type . "\t" . $record->content);
291 | }
292 | }
293 | }
294 | if (count($keysToDelete) > 0) {
295 | foreach ($keysToDelete as $key) {
296 | $success = $dns->deleteRecord($zoneID, $key);
297 | logMessage('Delete Record ID ' . $key . "\t" . ($success == true ? 'SUCCESSFUL' : 'FAILED'));
298 | }
299 | }
300 |
301 | /**
302 | * Add new records to Cloudflare
303 | */
304 | foreach ($recordsToAdd as $record) {
305 | $priority = isset($record->priority) ? $record->priority : '';
306 | $data = isset($record->data) && count($record->data) > 0 ? $record->data : [];
307 | $proxied = $record->proxied;
308 | $ttl = $record->ttl > 0 ? $record->ttl : 0; // use default TTL
309 | try {
310 | $success = $dns->addRecord($zoneID, $record->type, $record->name, $record->content, $ttl, $proxied, $priority, $data);
311 | logMessage('Add Record: ' . $record->name . "\t" . $record->type . "\t" . $record->content . "\t" . ($success == true ? 'SUCCESSFUL' : 'FAILED') . "\t" . "Proxy: " . ($proxied ? 'on' : 'off'), !$success);
312 | } catch (\GuzzleHttp\Exception\ClientException $e) {
313 | logMessage('Add Record: ' . $record->name . "\t" . $record->type . "\t" . $record->content . "\t" . 'FAILED', true);
314 | logMessage($e->getResponse()->getBody()->getContents(), true);
315 | }
316 | }
317 |
318 | logMessage('Result: ' . get_records_modified_text($keysToDelete) . ' deleted, ' . get_records_modified_text($recordsToAdd) . ' added.');
319 |
320 | // Output any errors to be displayed in the DirectAdmin console
321 | if ($hasErrors) {
322 | echo join("\n\n", $errorList);
323 | exit(1);
324 | } else {
325 | exit(0);
326 | }
327 |
328 | function is_proxied($record_type, $record_name)
329 | {
330 | global $domain_settings;
331 | if($record_name == 'localhost') {
332 | return false;
333 | }
334 | if (isset($domain_settings['proxy_record']) && isset($domain_settings['proxy_record'][$record_type])) {
335 | if(is_array($domain_settings['proxy_record'][$record_type])) {
336 | if(isset($domain_settings['proxy_record'][$record_type][$record_name])) {
337 | return $domain_settings['proxy_record'][$record_type][$record_name] === true;
338 | }
339 | }
340 | elseif(is_bool($domain_settings['proxy_record'][$record_type]) ) {
341 | return $domain_settings['proxy_record'][$record_type] === true;
342 | }
343 | }
344 | return isset($domain_settings['proxy_default']) && $domain_settings['proxy_default'] === true;
345 | }
346 |
347 | function get_records_modified_text($mod_array)
348 | {
349 | if (count($mod_array) == 1) {
350 | return '1 record';
351 | }
352 | return count($mod_array) . ' records';
353 | }
354 |
355 | /**
356 | * Log a message to the php error log
357 | */
358 | function logMessage($message, $error = false)
359 | {
360 | global $log_messages, $log_to_file, $log_filename, $errorList, $hasErrors;
361 | if ($error) {
362 | $hasErrors = true;
363 | $errorList[] = $message;
364 | }
365 | if (!$log_messages) {
366 | return;
367 | }
368 | $timestamp = date('Y-m-d H:i:s');
369 | if ($log_to_file) {
370 | error_log($timestamp . " - " . $message . "\n", 3, $log_filename);
371 | } else {
372 | error_log($timestamp . " - " . $message . "\n");
373 | }
374 | }
375 |
376 | /**
377 | * Search through array of records for a particular record
378 | */
379 | function doesRecordExist($records, $record)
380 | {
381 | global $use_da_ttl;
382 | if (count($records) == 0) {
383 | return false;
384 | }
385 | foreach ($records as $compare) {
386 | if ($record->type == 'SRV') {
387 | if (!isset($compare->data)) {
388 | continue;
389 | }
390 | $compare_data = (object) $compare->data;
391 | $record_data = (object) $record->data;
392 | if (
393 | compare_records($compare->type, $record->type) &&
394 | compare_records($compare->name, $record->name) &&
395 | compare_records($compare->proxied, $record->proxied) &&
396 | (!$use_da_ttl || compare_records($compare->ttl, $record->ttl)) &&
397 | compare_records($compare_data->weight, $record_data->weight) &&
398 | compare_records($compare_data->target, $record_data->target) &&
399 | compare_records($compare_data->proto, $record_data->proto) &&
400 | compare_records($compare_data->service, $record_data->service) &&
401 | compare_records($compare_data->priority, $record_data->priority) &&
402 | compare_records($compare_data->port, $record_data->port)
403 | ) {
404 | return true;
405 | }
406 | } else {
407 | if (
408 | compare_records($compare->type, $record->type) &&
409 | compare_records($compare->name, $record->name) &&
410 | compare_records($compare->content, $record->content) &&
411 | compare_records($compare->proxied, $record->proxied) &&
412 | (!$use_da_ttl || compare_records($compare->ttl, $record->ttl))
413 | ) {
414 | if ($record->type == 'MX' && !compare_records($compare->priority, $record->priority)) {
415 | continue;
416 | }
417 | return true;
418 | }
419 | }
420 | }
421 | return false;
422 | }
423 |
424 | /**
425 | * Compare two records to see if they match
426 | * @param string $r1 Existing Record on Cloudflare
427 | * @param string $r2 Record on server
428 | */
429 | function compare_records($r1, $r2)
430 | {
431 | return $r1 == $r2;
432 | }
433 |
434 | /**
435 | * Search through recordsToAdd and recordsThatExist to see if the existing record should be kept or deleted
436 | */
437 | function isRecordCurrent($recordsToAdd, $recordsThatExist, $record)
438 | {
439 | if (count($recordsToAdd) > 0) {
440 | if (doesRecordExist($recordsToAdd, $record)) {
441 | return true;
442 | }
443 | }
444 | if (count($recordsThatExist) > 0) {
445 | if (doesRecordExist($recordsThatExist, $record)) {
446 | return true;
447 | }
448 | }
449 | return false;
450 | }
451 |
452 | /**
453 | * Parse Environment variables from DirectAdmin and split into key and value pairs
454 | */
455 | function parseInput($input)
456 | {
457 | $pairs = explode('&', $input);
458 | $results = array();
459 | foreach ($pairs as $pair) {
460 | if ($pair == "") continue;
461 | list($key, $value) = explode('=', $pair, 2);
462 | if ($key == "" || $value == "") continue;
463 | $object = new StdClass();
464 | $object->key = $key;
465 | $object->value = $value;
466 | $results[] = $object;
467 | }
468 | return $results;
469 | }
470 |
471 | /**
472 | * Fully qualify the record name
473 | */
474 | function qualifyRecordName($name)
475 | {
476 | global $domain;
477 | return trim($name . (substr($name, -1) !== '.' ? '.' . $domain : ''), '.');
478 | }
479 |
--------------------------------------------------------------------------------
/da_cloudflare_dns_sync/domains/default.json:
--------------------------------------------------------------------------------
1 | {
2 | "proxy_default": false,
3 | "proxy_record": {
4 | "A": {
5 | },
6 | "CNAME": {
7 | },
8 | "AAAA": {
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/da_cloudflare_dns_sync/verify_connection.php:
--------------------------------------------------------------------------------
1 | getUserID() . PHP_EOL;
12 |
13 | ?>
--------------------------------------------------------------------------------
/da_cloudflare_dns_sync/verify_domain_config.php:
--------------------------------------------------------------------------------
1 | schemaValidation($data, $schema);
73 |
74 | if ($result->isValid()) {
75 | echo "\tConfig file is valid", PHP_EOL;
76 | echo "\n\tResult: SUCCESS", PHP_EOL;
77 | } else {
78 | /** @var ValidationError $error */
79 | $error = $result->getFirstError();
80 | echo "\tConfig file is invalid", PHP_EOL;
81 | $error_node = $error->dataPointer();
82 | echo "\tNode containing the error: " . json_encode($error_node), PHP_EOL;
83 | $error_keyword = $error->keyword();
84 | if ($error_keyword == 'patternProperties') {
85 | $error_keyword = "JSON values must be boolean (true or false) without speechmarks";
86 | }
87 |
88 | if ($error_keyword == 'additionalProperties' && count(array_diff(array("proxy_record"), $error_node)) == 0) {
89 | $error_keyword = "The only domain records that Cloudflare can proxy are A, CNAME and AAAA.\n\tInvalid record type found in file.\n";
90 | }
91 | echo "\tError: ", $error_keyword, PHP_EOL;
92 |
93 | if (count($error->keywordArgs()) > 0) {
94 | echo "\n" . json_encode($error->keywordArgs()), PHP_EOL;
95 | }
96 | echo "\n\tResult: FAIL", PHP_EOL;
97 | }
98 | echo "\n---------------------------------------------------------------------------------\n\n";
99 | }
100 |
--------------------------------------------------------------------------------
/dns_write_post.sh:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/php
2 |
--------------------------------------------------------------------------------