add weekly notif msg; add variable refresh timer
This commit is contained in:
42
README.md
42
README.md
@@ -14,20 +14,18 @@ This is intended to be installed on a public-facing loadbalancer.
|
|||||||
|
|
||||||
## Assumptions
|
## 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. 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.
|
If so, this is for you.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
1. get
|
||||||
1. setup `Route53AllowRecordUpdate.policy`
|
1. in [aws console](https://console.aws.amazon.com):
|
||||||
```zsh
|
- create IAM user
|
||||||
DNS_ZONE_ID=YOURZONEIDHERE \
|
- attach policy `aws.policy` file provided
|
||||||
envsubst < aws.policy > Route53AllowRecordUpdate.policy
|
- generate access keys for automated service
|
||||||
```
|
|
||||||
1. in aws, create IAM user, attach policy, generate access keys for automated service
|
|
||||||
1. log into aws cli with the account you created above
|
1. log into aws cli with the account you created above
|
||||||
```
|
```
|
||||||
aws configure
|
aws configure
|
||||||
@@ -36,14 +34,18 @@ If so, this is for you.
|
|||||||
``` zsh
|
``` zsh
|
||||||
ln -sf ~/r53-ddns/target/release/r53-ddns /usr/bin/r53-ddns
|
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
|
1. setup systemd service and then install as normal
|
||||||
```zsh
|
``` zsh
|
||||||
DNS_ZONE_ID=YOURZONEIDHERE \
|
DNS_ZONE_ID=YOURZONEIDHERE \
|
||||||
DOMAIN_NAME=your.domain.com. \
|
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
|
$ r53-ddns -h
|
||||||
@@ -54,6 +56,7 @@ Usage: r53-ddns --dns-zone-id <DNS_ZONE_ID> --domain-name <DOMAIN_NAME>
|
|||||||
Options:
|
Options:
|
||||||
-z, --dns-zone-id <DNS_ZONE_ID> DNS ZONE ID (see AWS Console Route53)
|
-z, --dns-zone-id <DNS_ZONE_ID> DNS ZONE ID (see AWS Console Route53)
|
||||||
-d, --domain-name <DOMAIN_NAME> DOMAIN NAME (ex. 'docs.rskio.com.')
|
-d, --domain-name <DOMAIN_NAME> DOMAIN NAME (ex. 'docs.rskio.com.')
|
||||||
|
-s, --seconds <SECONDS> SECONDS refresh timer in seconds [default: 180]
|
||||||
-h, --help Print help
|
-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
|
● r53-ddns.service - Route53 Dynamic DNS Service
|
||||||
Loaded: loaded (/etc/systemd/system/r53-ddns.service; enabled; vendor preset: enabled)
|
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
|
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 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] 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::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] 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: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
|
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.
|
||||||
|
|||||||
47
aws.policy
47
aws.policy
@@ -1,24 +1,27 @@
|
|||||||
{
|
{
|
||||||
"Version": "2012-10-17",
|
"Version": "2012-10-17",
|
||||||
"Statement": [
|
"Statement": [
|
||||||
{
|
{
|
||||||
"Sid": "VisualEditor0",
|
"Sid": "VisualEditor0",
|
||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Action": [
|
"Action": [
|
||||||
"route53:ListResourceRecordSets",
|
"route53:ListResourceRecordSets",
|
||||||
"route53:ChangeResourceRecordSets",
|
"route53:ChangeResourceRecordSets",
|
||||||
"route53:GetChange"
|
"route53:GetChange"
|
||||||
],
|
],
|
||||||
"Resource": [
|
"Resource": [
|
||||||
"arn:aws:route53:::hostedzone/${DNS_ZONE_ID}",
|
"arn:aws:route53:::hostedzone/*",
|
||||||
"arn:aws:route53:::change/*"
|
"arn:aws:route53:::change/*"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Sid": "VisualEditor1",
|
"Sid": "VisualEditor1",
|
||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Action": "route53:TestDNSAnswer",
|
"Action": [
|
||||||
"Resource": "*"
|
"route53:TestDNSAnswer",
|
||||||
}
|
"route53:ListHostedZones"
|
||||||
]
|
],
|
||||||
|
"Resource": "*"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
30
src/main.rs
30
src/main.rs
@@ -6,19 +6,28 @@ use env_logger::Builder;
|
|||||||
use log::info;
|
use log::info;
|
||||||
use reqwest::get;
|
use reqwest::get;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
use std::time::SystemTime;
|
||||||
use tokio::time::{sleep, Duration};
|
use tokio::time::{sleep, Duration};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[clap(
|
#[clap(
|
||||||
name = "r53-ddns",
|
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 {
|
struct Args {
|
||||||
#[clap(short = 'z', long, help = "DNS ZONE ID\t(see AWS Console Route53)")]
|
#[clap(short = 'z', long, help = "DNS ZONE ID\t(see AWS Console Route53)")]
|
||||||
dns_zone_id: String,
|
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,
|
domain_name: String,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
short = 's',
|
||||||
|
long,
|
||||||
|
help = "SECONDS\trefresh timer in seconds",
|
||||||
|
default_value = "180"
|
||||||
|
)]
|
||||||
|
seconds: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@@ -27,18 +36,27 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
Builder::new().filter(None, log::LevelFilter::Info).init();
|
Builder::new().filter(None, log::LevelFilter::Info).init();
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"starting with options: -z {} -d {}",
|
"starting with options: -z {} -d {} -s {}",
|
||||||
&args.dns_zone_id, &args.domain_name,
|
&args.dns_zone_id, &args.domain_name, &args.seconds,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut next_status_time = SystemTime::now();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let public_ip = get_public_ip().await?;
|
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? {
|
if !dns::is_addr_current(&args.domain_name, public_ip).await? {
|
||||||
route53::update_record(&args.dns_zone_id, &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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user