Add r53 command
This commit is contained in:
+175
@@ -0,0 +1,175 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json/jsontext"
|
||||
"encoding/json/v2"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/service/route53"
|
||||
"github.com/aws/aws-sdk-go-v2/service/route53/types"
|
||||
|
||||
"code.chimeric.al/dwrz/r53/pkg/digip"
|
||||
)
|
||||
|
||||
var (
|
||||
ttl = flag.Int64("ttl", 300, "TTL of the DNS record; seconds")
|
||||
zone = flag.String("zone", "", "hosted zone id")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
var log = slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
|
||||
AddSource: true,
|
||||
Level: slog.LevelDebug,
|
||||
}))
|
||||
|
||||
// Setup the main context.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
if err := run(ctx, log); err != nil {
|
||||
log.Error("failed", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func run(ctx context.Context, log *slog.Logger) error {
|
||||
if *zone == "" {
|
||||
return fmt.Errorf("missing hosted zone id")
|
||||
}
|
||||
|
||||
// Load the AWS configuration.
|
||||
awscfg, err := config.LoadDefaultConfig(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve AWS config: %w", err)
|
||||
}
|
||||
|
||||
// Create the Route53 client.
|
||||
var svc = route53.NewFromConfig(awscfg)
|
||||
|
||||
// Get the IP address.
|
||||
var ip4, ip6 string
|
||||
|
||||
// Dig with OpenDNS.
|
||||
if ip, err := digip.OpenDNS(ctx, digip.A); err != nil {
|
||||
log.ErrorContext(
|
||||
ctx,
|
||||
"failed to get opendns ipv4 ip",
|
||||
slog.Any("error", err),
|
||||
)
|
||||
} else {
|
||||
ip4 = ip.String()
|
||||
}
|
||||
if ip, err := digip.OpenDNS(ctx, digip.AAAA); err != nil {
|
||||
log.ErrorContext(
|
||||
ctx,
|
||||
"failed to get opendns ipv6 ip",
|
||||
slog.Any("error", err),
|
||||
)
|
||||
} else {
|
||||
ip6 = ip.String()
|
||||
}
|
||||
|
||||
// Fallback on Google.
|
||||
if ip4 == "" {
|
||||
if ip, err := digip.Google(ctx, digip.V4); err != nil {
|
||||
log.ErrorContext(
|
||||
ctx,
|
||||
"failed to get google ipv4 ip",
|
||||
slog.Any("error", err),
|
||||
)
|
||||
} else {
|
||||
ip4 = ip.String()
|
||||
}
|
||||
}
|
||||
if ip6 == "" {
|
||||
if ip, err := digip.Google(ctx, digip.V6); err != nil {
|
||||
log.ErrorContext(
|
||||
ctx,
|
||||
"failed to get google ipv6 ip",
|
||||
slog.Any("error", err),
|
||||
)
|
||||
} else {
|
||||
ip6 = ip.String()
|
||||
}
|
||||
}
|
||||
|
||||
// Abort if we don't have any IP address.
|
||||
if ip4 == "" && ip6 == "" {
|
||||
return fmt.Errorf("failed to retrieve public ip")
|
||||
}
|
||||
|
||||
// Update the domains.
|
||||
for _, domain := range flag.Args() {
|
||||
// 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: 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: ttl,
|
||||
Type: types.RRTypeAaaa,
|
||||
},
|
||||
}
|
||||
changes = append(changes, change)
|
||||
}
|
||||
|
||||
input := &route53.ChangeResourceRecordSetsInput{
|
||||
ChangeBatch: &types.ChangeBatch{
|
||||
Changes: changes,
|
||||
Comment: aws.String("DDNS"),
|
||||
},
|
||||
HostedZoneId: zone,
|
||||
}
|
||||
|
||||
result, err := svc.ChangeResourceRecordSets(ctx, input)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update route 53: %w", err)
|
||||
}
|
||||
|
||||
outcome, err := json.Marshal(result, jsontext.WithIndent(" "))
|
||||
if err != nil {
|
||||
log.ErrorContext(
|
||||
ctx,
|
||||
"failed to unmarshal response",
|
||||
slog.Any("error", err),
|
||||
)
|
||||
|
||||
outcome = []byte(result.ChangeInfo.Status)
|
||||
}
|
||||
|
||||
log.DebugContext(
|
||||
ctx,
|
||||
"updated route 53 records",
|
||||
slog.String("domain", domain),
|
||||
slog.Any("outcome", outcome),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user