Feature request: Add Aliyun DNS as provider #116

Closed
opened 2025-11-20 04:20:28 -05:00 by saavagebueno · 15 comments
Owner

Originally created by @zzzhouuu on GitHub (May 2, 2021).

Originally assigned to: @fredericrous on GitHub.

  1. What's the feature?
    Include Aliyun DNS as provider please

  2. Extra information?
    Thanks. Many thanks for all.

  3. Why do you need this feature?
    I have a home server with Aliyun DNS and Aliyun Domains and would be awesome to have automatic ddns update.

Originally created by @zzzhouuu on GitHub (May 2, 2021). Originally assigned to: @fredericrous on GitHub. 1. What's the feature? Include Aliyun DNS as provider please 2. Extra information? Thanks. Many thanks for all. 3. Why do you need this feature? I have a home server with Aliyun DNS and Aliyun Domains and would be awesome to have automatic ddns update.
Author
Owner

@fredericrous commented on GitHub (May 18, 2021):

Aliyun is Alibaba cloud, correct? Am I looking at the correct documentation? https://www.alibabacloud.com/help/doc-detail/29772.htm?spm=a2c63.p38356.b99.91.badb2d7bjYrGPW

@fredericrous commented on GitHub (May 18, 2021): Aliyun is Alibaba cloud, correct? Am I looking at the correct documentation? https://www.alibabacloud.com/help/doc-detail/29772.htm?spm=a2c63.p38356.b99.91.badb2d7bjYrGPW
Author
Owner

@qdm12 commented on GitHub (May 18, 2021):

Thanks @fredericrous! Let me know if you're on it 😉 I'm still a few days away from it (in my work queue, working on other repos right now) anyway.

@qdm12 commented on GitHub (May 18, 2021): Thanks @fredericrous! Let me know if you're on it 😉 I'm still a few days away from it (in my work queue, working on other repos right now) anyway.
Author
Owner

@fredericrous commented on GitHub (May 18, 2021):

@qdm12 I should have spare time end of next week to work on this.
I did more digging. Alibaba cloud has a SDK: https://github.com/aliyun/alibaba-cloud-sdk-go/blob/master/services/alidns/update_domain_record.go
documentation: https://www.alibabacloud.com/help/doc-detail/66217.htm

@fredericrous commented on GitHub (May 18, 2021): @qdm12 I should have spare time end of next week to work on this. I did more digging. Alibaba cloud has a SDK: https://github.com/aliyun/alibaba-cloud-sdk-go/blob/master/services/alidns/update_domain_record.go documentation: https://www.alibabacloud.com/help/doc-detail/66217.htm
Author
Owner

@zzzhouuu commented on GitHub (May 20, 2021):

package settings

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/alidns"
	"github.com/qdm12/ddns-updater/internal/models"
	"github.com/qdm12/ddns-updater/internal/regex"
	"github.com/qdm12/ddns-updater/pkg/publicip/ipversion"
	"net"
	"net/http"
)

type aliyun struct {
	domain          string
	host            string
	ipVersion       ipversion.IPVersion
	regionId        string
	accessKeyId     string
	accessKeySecret string
}

func NewAliyun(data json.RawMessage, domain, host string, ipVersion ipversion.IPVersion,
	_ regex.Matcher) (s Settings, err error) {
	extraSettings := struct {
		RegionId        string `json:"region_id"`
		AccessKeyId     string `json:"access_key_id"`
		AccessKeySecret string `json:"access_key_secret"`
	}{}
	if err := json.Unmarshal(data, &extraSettings); err != nil {
		return nil, err
	}
	d := &aliyun{
		domain:          domain,
		host:            host,
		ipVersion:       ipVersion,
		regionId:        extraSettings.RegionId,
		accessKeyId:     extraSettings.AccessKeyId,
		accessKeySecret: extraSettings.AccessKeySecret,
	}
	if err := d.isValid(); err != nil {
		return nil, err
	}
	return d, nil
}

