Closed zzzhouuu closed 2 years ago
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
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 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
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
}
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.
Thanks! I can do it right now as a PR don't worry 😉 be back soon with a test image
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.
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
Support added in #252 through the SDK. I'll eventually get to removing the SDK dependency because I'm stubborn 😄
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 😉
@zzzhouuu does the current ddns-updater program work today with Aliyun? Thanks!
@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
Thank you so much @zzzhouuu glad it's working fine (I was wondering if it was working ok!) 👍 Enjoy!
Folks, is it expected if I see "Code":"DomainRecordDuplicate","Message":"The DNS record already exists."
but can confirm ddns-updater
writes to Aliyun successfully?
@a-w-1806 which program version are you using?
What's the feature? Include Aliyun DNS as provider please
Extra information? Thanks. Many thanks for all.
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.