跳至正文
  • 551 views
  • 5 min read

cloudflare ddns自用更新脚本

新浪微博 豆瓣 QQ 百度贴吧 QQ空间

cloudflare ddns自用更新脚本,自建邮件+博客服务器专用,记录一下,以备下次需要的时候使用

使用方法:

在crontab里面添加如下行,每20分钟运行一次:

*/20 * * * * /usr/bin/python3 /usr/local/bin/update_all.py >> /var/log/update_all.log 2>&1

python版本:

#!/bin/env python3
# -*- coding: UTF-8 -*-
import requests
from requests.adapters import HTTPAdapter
import re
import json
import time
import sys
import os

req = requests.session()
req.mount('https://', HTTPAdapter(max_retries=9))
req.mount('http://', HTTPAdapter(max_retries=9))
base_url = "https://api.cloudflare.com/client/v4/zones"


def timeStamp():
    tStamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    return tStamp


def prtMsg(msgType="None", msgConn="Not defined msg type."):
    msgStamp = timeStamp()
    if msgType == "err":
        print(msgStamp + " [ERROR]" + ":" + msgConn)
    elif msgType == "msg":
        print(msgStamp + " [MESSAGE]" + ":" + msgConn)
    else:
        print(msgStamp + ":" + msgConn)


def get_ipv6_addr():
    res = os.popen("""/sbin/ip -6 addr show $(/sbin/ip a|awk -F ":" '/ ens| eth/&&!/inet/ {print $2}'
                        )|awk '/inet6/&&/240e/ {print $2}'|awk -F "/" '{print $1}'""").read()
    ipv6_list = [i for i in res.splitlines() if len(i) > 0]
    if ipv6_list:
        return (max(ipv6_list, key=len))
    else:
        return False


def get_ipv4_addr():
    ip = ''
    try:
        res = req.get('https://myip.ipip.net', timeout=5).text
        ip = re.findall(r'(\d+\.\d+\.\d+\.\d+)', res)
        ip = ip[0] if ip else ''
    except:
        pass
    return ip if ip else False


def get_cf_all_record(get_headers, zone_id):
    api_url = "{0}/{1}/dns_records".format(base_url, zone_id)
    res = req.get(api_url, headers=get_headers)
    return json.loads(res.text)['result']


def cr_headers_data(email, apikey):
    cf_headers = {
        "Content-Type": "application/json",
        "X-Auth-Email": email,
        "X-Auth-Key": apikey
    }
    return cf_headers

def cr_record_dict(headers, zone_id):
    rec_dict, zone_name = {}, ""
    all_rec = get_cf_all_record(headers, zone_id)
    for rec in all_rec:
        if not zone_name:
            zone_name = rec['zone_name']
        domain_name = ""
        if isinstance(rec, dict):
            if "name" in rec.keys():
                domain_name = "{0}-{1}".format(rec['name'], rec['type'])
        if domain_name:
            dom_dict = {'domain': rec['name'], 'id': rec['id'], 'type': rec['type'], 'content': rec['content'],
                        'proxied': rec['proxied']}
            if dom_dict:
                rec_dict[domain_name] = dom_dict
    return (rec_dict, zone_name) if rec_dict else ({}, "")


def cr_post_data(post_type, post_domain, post_content, proxy_type):
    post_data_json = {
      "type": post_type,
      "name": post_domain,
      "content": post_content,
      "ttl": 1,
      "proxied": proxy_type
    }
    return post_data_json


def put_cf_record(zone_id, record_id, put_headers, post_data):
    api_url = "{0}/{1}/dns_records/{2}".format(base_url, zone_id, record_id)
    res = req.put(api_url, headers=put_headers, json=post_data)
    if res.status_code == 200:
        return True
    else:
        return False


