/
main.go
100 lines (83 loc) · 2.71 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package main
import (
"context"
"crypto/sha512"
"encoding/hex"
"fmt"
"log"
"net/http"
"time"
arguments "github.com/aeron/digitalocean-ddns-updater/app/args"
clients "github.com/aeron/digitalocean-ddns-updater/app/clients"
"github.com/digitalocean/godo"
"golang.org/x/oauth2"
)
var args struct {
Address *string `default:":8080" help:"address to listen on"`
Endpoint *string `default:"/ddns" help:"endpoint path to handle updates"`
DOAPIToken *string `name:"digitalocean-api-token" environ:"DIGITALOCEAN_API_TOKEN" help:"DigitalOcean API token"`
SecurityToken *string `name:"security-token" environ:"SECURITY_TOKEN" help:"application security token"`
LimitRPS *float64 `name:"limit-rps" default:".01" environ:"LIMIT_RPS" help:"limit requests per second"`
LimitBurst *int `name:"limit-burst" default:"1" environ:"LIMIT_BURST" help:"limit a single burst size"`
}
func main() {
if err := arguments.Parse(&args); err != nil {
log.Fatalln("Argument parsing error:", err.Error())
}
if *args.DOAPIToken == "" {
log.Fatalln("DigitalOcean API token is required")
}
if *args.SecurityToken == "" {
hash := sha512.Sum512_256([]byte(*args.DOAPIToken))
*args.SecurityToken = hex.EncodeToString(hash[:])
log.Println("New auth token:", *args.SecurityToken)
}
client := clients.DigitalOceanDomains{
Op: godo.NewClient(
oauth2.NewClient(
context.Background(),
oauth2.StaticTokenSource(&oauth2.Token{AccessToken: *args.DOAPIToken}),
),
).Domains,
Timeout: 5 * time.Second,
}
mux := http.NewServeMux()
mux.HandleFunc(*args.Endpoint, func(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
par, err := ParseParams(&query)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if par.Token != *args.SecurityToken {
http.Error(w, "Authentication failed", http.StatusUnauthorized)
return
}
domain, err := par.Domain()
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
log.Printf("New IP for [%s] %s: %s", par.Kind, par.Name, par.Addr)
if id, err := client.GetDNSRecordId(domain, par.Kind, par.Name); err == nil {
if err := client.UpdateDNSRecord(domain, id, par.Addr); err != nil {
http.Error(w, err.Error(), http.StatusFailedDependency)
return
}
} else {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(200)
fmt.Fprintln(w, "Done")
})
go func() {
log.Println("Starting server on", *args.Address)
if err := http.ListenAndServe(*args.Address, limit(mux)); err != nil {
log.Fatalln("Server error:", err)
}
}()
select {}
}