From 46fb17da00cc25c6b7078f655923d1f13843c084 Mon Sep 17 00:00:00 2001 From: dino2895un Date: Thu, 24 Oct 2024 19:46:38 +0800 Subject: [PATCH 1/9] add IPv6 version --- CloudFlare-DDNS-Setup-v6.sh | 237 ++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 CloudFlare-DDNS-Setup-v6.sh diff --git a/CloudFlare-DDNS-Setup-v6.sh b/CloudFlare-DDNS-Setup-v6.sh new file mode 100644 index 0000000..35afac1 --- /dev/null +++ b/CloudFlare-DDNS-Setup-v6.sh @@ -0,0 +1,237 @@ +#!/bin/bash -e +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +export PATH + +RED='\033[0;31m' +NC='\033[0m' # No Color +GREEN='\033[0;32m' +bold=$(tput bold) +normal=$(tput sgr0) + +function exit_badly { + printf "$1" + exit 1 +} + +function chk_root { + if [[ "${EUID}" -ne 0 ]]; then + exit_badly "Please re-run as root (e.g. sudo ./path/to/this/script)" + fi +} + +function interactive { + printf "[${GREEN}${bold}輸入${NC}${normal}] 請輸入登入 CloudFlare 的電子郵箱:" + read auth_email + + printf "[${GREEN}${bold}輸入${NC}${normal}] 請前往 CloudFlare 尋找 API Key,方法如下\n" + printf " 登入後請點選右上角的頭像,點選帳號郵箱\n" + printf " 下方有個 Global API Key,對他旁邊的 View 按一下\n" + printf " 跳出密碼視窗,輸入密碼登入。登入完畢後金鑰會出現\n" + printf " 把它複製起來,並貼上到這裡:" + read auth_key + + printf "[${GREEN}${bold}輸入${NC}${normal}] 請前往 CloudFlare 尋找 Zone ID,方法如下\n" + printf " 登入後左上位置的選單選HOME,\n" + printf " 選擇想DDNS的網域名,點下去後\n" + printf " 頁面中應該可以看到 Zone ID 點 Copy\n" + printf " 然後貼到這裡:" + read zone_identifier + + printf "[${RED}${bold}提示${NC}${normal}] 請${RED}${bold}務必${NC}${normal}先前往 CloudFlare 添加想 DDNS 的域名的紀錄,但可隨便指向任意 IP\n" + printf "[${GREEN}${bold}輸入${NC}${normal}] 請輸入想 DDNS 的域名全名(如:foo.example.com):" + read record_name + + printf "[${GREEN}${bold}選擇${NC}${normal}] 請問更新頻率?(CloudFlare 的 API 要求限制為 1200次/秒,若共用 IP,請選較低的頻率)\n" + printf "${RED}${bold}1.${NC}${normal} 3 秒\n" + printf "${RED}${bold}2.${NC}${normal} 5 秒\n" + printf "${RED}${bold}3.${NC}${normal} 10 秒\n" + printf "${RED}${bold}4.${NC}${normal} 15 秒\n" + printf "${RED}${bold}5.${NC}${normal} 20 秒\n" + printf "${RED}${bold}6.${NC}${normal} 30 秒\n" + printf "${RED}${bold}7.${NC}${normal} 1 分\n" + printf "${RED}${bold}8.${NC}${normal} 2 分\n" + printf "${RED}${bold}9.${NC}${normal} 5 分\n" + printf "選擇 [預設:7]:" + read secondselect + if [ -z "$secondselect" ]; then + secondselect="7" + fi + getSeconds + printf "[${GREEN}${bold}提示${NC}${normal}] 這樣就是我需要的全部資料了,請等待完成\n" +} + +function getSeconds { + case "$secondselect" in + "1") + seconds=*:0/3 + ;; + "2") + seconds=*:0/5 + ;; + "3") + seconds=*:0/10 + ;; + "4") + seconds=*:0/15 + ;; + "5") + seconds=*:0/20 + ;; + "6") + seconds=*:0/30 + ;; + "7") + seconds=0/1 + ;; + "8") + seconds=0/2 + ;; + "9") + seconds=0/5 + ;; + *) + exit_badly "[${RED}${bold}錯誤${NC}${normal}] 選擇錯誤!\n" + ;; + esac +} + +function install_dependencies { + printf "[${GREEN}${bold}配置${NC}${normal}] 開始安裝依賴(需要20秒到1分鐘,取決於網速和電腦速度,若之前全裝過則一瞬間完成)\n" + apt-get update &> /dev/null + apt-get install -y ca-certificates golang-go make grep curl &> /dev/null + printf "[${GREEN}${bold}完成${NC}${normal}] 安裝依賴完成\n" +} + +function set_timezone { + printf "[${GREEN}${bold}配置${NC}${normal}] 開始設定時區\n" + ln -fs /usr/share/zoneinfo/${timezone} /etc/localtime &> /dev/null + dpkg-reconfigure -f noninteractive tzdata &> /dev/null + printf "[${GREEN}${bold}完成${NC}${normal}] 時區設定完成\n" +} + +function setup_ntp { + printf "[${GREEN}${bold}配置${NC}${normal}] 開始設定NTP\n" + timedatectl set-ntp true &> /dev/null + cat <<'EOF' >> /etc/systemd/timesyncd.conf +NTP=time1.google.com time2.google.com time3.google.com time4.google.com +FallbackNTP=time1.google.com time2.google.com time3.google.com time4.google.com +EOF + printf "[${GREEN}${bold}完成${NC}${normal}] NTP設定完成\n" +} + +function generate_cfupdater_script { +printf "[${GREEN}${bold}配置${NC}${normal}] 開始生成 CloudFlare DDNS 腳本\n" +touch /var/log/cfupdater.log +cat < /usr/bin/cfupdater-v6 +#!/bin/bash +# Forked by benkulbertis/cloudflare-update-record.sh +# CHANGE THESE +auth_email="${auth_email}" # The email used to login 'https://dash.cloudflare.com' +auth_key="${auth_key}" # Top right corner, "My profile" > "Global API Key" +zone_identifier="${zone_identifier}" # Can be found in the "Overview" tab of your domain +record_name="${record_name}" # Which record you want to be synced +EOF + +cat <<'EOF' >> /usr/bin/cfupdater-v6 +# DO NOT CHANGE LINES BELOW +ip=$(curl -s -6 https://ip.made.moe/) +comment="Updated by Cloudflare-DDNS at "$(TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8") +# SCRIPT START +echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater.log +echo "[Cloudflare DDNS] Check Initiated" >> /var/log/cfupdater.log +echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater.log +# Seek for the record +record=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?name=$record_name" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json") +# Can't do anything without the record +if [[ $record == *"\"count\":0"* ]]; then + >&2 echo -e "[Cloudflare DDNS] Record does not exist, perhaps create one first?" + exit 1 +fi +# Set existing IP address from the fetched record +old_ip=$(echo "$record" | grep -Po '(?<="content":")[^"]*' | head -1) +# Compare if they're the same +if [ $ip == $old_ip ]; then + echo "[Cloudflare DDNS] IP has not changed." >> /var/log/cfupdater.log + exit 0 +fi +# Set the record identifier from result +record_identifier=$(echo "$record" | grep -Po '(?<="id":")[^"]*' | head -1) +# The execution of update +update=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records/$record_identifier" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" --data "{\"id\":\"$zone_identifier\",\"type\":\"AAAA\",\"proxied\":false,\"name\":\"$record_name\",\"comment\":\"$comment\",\"content\":\"$ip\"}") +# The moment of truth +case "$update" in +*"\"success\":false"*) + >&2 echo -e "[Cloudflare DDNS] Update failed for $record_identifier. DUMPING RESULTS:\n$update" >> /var/log/cfupdater.log + exit 1;; +*) + echo "[Cloudflare DDNS] IPv6 context '$ip' has been synced to Cloudflare." >> /var/log/cfupdater.log;; +esac +EOF + +chmod 700 /usr/bin/cfupdater-v6 +printf "[${GREEN}${bold}完成${NC}${normal}] CloudFlare DDNS 腳本生成完成\n" +} +function setup_systemd { +printf "[${GREEN}${bold}配置${NC}${normal}] 開始配置 Systemd\n" +cat <<'EOF' > /etc/systemd/system/cfupdate.service +[Unit] +Description=Cloudflare DDNS service +After=network.target +[Service] +Type=oneshot +ExecStart=/usr/bin/cfupdater-v6 +[Install] +WantedBy=multi-user.target +EOF + +chmod 644 /etc/systemd/system/cfupdate.service + +cat < /etc/systemd/system/cfupdate.timer +[Unit] +Description=Run cfupdate.service when the timer ticks +[Timer] +OnCalendar=*:${seconds} +AccuracySec=1ms +[Install] +WantedBy=timers.target +EOF +chmod 644 /etc/systemd/system/cfupdate.timer + +mkdir ~/systemd-timesyncd-wait/ +cd ~/systemd-timesyncd-wait/ +curl -LJO https://github.com/assisi/systemd-timesyncd-wait/raw/master/Makefile &> /dev/null +curl -LJO https://github.com/assisi/systemd-timesyncd-wait/raw/master/systemd-timesyncd-wait.go &> /dev/null +curl -LJO https://github.com/assisi/systemd-timesyncd-wait/raw/master/systemd-timesyncd-wait.service &> /dev/null +curl -LJO https://github.com/assisi/systemd-timesyncd-wait/raw/master/systemd-timesyncd-wait.socket &> /dev/null +curl -LJO https://github.com/assisi/systemd-timesyncd-wait/raw/master/systemd-timesyncd-wrap.go &> /dev/null +curl -LJO https://github.com/assisi/systemd-timesyncd-wait/raw/master/systemd-timesyncd.service.d-wait.conf &> /dev/null +make &> /dev/null +make install &> /dev/null +cd ~/ + +cat <<'EOF' >> /lib/systemd/system/timers.target +Requires=systemd-timesyncd-wait.service +EOF +systemctl enable cfupdate.timer &> /dev/null +printf "[${GREEN}${bold}完成${NC}${normal}] Systemd 配置完成\n" +printf "[${GREEN}${bold}提示${NC}${normal}] 設置完成,計時器執行紀錄紀錄於 /var/log/cfupdater.log\n" +} +function start_systemd_timer { +printf "[${GREEN}${bold}啟動${NC}${normal}] 正在啟動 Systemd 計時器\n" +#systemctl daemon-reload +systemctl start cfupdate.timer +#systemctl status cfupdate.timer +printf "[${GREEN}${bold}啟動${NC}${normal}] 已啟動 Systemd 計時器\n" +} + +printf "[${GREEN}${bold}開始${NC}${normal}] 腳本開始\n" +chk_root +interactive +install_dependencies +set_timezone +setup_ntp +generate_cfupdater_script +setup_systemd +start_systemd_timer +printf "[${GREEN}${bold}完成${NC}${normal}] 腳本完成\n" +exit 0 From d3576d97577ac8796b7c48fe158099616b078b5d Mon Sep 17 00:00:00 2001 From: dino2895un Date: Thu, 24 Oct 2024 19:48:26 +0800 Subject: [PATCH 2/9] Add UTC+8 timestamp to log --- CloudFlare-DDNS-Setup.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CloudFlare-DDNS-Setup.sh b/CloudFlare-DDNS-Setup.sh index 36dcbcc..ae0c31d 100644 --- a/CloudFlare-DDNS-Setup.sh +++ b/CloudFlare-DDNS-Setup.sh @@ -134,11 +134,12 @@ EOF cat <<'EOF' >> /usr/bin/cfupdater-v4 # DO NOT CHANGE LINES BELOW -ip=$(curl -s https://ipv4.icanhazip.com/) +ip=$(curl -s -6 https://ip.made.moe/) +comment="Updated by Cloudflare-DDNS at "$(TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8") # SCRIPT START -echo -n `date +"[%m/%d %H:%M:%S] "` >> /var/log/cfupdater.log +echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater.log echo "[Cloudflare DDNS] Check Initiated" >> /var/log/cfupdater.log -echo -n `date +"[%m/%d %H:%M:%S] "` >> /var/log/cfupdater.log +echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater.log # Seek for the record record=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?name=$record_name" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json") # Can't do anything without the record @@ -156,7 +157,7 @@ fi # Set the record identifier from result record_identifier=$(echo "$record" | grep -Po '(?<="id":")[^"]*' | head -1) # The execution of update -update=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records/$record_identifier" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" --data "{\"id\":\"$zone_identifier\",\"type\":\"A\",\"proxied\":false,\"name\":\"$record_name\",\"content\":\"$ip\"}") +update=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records/$record_identifier" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" --data "{\"id\":\"$zone_identifier\",\"type\":\"A\",\"proxied\":false,\"name\":\"$record_name\",\"comment\":\"$comment\",\"content\":\"$ip\"}") # The moment of truth case "$update" in *"\"success\":false"*) From e28471b98f1c76c68f71da08b6292218b50bfd77 Mon Sep 17 00:00:00 2001 From: dino2895un Date: Fri, 22 Nov 2024 14:40:42 +0800 Subject: [PATCH 3/9] Update: Using separate v4 log file --- CloudFlare-DDNS-Setup.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/CloudFlare-DDNS-Setup.sh b/CloudFlare-DDNS-Setup.sh index ae0c31d..d93b0bf 100644 --- a/CloudFlare-DDNS-Setup.sh +++ b/CloudFlare-DDNS-Setup.sh @@ -121,7 +121,7 @@ EOF function generate_cfupdater_script { printf "[${GREEN}${bold}配置${NC}${normal}] 開始生成 CloudFlare DDNS 腳本\n" -touch /var/log/cfupdater.log +touch /var/log/cfupdater-v4.log cat < /usr/bin/cfupdater-v4 #!/bin/bash # Forked by benkulbertis/cloudflare-update-record.sh @@ -134,12 +134,12 @@ EOF cat <<'EOF' >> /usr/bin/cfupdater-v4 # DO NOT CHANGE LINES BELOW -ip=$(curl -s -6 https://ip.made.moe/) +ip=$(curl -s https://ip.made.moe/) comment="Updated by Cloudflare-DDNS at "$(TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8") # SCRIPT START -echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater.log -echo "[Cloudflare DDNS] Check Initiated" >> /var/log/cfupdater.log -echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater.log +echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater-4.log +echo "[Cloudflare DDNS] Check Initiated" >> /var/log/cfupdater-4.log +echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater-4.log # Seek for the record record=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?name=$record_name" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json") # Can't do anything without the record @@ -151,7 +151,7 @@ fi old_ip=$(echo "$record" | grep -Po '(?<="content":")[^"]*' | head -1) # Compare if they're the same if [ $ip == $old_ip ]; then - echo "[Cloudflare DDNS] IP has not changed." >> /var/log/cfupdater.log + echo "[Cloudflare DDNS] IP has not changed." >> /var/log/cfupdater-4.log exit 0 fi # Set the record identifier from result @@ -161,10 +161,10 @@ update=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identi # The moment of truth case "$update" in *"\"success\":false"*) - >&2 echo -e "[Cloudflare DDNS] Update failed for $record_identifier. DUMPING RESULTS:\n$update" >> /var/log/cfupdater.log + >&2 echo -e "[Cloudflare DDNS] Update failed for $record_identifier. DUMPING RESULTS:\n$update" >> /var/log/cfupdater-4.log exit 1;; *) - echo "[Cloudflare DDNS] IPv4 context '$ip' has been synced to Cloudflare." >> /var/log/cfupdater.log;; + echo "[Cloudflare DDNS] IPv4 context '$ip' has been synced to Cloudflare." >> /var/log/cfupdater-4.log;; esac EOF @@ -214,7 +214,7 @@ Requires=systemd-timesyncd-wait.service EOF systemctl enable cfupdate.timer &> /dev/null printf "[${GREEN}${bold}完成${NC}${normal}] Systemd 配置完成\n" -printf "[${GREEN}${bold}提示${NC}${normal}] 設置完成,計時器執行紀錄紀錄於 /var/log/cfupdater.log\n" +printf "[${GREEN}${bold}提示${NC}${normal}] 設置完成,計時器執行紀錄紀錄於 /var/log/cfupdater-4.log\n" } function start_systemd_timer { printf "[${GREEN}${bold}啟動${NC}${normal}] 正在啟動 Systemd 計時器\n" From 5cb694d74117420652f0ce97582e1eb9bfdc2803 Mon Sep 17 00:00:00 2001 From: dino2895un Date: Fri, 22 Nov 2024 14:41:19 +0800 Subject: [PATCH 4/9] Update: useing separate v6 logfile --- CloudFlare-DDNS-Setup-v6.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CloudFlare-DDNS-Setup-v6.sh b/CloudFlare-DDNS-Setup-v6.sh index 35afac1..6947d95 100644 --- a/CloudFlare-DDNS-Setup-v6.sh +++ b/CloudFlare-DDNS-Setup-v6.sh @@ -121,7 +121,7 @@ EOF function generate_cfupdater_script { printf "[${GREEN}${bold}配置${NC}${normal}] 開始生成 CloudFlare DDNS 腳本\n" -touch /var/log/cfupdater.log +touch /var/log/cfupdater-6.log cat < /usr/bin/cfupdater-v6 #!/bin/bash # Forked by benkulbertis/cloudflare-update-record.sh @@ -137,9 +137,9 @@ cat <<'EOF' >> /usr/bin/cfupdater-v6 ip=$(curl -s -6 https://ip.made.moe/) comment="Updated by Cloudflare-DDNS at "$(TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8") # SCRIPT START -echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater.log -echo "[Cloudflare DDNS] Check Initiated" >> /var/log/cfupdater.log -echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater.log +echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater-6.log +echo "[Cloudflare DDNS] Check Initiated" >> /var/log/cfupdater-6.log +echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater-6.log # Seek for the record record=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?name=$record_name" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json") # Can't do anything without the record @@ -151,7 +151,7 @@ fi old_ip=$(echo "$record" | grep -Po '(?<="content":")[^"]*' | head -1) # Compare if they're the same if [ $ip == $old_ip ]; then - echo "[Cloudflare DDNS] IP has not changed." >> /var/log/cfupdater.log + echo "[Cloudflare DDNS] IP has not changed." >> /var/log/cfupdater-6.log exit 0 fi # Set the record identifier from result @@ -161,10 +161,10 @@ update=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identi # The moment of truth case "$update" in *"\"success\":false"*) - >&2 echo -e "[Cloudflare DDNS] Update failed for $record_identifier. DUMPING RESULTS:\n$update" >> /var/log/cfupdater.log + >&2 echo -e "[Cloudflare DDNS] Update failed for $record_identifier. DUMPING RESULTS:\n$update" >> /var/log/cfupdater-6.log exit 1;; *) - echo "[Cloudflare DDNS] IPv6 context '$ip' has been synced to Cloudflare." >> /var/log/cfupdater.log;; + echo "[Cloudflare DDNS] IPv6 context '$ip' has been synced to Cloudflare." >> /var/log/cfupdater-6.log;; esac EOF @@ -214,7 +214,7 @@ Requires=systemd-timesyncd-wait.service EOF systemctl enable cfupdate.timer &> /dev/null printf "[${GREEN}${bold}完成${NC}${normal}] Systemd 配置完成\n" -printf "[${GREEN}${bold}提示${NC}${normal}] 設置完成,計時器執行紀錄紀錄於 /var/log/cfupdater.log\n" +printf "[${GREEN}${bold}提示${NC}${normal}] 設置完成,計時器執行紀錄紀錄於 /var/log/cfupdater-6.log\n" } function start_systemd_timer { printf "[${GREEN}${bold}啟動${NC}${normal}] 正在啟動 Systemd 計時器\n" From 340d3dcf917bef7e70c2dcd742eb06a7cc15f90c Mon Sep 17 00:00:00 2001 From: dino2895un Date: Fri, 22 Nov 2024 15:13:45 +0800 Subject: [PATCH 5/9] fix: v4 log file --- CloudFlare-DDNS-Setup.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CloudFlare-DDNS-Setup.sh b/CloudFlare-DDNS-Setup.sh index d93b0bf..4ae9da2 100644 --- a/CloudFlare-DDNS-Setup.sh +++ b/CloudFlare-DDNS-Setup.sh @@ -137,9 +137,9 @@ cat <<'EOF' >> /usr/bin/cfupdater-v4 ip=$(curl -s https://ip.made.moe/) comment="Updated by Cloudflare-DDNS at "$(TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8") # SCRIPT START -echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater-4.log -echo "[Cloudflare DDNS] Check Initiated" >> /var/log/cfupdater-4.log -echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater-4.log +echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater-v4.log +echo "[Cloudflare DDNS] Check Initiated" >> /var/log/cfupdater-v4.log +echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater-v4.log # Seek for the record record=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?name=$record_name" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json") # Can't do anything without the record @@ -151,7 +151,7 @@ fi old_ip=$(echo "$record" | grep -Po '(?<="content":")[^"]*' | head -1) # Compare if they're the same if [ $ip == $old_ip ]; then - echo "[Cloudflare DDNS] IP has not changed." >> /var/log/cfupdater-4.log + echo "[Cloudflare DDNS] IP has not changed." >> /var/log/cfupdater-v4.log exit 0 fi # Set the record identifier from result @@ -161,10 +161,10 @@ update=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identi # The moment of truth case "$update" in *"\"success\":false"*) - >&2 echo -e "[Cloudflare DDNS] Update failed for $record_identifier. DUMPING RESULTS:\n$update" >> /var/log/cfupdater-4.log + >&2 echo -e "[Cloudflare DDNS] Update failed for $record_identifier. DUMPING RESULTS:\n$update" >> /var/log/cfupdater-v4.log exit 1;; *) - echo "[Cloudflare DDNS] IPv4 context '$ip' has been synced to Cloudflare." >> /var/log/cfupdater-4.log;; + echo "[Cloudflare DDNS] IPv4 context '$ip' has been synced to Cloudflare." >> /var/log/cfupdater-v4.log;; esac EOF @@ -214,7 +214,7 @@ Requires=systemd-timesyncd-wait.service EOF systemctl enable cfupdate.timer &> /dev/null printf "[${GREEN}${bold}完成${NC}${normal}] Systemd 配置完成\n" -printf "[${GREEN}${bold}提示${NC}${normal}] 設置完成,計時器執行紀錄紀錄於 /var/log/cfupdater-4.log\n" +printf "[${GREEN}${bold}提示${NC}${normal}] 設置完成,計時器執行紀錄紀錄於 /var/log/cfupdater-v4.log\n" } function start_systemd_timer { printf "[${GREEN}${bold}啟動${NC}${normal}] 正在啟動 Systemd 計時器\n" From 1393353ff5ca1f53368553c3fca0e676d4276d3d Mon Sep 17 00:00:00 2001 From: dino2895un Date: Fri, 22 Nov 2024 15:17:53 +0800 Subject: [PATCH 6/9] fix: use IPv4 for IP retrieval in cfupdater-v4 script --- CloudFlare-DDNS-Setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CloudFlare-DDNS-Setup.sh b/CloudFlare-DDNS-Setup.sh index 4ae9da2..aabeee4 100644 --- a/CloudFlare-DDNS-Setup.sh +++ b/CloudFlare-DDNS-Setup.sh @@ -134,7 +134,7 @@ EOF cat <<'EOF' >> /usr/bin/cfupdater-v4 # DO NOT CHANGE LINES BELOW -ip=$(curl -s https://ip.made.moe/) +ip=$(curl -s -4 https://ip.made.moe/) comment="Updated by Cloudflare-DDNS at "$(TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8") # SCRIPT START echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater-v4.log From c4bf90050274f47669ed0d6e20ca5f025cec4972 Mon Sep 17 00:00:00 2001 From: dino2895un Date: Fri, 22 Nov 2024 15:23:05 +0800 Subject: [PATCH 7/9] fix: update log file name to cfupdater-v6.log for IPv6 script --- CloudFlare-DDNS-Setup-v6.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CloudFlare-DDNS-Setup-v6.sh b/CloudFlare-DDNS-Setup-v6.sh index 6947d95..360c507 100644 --- a/CloudFlare-DDNS-Setup-v6.sh +++ b/CloudFlare-DDNS-Setup-v6.sh @@ -121,7 +121,7 @@ EOF function generate_cfupdater_script { printf "[${GREEN}${bold}配置${NC}${normal}] 開始生成 CloudFlare DDNS 腳本\n" -touch /var/log/cfupdater-6.log +touch /var/log/cfupdater-v6.log cat < /usr/bin/cfupdater-v6 #!/bin/bash # Forked by benkulbertis/cloudflare-update-record.sh @@ -137,9 +137,9 @@ cat <<'EOF' >> /usr/bin/cfupdater-v6 ip=$(curl -s -6 https://ip.made.moe/) comment="Updated by Cloudflare-DDNS at "$(TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8") # SCRIPT START -echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater-6.log -echo "[Cloudflare DDNS] Check Initiated" >> /var/log/cfupdater-6.log -echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater-6.log +echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater-v6.log +echo "[Cloudflare DDNS] Check Initiated" >> /var/log/cfupdater-v6.log +echo -n `TZ='Asia/Taipei' date +"[%m/%d %H:%M:%S] UTC+8 "` >> /var/log/cfupdater-v6.log # Seek for the record record=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?name=$record_name" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json") # Can't do anything without the record @@ -151,7 +151,7 @@ fi old_ip=$(echo "$record" | grep -Po '(?<="content":")[^"]*' | head -1) # Compare if they're the same if [ $ip == $old_ip ]; then - echo "[Cloudflare DDNS] IP has not changed." >> /var/log/cfupdater-6.log + echo "[Cloudflare DDNS] IP has not changed." >> /var/log/cfupdater-v6.log exit 0 fi # Set the record identifier from result @@ -161,10 +161,10 @@ update=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identi # The moment of truth case "$update" in *"\"success\":false"*) - >&2 echo -e "[Cloudflare DDNS] Update failed for $record_identifier. DUMPING RESULTS:\n$update" >> /var/log/cfupdater-6.log + >&2 echo -e "[Cloudflare DDNS] Update failed for $record_identifier. DUMPING RESULTS:\n$update" >> /var/log/cfupdater-v6.log exit 1;; *) - echo "[Cloudflare DDNS] IPv6 context '$ip' has been synced to Cloudflare." >> /var/log/cfupdater-6.log;; + echo "[Cloudflare DDNS] IPv6 context '$ip' has been synced to Cloudflare." >> /var/log/cfupdater-v6.log;; esac EOF @@ -214,7 +214,7 @@ Requires=systemd-timesyncd-wait.service EOF systemctl enable cfupdate.timer &> /dev/null printf "[${GREEN}${bold}完成${NC}${normal}] Systemd 配置完成\n" -printf "[${GREEN}${bold}提示${NC}${normal}] 設置完成,計時器執行紀錄紀錄於 /var/log/cfupdater-6.log\n" +printf "[${GREEN}${bold}提示${NC}${normal}] 設置完成,計時器執行紀錄紀錄於 /var/log/cfupdater-v6.log\n" } function start_systemd_timer { printf "[${GREEN}${bold}啟動${NC}${normal}] 正在啟動 Systemd 計時器\n" From 2ef00234b357fe54f9ada6c2ba11fd3561938533 Mon Sep 17 00:00:00 2001 From: dino2895un Date: Fri, 22 Nov 2024 15:44:05 +0800 Subject: [PATCH 8/9] fix: rename cfupdate service and timer for IPv6 support --- CloudFlare-DDNS-Setup-v6.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CloudFlare-DDNS-Setup-v6.sh b/CloudFlare-DDNS-Setup-v6.sh index 360c507..b3c5f00 100644 --- a/CloudFlare-DDNS-Setup-v6.sh +++ b/CloudFlare-DDNS-Setup-v6.sh @@ -173,7 +173,7 @@ printf "[${GREEN}${bold}完成${NC}${normal}] CloudFlare DDNS 腳本生成完成 } function setup_systemd { printf "[${GREEN}${bold}配置${NC}${normal}] 開始配置 Systemd\n" -cat <<'EOF' > /etc/systemd/system/cfupdate.service +cat <<'EOF' > /etc/systemd/system/cfupdate-v6.service [Unit] Description=Cloudflare DDNS service After=network.target @@ -184,18 +184,18 @@ ExecStart=/usr/bin/cfupdater-v6 WantedBy=multi-user.target EOF -chmod 644 /etc/systemd/system/cfupdate.service +chmod 644 /etc/systemd/system/cfupdate-v6.service -cat < /etc/systemd/system/cfupdate.timer +cat < /etc/systemd/system/cfupdate-v6.timer [Unit] -Description=Run cfupdate.service when the timer ticks +Description=Run cfupdate-v6.service when the timer ticks [Timer] OnCalendar=*:${seconds} AccuracySec=1ms [Install] WantedBy=timers.target EOF -chmod 644 /etc/systemd/system/cfupdate.timer +chmod 644 /etc/systemd/system/cfupdate-v6.timer mkdir ~/systemd-timesyncd-wait/ cd ~/systemd-timesyncd-wait/ @@ -212,15 +212,15 @@ cd ~/ cat <<'EOF' >> /lib/systemd/system/timers.target Requires=systemd-timesyncd-wait.service EOF -systemctl enable cfupdate.timer &> /dev/null +systemctl enable cfupdate-v6.timer &> /dev/null printf "[${GREEN}${bold}完成${NC}${normal}] Systemd 配置完成\n" printf "[${GREEN}${bold}提示${NC}${normal}] 設置完成,計時器執行紀錄紀錄於 /var/log/cfupdater-v6.log\n" } function start_systemd_timer { printf "[${GREEN}${bold}啟動${NC}${normal}] 正在啟動 Systemd 計時器\n" #systemctl daemon-reload -systemctl start cfupdate.timer -#systemctl status cfupdate.timer +systemctl start cfupdate-v6.timer +#systemctl status cfupdate-v6.timer printf "[${GREEN}${bold}啟動${NC}${normal}] 已啟動 Systemd 計時器\n" } From 822d9025670a84562be07d1eb006669cc0a50f6c Mon Sep 17 00:00:00 2001 From: dino2895un Date: Mon, 23 Dec 2024 02:47:30 +0800 Subject: [PATCH 9/9] newfile:cfupdater.sh and dockerfile --- .env.example | 7 ++ .gitignore | 2 + Dockerfile | 17 +++++ cfupdater.service | 12 ++++ cfupdater.sh | 157 ++++++++++++++++++++++++++++++++++++++++++++ docker-compose.yaml | 17 +++++ 6 files changed, 212 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 cfupdater.service create mode 100755 cfupdater.sh create mode 100644 docker-compose.yaml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..65314ae --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +AUTH_EMAIL="your_email@example.com" +AUTH_KEY="your_global_api_key" +ZONE_IDENTIFIER="your_zone_id" +DOMAIN_SUFFIX=".ptr.mydomain.com" +UPDATE_INTERVAL=60 +ENABLE_IPV4=true +ENABLE_IPV6=true \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c842e4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +.DS_Store \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e46d044 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +# 使用輕量級的基礎映像 +FROM alpine:latest + +# 安裝必要工具 +RUN apk add --no-cache bash curl jq tzdata + +# 設置工作目錄 +WORKDIR /app + +# 複製腳本到容器中 +COPY cfupdater.sh /app/cfupdater.sh + +# 設置腳本可執行權限 +RUN chmod +x /app/cfupdater.sh + +# 執行腳本 +CMD ["/app/cfupdater.sh"] \ No newline at end of file diff --git a/cfupdater.service b/cfupdater.service new file mode 100644 index 0000000..f0b51c5 --- /dev/null +++ b/cfupdater.service @@ -0,0 +1,12 @@ +#/etc/systemd/system/cfupdater.service +[Unit] +Description=Cloudflare DDNS Updater +After=network.target + +[Service] +ExecStart=/path/to/cfupdater.sh +EnvironmentFile=/path/to/.env +Restart=always + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/cfupdater.sh b/cfupdater.sh new file mode 100755 index 0000000..8e7c609 --- /dev/null +++ b/cfupdater.sh @@ -0,0 +1,157 @@ +#!/bin/bash + +# set -ex # Uncomment for debugging +set -e + +RED='\033[0;31m' +NC='\033[0m' # No Color +GREEN='\033[0;32m' + +# 捕捉 SIGINT(Ctrl-C) 和 SIGTERM 信號 +trap "echo -e '\n[${RED}INFO${NC}] Script terminated by user'; exit 0" SIGINT SIGTERM + +# 檢查所需工具是否安裝 +function check_dependencies() { + local dependencies=("bash" "curl" "jq" "tzdata") + local missing=() + + for dep in "${dependencies[@]}"; do + if ! command -v "$dep" &> /dev/null; then + missing+=("$dep") + fi + done + + if [[ ${#missing[@]} -ne 0 ]]; then + echo -e "[${RED}ERROR${NC}] Missing dependencies: ${missing[*]}" + echo -e "[${GREEN}INFO${NC}] Please install the missing tools and try again." + exit 1 + fi +} + +# 檢查依賴 +# check_dependencies + +if [ -f .env ]; then + export $(grep -v '^#' .env | xargs) +fi + +# 檢查必要環境變數 +: "${AUTH_EMAIL:?Please set AUTH_EMAIL}" +: "${AUTH_KEY:?Please set AUTH_KEY}" +: "${ZONE_IDENTIFIER:?Please set ZONE_IDENTIFIER}" +: "${DOMAIN_SUFFIX:?Please set DOMAIN_SUFFIX}" +: "${UPDATE_INTERVAL:=60}" +: "${ENABLE_IPV4:=true}" # 默認啟用 IPv4 更新 +: "${ENABLE_IPV6:=true}" # 默認啟用 IPv6 更新 + +# 判斷系統類型並生成持久 ID +if [[ -f "/etc/machine-id" ]]; then + UUID=$(cat /etc/machine-id) +elif [[ "$OSTYPE" == "darwin"* ]]; then + # 使用 ioreg 提取硬碟序列號作為持久 ID + UUID=$(ioreg -rd1 -c IOPlatformExpertDevice | awk -F\" '/IOPlatformUUID/{print $4}') +else + echo -e "[${RED}ERROR${NC}] Unable to determine a persistent machine ID for $OSTYPE" + exit 1 +fi + +DOMAIN="${UUID}${DOMAIN_SUFFIX}" + +# # 生成 UUID 格式的 domain +# UUID=$(cat /proc/sys/kernel/random/uuid) +# DOMAIN="${UUID}${DOMAIN_SUFFIX}" + +echo -e "[${GREEN}INFO${NC}] Generated domain: ${DOMAIN}" + + +while true; do + echo -e "[${GREEN}INFO${NC}] Starting IP update process..." + + # 獲取 IPv4 和 IPv6 地址 + IP4=$(curl -s -4 https://api.ipify.org || echo "ERROR") + IP6=$(curl -s -6 https://api64.ipify.org || echo "ERROR") + + if [[ "$ENABLE_IPV4" == "true" ]]; then + if [[ "$IP4" == "ERROR" ]]; then + echo -e "[${RED}ERROR${NC}] Failed to fetch IPv4 address." + else + echo -e "[${GREEN}INFO${NC}] Detected IPv4: $IP4" + fi + fi + + if [[ "$ENABLE_IPV6" == "true" ]]; then + if [[ "$IP6" == "ERROR" || -z "$IP6" ]]; then + echo -e "[${RED}WARNING${NC}] Failed to fetch IPv6 address or IPv6 not supported." + else + echo -e "[${GREEN}INFO${NC}] Detected IPv6: $IP6" + fi + fi + + # 獲取主機名和正確的 UTC+8 時間 + HOSTNAME=$(hostname) + TIMESTAMP=$(TZ='Asia/Taipei' date +"[%Y-%m-%d %H:%M:%S] UTC+8") + + # 查詢是否已有 A 和 AAAA 記錄 + if [[ "$ENABLE_IPV4" == "true" ]]; then + RECORD_A=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/${ZONE_IDENTIFIER}/dns_records?type=A&name=${DOMAIN}" \ + -H "X-Auth-Email: ${AUTH_EMAIL}" \ + -H "X-Auth-Key: ${AUTH_KEY}" \ + -H "Content-Type: application/json") + + RECORD_A_ID=$(echo "$RECORD_A" | jq -r '.result[0].id') + CURRENT_IP4=$(echo "$RECORD_A" | jq -r '.result[0].content') + + if [[ "$IP4" != "ERROR" ]]; then + if [[ "$RECORD_A_ID" == "null" ]]; then + echo -e "[${GREEN}INFO${NC}] A record not found. Creating A record for ${DOMAIN}..." + curl -s -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_IDENTIFIER}/dns_records" \ + -H "X-Auth-Email: ${AUTH_EMAIL}" \ + -H "X-Auth-Key: ${AUTH_KEY}" \ + -H "Content-Type: application/json" \ + --data '{"type":"A","name":"'"$DOMAIN"'","content":"'"$IP4"'","ttl":120,"proxied":false,"comment":"Updated by '"$HOSTNAME"' at '"$TIMESTAMP"'"}' > /dev/null + elif [[ "$IP4" != "$CURRENT_IP4" ]]; then + echo -e "[${GREEN}INFO${NC}] Updating A record for ${DOMAIN} to ${IP4}..." + curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/${ZONE_IDENTIFIER}/dns_records/${RECORD_A_ID}" \ + -H "X-Auth-Email: ${AUTH_EMAIL}" \ + -H "X-Auth-Key: ${AUTH_KEY}" \ + -H "Content-Type: application/json" \ + --data '{"type":"A","name":"'"$DOMAIN"'","content":"'"$IP4"'","ttl":120,"proxied":false,"comment":"Updated by '"$HOSTNAME"' at '"$TIMESTAMP"'"}' > /dev/null + else + echo -e "[${GREEN}INFO${NC}] A record is already up-to-date: $IP4" + fi + fi + fi + + if [[ "$ENABLE_IPV6" == "true" ]]; then + RECORD_AAAA=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/${ZONE_IDENTIFIER}/dns_records?type=AAAA&name=${DOMAIN}" \ + -H "X-Auth-Email: ${AUTH_EMAIL}" \ + -H "X-Auth-Key: ${AUTH_KEY}" \ + -H "Content-Type: application/json") + + RECORD_AAAA_ID=$(echo "$RECORD_AAAA" | jq -r '.result[0].id') + CURRENT_IP6=$(echo "$RECORD_AAAA" | jq -r '.result[0].content') + + if [[ "$IP6" != "ERROR" && -n "$IP6" ]]; then + if [[ "$RECORD_AAAA_ID" == "null" ]]; then + echo -e "[${GREEN}INFO${NC}] AAAA record not found. Creating AAAA record for ${DOMAIN}..." + curl -s -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_IDENTIFIER}/dns_records" \ + -H "X-Auth-Email: ${AUTH_EMAIL}" \ + -H "X-Auth-Key: ${AUTH_KEY}" \ + -H "Content-Type: application/json" \ + --data '{"type":"AAAA","name":"'"$DOMAIN"'","content":"'"$IP6"'","ttl":120,"proxied":false,"comment":"Updated by '"$HOSTNAME"' at '"$TIMESTAMP"'"}' > /dev/null + elif [[ "$IP6" != "$CURRENT_IP6" ]]; then + echo -e "[${GREEN}INFO${NC}] Updating AAAA record for ${DOMAIN} to ${IP6}..." + curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/${ZONE_IDENTIFIER}/dns_records/${RECORD_AAAA_ID}" \ + -H "X-Auth-Email: ${AUTH_EMAIL}" \ + -H "X-Auth-Key: ${AUTH_KEY}" \ + -H "Content-Type: application/json" \ + --data '{"type":"AAAA","name":"'"$DOMAIN"'","content":"'"$IP6"'","ttl":120,"proxied":false,"comment":"Updated by '"$HOSTNAME"' at '"$TIMESTAMP"'"}' > /dev/null + else + echo -e "[${GREEN}INFO${NC}] AAAA record is already up-to-date: $IP6" + fi + fi + fi + + echo -e "[${GREEN}INFO${NC}] Update completed. Sleeping for ${UPDATE_INTERVAL} seconds..." + sleep "${UPDATE_INTERVAL}" & wait $! +done diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..daae687 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,17 @@ +version: '3.8' + +services: + cloudflare-ddns: + image: cloudflare-ddns:latest + build: + context: . + container_name: cloudflare-ddns + restart: always + environment: + - AUTH_EMAIL=${AUTH_EMAIL} + - AUTH_KEY=${AUTH_KEY} + - ZONE_IDENTIFIER=${ZONE_IDENTIFIER} + - DOMAIN_SUFFIX=${DOMAIN_SUFFIX} + - UPDATE_INTERVAL=${UPDATE_INTERVAL} + - ENABLE_IPV4=${ENABLE_IPV4} + - ENABLE_IPV6=${ENABLE_IPV6}