diff --git a/.github/workflows/buildAndPush-binary-to-release.yml b/.github/workflows/buildAndPush-binary-to-release.yml index 2012640..3d3b2d6 100644 --- a/.github/workflows/buildAndPush-binary-to-release.yml +++ b/.github/workflows/buildAndPush-binary-to-release.yml @@ -27,6 +27,7 @@ jobs: overwrite: true build_command: "make" build_flags: "build VERSION=${{ env.GITHUB_REF_NAME }}" + executable_compression: "upx -9" goversion: 1.22 # 可以指定编译使用的 Golang 版本 binary_name: "cloud_dns_exporter" # 可以指定二进制文件的名称 extra_files: config.example.yaml LICENSE README.md # 需要包含的额外文件 \ No newline at end of file diff --git a/README.md b/README.md index e7c09c6..a9cd158 100644 --- a/README.md +++ b/README.md @@ -24,15 +24,15 @@ 这个项目,希望能够让你轻松掌握到每个域名解析的证书信息,从而在更换证书时,不会遗漏任何一个解析。 -支持多种DNS提供商(目前支持阿里云,腾讯云,其他更多,期待有缘人PR),且支持单提供商多账号管理。 +支持多种 DNS 提供商(目前支持阿里云,腾讯云,其他更多,期待有缘人PR),且支持单提供商多账号管理。 ## 如何使用 可以直接在 [Release](https://github.com/eryajf/cloud_dns_exporter/releases) 发布页面下载二进制,然后更改配置文件,直接运行即可。 -默认端口监听在`21798`,为什么选择这个端口,因为项目对应在grafana中的仪表板ID就是[21798](https://grafana.com/grafana/dashboards/21798-cloud-dns-record-info/)。 +默认端口监听在 `21798`,为什么选择这个端口,因为项目对应在 Grafana 中的仪表板ID就是 [21798](https://grafana.com/grafana/dashboards/21798-cloud-dns-record-info/)。 -你也可以选择使用docker部署,部署时把config.yaml在本地配置好,然后运行时,通过挂载(`-v ./config.yaml:/app/config.yaml`)覆盖容器内默认配置即可。 +你也可以选择使用 Docker 部署,部署时把 `config.yaml` 在本地配置好,然后运行时,通过挂载(`-v ./config.yaml:/app/config.yaml`)覆盖容器内默认配置即可。 镜像地址: - 国外: `eryajf/cloud_dns_exporter` @@ -108,13 +108,19 @@ record_cert_info{ error_msg="错误信息"} 30 (此value为记录的证书距离到期的天数) ``` +## 已支持 DNS 服务商 + +- [x] Tencent DnsPod +- [x] Aliyun Dns +- [x] Godaddy + ## Grafana 仪表板 项目对应的 Grafana Dashboard ID: [21798](https://grafana.com/grafana/dashboards/21798-cloud-dns-record-info/) 概览与域名列表: -![](https://t.eryajf.net/imgs/2024/08/1725118602116.webp) +![](https://t.eryajf.net/imgs/2024/09/1725288099522.webp) 解析记录与证书详情列表: diff --git a/config.example.yaml b/config.example.yaml index 9604572..ed0d2a8 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -15,4 +15,9 @@ cloud_providers: - name: a1 secretId: "xxxxx" secretKey: "xxxxx" - # 目前仅支持腾讯云和阿里云,如需支持更多云厂商,请提交 issue,也欢迎 PR \ No newline at end of file + godaddy: + accounts: + - name: g1 + secretId: "xxxxx" + secretKey: "xxxxx" + # 目前支持Tencent, Aliyun, Godaddy,如需支持更多云厂商,请提交 issue,也欢迎 PR \ No newline at end of file diff --git a/go.mod b/go.mod index 8bff2f0..6ab2beb 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/alibabacloud-go/domain-20180129/v4 v4.2.0 github.com/alibabacloud-go/tea v1.2.2 github.com/allegro/bigcache/v3 v3.1.0 + github.com/alyx/go-daddy v0.0.0-20240819232932-c2e4d209da9b github.com/charmbracelet/log v0.2.2 github.com/golang-module/carbon/v2 v2.3.12 github.com/prometheus/client_golang v1.16.0 diff --git a/go.sum b/go.sum index 28e38a2..6c18ba6 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,8 @@ github.com/aliyun/credentials-go v1.3.1 h1:uq/0v7kWrxmoLGpqjx7vtQ/s03f0zR//0br/x github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0= github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk= github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I= +github.com/alyx/go-daddy v0.0.0-20240819232932-c2e4d209da9b h1:ITwV8o+xmGVD6IbBvjIlOoAsxX7me0ounq5UwzwdQlk= +github.com/alyx/go-daddy v0.0.0-20240819232932-c2e4d209da9b/go.mod h1:JEEXFFpdZOowtBJN6+kUCQ+okHa4UfZtMBfWVRf71EM= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= diff --git a/pkg/provider/godaddy.go b/pkg/provider/godaddy.go new file mode 100644 index 0000000..5417d1e --- /dev/null +++ b/pkg/provider/godaddy.go @@ -0,0 +1,163 @@ +package provider + +import ( + "encoding/json" + "fmt" + "strconv" + "sync" + "time" + + "github.com/eryajf/cloud_dns_exporter/public/logger" + "github.com/golang-module/carbon/v2" + + "github.com/alyx/go-daddy/daddy" + "github.com/eryajf/cloud_dns_exporter/public" +) + +type GodaddyDNS struct { + account public.Account + client *daddy.Client +} + +// NewGodaddyClient 初始化客户端 +func NewGodaddyClient(secretID, secretKey string) (*daddy.Client, error) { + client, err := daddy.NewClient(secretID, secretKey, false) + if err != nil { + return nil, err + } + return client, nil +} + +// NewGodaddyDNS 创建 GodaddyDNS 实例 +func NewGodaddyDNS(account public.Account) (*GodaddyDNS, error) { + client, err := NewGodaddyClient(account.SecretID, account.SecretKey) + if err != nil { + return nil, err + } + return &GodaddyDNS{ + account: account, + client: client, + }, nil +} + +// ListDomains 获取域名列表 +func (g *GodaddyDNS) ListDomains() ([]Domain, error) { + gd, err := NewGodaddyDNS(public.Account{ + CloudProvider: g.account.CloudProvider, + CloudName: g.account.CloudName, + SecretID: g.account.SecretID, + SecretKey: g.account.SecretKey, + }) + if err != nil { + return nil, err + } + g.client = gd.client + var dataObj []Domain + domains, err := g.getDomainList() + if err != nil { + return nil, err + } + for _, v := range domains { + dataObj = append(dataObj, Domain{ + CloudProvider: g.account.CloudProvider, + CloudName: g.account.CloudName, + DomainID: strconv.Itoa(v.DomainID), + DomainName: v.Domain, + DomainRemark: v.Domain, + DomainStatus: oneStatus(v.Status), + CreatedDate: v.CreatedAt, + ExpiryDate: v.Expires, + DaysUntilExpiry: carbon.Now().DiffInDays(carbon.Parse(v.Expires)), + }) + } + return dataObj, nil +} + +// ListRecords 获取记录列表 +func (g *GodaddyDNS) ListRecords() ([]Record, error) { + var ( + dataObj []Record + domains []Domain + wg sync.WaitGroup + mu sync.Mutex + ) + tcd, err := NewGodaddyDNS(public.Account{ + CloudProvider: g.account.CloudProvider, + CloudName: g.account.CloudName, + SecretID: g.account.SecretID, + SecretKey: g.account.SecretKey, + }) + if err != nil { + return nil, err + } + g.client = tcd.client + rst, err := public.Cache.Get(public.DomainList + "_" + g.account.CloudProvider + "_" + g.account.CloudName) + if err != nil { + return nil, err + } + err = json.Unmarshal(rst, &domains) + if err != nil { + return nil, err + } + results := make(map[string][]daddy.DNSRecord) + ticker := time.NewTicker(time.Second) + for _, domain := range domains { + wg.Add(1) + go func(domain string) { + defer wg.Done() + <-ticker.C + records, err := g.getRecordList(domain) + if err != nil { + logger.Error(fmt.Sprintf("[ %s_%s ] get record list failed: %v", g.account.CloudProvider, g.account.CloudName, err)) + } + if len(records) == 0 { + return + } + mu.Lock() + results[domain] = records + mu.Unlock() + }(domain.DomainName) + } + wg.Wait() + for domain, records := range results { + for _, v := range records { + dataObj = append(dataObj, Record{ + CloudProvider: g.account.CloudProvider, + CloudName: g.account.CloudName, + DomainName: domain, + RecordID: v.Data, + RecordType: v.Type, + RecordName: v.Name, + RecordValue: v.Data, + RecordTTL: strconv.Itoa(v.TTL), + RecordWeight: strconv.Itoa(v.Weight), + RecordStatus: "enable", + RecordRemark: v.Name, + FullRecord: v.Name + "." + domain, + }) + } + } + return dataObj, nil +} + +// https://developer.godaddy.com/doc/endpoint/domains +// GetDomainList 获取云解析中域名列表 +func (g *GodaddyDNS) getDomainList() ([]daddy.DomainSummary, error) { + domains, err := g.client.Domains.List(nil, nil, 0, "", nil, "") + if err != nil { + return nil, err + } + + return domains, nil +} + +// https://developer.godaddy.com/doc/endpoint/domains +// RecordList 域名记录列表 +func (g *GodaddyDNS) getRecordList(domain string) ([]daddy.DNSRecord, error) { + // TODO 目前写死的获取500条记录 + rds, err := g.client.Domains.GetRecords(domain, "", "", 0, 500) + if err != nil { + fmt.Printf("Error listing records: %v\n", err) + } + return rds, err +} diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 1203074..bc5a05f 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -32,6 +32,16 @@ func init() { }, } }) + Factory.Register(public.GodaddyDnsProvider, func(account map[string]string) DNSProvider { + return &GodaddyDNS{ + account: public.Account{ + CloudProvider: public.GodaddyDnsProvider, + CloudName: account["name"], + SecretID: account["secretId"], + SecretKey: account["secretKey"], + }, + } + }) } // Doamin 域名信息 @@ -123,7 +133,7 @@ func (f *DNSProviderFactory) Create(cloudProvider string, account map[string]str // 统一记录状态的值 func oneStatus(status string) string { // tencent 的记录状态是 ENABLE 和 DISABLE - if status == "ENABLE" { + if status == "ENABLE" || status == "ACTIVE" { return "enable" } if status == "DISABLE" { diff --git a/public/public.go b/public/public.go index ddc1fe0..19e29cb 100644 --- a/public/public.go +++ b/public/public.go @@ -22,6 +22,7 @@ const ( // Cloud Providers TencentDnsProvider string = "tencent" AliyunDnsProvider string = "aliyun" + GodaddyDnsProvider string = "godaddy" HuaweiDnsProvider string = "huawei" // Metrics Name DomainList string = "domain_list"