架设DDNS服务器
文章目录
部署自己的动态DNS解析服务器.
部署原因
dnspod或者阿里云或者其他DDNS解析,ttl值基本上是600,
也就是缓存10分钟.
也就是你提交了IP变动,也需要10分钟其他人才会刷新.
即使你在这些平台花钱了,只能缩短ttl到60,也就是1分钟缓存.
如果想ttl变成1,也就是1秒,那么就要自己部署.
PDNS
安装
一般一键安装即可,各大linux软件包都有,直接请求.
可以用文件存解析,也可以用数据库.
我用的mysql,需要多安装一个pdns-backend-mysql
sudo apt install pdns-server pdns-backend-mysql
设置
一般就是指定下数据库,还有监听IP和端口
#指定mysql,数据库需要自己手动创建.
launch=gmysql
gmysql-host=127.0.0.1
gmysql-port=3307
gmysql-user=powerdns
gmysql-password=Password123
gmysql-dbname=powerdns
#设置监听IP范围和端口
local-address=0.0.0.0, ::1
local-port=53
开机启动
sudo systemctl enable pdns
sudo systemctl start pdns
#查看状态
sudo systemctl status pdns
开放端口
也就是53端口,除了系统还需要在VPS控制面板放行.
PowerAdmin
这个是一个web管理端. 有python也有php的还有其他开发语言(作者都不是同一个人,也不是一个团队).
我装的是PHP版本,而且是旧版本,因为新版本要php8.1 我服务器核心php版本是7.4
https://github.com/poweradmin/poweradmin/tree/v3.4.2
如果你不想装poweradmin,可以只导入PDNS数据库文件
https://github.com/poweradmin/poweradmin/blob/v3.4.2/sql/pdns/4.7.x/schema.mysql.sql
保证PDNS先运行.
建议就是poweradmin先安装,安装的时候web简单配置,自动导入数据库.
后续用不太上,后续禁用即可.
部署解析
我单独阿里云注册一个了纯数字xyz域名.60元还是多少10年有效期.
在阿里云解析里面指定域名DNS服务器为我域名
NS1.getce.cn和NS2.getce.cn 这两个都指向我服务器IP(只有一个服务器,所以是同一个IP).
poweradmin添加域名
添加模板(可选)
添加域名
添加解析
如果前面添加了模板,那么添加域名的时候,自动有前三条.
SOA 第一个NS1.getce.cn 是NS服务器,
第二个y.yge.me 实际上是邮箱,这里用点代替@,
第三个数字是序列号,20250524是日期,11是计数,每更新一次都会自动+1.
测试
可以用dig @服务器ip -p 端口 AAAA/SOA/A/NS 域名 查询看看生效情况.
一般新域名解析需要72小时通告全球(实际上半小时即可,最晚不超过72小时)
也可以用https://dnschecker.org查询
意外情况
我还没开始用,腾讯云就封停了53端口.
还发短信告诉我, 是"接到用户举报",所以关停了53端口.
后面我用我朋友海外服务器,做了DNS转发.核心还在我腾讯云服务器上.
DNS转发(可选)
如果服务器在国外直接53端口出来即可.我实在没办法,腾讯不给开放53,要转发.
我在我朋友海外服务器上安装了pdns-recursor.
改配置文件,
# /etc/powerdns/recursor.conf
local-address = 0.0.0.0, ::
local-port = 53
forward-zones = 5043240.xyz=106.55.33.60:54
allow-from = 0.0.0.0/0, ::/0 # 允许所有客户端(生产环境建议限制 IP 范围)
特别是5043240.xyz=106.55.33.60:54
将5043240.xyz所有解析全部指向106.55.33.60:54
部署DDNS
控制pdns有两种方式,一种是API,一种是直接操作数据库.我选择的后者.能用就行.比较好开发.
web后台
我web后台是thinkphp,在数据库配置文件中添加powerdns数据库配置,并添加一个模型.
<?php
namespace app\common\model\powerdns;
use app\common\model\BaseModel;
class Records extends BaseModel
{
protected $connection = 'powerdns';
protected $name = 'records';
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = false;
// 定义时间戳字段名
protected $createTime = false;
protected $updateTime = false;
}
添加一个API接口,后台接收了直接写入数据库.
注意要更新下SOA的系列号.
function updateSOARecord($soaRecord) {
// 分割 SOA 记录的各个部分
$parts = explode(' ', $soaRecord);
if (count($parts) != 7) {
throw new Exception("Invalid SOA record format");
}
// 提取旧的 Serial
$oldSerial = $parts[2];
// 分割日期部分和序号
$datePart = substr($oldSerial, 0, 8);
$sequence = intval(substr($oldSerial, 8, 2));
// 获取今日日期
$today = date("Ymd");
// 判断并生成新序列号
if ($datePart === $today) {
// 当天递增序号(00-99循环)
$newSequence = ($sequence + 1) % 100;
} else {
// 非当天重置序号
$newSequence = 0;
}
// 组合新序列号
$newSerial = $today . str_pad($newSequence, 2, '0', STR_PAD_LEFT);
// 更新 SOA 记录中的 Serial
$parts[2] = $newSerial;
// 重新组合 SOA 记录
return implode(' ', $parts);
}
客户端部分
如何提交
有两种常用方式
简单一点就是计划任务.好处可以适配任何linux系统. 特别我主要用在openwrt路由系统.缺点就是计划任务最短间隔是1分钟.
另外一种就是热拔插事件.好处就是响应极快.一旦获取IP变动就请求提交到web服务器.但缺点也非常明显,要网口断开或者连接才会触发热拔插.ipv6刷新不触发.
所以上面两个方法我都没用.
我用的自写服务,监控IPV6变化.
这是deepseek的方法,通过 ip monitor
结合 systemd 服务 实现IPv6地址变更时自动触发脚本
#!/bin/bash
# 监控 eth0 的地址变化事件
ip monitor address dev eth0 | while read -r line; do
# 检测到新增IPv6地址时(包含 "inet6" 和 "valid_lft" 关键字)
if echo "$line" | grep -q "inet6.*valid_lft"; then
# 延迟1秒等待地址稳定(可选)
sleep 1
# 执行更新脚本
/root/post_ipv6.sh
fi
done
添加服务并开机启动
# /etc/systemd/system/ipv6-monitor.service
# systemctl daemon-reload
# systemctl enable --now ipv6-monitor.service
[Unit]
Description=IPv6 Address Change Monitor
After=network.target
[Service]
ExecStart=/usr/local/bin/ipv6-monitor.sh
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
如何获取IP
有个LINUX系统通用的IP获取命令就是ip addr show [设备名] 然后过滤下就能获取到IP.
openwrt还有uci命令. ip addr show 方式更通用,甚至安卓系统也可以.
下面是我的/root/post_ipv6.sh脚本, 如果IPV4稍微调整下即可.
这个脚本可以用于openwrt/安卓手机
#!/bin/bash
# 设备名,自己可以先用ip -6 addr show 看看哪个设备名对应公网IP能被外部ping通,那么指定它的设备名
# 比如路由器拨号的一般是pppoe-wan
dev_name='eth0'
# 子域名 baidu 就是 baidu.5043240.xyz
sub_domian_name="baidu"
#############################################################################################################################
# 定义缓存文件路径
cache_file="/tmp/ipv6_addresses_cache"
# 提交到Web后台参数
url="https://api.getce.cn/v1/tools/ddns.html"
appid="***********************"
secret="*************************"
# 获取IPv6地址列表
ipv6_addresses=$(ip -6 addr show $dev_name | grep 'inet6'| grep '240' | awk '{print $2}' | cut -d/ -f1 | head -n1)
# 如果 ipv6_addresses 为空,则尝试不指定设备获取一个公网IPV6
if [ -z "$ipv6_addresses" ]; then
ipv6_addresses=$(ip -6 addr show | grep 'inet6'| grep '240' | awk '{print $2}' | cut -d/ -f1 | head -n1)
fi
echo "获取的地址:$ipv6_addresses"
# 读取缓存内容
if [ -f "$cache_file" ]; then
cached_ipv6_addresses=$(cat "$cache_file")
else
cached_ipv6_addresses=""
fi
# 比较新的IPv6地址列表与缓存内容
if [ "$ipv6_addresses" == "$cached_ipv6_addresses" ]; then
echo "IPV6地址未变化,无需更新."
else
# 发送POST请求
response=$(curl -X POST -s -o /dev/null -w "%{http_code}" -d "appid=${appid}" -d "secret=${secret}" -d "tools_name=${sub_domian_name}" -d "ip=${ipv6_addresses}" "$url")
echo "HTTP response code: $response"
# 检查HTTP响应码,并假设200表示成功(你应该解析JSON响应来确认)
if [ "$response" -eq 200 ]; then
# 更新缓存文件
echo "$ipv6_addresses" > "$cache_file"
echo "提交成功."
else
echo "提交失败."
fi
fi
最终效果
dhcpv6更新ip->ipv6-monitor.sh瞬间感知->post_ipv6.sh 提交IP ->web后台写入pdns数据库.->pdns更新.
大概2-3秒即可完成全球刷新.