func (d *aliyun) isValid() error {
	switch {
	case len(d.accessKeyId) == 0:
		return ErrEmptyAccessKeyId
	case len(d.accessKeySecret) == 0:
		return ErrEmptyAccessKeySecret
	case d.host == "*":
		return ErrHostWildcard
	}
	return nil
}

func (d *aliyun) String() string {
	return fmt.Sprintf("[domain: %s | host: %s | provider: aliyun]", d.domain, d.host)
}

func (d *aliyun) Domain() string {
	return d.domain
}

func (d *aliyun) Host() string {
	return d.host
}

func (d *aliyun) IPVersion() ipversion.IPVersion {
	return d.ipVersion
}

func (d *aliyun) Proxied() bool {
	return false
}

func (d *aliyun) BuildDomainName() string {
	return buildDomainName(d.host, d.domain)
}

func (d *aliyun) newClient() *sdk.Client {
	client, err := sdk.NewClientWithAccessKey(d.regionId, d.accessKeyId, d.accessKeySecret)
	if err != nil {
		// Handle exceptions
		panic(err)
	}
	return client
}

func (d *aliyun) HTML() models.HTMLRow {
	return models.HTMLRow{
		Domain:    models.HTML(fmt.Sprintf("<a href=\"http://%s\">%s</a>", d.BuildDomainName(), d.BuildDomainName())),
		Host:      models.HTML(d.Host()),
		Provider:  "<a href=\"https://aliyun.com/\">AliDNS</a>",
		IPVersion: models.HTML(d.ipVersion.String()),
	}
}

func (d *aliyun) getRecord(recordType string, client *sdk.Client) (record alidns.Record, err error) {
	request := alidns.CreateDescribeSubDomainRecordsRequest()
	request.SubDomain = d.BuildDomainName()
	request.Type = recordType

	response := alidns.CreateDescribeSubDomainRecordsResponse()
	err = client.DoAction(request, response)
	if err != nil {
		return alidns.Record{}, err
	}

	if len(response.DomainRecords.Record) == 0 {
		return alidns.Record{}, ErrRecordNotFound
	} else if response.DomainRecords.Record[0].RecordId == "" {
		return alidns.Record{}, ErrRecordIDNotFound
	}

	return response.DomainRecords.Record[0], nil
}

