Syno NAS: DDNS cập nhật IP của Cloudflare

Chúng ta có thể xây dựng một script để DDNS service của DSM tự động cập nhật IP cho DNS Cloudflare. Cách làm này có những cái tiện/bất tiện như sau:

  • DSM tự lấy WAN IP cung cấp cho script
  • DSM đã cài sẵn trình phân tích json jq
  • DSM tự kiểm tra xem IP có thay đổi hay không để gọi script cập nhật
  • DSM không cho cài đặt nhiều DDNS từ một Service Provider

Script của chúng ta cập nhật tất cả domain/subdomain của một account Cloudflare, trừ những domain/subdomain được chỉ định rõ qua regular expression. Chúng ta chỉ định qua tham số hostname như sau:

# Các domain sau đây có IP tĩnh, không update
# Đặt cách nhau chuỗi ---
hostname: example.org---sub.domain.com

Trường hợp có những ký tự không thể nhập ở input hostname thì nhập trực tiếp ở file /etc/ddns.conf

[USER_Cloudflare_All]
        interface_v6=default
        hostname=example.org---^sub.domain.com$
        passwd=XZfkh1234567890l4OKi8s+abcdef7i0hTxisDlTHA==
        net=DEFAULT
        status=
        ip=114.16.150.39
        service=true
        username=email@gmail.com
        enable_heartbeat=no
        provider=USER_Cloudflare_All
        ipv6=0:0:0:0:0:0:0:0
        interface_v4=default

Script cfip_all.sh

Bash script có sử dụng curl jq

#!/bin/bash
msg=$(cat <<EOT
cfip_all.sh
   © 2020 LNT <lnt@ly-le.info>
   version 20231001
   Cập nhật IP cho tất cả record A thuộc một tài khoản cloudflare.com
---
EOT
)

