前置条件:

  • 家庭公网 IP

  • 魔法上网

  • Python3.7+

  • Linux/NAS 服务器

一、NameSilo 购买域名

  1. 注册 NameSilo,我这里就不写了参考这个 https://blog.naibabiji.com/tutorial/namesilo-zhu-ce-jiao-cheng.html

  2. 在首页搜索你想要的域名

    image

    image​​​

image

image

弹出支付宝支付码扫码支付即可

  1. 域名配置,点击 蓝色地球​

image

删除原有的域名解析

image

二、CloudFlare 注册配置

  1. 注册并登录 CloudFlare 账号,点击 网站​-添加站点​

image

image

image

image

image

  • marty.ns.cloudflare.com

  • savanna.ns.cloudflare.com

回到 Namesilo

image

image

image

回到 CloudFlare 继续​ ​下一步

image

image

这里不要打开 https 重定向

跳转完成后点击 DNS记录​

image

image

打开电脑 cmd,先 ping 一下,看看是否 ping 通

image

OK,ping 通的进行下一步,再添加一个 www​ ​的 DNS 记录,ip 先随便填。

回到概览,先拷贝 API 的区域 ID 和账户 ID,再获取 API 令牌。

image

  • 区域 id:c998438d3******074bbe17ff96​

  • 账户 id:4b939c4eca******9382f30ec59​

获取 API 令牌

image

image

image

  • APIkey:HzzClZ19WX8*******Up27xLkE_Fzk1xa

python 自动更新脚本

#!/usr/bin/env python
"""
-------------------------------------------------
   FileName:        get_public_ip   
   Description:   获取家庭公网ip地址并自动ddns解析域名上
   Author:          Kizai
   Date:            2024/5/12
-------------------------------------------------
"""
# -*- coding: UTF-8 -*-
import os.path
import requests
import time
import hmac
import urllib
import base64
import hashlib
import json
import logging
import datetime

# 路径改成你脚本所在的位置 
file_path = '~/scripts/' # 全局路径 
log_path =  file_path + 'get_public_ip.log'
logging.basicConfig(filename=log_path, filemode='a+', level=logging.INFO,
 					format='%(asctime)s %(levelname)s %(filename)s %(funcName)s %(lineno)d %(message)s',
 					datefmt="%Y-%m-%d %H:%M:%S")

# Cloudfrare Setting
email = 'ki****@gmail.com'
zone_id = 'c998438d3****74bbe17ff96'
cf_api_key = '-7ixSCQJ8YyAAa****3bSRz74mEzF8tta'
headers = {
    'Authorization': 'Bearer ' + cf_api_key,
    'Content-Type': 'application/json'
}


def get_public_ip():
	url = "https://api.ipify.org?format=text"
	payload = ""
	headers = {}
	response = requests.request("GET", url, headers=headers, data=payload)
	public_ip = response.text
	logging.info(f"当前的公网IP为:{public_ip}")
	return public_ip


# 检查前一天的IP地址
def get_previous_ip(filepath: str) -> str:
	if os.path.exists(filepath):
		with open(filepath, 'r') as f:
			return f.read().strip()
	return None


# 保存当前的IP地址以供将来检查
def save_current_ip(filepath: str, ip: str):
	with open(filepath, 'w') as f:
		f.write(ip)

def get_dns_records():
    """
    获取DNS记录字典
    :return: DNS记录字典
    """
    response = requests.get(
        url='https://api.cloudflare.com/client/v4/zones/{}/dns_records?type=A&order=type&direction=desc&match=all'.format(
            zone_id), headers=headers).json()
    dns_dict = {}
    if response.get('result'):
        for dns in response.get('result'):
            dns_dict[dns.get('name')] = {'id': dns.get('id'), 'ip': dns.get('content'),
                                         'zone_name': dns.get('zone_name')}
        return dns_dict
    print('获取DNS记录列表失败:{}'.format(str(response)))
    return None

def create_dns_record(name, ip, ttl=1, dns_type='A'):
    """
    创建DNS记录
    :param name: 记录名称
    :param ip: 记录值(IP)
    :param ttl: 默认自动
    :param dns_type: 默认A记录
    :return: DNS记录信息
    """
    # 发送请求
    response = requests.post(url='https://api.cloudflare.com/client/v4/zones/{}/dns_records'.format(zone_id),
                             headers=headers, json={
            'type': dns_type,
            'name': name,
            'content': ip,
            'ttl': ttl
        }).json()
    # 判断结果
    if not response['success']:
        if response['errors'][0]['message'] == 'Record already exists.':
            print('记录已存在')
        else:
            print(response['errors'][0]['message'])
        return
    print(response)

# 更新cloudflare的DNS记录
def update_dns_record(record_id, name, ip, ttl=1, dns_type='A'):
    """
    更新DNS记录
    :param name: 记录名称
    :param ip: 记录值(IP)
    :param ttl: 默认自动
    :param dns_type: 默认A记录
    :return: DNS记录信息
    """
    response = requests.put('https://api.cloudflare.com/client/v4/zones/{}/dns_records/{}'.format(zone_id, record_id),
                            headers=headers, json={'type': dns_type, 'name': name, 'content': ip, 'ttl': ttl}).json()
    # 判断结果
    if not response['success']:
        print('更新DNS记录列表失败:'.format(str(response)))
    else:
        print('更新记录{} {}成功'.format(name, ip))
  
# 执行cf DNS解析
def run():
    # 获取该区域所有DNS A记录
    ip_filepath = file_path + 'previous_ip.txt' 
    new_ip = get_public_ip()
    previous_ip = get_previous_ip(ip_filepath)
    record_dict = get_dns_records()
    logging.info(record_dict)
    # print(record_dict)
    if not record_dict:
        return
  
    dns_name = 'blog' # 需要更新的DNS记录
    domain = record_dict[list(record_dict.keys())[0]].get('zone_name')
    # print(domain)
    dns_domain_name = f'{dns_name}.{domain}'
    # print(dns_domain_name)
    if new_ip and (not previous_ip or new_ip != previous_ip):
        update_dns_record(record_dict.get(dns_domain_name).get('id'), dns_name, ip=new_ip)  # 更新记录
        logging.info('更新记录:{}:{}'.format(dns_domain_name, new_ip))
    else:
        logging.info('记录值不一致,无需更新')
        # print('记录值一致,不需要更新')
  
if __name__ == "__main__":
    run()

后面就把这个脚本放在你创建的文件夹下,添加定时任务每 10 分钟运行一次即可:

sudo su
# 添加定时任务
vim /etc/crontab
# 添加下面这行,根据你的脚本路径来。
*/10 *   *   *   *   root    /bin/python /xxxx/Scripts/get_public_ip.py