func (d *aliyun) Update(ctx context.Context, client *http.Client, ip net.IP) (newIP net.IP, err error) {
	recordType := A
	var ipStr string
	if ip.To4() == nil { // IPv6
		recordType = AAAA
		ipStr = ip.To16().String()
	} else {
		ipStr = ip.To4().String()
	}

	aliClient := d.newClient()
	record, err := d.getRecord(recordType, aliClient)
	if err != nil {
		return nil, fmt.Errorf("%s: %w", ErrGetRecordID, err)
	}

	if record.Value == ipStr {
		return ip, nil
	}

	request := alidns.CreateUpdateDomainRecordRequest()
	request.RR = d.host
	request.RecordId = record.RecordId
	request.Type = recordType
	request.Value = ipStr

	response := alidns.CreateUpdateDomainRecordResponse()

	err = aliClient.DoAction(request, response)
	if err != nil {
		return nil, err
	}

	return ip, nil
}
@zzzhouuu commented on GitHub (May 20, 2021): ```go package settings import ( "context" "encoding/json" "fmt" "github.com/aliyun/alibaba-cloud-sdk-go/sdk" "github.com/aliyun/alibaba-cloud-sdk-go/services/alidns" "github.com/qdm12/ddns-updater/internal/models" "github.com/qdm12/ddns-updater/internal/regex" "github.com/qdm12/ddns-updater/pkg/publicip/ipversion" "net" "net/http" ) type aliyun struct { domain string host string ipVersion ipversion.IPVersion regionId string accessKeyId string accessKeySecret string } func NewAliyun(data json.RawMessage, domain, host string, ipVersion ipversion.IPVersion, _ regex.Matcher) (s Settings, err error) { extraSettings := struct { RegionId string `json:"region_id"` AccessKeyId string `json:"access_key_id"` AccessKeySecret string `json:"access_key_secret"` }{} if err := json.Unmarshal(data, &extraSettings); err != nil { return nil, err } d := &aliyun{ domain: domain, host: host, ipVersion: ipVersion, regionId: extraSettings.RegionId, accessKeyId: extraSettings.AccessKeyId, accessKeySecret: extraSettings.AccessKeySecret, } if err := d.isValid(); err != nil { return nil, err } return d, nil } func (d *aliyun) isValid() error { switch { case len(d.accessKeyId) == 0: return ErrEmptyAccessKeyId case len(d.accessKeySecret) == 0: return ErrEmptyAccessKeySecret case d.host == "*": return ErrHostWildcard } return nil } func (d *aliyun) String() string { return fmt.Sprintf("[domain: %s | host: %s | provider: aliyun]", d.domain, d.host) } func (d *aliyun) Domain() string { return d.domain } func (d *aliyun) Host() string { return d.host } func (d *aliyun) IPVersion() ipversion.IPVersion { return d.ipVersion } func (d *aliyun) Proxied() bool { return false } func (d *aliyun) BuildDomainName() string { return buildDomainName(d.host, d.domain) } func (d *aliyun) newClient() *sdk.Client { client, err := sdk.NewClientWithAccessKey(d.regionId, d.accessKeyId, d.accessKeySecret) if err != nil { // Handle exceptions panic(err) } return client } func (d *aliyun) HTML() models.HTMLRow { return models.HTMLRow{ Domain: models.HTML(fmt.Sprintf("<a href=\"http://%s\">%s</a>", d.BuildDomainName(), d.BuildDomainName())), Host: models.HTML(d.Host()), Provider: "<a href=\"https://aliyun.com/\">AliDNS</a>", IPVersion: models.HTML(d.ipVersion.String()), } } func (d *aliyun) getRecord(recordType string, client *sdk.Client) (record alidns.Record, err error) { request := alidns.CreateDescribeSubDomainRecordsRequest() request.SubDomain = d.BuildDomainName() request.Type = recordType response := alidns.CreateDescribeSubDomainRecordsResponse() err = client.DoAction(request, response) if err != nil { return alidns.Record{}, err } if len(response.DomainRecords.Record) == 0 { return alidns.Record{}, ErrRecordNotFound } else if response.DomainRecords.Record[0].RecordId == "" { return alidns.Record{}, ErrRecordIDNotFound } return response.DomainRecords.Record[0], nil } func (d *aliyun) Update(ctx context.Context, client *http.Client, ip net.IP) (newIP net.IP, err error) { recordType := A var ipStr string if ip.To4() == nil { // IPv6 recordType = AAAA ipStr = ip.To16().String() } else { ipStr = ip.To4().String() } aliClient := d.newClient() record, err := d.getRecord(recordType, aliClient) if err != nil { return nil, fmt.Errorf("%s: %w", ErrGetRecordID, err) } if record.Value == ipStr { return ip, nil } request := alidns.CreateUpdateDomainRecordRequest() request.RR = d.host request.RecordId = record.RecordId request.Type = recordType request.Value = ipStr response := alidns.CreateUpdateDomainRecordResponse() err = aliClient.DoAction(request, response) if err != nil { return nil, err } return ip, nil } ```
Author
Owner

@fredericrous commented on GitHub (May 20, 2021):

nice work @zzzhouuu could you create a PR that way will begin the review process.
Also I don't think ErrHostWildcard is relevant for alidns. It's possible to set a wildcard with this api, isn't it? therefore I would let the possibility to set it.

