src

Go monorepo.
Log | Files | Refs

commit fcea75e60c9f415bcf2d58bec25a4867e5ae3d38
parent 4227303c59f462e2c8f52528a350d6b2a053d951
Author: dwrz <dwrz@dwrz.net>
Date:   Fri,  2 Dec 2022 14:22:33 +0000

Add r53 cmd

Diffstat:
Acmd/r53/config/config.go | 48++++++++++++++++++++++++++++++++++++++++++++++++
Acmd/r53/config/config.json.template | 3+++
Acmd/r53/main.go | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 181 insertions(+), 0 deletions(-)

diff --git a/cmd/r53/config/config.go b/cmd/r53/config/config.go @@ -0,0 +1,48 @@ +package config + +import ( + "context" + _ "embed" + "encoding/json" + "flag" + "fmt" + "strconv" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" +) + +var ( + //go:embed config.json + configuration []byte + ttl = flag.String("ttl", "300", "TTL") +) + +type Config struct { + AWS *aws.Config `json:"-"` + HostedZoneId string `json:"hostedZoneId"` + TTL int64 `json:"-"` +} + +func New(ctx context.Context) (*Config, error) { + flag.Parse() + + var cfg = &Config{} + if err := json.Unmarshal(configuration, cfg); err != nil { + return nil, fmt.Errorf("failed to parse config file: %v", err) + } + + awscfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + return nil, fmt.Errorf("failed to retrieve AWS config: %v", err) + } + cfg.AWS = &awscfg + + if n, err := strconv.ParseInt(*ttl, 10, 64); err != nil { + return nil, fmt.Errorf("failed to parse ttl: %v", err) + } else { + cfg.TTL = n + } + + return cfg, nil +} diff --git a/cmd/r53/config/config.json.template b/cmd/r53/config/config.json.template @@ -0,0 +1,3 @@ +{ + "hostedZoneId": "" +} diff --git a/cmd/r53/main.go b/cmd/r53/main.go @@ -0,0 +1,130 @@ +package main + +import ( + "context" + "encoding/json" + "os" + + "code.dwrz.net/src/pkg/digip" + "code.dwrz.net/src/pkg/log" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/route53" + "github.com/aws/aws-sdk-go-v2/service/route53/types" + + "code.dwrz.net/src/cmd/r53/config" +) + +func main() { + // Setup the logger. + l := log.New(os.Stderr) + + // Setup the main context. + ctx, cancel := context.WithCancel(context.Background()) + + // Get the service configuration. + cfg, err := config.New(ctx) + if err != nil { + l.Error.Fatalf("failed to get config: %v", err) + } + + // Create the Route53 client. + var svc = route53.NewFromConfig(*cfg.AWS) + + // Get the IP address. + var ip4, ip6 string + + // Dig with OpenDNS. + if ip, err := digip.OpenDNS(ctx, digip.A); err != nil { + l.Error.Printf("failed to get opendns ipv4 ip: %v", err) + } else { + ip4 = ip.String() + } + if ip, err := digip.OpenDNS(ctx, digip.AAAA); err != nil { + l.Error.Printf("failed to get opendns ipv6 ip: %v", err) + } else { + ip6 = ip.String() + } + + // Fallback on Google. + if ip4 == "" { + if ip, err := digip.Google(ctx, digip.V4); err != nil { + l.Error.Printf("failed to get google ipv4 ip: %v", err) + } else { + ip4 = ip.String() + } + } + if ip6 == "" { + if ip, err := digip.Google(ctx, digip.V6); err != nil { + l.Error.Printf("failed to get google ipv6 ip: %v", err) + } else { + ip6 = ip.String() + } + } + + // Abort if we don't have any IP address. + if ip4 == "" && ip6 == "" { + l.Error.Fatalf("failed to retrieve public ip") + } + + // Update the domains. + for _, domain := range os.Args[1:] { + // Assemble the change(s). + var changes []types.Change + if ip4 != "" { + change := types.Change{ + Action: types.ChangeActionUpsert, + ResourceRecordSet: &types.ResourceRecordSet{ + Name: &domain, + ResourceRecords: []types.ResourceRecord{ + {Value: &ip4}, + }, + TTL: &cfg.TTL, + Type: types.RRTypeA, + }, + } + + changes = append(changes, change) + } + if ip6 != "" { + change := types.Change{ + Action: types.ChangeActionUpsert, + ResourceRecordSet: &types.ResourceRecordSet{ + Name: &domain, + ResourceRecords: []types.ResourceRecord{ + {Value: &ip6}}, + TTL: &cfg.TTL, + Type: types.RRTypeAaaa, + }, + } + changes = append(changes, change) + } + + input := &route53.ChangeResourceRecordSetsInput{ + ChangeBatch: &types.ChangeBatch{ + Changes: changes, + Comment: aws.String("DDNS"), + }, + HostedZoneId: &cfg.HostedZoneId, + } + + result, err := svc.ChangeResourceRecordSets(ctx, input) + if err != nil { + l.Error.Fatalf("failed to update route 53: %v", err) + } + + outcome, err := json.MarshalIndent(result, "", " ") + if err != nil { + l.Error.Printf("failed to unmarshal response: %v", err) + + outcome = []byte(result.ChangeInfo.Status) + } + + l.Debug.Printf( + "updated %s route 53 records:\n%s", domain, outcome, + ) + } + + // Cancel the main context. + cancel() +}