golang基于Digital Ocean DNS的个人动态域名解析插件DDNS的使用

Golang基于Digital Ocean DNS的个人动态域名解析插件DDNS的使用

什么是DDNS

动态DNS(DDNS)是一种自动更新域名系统(DNS)中名称服务器的方法,通常实时更新其配置的主机名、地址或其他信息。

动机

虽然有DynDNS、No-IP等服务可以远程访问PC,但我们真的需要它们吗?这是一个免费的DDNS解决方案(感谢Digital Ocean Networking DNS)。

安装

从发布页面下载二进制文件,并启动它:

ddns

或者使用Docker镜像:

docker run \
  -v /path/to/config.yml:/config/ddns.yml \
  skibish/ddns -conf-file /config/ddns.yml

文档

运行ddns -h查看帮助,输出如下:

Usage of ./ddns:
  -conf-file string
        Location of the configuration file. If not provided, searches current directory, then $HOME for ddns.yml file
  -ver
        Show version

你需要在Digital Ocean Networks面板中设置你的域名,并在域名提供商配置中将域名指向Digital Ocean NS记录。

配置文件示例

ddns.yml配置文件示例:

# DDNS configuration file.

# Mandatory, DigitalOcean API token.
# It can be also set using environment variable DDNS_TOKEN.
token: ""

# By default, IP check occurs every 5 minutes.
# It can be also set using environment variable DDNS_CHECKPERIOD.
checkPeriod: "5m"

# By default, timeout to external resources is set to 10 seconds.
# It can be also set using environment variable DDNS_REQUESTTIMEOUT.
requestTimeout: "10s"

# By default, IPv6 address is not requested.
# IPv6 address can be forced by setting it to `true`.
# It can be also set using environment variable DDNS_IPV6.
ipv6: false

# List of domains and their records to update.
domains:
  example.com:
  # More details about the fields can be found here:
  # https://developers.digitalocean.com/documentation/v2/#create-a-new-domain-record
  - type: "A"
    name: "www"
  - type: "TXT"
    name: "demo"

    # By default, is set to "{{.IP}}" (key .IP is reserved).
    # Supports Go template engine.
    # Additional keys can be set in "params" block below.
    data: "My IP is {{.IP}} and I am {{.mood}}"

    # By default, 1800 seconds (5 minutes).
    ttl: 1800

# By default, params is empty.
params:
  mood: "cool"

# By default, notifications is empty.
notifications:

  # Gotify (https://gotify.net)
- type: "gotify"
  app_url: "https://gotify.example.com"
  app_token: ""
  title: "DDNS" 

  # SMTP
- type: "smtp"
  user: "foo@bar.com"
  password: "1234"
  host: "localhost"
  port: "468"
  from: "bar@foo.com"
  to: "foo@foo.com"
  subject: "My DDNS sending me a message"

  # Telegram (https://telegram.org)
- type: "telegram"
  token: "telegram bot token"
  chat_id: "1234"

Go代码示例

以下是一个简单的Go代码示例,展示如何使用Digital Ocean API更新DNS记录:

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/digitalocean/godo"
	"golang.org/x/oauth2"
)

// TokenSource 实现了oauth2.TokenSource接口
type TokenSource struct {
	AccessToken string
}

// Token 返回一个oauth2.Token
func (t *TokenSource) Token() (*oauth2.Token, error) {
	token := &oauth2.Token{
		AccessToken: t.AccessToken,
	}
	return token, nil
}

func main() {
	// 设置Digital Ocean API token
	token := "your_digital_ocean_token"
	
	// 创建OAuth2 token源
	tokenSource := &TokenSource{
		AccessToken: token,
	}
	
	// 创建OAuth2客户端
	oauthClient := oauth2.NewClient(context.Background(), tokenSource)
	
	// 创建Digital Ocean客户端
	client := godo.NewClient(oauthClient)
	
	// 设置域名和记录
	domain := "example.com"
	recordName := "www"
	recordType := "A"
	recordData := "192.168.1.1" // 这里应该是你的当前IP地址
	recordTTL := 1800
	
	// 创建DNS记录
	createRequest := &godo.DomainRecordEditRequest{
		Type: recordType,
		Name: recordName,
		Data: recordData,
		TTL:  recordTTL,
	}
	
	// 调用API创建记录
	ctx := context.TODO()
	record, _, err := client.Domains.CreateRecord(ctx, domain, createRequest)
	if err != nil {
		log.Fatalf("Error creating DNS record: %v", err)
	}
	
	fmt.Printf("Created DNS record: %+v\n", record)
	
	// 定期检查并更新IP
	ticker := time.NewTicker(5 * time.Minute)
	defer ticker.Stop()
	
	for range ticker.C {
		// 这里应该获取当前IP地址
		newIP := "192.168.1.1" // 替换为获取当前IP的逻辑
		
		// 更新DNS记录
		updateRequest := &godo.DomainRecordEditRequest{
			Type: recordType,
			Name: recordName,
			Data: newIP,
			TTL:  recordTTL,
		}
		
		_, _, err := client.Domains.EditRecord(ctx, domain, record.ID, updateRequest)
		if err != nil {
			log.Printf("Error updating DNS record: %v", err)
			continue
		}
		
		fmt.Printf("Updated DNS record with new IP: %s\n", newIP)
	}
}

