From 25a950f7582b8b262a6b9f947a2b3b181ab939dc Mon Sep 17 00:00:00 2001 From: rskntroot Date: Mon, 16 Jun 2025 01:29:25 +0000 Subject: [PATCH] add weekly notif msg; add variable refresh timer --- README.md | 42 ++++++++++++++++++++++++------------------ aws.policy | 49 ++++++++++++++++++++++++++----------------------- src/main.rs | 30 ++++++++++++++++++++++++------ 3 files changed, 74 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 1834055..42b6c15 100644 --- a/README.md +++ b/README.md @@ -14,20 +14,18 @@ This is intended to be installed on a public-facing loadbalancer. ## Assumptions -1. Your ISP randomly changes your PublicIP and that pisses you off. +1. Your ISP randomly changes your PublicIP and that upsets you. 1. You just want something that will curl `ipv4.icanhazip.com`, check 3rd-party dns, and update Route53. -1. Your Name records only contain a single IP. (future update maybe). +1. Your Name records only contain a single IP. If so, this is for you. ## Setup - -1. setup `Route53AllowRecordUpdate.policy` - ```zsh - DNS_ZONE_ID=YOURZONEIDHERE \ - envsubst < aws.policy > Route53AllowRecordUpdate.policy - ``` -1. in aws, create IAM user, attach policy, generate access keys for automated service +1. get +1. in [aws console](https://console.aws.amazon.com): + - create IAM user + - attach policy `aws.policy` file provided + - generate access keys for automated service 1. log into aws cli with the account you created above ``` aws configure @@ -36,14 +34,18 @@ If so, this is for you. ``` zsh ln -sf ~/r53-ddns/target/release/r53-ddns /usr/bin/r53-ddns ``` +1. get your hosted_zone_id + ``` zsh + aws route53 list-hosted-zones + ``` 1. setup systemd service and then install as normal - ```zsh + ``` zsh DNS_ZONE_ID=YOURZONEIDHERE \ DOMAIN_NAME=your.domain.com. \ - envsubst < r53-ddns.service | sudo tee -a /etc/systemd/system/r53-ddns.service + envsubst < r53-ddns.service | sudo tee /etc/systemd/system/r53-ddns.service ``` -## CLI Usage +## Usage ``` $ r53-ddns -h @@ -54,6 +56,7 @@ Usage: r53-ddns --dns-zone-id --domain-name Options: -z, --dns-zone-id DNS ZONE ID (see AWS Console Route53) -d, --domain-name DOMAIN NAME (ex. 'docs.rskio.com.') + -s, --seconds SECONDS refresh timer in seconds [default: 180] -h, --help Print help ``` @@ -73,7 +76,9 @@ sudo systemctl status r53-ddns.service ``` ``` -$ systemctl status r53-ddns.service +$ envsubst < r53-ddns.service | sudo tee /etc/systemd/system/r53-ddns.service +$ sudo systemctl enable --now r53-ddns.service +$ sudo systemctl status r53-ddns.service ● r53-ddns.service - Route53 Dynamic DNS Service Loaded: loaded (/etc/systemd/system/r53-ddns.service; enabled; vendor preset: enabled) Active: active (running) since Mon 2024-07-29 09:03:40 UTC; 7min ago @@ -86,18 +91,19 @@ $ systemctl status r53-ddns.service Jul 29 09:03:40 hostname systemd[1]: Started Route53 Dynamic DNS Service. Jul 29 09:03:40 hostname r53-ddns[215630]: [2024-07-29T09:03:40Z INFO r53_ddns] starting with options: -z [##TRUNCATED##] -d rskio.com. +Jul 29 09:03:40 hostname r53-ddns[215630]: [2024-07-29T09:03:40Z INFO r53_ddns] current public address is: 10.0.0.1 Jul 29 09:09:41 hostname r53-ddns[215630]: [2024-07-29T09:09:41Z INFO r53_ddns::dns] dynamic ip drift detected: 10.0.0.1 -> 71.211.88.219 Jul 29 09:09:41 hostname r53-ddns[215630]: [2024-07-29T09:09:41Z INFO r53_ddns::route53] requesting update to route53 record for A rskio.com. -> 71.211.88.219 Jul 29 09:09:41 hostname r53-ddns[215630]: [2024-07-29T09:09:41Z INFO r53_ddns::route53] change_id: /change/C02168177BNS6R50C32Q has status: Pending Jul 29 09:10:41 hostname r53-ddns[215630]: [2024-07-29T09:09:41Z INFO r53_ddns::route53] change_id: /change/C02168177BNS6R50C32Q has status: Insync ``` -## Q&A +## FAQs -> Why did you do create this monster in rust? +> Does this handle multiple record updates? -To be able to handle errors in the future. +No. The goal here was for a single server to sync its dns record. If you are running multiple services from the same host, then consider using CNAMEs to point at a global A|AAAA record for this to update. -> wen IPv6? +> What if I need to update only a single address in the record? -It should work with IPv6. +Let me know. I have been considering this use-case, but haven't implemented it yet. diff --git a/aws.policy b/aws.policy index 18fcfaa..5d483ea 100644 --- a/aws.policy +++ b/aws.policy @@ -1,24 +1,27 @@ { - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "VisualEditor0", - "Effect": "Allow", - "Action": [ - "route53:ListResourceRecordSets", - "route53:ChangeResourceRecordSets", - "route53:GetChange" - ], - "Resource": [ - "arn:aws:route53:::hostedzone/${DNS_ZONE_ID}", - "arn:aws:route53:::change/*" - ] - }, - { - "Sid": "VisualEditor1", - "Effect": "Allow", - "Action": "route53:TestDNSAnswer", - "Resource": "*" - } - ] -} \ No newline at end of file + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "VisualEditor0", + "Effect": "Allow", + "Action": [ + "route53:ListResourceRecordSets", + "route53:ChangeResourceRecordSets", + "route53:GetChange" + ], + "Resource": [ + "arn:aws:route53:::hostedzone/*", + "arn:aws:route53:::change/*" + ] + }, + { + "Sid": "VisualEditor1", + "Effect": "Allow", + "Action": [ + "route53:TestDNSAnswer", + "route53:ListHostedZones" + ], + "Resource": "*" + } + ] +} diff --git a/src/main.rs b/src/main.rs index ba04cdd..3ef0705 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,19 +6,28 @@ use env_logger::Builder; use log::info; use reqwest::get; use std::net::IpAddr; +use std::time::SystemTime; use tokio::time::{sleep, Duration}; #[derive(Parser)] #[clap( name = "r53-ddns", - about = "A CLI tool for correcting drift between your PublicIP and Route53 DNS A RECORD" + about = "A CLI tool for correcting drift between your PublicIP and a Route53 DNS A|AAAA RECORD" )] struct Args { #[clap(short = 'z', long, help = "DNS ZONE ID\t(see AWS Console Route53)")] dns_zone_id: String, - #[clap(short, long, help = "DOMAIN NAME\t(ex. 'docs.rskio.com.')")] + #[clap(short = 'd', long, help = "DOMAIN NAME\t(ex. 'docs.rskio.com.')")] domain_name: String, + + #[clap( + short = 's', + long, + help = "SECONDS\trefresh timer in seconds", + default_value = "180" + )] + seconds: u64, } #[tokio::main] @@ -27,18 +36,27 @@ async fn main() -> Result<(), Box> { Builder::new().filter(None, log::LevelFilter::Info).init(); info!( - "starting with options: -z {} -d {}", - &args.dns_zone_id, &args.domain_name, + "starting with options: -z {} -d {} -s {}", + &args.dns_zone_id, &args.domain_name, &args.seconds, ); + let mut next_status_time = SystemTime::now(); + loop { let public_ip = get_public_ip().await?; + // print the current public ip each week + if SystemTime::now() > next_status_time { + info!("current public address is: {}", public_ip); + next_status_time += Duration::from_secs(7 * 24 * 60 * 60); + } + + // update record if current public ip drifts from dns record if !dns::is_addr_current(&args.domain_name, public_ip).await? { route53::update_record(&args.dns_zone_id, &args.domain_name, public_ip).await?; - }; + } - sleep(Duration::from_secs(60)).await; + sleep(Duration::from_secs(args.seconds)).await; } }