From a685f0b0acc46faf416424201fc48f218bc347de Mon Sep 17 00:00:00 2001 From: Artheriom Date: Tue, 4 Oct 2022 02:04:21 +0200 Subject: [PATCH] v2 for Scaleway DynDNS updater. --- .gitignore | 2 + bash/OnlineDynDNS.sh | 28 ------ bash/README.md | 7 -- cmd/main.go | 19 ++++ go.mod | 7 ++ go.sum | 15 +++ golang/README.md | 6 -- golang/main/main.go | 94 ------------------- internal/connectors/get_ip.go | 39 ++++++++ internal/connectors/update_scaleway_domain.go | 45 +++++++++ internal/helpers/configuration.go | 46 +++++++++ internal/structures/structures.go | 16 ++++ readme.md | 11 ++- 13 files changed, 197 insertions(+), 138 deletions(-) create mode 100644 .gitignore delete mode 100644 bash/OnlineDynDNS.sh delete mode 100644 bash/README.md create mode 100644 cmd/main.go create mode 100644 go.mod create mode 100644 go.sum delete mode 100644 golang/README.md delete mode 100644 golang/main/main.go create mode 100644 internal/connectors/get_ip.go create mode 100644 internal/connectors/update_scaleway_domain.go create mode 100644 internal/helpers/configuration.go create mode 100644 internal/structures/structures.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e25fa60 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/* +.idea \ No newline at end of file diff --git a/bash/OnlineDynDNS.sh b/bash/OnlineDynDNS.sh deleted file mode 100644 index 91c5b55..0000000 --- a/bash/OnlineDynDNS.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -ONLINE_API="" # Define your Online API key. You can find it at https://console.online.net/fr/api/access -DOMAIN="" ## Your domain name, eg : example.com -SUB="" ## Your subdomain you want to update. For example, "home" for "home.example.com" - -# Now son, don't touch the following. -TYPE="A" -IPYET=$(dig +short $SUB.$DOMAIN) -ADDRESS=$(curl -s ifconfig.me) - -#Check if there is a new IP to update, if not the API will not be called. -if [ "$IPYET" != "$ADDRESS" ] -then - # Check if address is IPv4 or IPv6. - if [[ $ADDRESS =~ ":" ]] - then - TYPE="AAAA" - fi - - # Call Online API - curl -H "Authorization: Bearer $ONLINE_API" -X PATCH --data "[{\"name\": \"$SUB\",\"type\": \"$TYPE\",\"changeType\": \"REPLACE\",\"records\": [{\"name\": \"$SUB\",\"type\": \"$TYPE\",\"priority\": 0,\"ttl\": 3600,\"data\": \"$ADDRESS\"}]}]" "https://api.online.net/api/v1/domain/$DOMAIN/version/active" - -else - echo Ip stay the same ! -fi - -#End. diff --git a/bash/README.md b/bash/README.md deleted file mode 100644 index 6864b96..0000000 --- a/bash/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Online DynDNS - Bash version - -## How to use -1. Open the .sh file -2. Define the first three lines with your informations -3. Set the file as executable (`chmod +x OnlineDynDNS.sh`) -4. Use a crontask to call automatically the code \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..342c9a3 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "github.com/Artheriom/ScalewayDynDNS/internal/connectors" + "github.com/Artheriom/ScalewayDynDNS/internal/helpers" + "github.com/sirupsen/logrus" +) + +func main() { + + ip, kind, err := connectors.GetIPAddress() + if err != nil { + logrus.Fatalln(err.Error()) + } + + for k, v := range helpers.Domains { + _ = connectors.UpdateScalewayDomain(k, v, ip, kind) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1f2dc12 --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module github.com/Artheriom/ScalewayDynDNS + +go 1.19 + +require github.com/sirupsen/logrus v1.9.0 + +require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ed65537 --- /dev/null +++ b/go.sum @@ -0,0 +1,15 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/golang/README.md b/golang/README.md deleted file mode 100644 index 0a4e3e7..0000000 --- a/golang/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Online DynDNS - Golang version - -## How to use -1. (Optional) build the main.go file -2. Start the program with `domain` and `key` parameter. Example : `go main.go -domain "my.domain.that.i.want" -key "OnlineToken"` -3. Use a crontask to call automatically the code \ No newline at end of file diff --git a/golang/main/main.go b/golang/main/main.go deleted file mode 100644 index c576bea..0000000 --- a/golang/main/main.go +++ /dev/null @@ -1,94 +0,0 @@ -package main - -import ( - "encoding/json" - "flag" - "fmt" - "io/ioutil" - "log" - "net/http" - "strings" - "time" -) - -type Connect struct { - Name string `json:"name"` - Type string `json:"type"` - ChangeType string `json:"changeType"` - Records []Records `json:"records"` -} - -type Records struct { - Name string `json:"name"` - Type string `json:"type"` - Priority int `json:"priority"` - TTL int `json:"ttl"` - Data string `json:"data"` -} - -func main() { - //Get data - var onlineDomain string - var onlineAPI string - - flag.StringVar(&onlineDomain, "domain", "", "Define the domain or subdomain to update (eg github.com or example.github.com)") - flag.StringVar(&onlineAPI, "key", "", "Set the key for your Online API access") - flag.Parse() - - if onlineDomain=="" || onlineAPI=="" { - log.Fatal("No domain or key given, abort") - } - - //Exploding onlineDomain - exploded := strings.Split(onlineDomain, ".") - - domain := exploded[len(exploded)-2]+"."+ exploded[len(exploded)-1] - subdomain := "" - if len(exploded) > 2 { - for i:= len(exploded)-3; i>=0; i-- { - subdomain = exploded[i]+"."+subdomain - } - subdomain = subdomain[:len(subdomain)-1] - } - - - //Get your IP on ifconfig.me - doConfigCall, _ := http.NewRequest("GET", "http://ifconfig.me", nil) - client := &http.Client{Timeout: time.Second * 20} - - ipAnswer, err := client.Do(doConfigCall) - if err != nil || ipAnswer.StatusCode != 200 { - log.Fatal("Error while trying to GET ifconfig.me !") - } - - body, _ := ioutil.ReadAll(ipAnswer.Body) - ip := string(body) - - //Define addressType - addressType := "AAAA" - if len(strings.Split(ip, "."))>1 { - addressType = "A" - } - - //Building request to Scaleway API - var contentToSend []Connect - var records []Records - - records = append(records, Records{Name:subdomain, Type:addressType, Priority:0, TTL:3600, Data:ip}) - contentToSend = append(contentToSend, Connect{Name: subdomain, ChangeType:"REPLACE", Type:addressType, Records:records}) - - marshalled, _ := json.Marshal(contentToSend) - fmt.Println(string(marshalled)) - - readyToSend := strings.NewReader(string(marshalled)) - - res2, _ := http.NewRequest("PATCH", "https://api.online.net/api/v1/domain/"+domain+"/version/active", readyToSend) - res2.Header.Set("Authorization", "Bearer "+onlineAPI) - - client = &http.Client{Timeout: time.Second * 20} - - _, err = client.Do(res2) - if err != nil { - log.Fatal("Error while trying to PATCH online DNS !") - } -} \ No newline at end of file diff --git a/internal/connectors/get_ip.go b/internal/connectors/get_ip.go new file mode 100644 index 0000000..99ba73d --- /dev/null +++ b/internal/connectors/get_ip.go @@ -0,0 +1,39 @@ +package connectors + +import ( + "errors" + "github.com/sirupsen/logrus" + "io" + "net/http" + "strings" + "time" +) + +func GetIPAddress() (ip string, addressType string, err error) { + //Get IP on ifconfig.me + doConfigCall, _ := http.NewRequest("GET", "https://ifconfig.me", nil) + client := &http.Client{Timeout: time.Second * 20} + + ipAnswer, err := client.Do(doConfigCall) + if err != nil { + logrus.Warnf("An error occurred while fetching IP from ifconfig.me: %s.", err.Error()) + return + } + if ipAnswer.StatusCode != 200 { + logrus.Warnf("ifconfig.me returned invalid status code: %d.", ipAnswer.StatusCode) + return "", "", errors.New("invalid status code") + } + + body, _ := io.ReadAll(ipAnswer.Body) + ip = string(body) + + //Define addressType + addressType = "AAAA" + if len(strings.Split(ip, ".")) > 1 { + addressType = "A" + } + + logrus.Infof("Detected IP for your home is %s (which will create and/or update %s records)", ip, addressType) + + return +} diff --git a/internal/connectors/update_scaleway_domain.go b/internal/connectors/update_scaleway_domain.go new file mode 100644 index 0000000..9c97367 --- /dev/null +++ b/internal/connectors/update_scaleway_domain.go @@ -0,0 +1,45 @@ +package connectors + +import ( + "encoding/json" + "github.com/Artheriom/ScalewayDynDNS/internal/helpers" + "github.com/Artheriom/ScalewayDynDNS/internal/structures" + "github.com/sirupsen/logrus" + "net/http" + "strings" + "time" +) + +func UpdateScalewayDomain(domainName string, subdomains []string, newIp string, ipKind string) (err error) { + + logrus.Infof("Updating domain %s and its subdomains.", domainName) + + //Building request to Scaleway API + var contentToSend []structures.Connect + var records []structures.Records + + for _, k := range subdomains { + records = append(records, structures.Records{Name: k, Type: ipKind, Priority: 0, TTL: 3600, Data: newIp}) + contentToSend = append(contentToSend, structures.Connect{Name: k, ChangeType: "REPLACE", Type: ipKind, Records: records}) + records = []structures.Records{} + } + + marshalled, _ := json.Marshal(contentToSend) + + readyToSend := strings.NewReader(string(marshalled)) + res2, _ := http.NewRequest("PATCH", "https://api.online.net/api/v1/domain/"+domainName+"/version/active", readyToSend) + res2.Header.Set("Authorization", "Bearer "+helpers.Key) + + client := &http.Client{Timeout: time.Second * 20} + + resp, err := client.Do(res2) + if err != nil { + logrus.Warnf("Error while trying to PATCH online DNS. Error was: %s", err.Error()) + } else if resp.StatusCode != 204 { + logrus.Warnf("Server responded %d on PATCH for domain %s.", resp.StatusCode, domainName) + } else { + logrus.Infof("%s and its subdomains have been updated to new IP %s.", domainName, newIp) + } + + return +} diff --git a/internal/helpers/configuration.go b/internal/helpers/configuration.go new file mode 100644 index 0000000..d07372b --- /dev/null +++ b/internal/helpers/configuration.go @@ -0,0 +1,46 @@ +package helpers + +import ( + "flag" + "github.com/sirupsen/logrus" + "log" + "os" + "strings" +) + +var Domains = map[string][]string{} +var Key = "" + +func init() { + logrus.Infof("Acquiring configuration...") + var onlineDomain string + + flag.StringVar(&onlineDomain, "domain", "", "Define the domain or subdomain to update (eg github.com or example.github.com)") + flag.StringVar(&Key, "key", "", "Set the key for your Online API access") + flag.Parse() + + if onlineDomain == "" { + // Try to get variable through environment + onlineDomain = os.Getenv("SCALEWAY_DYNDNS_DOMAIN") + if onlineDomain == "" { + log.Fatal("No domain given, abort") + } + } + if Key == "" { + // Try to get variable through environment + Key = os.Getenv("SCALEWAY_API_KEY") + if Key == "" { + log.Fatal("No API key given, abort") + } + } + + // Explode domains through ";" + list := strings.Split(onlineDomain, ";") + for _, k := range list { + exploded := strings.Split(k, ".") + domain := exploded[len(exploded)-2] + "." + exploded[len(exploded)-1] + Domains[domain] = append(Domains[domain], k+".") + } + + logrus.Infof("Configuration loaded.") +} diff --git a/internal/structures/structures.go b/internal/structures/structures.go new file mode 100644 index 0000000..0c4caab --- /dev/null +++ b/internal/structures/structures.go @@ -0,0 +1,16 @@ +package structures + +type Connect struct { + Name string `json:"name"` + Type string `json:"type"` + ChangeType string `json:"changeType"` + Records []Records `json:"records"` +} + +type Records struct { + Name string `json:"name"` + Type string `json:"type"` + Priority int `json:"priority"` + TTL int `json:"ttl"` + Data string `json:"data"` +} diff --git a/readme.md b/readme.md index b6a010c..a0347de 100644 --- a/readme.md +++ b/readme.md @@ -2,9 +2,14 @@ This little project offer tools to help people who want to set a DNS dynamically. For example, for people who are self-hosting their stuff at home. -You will find some implementations into the dedicated subfolders. - -You are free to contribute, but please : Keep it Simple. +## How to use +1. Build the go program : `go build -o dyndns-scaleway ./cmd`, or take it from Release page. +2. Start the program with `domain` and `key` parameter. + * Example : `./dyndns-scaleway -domain "my.domain.that.i.want" -key "OnlineToken"`. + * You can use `SCALEWAY_DYNDNS_DOMAIN` and `SCALEWAY_API_KEY` environment variables to replace command-line arguments. + * You can specify multiple domains, BUT they must belong to the same user (because you can define only one API key). Simply separate each domain by a semicolon. + * Example : `SCALEWAY_DYNDNS_DOMAIN="my_subdomain.artheriom.fr;my_other_subdomain.artheriom.fr" ./dyndns-scaleway` +3. Use a crontab to call automatically the program on a regular basis ## Licence