@fredericrous commented on GitHub (May 20, 2021): nice work @zzzhouuu could you create a PR that way will begin the review process. Also I don't think `ErrHostWildcard` is relevant for alidns. It's possible to set a wildcard with this api, isn't it? therefore I would let the possibility to set it.
Author
Owner

@qdm12 commented on GitHub (May 20, 2021):

Thanks! I can do it right now as a PR don't worry 😉 be back soon with a test image

@qdm12 commented on GitHub (May 20, 2021): Thanks! I can do it right now as a PR don't worry 😉 be back soon with a test image
Author
Owner

@qdm12 commented on GitHub (May 20, 2021):

Actually I'll use https://www.alibabacloud.com/help/doc-detail/34272.html?spm=a2c5t.11065259.1996646101.searchclickresult.56ec6428yZ0qoE directly to have one less dependency.

I'm currently fighting against vulnerabilities on other repos due to dependencies, so the less the better, especially since Alibaba's one seems rather huge.

@qdm12 commented on GitHub (May 20, 2021): Actually I'll use https://www.alibabacloud.com/help/doc-detail/34272.html?spm=a2c5t.11065259.1996646101.searchclickresult.56ec6428yZ0qoE directly to have one less dependency. I'm currently fighting against vulnerabilities on other repos due to dependencies, so the less the better, especially since Alibaba's one seems rather huge.
Author
Owner

@fredericrous commented on GitHub (May 21, 2021):

yeah idk, normally Go compiler should compile only what's needed so even if the lib is huge this shouldn't impact more than necessary the size of the binary. Regarding vulnerabilities...alibaba's sdk looks pretty active but that's true that among the dependencies it depends on there are some old ones that doesn't look active like github.com/davecgh/go-spew . Apparently authentication is not hard to do see https://github.com/TimothyYe/godns/blob/master/handler/alidns/alidns.go

@fredericrous commented on GitHub (May 21, 2021): yeah idk, normally Go compiler should compile only what's needed so even if the lib is huge this shouldn't impact more than necessary the size of the binary. Regarding vulnerabilities...alibaba's sdk looks pretty active but that's true that among the dependencies it depends on there are some old ones that doesn't look active like github.com/davecgh/go-spew . Apparently authentication is not hard to do see https://github.com/TimothyYe/godns/blob/master/handler/alidns/alidns.go
Author
Owner

@qdm12 commented on GitHub (Oct 14, 2021):

Support added in #252 through the SDK. I'll eventually get to removing the SDK dependency because I'm stubborn 😄

@qdm12 commented on GitHub (Oct 14, 2021): Support added in #252 through the SDK. I'll eventually get to removing the SDK dependency because I'm stubborn 😄
Author
Owner

@qdm12 commented on GitHub (Oct 15, 2021):

I reworked the code in 8a14828cdc5a6e311788f479c5745ff26ce6aebe to have no SDK dependency and only do http calls to their API directly. The Docker image is available qmcgaw/ddns-updater:aliyun-nosdk, maybe @zzzhouuu can you try it sometime?

Also thanks @fredericrous the links you posted helped me quite a bit 😉

@qdm12 commented on GitHub (Oct 15, 2021): I reworked the code in 8a14828cdc5a6e311788f479c5745ff26ce6aebe to have no SDK dependency and only do http calls to their API directly. The Docker image is available `qmcgaw/ddns-updater:aliyun-nosdk`, maybe @zzzhouuu can you try it sometime? Also thanks @fredericrous the links you posted helped me quite a bit 😉
Author
Owner

@qdm12 commented on GitHub (Jun 15, 2024):

@zzzhouuu does the current ddns-updater program work today with Aliyun? Thanks!

@qdm12 commented on GitHub (Jun 15, 2024): @zzzhouuu does the current ddns-updater program work today with Aliyun? Thanks!
Author
Owner

@zzzhouuu commented on GitHub (Jun 15, 2024):

@zzzhouuu does the current ddns-updater program work today with Aliyun? Thanks!