这个示例展示了如何使用Digital Ocean的Go客户端库创建和更新DNS记录。在实际应用中,你需要添加获取当前IP地址的逻辑,并处理错误情况。


更多关于golang基于Digital Ocean DNS的个人动态域名解析插件DDNS的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang基于Digital Ocean DNS的个人动态域名解析插件DDNS的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


基于Digital Ocean DNS的Golang动态域名解析插件(DDNS)

概述

动态域名解析(DDNS)允许你将动态IP地址映射到一个固定的域名上。下面我将介绍如何使用Golang开发一个基于Digital Ocean DNS的DDNS插件。

实现原理

  1. 定期检测本机公网IP
  2. 与DNS记录中的IP对比
  3. 如果不同则更新DNS记录

代码实现

package main

import (
	"context"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/digitalocean/godo"
	"golang.org/x/oauth2"
)

// 配置参数
const (
	doToken     = "your_digitalocean_api_token" // DigitalOcean API令牌
	domain      = "example.com"                 // 你的域名
	recordName  = "home"                        // 记录名称(如home.example.com)
	checkPeriod = 5 * time.Minute                // 检查间隔
)

// TokenSource 用于DigitalOcean API认证
type TokenSource struct {
	AccessToken string
}

func (t *TokenSource) Token() (*oauth2.Token, error) {
	token := &oauth2.Token{
		AccessToken: t.AccessToken,
	}
	return token, nil
}

// 获取当前公网IP
func getPublicIP() (string, error) {
	resp, err := http.Get("https://api.ipify.org?format=text")
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	ip, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	return string(ip), nil
}

// 获取DNS记录ID
func getDNSRecordID(client *godo.Client, domain, name string) (int, string, error) {
	records, _, err := client.Domains.Records(context.Background(), domain, nil)
	if err != nil {
		return 0, "", err
	}

	for _, record := range records {
		if record.Name == name && record.Type == "A" {
			return record.ID, record.Data, nil
		}
	}

	return 0, "", fmt.Errorf("record not found")
}

// 更新DNS记录
func updateDNSRecord(client *godo.Client, domain string, recordID int, ip string) error {
	editRequest := &godo.DomainRecordEditRequest{
		Type: "A",
		Name: recordName,
		Data: ip,
	}

	_, _, err := client.Domains.EditRecord(context.Background(), domain, recordID, editRequest)
	return err
}

func main() {
	// 创建DigitalOcean客户端
	tokenSource := &TokenSource{
		AccessToken: doToken,
	}
	oauthClient := oauth2.NewClient(context.Background(), tokenSource)
	client := godo.NewClient(oauthClient)

	// 获取初始记录ID
	recordID, currentIP, err := getDNSRecordID(client, domain, recordName)
	if err != nil {
		fmt.Printf("Error getting DNS record: %v\n", err)
		return
	}

	fmt.Printf("Initial DNS record: ID=%d, IP=%s\n", recordID, currentIP)

	// 定时检查并更新
	ticker := time.NewTicker(checkPeriod)
	defer ticker.Stop()

	for range ticker.C {
		publicIP, err := getPublicIP()
		if err != nil {
			fmt.Printf("Error getting public IP: %v\n", err)
			continue
		}

		if publicIP == currentIP {
			fmt.Printf("IP unchanged (%s), skipping update\n", publicIP)
			continue
		}

		fmt.Printf("IP changed from %s to %s, updating...\n", currentIP, publicIP)
		err = updateDNSRecord(client, domain, recordID, publicIP)
		if err != nil {
			fmt.Printf("Error updating DNS record: %v\n", err)
			continue
		}

		currentIP = publicIP
		fmt.Println("DNS record updated successfully")
	}
}

使用说明

  1. 安装依赖:
go get github.com/digitalocean/godo
go get golang.org/x/oauth2
  1. 修改配置参数:

    • doToken: 你的DigitalOcean API令牌
    • domain: 你的域名
    • recordName: 要更新的记录名称(如"home"对应home.example.com)
  2. 运行程序:

go run ddns.go

进阶功能

  1. 配置持久化:可以将配置存储在配置文件中
  2. 多记录支持:支持同时更新多个DNS记录
  3. 通知功能:当IP变更时发送邮件或短信通知
  4. 日志记录:将变更历史记录到文件中

部署建议

  1. 在服务器上以后台服务方式运行
  2. 使用systemd或supervisor管理进程
  3. 设置合理的检查间隔(建议5-10分钟)

这个简单的DDNS实现可以满足基本需求,根据实际情况可以进一步扩展功能。

回到顶部