-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
140 lines (115 loc) · 3.48 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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package main
import (
"context"
"fmt"
"log"
"net"
"os"
"os/signal"
"strconv"
"syscall"
"time"
"github.com/cloudflare/cloudflare-go"
"github.com/vishvananda/netlink"
)
func main() {
cf_token, ok := os.LookupEnv("CF_API_TOKEN")
if !ok {
log.Fatalf("CF_API_TOKEN environment variable must be set\n")
}
zoneName, ok := os.LookupEnv("CF_ZONE_NAME")
if !ok {
log.Fatalf("CF_ZONE_NAME environment variable must be set\n")
}
recordName, ok := os.LookupEnv("CF_RECORD_NAME")
if !ok {
log.Fatalf("CF_RECORD_NAME environment variable must be set\n")
}
var sis string
sis, ok = os.LookupEnv("CF_SYNC_INTERVAL_MINUTES")
if !ok {
sis = "10"
}
sii, err := strconv.ParseInt(sis, 10, 64)
if err != nil {
log.Fatalf("failed to parse sync interval string %v:\n\t%v\n", sis, err)
}
defRoute := net.ParseIP("8.8.8.8")
routes, err := netlink.RouteGet(defRoute)
if err != nil {
log.Fatalf("failed to get default route:\n\t%v\n", err)
}
if len(routes) < 1 {
log.Fatal("no route found to 8.8.8.8")
}
localAddress := routes[0].Src.String()
localLink := routes[0].LinkIndex
syncInterval := time.Duration(sii) * time.Minute
err = syncAddress(cf_token, zoneName, recordName, localAddress)
if err != nil {
log.Fatalf("failure during initial sync'ing of address:\n\t%v\n", err)
}
done := make(chan struct{}, 1)
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
auc := make(chan netlink.AddrUpdate, 1)
err = netlink.AddrSubscribe(auc, done)
if err != nil {
log.Fatalf("failure subscribing to address updates\n")
}
timec := time.NewTicker(syncInterval)
MainLoop:
for {
select {
case s := <-sigc:
fmt.Printf("captured %v signal, exiting\n", s)
break MainLoop
case au := <-auc:
if au.LinkIndex == localLink {
log.Printf("detected address change on default link\n")
err = syncAddress(cf_token, zoneName, recordName, localAddress)
if err != nil {
log.Printf("failure during sync after detected address change:\n\t%v\n", err)
}
}
case t := <-timec.C:
log.Printf("ticker fired at %v, sync'ing address\n", t)
err = syncAddress(cf_token, zoneName, recordName, localAddress)
if err != nil {
log.Printf("failure during sync on interval:\n\t%v\n", err)
}
}
}
close(done)
fmt.Println("tetelestai")
}
func syncAddress(cf_token, zone, hostname, address string) error {
api, err := cloudflare.NewWithAPIToken(cf_token)
if err != nil {
return fmt.Errorf("failed to get cloudflare API:\n\t%w\n", err)
}
ctx := context.Background()
zoneID, err := api.ZoneIDByName(zone)
if err != nil {
return fmt.Errorf("failed to get zone %v: %w", zone, err)
}
searchRec := cloudflare.DNSRecord{Type: "A", Name: hostname}
recs, err := api.DNSRecords(ctx, zoneID, searchRec)
if err != nil {
return fmt.Errorf("failed to search for dns record %v:\n\t%w\n", hostname, err)
}
if len(recs) < 1 {
return fmt.Errorf("no records found for %v in zone %v", hostname, zone)
}
if recs[0].Content != address {
log.Printf("dns record points to %v, but our interface is %v. Updating dns record...", recs[0].Content, address)
newRec := cloudflare.DNSRecord{Type: "A", Name: hostname, Content: address, TTL: 60}
err = api.UpdateDNSRecord(ctx, zoneID, recs[0].ID, newRec)
if err != nil {
return fmt.Errorf("failed to update record %v to content %v:\n\t%w\n", hostname, address, err)
}
return nil
}
log.Printf("dns record matches local interface, no updates necessary")
return nil
}