I tested it to work.

========================================
========================================
============= ddns-updater =============
========================================
=========== Made with ❤️ by ============
======= https://github.com/qdm12 =======
========================================
========================================

Running version latest built on 2024-06-15T09:57:45.623Z (commit 3494cfb)

🔧 Need help? https://github.com/qdm12/ddns-updater/discussions/new
🐛 Bug? https://github.com/qdm12/ddns-updater/issues/new
✨ New feature? https://github.com/qdm12/ddns-updater/issues/new
☕ Discussion? https://github.com/qdm12/ddns-updater/discussions/new
💻 Email? quentin.mcgaw@gmail.com
💰 Help me? https://www.paypal.me/qmcgaw https://github.com/sponsors/qdm12
2024-06-15T10:39:40Z INFO Settings summary:
├── HTTP client
|   └── Timeout: 10s
├── Update
|   ├── Period: 5m0s
|   └── Cooldown: 5m0s
├── Public IP fetching
|   ├── HTTP enabled: yes
|   ├── HTTP IP providers
|   |   └── all
|   ├── HTTP IPv4 providers
|   |   └── all
|   ├── HTTP IPv6 providers
|   |   └── all
|   ├── DNS enabled: yes
|   ├── DNS timeout: 3s
|   └── DNS over TLS providers
|       └── all
├── Resolver: use Go default resolver
├── Server
|   ├── Listening address: :8000
|   └── Root URL: /
├── Health
|   └── Server listening address: 127.0.0.1:9999
├── Paths
|   └── Data directory: /updater/data
├── Backup: disabled
└── Logger
    ├── Level: info
    └── Caller: hidden