def main():
    email = "xxxxxxxxxxxxxxxxxxxxxxxxxxx"
    apikey = "xxxxxxxxxxxxxxxxxxxxxxxxxxx"
    zone_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxx"
    headers = cr_headers_data(email, apikey)
    all_rec_dict, zone_name = cr_record_dict(headers, zone_id)
    cf_list = ["www-AAAA", "gra-AAAA", "hub-AAAA", "ai-AAAA", "zbx-AAAA", "mail-TXT", "m-AAAA", "mail-A"]
    ipv6 = get_ipv6_addr()
    ipv4 = get_ipv4_addr()
    print("-" * 20)
    if ipv6 and ipv4:
        prtMsg('msg', "Current IPV6 address is {0}".format(ipv6))
        prtMsg('msg', "Current IPV4 address is {0}".format(ipv4))
    else:
        prtMsg('err', "GET IP address failed.")
        sys.exit(10)
    for cf in cf_list:
        post_domain, post_type = cf.split("-")
        if post_type == "AAAA":
            post_content = ipv6
        elif post_type == "A":
            post_content = ipv4
        else:
            post_content = "v=spf1 mx ip4:{0} ip6:{1} ~all".format(ipv4, ipv6)
        post_domain_name = "{0}.{1}".format(post_domain, zone_name)
        if post_type == "TXT":
            post_domain_name = zone_name
        post_key = "{0}-{1}".format(post_domain_name, post_type)
        if post_key in str(all_rec_dict):
            if post_content != all_rec_dict[post_key]['content']:
                proxy_type = all_rec_dict[post_key]['proxied']
                record_id = all_rec_dict[post_key]['id']
                post_data = cr_post_data(post_type, post_domain_name, post_content, proxy_type)
                res = put_cf_record(zone_id, record_id, headers, post_data)
                if res:
                    prtMsg('msg', 'Update {0} record for {1} success.'.format(post_domain_name, post_type))
                else:
                    prtMsg('err', 'Update {0} record for {1} failed.'.format(post_domain_name, post_type))
            else:
                prtMsg('msg', 'No needs to update {0} record for {1}.'.format(post_domain_name, post_type))
        else:
            prtMsg('err', 'GET {0} Record for {1} failed.'.format(post_domain_name, post_type))



if __name__ == '__main__':
    main()

shell版本:

#!/bin/sh

# --- Configuration ---
# Your Cloudflare credentials
EMAIL="[email protected]"
API_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
ZONE_ID="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# Base API URL for Cloudflare
BASE_URL="https://api.cloudflare.com/client/v4/zones"

# List of DNS records to check and update
# Format: <subdomain>-<type>
CF_LIST="www-AAAA is-AAAA mail-TXT m-AAAA mail-A"

# --- Functions ---

# Print messages with a timestamp
prt_msg() {
  local msg_type="$1"
  local msg_conn="$2"
  local timestamp=$(date +"%Y-%m-%d %H:%M:%S")

  if [ "$msg_type" = "err" ]; then
    echo "$timestamp [ERROR]:$msg_conn"
  elif [ "$msg_type" = "msg" ]; then
    echo "$timestamp [MESSAGE]:$msg_conn"
  else
    echo "$timestamp:$msg_conn"
  fi
}

# Get the IPv6 address
get_ipv6_addr() {
  /sbin/ip -6 addr show br-lan | awk '/inet6/&&/240e/ {print $2}' | awk -F'/' '{print $1}' | sort | tail -n 1
}

# Get the IPv4 address
get_ipv4_addr() {
  curl -s https://myip.ipip.net | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -n 1
}

# Get Cloudflare DNS records and zone name
get_cf_records() {
  local get_headers="-H Content-Type:application/json -H X-Auth-Email:$EMAIL -H X-Auth-Key:$API_KEY"
  local api_url="${BASE_URL}/${ZONE_ID}/dns_records"
  local zone_url="${BASE_URL}/${ZONE_ID}"

  RECORDS=$(curl -s "$api_url" $get_headers)
  ZONE_INFO=$(curl -s "$zone_url" $get_headers)
  ZONE_NAME=$(echo "$ZONE_INFO" | jq -r '.result.name')

  if [ -z "$ZONE_NAME" ]; then
    prt_msg "err" "Failed to get Zone Name and DNS records."
    exit 10
  fi
}

