DNSPOD使用教程
1、登录DNSPod
DNSPod:https://console.dnspod.cn/
2、创建API密钥
注意保存密码,只显示一次
3、配置请求
3.1、获取RecordList、RecordId
- Domain填写一级域名
- Subdomain填写二级域名
3.2、修改解析值
https://console.cloud.tencent.com/api/explorer?Product=dnspod&Version=2021-03-23&Action=ModifyRecord
- Domain填写一级域名
- Subdomain填写二级域名
- RecordId填写要修改的记录ID
- RecordType记录类型(我这里修改的IPV6,所以填AAAA;可以在https://console.cloud.tencent.com/cns/detail/xumeng03.com/records查阅记录类型)
- RecordLine填写“默认”即可
- Value填写要换的IP地址
确认在此页面可以修改正确后就进行下一步
4、修改域名工具(go)
编写过程中多有参考ddns-go:https://github.com/jeessy2/ddns-go,本工具链接:https://github.com/xumeng03/ddns
4.1、获取ipv6地址
package ip
import (
"fmt"
"net"
)
type NetInterface struct {
Name string
Address []string
}
func Ipv6() ([]NetInterface, error) {
// ipv6 有效地址是 2000::/3
_, effectiveIpv6, _ := net.ParseCIDR("2000::/3")
// 获取所有网络接口
allNetInterfaces, err := net.Interfaces()
if err != nil {
fmt.Println("net.Interfaces failed, err:", err.Error())
return nil, err
}
var ipv6NetInterfaces []NetInterface
// 遍历网络接口
for _, netInterface := range allNetInterfaces {
// 只处理接口状态处于活跃状态的网络接口
if netInterface.Flags&net.FlagUp == 0 {
continue
}
var ipv6 []string
// 获取网络接口的地址列表
if addrs, err := netInterface.Addrs(); err == nil {
// 遍历网络接口的地址列表
for _, addr := range addrs {
// 将接口值addr转换为*net.IPNet类型的指针
ipNet := addr.(*net.IPNet)
// 如果子网掩码长度 bits 为 128(即IPv6地址),如果子网掩码长度 bits 为 32(即IPv4地址)
_, bits := ipNet.Mask.Size()
if bits == 128 && effectiveIpv6.Contains(ipNet.IP) {
ipv6 = append(ipv6, ipNet.IP.String())
}
}
if len(ipv6) > 0 {
ipv6NetInterfaces = append(ipv6NetInterfaces, NetInterface{
Name: netInterface.Name,
Address: ipv6,
})
}
}
}
return ipv6NetInterfaces, nil
}
4.2、修改解析值
package dns
import (
"fmt"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323"
)
var client *dnspod.Client
func InitClient(secretId string, secretKey string) {
// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
// 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305
// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
credential := common.NewCredential(
secretId,
secretKey,
)
// 实例化一个client选项,可选的,没有特殊需求可以跳过
cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = "dnspod.tencentcloudapi.com"
// 实例化要请求产品的client对象,clientProfile是可选的
client, _ = dnspod.NewClient(credential, "", cpf)
}
func SelectRecord(domain string, subdomain string) *dnspod.RecordListItem {
// 实例化一个请求对象,每个接口都会对应一个request对象
request := dnspod.NewDescribeRecordListRequest()
request.Domain = common.StringPtr(domain)
request.Subdomain = common.StringPtr(subdomain)
// 返回的resp是一个DescribeRecordListResponse的实例,与请求对象对应
response, err := client.DescribeRecordList(request)
if _, ok := err.(*errors.TencentCloudSDKError); ok {
fmt.Printf("An API error has returned: %s", err)
}
if err != nil {
fmt.Printf("client.DescribeRecordList failed, err: %s", err)
}
if len(response.Response.RecordList) > 0 {
return response.Response.RecordList[0]
}
return nil
}
func UpdateRecord(domain string, subdomain string, ipv6 string, recordId uint64) {
// 实例化一个请求对象,每个接口都会对应一个request对象
request := dnspod.NewModifyRecordRequest()
request.Domain = common.StringPtr(domain)
request.SubDomain = common.StringPtr(subdomain)
request.RecordType = common.StringPtr("AAAA")
request.RecordLine = common.StringPtr("默认")
request.Value = common.StringPtr(ipv6)
request.RecordId = common.Uint64Ptr(recordId)
// 返回的resp是一个ModifyRecordResponse的实例,与请求对象对应
response, err := client.ModifyRecord(request)
if _, ok := err.(*errors.TencentCloudSDKError); ok {
fmt.Printf("An API error has returned: %s", err)
return
}
if err != nil {
panic(err)
}
// 输出json格式的字符串回包
fmt.Println(response.ToJsonString())
}
4.3、定时运行
package main
import (
"ddns/utils/dns"
"ddns/utils/effective"
"ddns/utils/ip"
"flag"
"fmt"
dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323"
"time"
)
var secretId = flag.String("secretId", "", "SecretId")
var secretKey = flag.String("secretKey", "", "SecretKey")
var domain = flag.String("domain", "", "Domain")
var subdomain = flag.String("subdomain", "", "Subdomain")
var interval = flag.Int64("interval", 15, "Interval")
var record *dnspod.RecordListItem
func main() {
// 解析命令行参数
flag.Parse()
if !effective.EffectiveString(*secretId, *secretKey, *domain, *subdomain) {
fmt.Println("secretId and secretKey and domain and subdomain are required!")
return
}
// 初始化 dns
dns.InitClient(*secretId, *secretKey)
// 查询 dns 记录
record = dns.SelectRecord(*domain, *subdomain)
if record == nil {
fmt.Println("No records were found from dnspod!")
return
}
// 启动先触发一次
fmt.Println("Execution time: ", time.Now())
dnns()
// 定时查看 ip 是否发生变化
ticker := time.NewTicker(time.Duration(*interval) * time.Minute)
defer ticker.Stop()
for {
select {
case <-ticker.C:
fmt.Println("Execution time: ", time.Now())
dnns()
}
}
}
func dnns() {
// 获取当前 ipv6 地址
ipv6NetInterfaces, err := ip.Ipv6()
if err != nil {
fmt.Println("Error getting ipv6 ", err)
return
}
// 遍历所有 ipv6 地址,查看是否有与 dns 解析一致的地址
f := false
for _, netInterface := range ipv6NetInterfaces {
for _, addr := range netInterface.Address {
if addr == *record.Value {
f = true
}
}
}
// 如果所有的 ipv6 地址与 dns 解析的地址都不一致,将设备中国第一个 ipv6 地址更新到 dns 解析中,并更新记录值
if !f {
fmt.Println("IPV6 has changed!")
dns.UpdateRecord(*domain, *subdomain, ipv6NetInterfaces[0].Address[0], *record.RecordId)
record = dns.SelectRecord(*domain, *subdomain)
} else {
fmt.Println("IPV6 has not changed!")
}
}
4.4、直接运行
- SECRETID:上文获取的API密钥的ID
- SECRETKEY:上文获取的API密钥
- DOMAIN:一级域名
- SUBDOMAIN:二级域名
- INTERVAL:运行间隔,单位分钟,默认15分钟
go run main.go -secretId SECRETID -secretKey SECRETKEY -domain DOMAIN -subdomain SUBDOMAIN -interval INTERVAL
4.5、作为守护进程运行
在/etc/systemd/system/
下新建ddns.service
[Unit]
Description=easily configure dynamic domain names
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/ddns/ddns -secretId SECRETID -secretKey SECRETKEY -domain DOMAIN -subdomain SUBDOMAIN -interval INTERVAL
StandardOutput=file:/var/log/ddns.log
StandardError=file:/var/log/ddns_error.log
Restart=always
User=root
[Install]
WantedBy=multi-user.target
# 修改文件重新加载 Systemd 并重新启动服务
# sudo systemctl daemon-reload
# 设置开机启动并立即启动服务
systemctl enable ddns.service --now