2024-06-15T10:39:40Z INFO reading JSON config from file /updater/data/config.json
2024-06-15T10:39:40Z INFO Found single setting to update record
2024-06-15T10:39:40Z INFO Reading history from database: domain xxxx.xxx host @ ipv4
2024-06-15T10:39:40Z INFO [healthcheck server] health http server listening on 127.0.0.1:9999
2024-06-15T10:39:40Z INFO [http server] http server listening on [::]:8000
2024-06-15T10:39:40Z INFO [backup] disabled
2024-06-15T10:39:41Z INFO ipv4 address of xxxx.xxx is 10.0.0.136 and your ipv4 address  is xx.xx.xx.xx
2024-06-15T10:39:41Z INFO Updating record [domain: xxxx.xxx | host: @ | provider: aliyun | ip: ipv4] to use xx.xx.xx.xx
2024-06-15T10:39:41Z ERROR updating record: HTTP status is not valid: 400: {"RequestId":"3CD076DF-14DD-578F-AFF5-AACF44F2C433","HostId":"alidns.aliyuncs.com","Code":"DomainRecordDuplicate","Message":"The DNS record already exists.","Recommend":"https://api.aliyun.com/troubleshoot?q=DomainRecordDuplicate&product=Alidns&requestId=3CD076DF-14DD-578F-AFF5-AACF44F2C433"}
2024-06-15T10:44:41Z INFO ipv4 address of xxxx.xxx is 10.0.0.136 and your ipv4 address  is xx.xx.xx.xx
2024-06-15T10:44:41Z INFO Updating record [domain: xxxx.xxx | host: @ | provider: aliyun | ip: ipv4] to use xx.xx.xx.xx
@zzzhouuu commented on GitHub (Jun 15, 2024): > @zzzhouuu does the current ddns-updater program work today with Aliyun? Thanks! I tested it to work. ``` ======================================== ======================================== ============= ddns-updater ============= ======================================== =========== Made with ❤️ by ============ ======= https://github.com/qdm12 ======= ======================================== ======================================== Running version latest built on 2024-06-15T09:57:45.623Z (commit 3494cfb) 🔧 Need help? https://github.com/qdm12/ddns-updater/discussions/new 🐛 Bug? https://github.com/qdm12/ddns-updater/issues/new ✨ New feature? https://github.com/qdm12/ddns-updater/issues/new ☕ Discussion? https://github.com/qdm12/ddns-updater/discussions/new 💻 Email? quentin.mcgaw@gmail.com 💰 Help me? https://www.paypal.me/qmcgaw https://github.com/sponsors/qdm12 2024-06-15T10:39:40Z INFO Settings summary: ├── HTTP client | └── Timeout: 10s ├── Update | ├── Period: 5m0s | └── Cooldown: 5m0s ├── Public IP fetching | ├── HTTP enabled: yes | ├── HTTP IP providers | | └── all | ├── HTTP IPv4 providers | | └── all | ├── HTTP IPv6 providers | | └── all | ├── DNS enabled: yes | ├── DNS timeout: 3s | └── DNS over TLS providers | └── all ├── Resolver: use Go default resolver ├── Server | ├── Listening address: :8000 | └── Root URL: / ├── Health | └── Server listening address: 127.0.0.1:9999 ├── Paths | └── Data directory: /updater/data ├── Backup: disabled └── Logger ├── Level: info └── Caller: hidden 2024-06-15T10:39:40Z INFO reading JSON config from file /updater/data/config.json 2024-06-15T10:39:40Z INFO Found single setting to update record 2024-06-15T10:39:40Z INFO Reading history from database: domain xxxx.xxx host @ ipv4 2024-06-15T10:39:40Z INFO [healthcheck server] health http server listening on 127.0.0.1:9999 2024-06-15T10:39:40Z INFO [http server] http server listening on [::]:8000 2024-06-15T10:39:40Z INFO [backup] disabled 2024-06-15T10:39:41Z INFO ipv4 address of xxxx.xxx is 10.0.0.136 and your ipv4 address is xx.xx.xx.xx 2024-06-15T10:39:41Z INFO Updating record [domain: xxxx.xxx | host: @ | provider: aliyun | ip: ipv4] to use xx.xx.xx.xx 2024-06-15T10:39:41Z ERROR updating record: HTTP status is not valid: 400: {"RequestId":"3CD076DF-14DD-578F-AFF5-AACF44F2C433","HostId":"alidns.aliyuncs.com","Code":"DomainRecordDuplicate","Message":"The DNS record already exists.","Recommend":"https://api.aliyun.com/troubleshoot?q=DomainRecordDuplicate&product=Alidns&requestId=3CD076DF-14DD-578F-AFF5-AACF44F2C433"} 2024-06-15T10:44:41Z INFO ipv4 address of xxxx.xxx is 10.0.0.136 and your ipv4 address is xx.xx.xx.xx 2024-06-15T10:44:41Z INFO Updating record [domain: xxxx.xxx | host: @ | provider: aliyun | ip: ipv4] to use xx.xx.xx.xx ```
Author
Owner

@qdm12 commented on GitHub (Jun 15, 2024):

Thank you so much @zzzhouuu glad it's working fine (I was wondering if it was working ok!) 👍 Enjoy!

@qdm12 commented on GitHub (Jun 15, 2024): Thank you so much @zzzhouuu glad it's working fine (I was wondering if it was working ok!) 👍 Enjoy!
Author
Owner

@ycsh-w commented on GitHub (Jun 27, 2024):

Folks, is it expected if I see "Code":"DomainRecordDuplicate","Message":"The DNS record already exists." but can confirm ddns-updater writes to Aliyun successfully?

@ycsh-w commented on GitHub (Jun 27, 2024): Folks, is it expected if I see `"Code":"DomainRecordDuplicate","Message":"The DNS record already exists."` but can confirm `ddns-updater` writes to Aliyun successfully?
Author
Owner

@qdm12 commented on GitHub (Jun 28, 2024):

@a-w-1806 which program version are you using?

@qdm12 commented on GitHub (Jun 28, 2024): @a-w-1806 which program version are you using?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/ddns-updater#116