debug=0 ## 0: không ghi log
E="✘ Bỏ qua!"
log=${0%/*}/$(basename $0).log
[ $# -ne 4 ] && { (($debug))&&echo -e "$msg\nLỗi sai tham số">>$log||echo 'Insufficient parameters';exit 1; }
AUTH_EMAIL="$1"
AUTH_KEY="$2"
re="`sed 's/---/|/g'<<<$3`"
[ -z "$re" ]||re="($re)"
ip="$4"

SHM="/dev/shm/cfip_all.sh"
[ -e "$SHM" ]&&oIp=$(<$SHM)||oIp=''
printf $ip > $SHM
[[ "$ip" == "$oIp" ]]&&{ (($debug))&&echo -e "$msg\n✓ Tất cả IP không thay đổi\n">>$log||echo 'nochg';exit 1; }

BASE_URL='https://api.cloudflare.com/client/v4/zones'
H1="X-Auth-Email:$AUTH_EMAIL"
H2="X-Auth-Key:$AUTH_KEY"
H3="Content-Type:application/json"

ZID=$(curl -s -G -H "$H1" -H "$H2" -H "$H3" "$BASE_URL" \
	|jq -r '.result|.[]|select(.status=="active")|with_entries(select(.key == ("id","name")))|"\(.id)=\(.name)"')
[ -z "$ZID" ]&&{ (($debug))&&echo -e "$msg\nLỗi đăng nhập">>$log||echo 'badauth';exit 1; }
for z in $ZID; do
  n="${z#*=}"
  ## Bỏ qua domain
  [[ ! -z "$re" && "$n" =~ $re ]]&&{ msg="$msg\n[Domain $n] $E";continue; }
  zid=${z%=*}
  ## Lấy DNS record A của $zId
  rs=$(curl -s -G -H "$H1" -H "$H2" -H "$H3" "${BASE_URL}/$zid/dns_records?type=A" \
	|jq --unbuffered -r '.result|.[]|[.id+"="+.name+"="+.content]|@tsv')
  [ -z "$rs" ]||msg="$msg\n[Domain $n]"
  s=''
  for rc in $rs; do
	IFS='=' read -r id n cIP <<<"$rc"; unset IFS
	## Bỏ qua subdomain
	[[ ! -z "$re" &&  "$n" =~ $re ]]&&{ s="${s}  ░ ${n}: $E\n";continue; }
	## Kiểm tra IP
	if [ "$ip" == "$cIP" ];then
		r="✓ IP $t không thay đổi"
	else
		ret=$(curl -s -X PUT -H "$H1" -H "$H2" -H "$H3" "${BASE_URL}/$zid/dns_records/$id" \
		--data "{\"type\":\"A\",\"name\":\"$n\",\"content\":\"$ip\",\"ttl\":120,\"proxied\":false}" \
		|jq -r '.success')
		[ "$ret" == 'true' ]&&r="$cIP → $ip"||r="⛔ Lỗi!"
	fi
	[ ! -z "$r" ]&&s="$s  ░ ${n}: $r\n"
  done
  [ ! -z "$s" ]&&msg="$msg\n${s::-2}"
done
(( $debug ))&&echo -e "$msg\n">>$log||echo 'good'

Script cfip_all.php

PHP script có nhiệm vụ tương tự nhưng có vẻ chậm hơn Bash script.

#!/usr/bin/php
<?php
/*
cfip_all.php
   © 2020 LNT <lnt@ly-le.info>
   version 20231001
   Cập nhật IP cho tất cả record A thuộc một tài khoản cloudflare.com
*/
if ($argc !== 5 || count($argv) != 5) {
    echo 'Không đủ/Sai tham số';
    exit();
}

$cf = new cfDDNS($argv);
$cf->updateDNS();

class cfDDNS {
	$this->authMail = (string) $argv[1];
	$this->authKey = (string) $argv[2];
	$xS = (string) $argv[3];
	$xH = preg_split('/(---)/', $xS, -1, PREG_SPLIT_NO_EMPTY);
	$this->xPat = ($xH) ? '/(' . implode('|', $xH) . ')/' : '';
	$this->ip = (string) $argv[4];
	$json = $this->cfApi('GET');
	$this->zIds = [];
	foreach ($json['result'] as $r) {
	    if ($r['status'] == 'active') {
		$this->zIds["$r[id]"] = "$r[name]";
	    }
	}
    }
    
    function updateDNS() {
	// Bỏ qua Domain
	foreach ($this->zIds as $zid => $name) {
	    if ("$this->xPat" && preg_match("$this->xPat", "$name")) continue;
	    // Lấy DNS record A của $zid
	    $json = $this->cfApi('GET', "/$zid/dns_records?type=A");
	    foreach ($json['result'] as $r) {
		$id = $r['id'];
		$name = $r['name'];
		$cIp = $r['content'];

		// Bỏ qua subdomain
		if ("$this->xPat" && preg_match("$this->xPat", "$name")) continue;

		// Kiểm tra IP
		if ("$this->ip" != "$cIp") {
		    $data = ["type"=>"A","name"=>"$name","content"=>"$this->ip","ttl"=>240,"proxied"=>false];
		    $json = $this->cfApi('PUT', "/$zid/dns_records/$id", $data);
		    if ($json['success'] != 'true') exit('badagent');
		}
	    }
	}
	exit('good');
    }

    function cfApi($method, $path = '', $data = []) {
	$options = [
	    CURLOPT_URL => 'https://api.cloudflare.com/client/v4/zones' . $path,
	    CURLOPT_HTTPHEADER => ["X-Auth-Email:$this->authMail", "X-Auth-Key:$this->authKey", "Content-Type: application/json"],
	    CURLOPT_RETURNTRANSFER => true,
	    CURLOPT_HEADER => false,
	    CURLOPT_VERBOSE => false,
	];
	if(empty($method)){
	    $this->badParam('Empty method');
	}
	switch($method) {
	    case "GET":
		$options[CURLOPT_HTTPGET] = true;
		break;
	    case "PUT":
		$options[CURLOPT_POST] = false;
		$options[CURLOPT_HTTPGET] = false;
		$options[CURLOPT_CUSTOMREQUEST] = "PUT";
		$options[CURLOPT_POSTFIELDS] = json_encode($data);
		break;
	}
	$req = curl_init();
	curl_setopt_array($req, $options);
	$res = curl_exec($req);
	curl_close($req);
	return json_decode($res, true);
    }
}
?>

Cài đặt

Để tiện theo dõi và chỉnh sửa, đặt script ở thư mục home của người dùng, tên script cfip_all.sh

# chmod +x /volume1/homes/myusr/cfip_all.sh
# cat <<EOT >> /etc/ddns_provider.conf
[USER_Cloudflare_All]
        queryurl=https://www.cloudflare.com/
        modulepath=/volume1/homes/myusr/cfip_all.sh
EOT

Sau đó, vào Control Panel > External Access > DDNS > Add.

Điền tham số như hình trên.

  • 2 là các domain/subdomain có IP tĩnh, không cập nhật. Nếu không có thì ghi tên một domain không tồn tại như notfound.net
  • 3 và 4 là tài khoản và API Token (All zones)

Comments Off on Syno NAS: DDNS cập nhật IP của Cloudflare

Filed under Software

Comments are closed.