# Update Cloudflare DNS record
put_cf_record() {
  local record_id="$1"
  local put_headers="-H Content-Type:application/json -H X-Auth-Email:$EMAIL -H X-Auth-Key:$API_KEY"
  local post_data="$2"
  local api_url="${BASE_URL}/${ZONE_ID}/dns_records/${record_id}"
  local post_domain_name="$3" # <-- New argument
  local post_type="$4"       # <-- New argument

  RESPONSE=$(curl -s -X PUT "$api_url" $put_headers --data-binary "$post_data")
  SUCCESS=$(echo "$RESPONSE" | jq -r '.success')

  if [ "$SUCCESS" = "true" ]; then
    prt_msg "msg" "Update $post_domain_name record for $post_type success."
  else
    prt_msg "err" "Update $post_domain_name record for $post_type failed."
  fi
}

# --- Main Logic ---

ipv6=$(get_ipv6_addr)
ipv4=$(get_ipv4_addr)

echo "--------------------"

if [ -z "$ipv6" ] || [ -z "$ipv4" ]; then
  prt_msg "err" "Failed to get IP address."
  exit 10
fi

prt_msg "msg" "Current IPv6 address is $ipv6"
prt_msg "msg" "Current IPv4 address is $ipv4"

get_cf_records

for CF_RECORD in $CF_LIST; do
  POST_DOMAIN=$(echo "$CF_RECORD" | awk -F'-' '{print $1}')
  POST_TYPE=$(echo "$CF_RECORD" | awk -F'-' '{print $2}')

  case "$POST_TYPE" in
    "AAAA")
      POST_CONTENT="$ipv6"
      ;;
    "A")
      POST_CONTENT="$ipv4"
      ;;
    "TXT")
      POST_CONTENT="v=spf1 mx ip4:$ipv4 ip6:$ipv6 ~all"
      ;;
  esac

  if [ "$POST_TYPE" = "TXT" ]; then
    POST_DOMAIN_NAME="$ZONE_NAME"
  else
    POST_DOMAIN_NAME="${POST_DOMAIN}.${ZONE_NAME}"
  fi

  RECORD_ID=$(echo "$RECORDS" | jq -r ".result[] | select(.name==\"$POST_DOMAIN_NAME\" and .type==\"$POST_TYPE\") | .id")
  CURRENT_CONTENT=$(echo "$RECORDS" | jq -r ".result[] | select(.name==\"$POST_DOMAIN_NAME\" and .type==\"$POST_TYPE\") | .content")
  PROXIED=$(echo "$RECORDS" | jq -r ".result[] | select(.name==\"$POST_DOMAIN_NAME\" and .type==\"$POST_TYPE\") | .proxied")
  
  if [ -n "$RECORD_ID" ] && [ -n "$CURRENT_CONTENT" ]; then
    if [ "$POST_CONTENT" != "$CURRENT_CONTENT" ]; then
      POST_DATA=$(jq -n \
        --arg type "$POST_TYPE" \
        --arg name "$POST_DOMAIN_NAME" \
        --arg content "$POST_CONTENT" \
        --argjson proxied "$PROXIED" \
        '{type: $type, name: $name, content: $content, ttl: 1, proxied: $proxied}')
      
      put_cf_record "$RECORD_ID" "$POST_DATA"
    else
      prt_msg "msg" "No needs to update ${POST_DOMAIN_NAME} record for ${POST_TYPE}."
    fi
  else
    prt_msg "err" "GET ${POST_DOMAIN_NAME} Record for ${POST_TYPE} failed."
  fi
done

发表回复

联系站长