feat: migrate to api tokens

Cloudflare's v4 API provides three ways of authenticating,
with the API tokens being the new, standard conform, recommended
way. This patch removes the use of the old auth_key + email combo
and replaces it with API tokens.

BREAKING CHANGE: auth keys can't be used anymore, switch to api tokens
This commit is contained in:
Jan Christian Grünhage 2020-04-20 23:04:31 +02:00
parent 2920328093
commit ec8b893f6a
2 changed files with 19 additions and 32 deletions

View file

@ -11,8 +11,7 @@ use anyhow::{Context, Result};
#[derive(Deserialize)] #[derive(Deserialize)]
struct Config { struct Config {
email: String, api_token: String,
auth_key: String,
zone: String, zone: String,
domain: String, domain: String,
} }
@ -20,17 +19,13 @@ struct Config {
#[derive(Debug, StructOpt)] #[derive(Debug, StructOpt)]
/// Inform Cloudflare's DDNS service of the current IP address for your domain /// Inform Cloudflare's DDNS service of the current IP address for your domain
struct Cli { struct Cli {
/// Your TOML config file containing all the required options (email, auth_key, zone, domain) which you can use instead of passing the arguments to the command line /// Your TOML config file containing all the required options (api_token, zone, domain) which you can use instead of passing the arguments to the command line
#[structopt(long = "config", short = "f")] #[structopt(long = "config", short = "f")]
config: Option<PathBuf>, config: Option<PathBuf>,
/// Your Cloudflare login email /// The api token you need to generate in your Cloudflare profile
#[structopt(long = "email", short = "e", required_unless = "config")] #[structopt(long = "token", short = "t", required_unless = "config")]
email: Option<String>, api_token: Option<String>,
/// The auth key you need to generate in your Cloudflare profile
#[structopt(long = "key", short = "k", required_unless = "config")]
auth_key: Option<String>,
/// The zone in which your domain is (usually that is your base domain name) /// The zone in which your domain is (usually that is your base domain name)
#[structopt(long = "zone", short = "z", required_unless = "config")] #[structopt(long = "zone", short = "z", required_unless = "config")]
@ -76,21 +71,20 @@ fn main() -> Result<()> {
write_file(&args.cache.unwrap(), &current_ip)?; write_file(&args.cache.unwrap(), &current_ip)?;
} }
let (email, auth_key, zone, domain) = match args.config { let (api_token, zone, domain) = match args.config {
Some(c) => { Some(c) => {
let config_str = read_file(&c)?; let config_str = read_file(&c)?;
let config: Config = toml::from_str(&config_str)?; let config: Config = toml::from_str(&config_str)?;
(config.email, config.auth_key, config.zone, config.domain) (config.api_token, config.zone, config.domain)
} }
None => ( None => (
args.email.expect("Email is not set"), args.api_token.expect("API token is not set"),
args.auth_key.expect("Auth key is not set"),
args.zone.expect("Zone is not set"), args.zone.expect("Zone is not set"),
args.domain.expect("Domain is not set"), args.domain.expect("Domain is not set"),
), ),
}; };
update(&current_ip, &email, &auth_key, &zone, &domain)?; update(&current_ip, &api_token, &zone, &domain)?;
println!( println!(
"Successfully updated the A record for {} to {}", "Successfully updated the A record for {} to {}",
@ -102,21 +96,19 @@ fn main() -> Result<()> {
fn update( fn update(
current_ip: &str, current_ip: &str,
email: &str, api_token: &str,
auth_key: &str,
zone: &str, zone: &str,
domain: &str, domain: &str,
) -> Result<()> { ) -> Result<()> {
let zone_id = get_zone_identifier(&zone, &email, &auth_key).context("Error getting the zone identifier")?; let zone_id = get_zone_identifier(&zone, &api_token).context("Error getting the zone identifier")?;
let record_id = get_dns_record_id(&zone_id, &domain, &email, &auth_key).context("Error getting the DNS record ID")?; let record_id = get_dns_record_id(&zone_id, &domain, &api_token).context("Error getting the DNS record ID")?;
update_ddns( update_ddns(
&current_ip, &current_ip,
&domain, &domain,
&zone_id, &zone_id,
&record_id, &record_id,
&email, &api_token,
&auth_key,
).context("Error updating the DNS record")?; ).context("Error updating the DNS record")?;
Ok(()) Ok(())

View file

@ -28,14 +28,13 @@ struct UpdateIpData {
content: String, content: String,
} }
pub fn get_zone_identifier(zone: &str, email: &str, key: &str) -> anyhow::Result<String> { pub fn get_zone_identifier(zone: &str, api_token: &str) -> anyhow::Result<String> {
let client = reqwest::blocking::Client::new(); let client = reqwest::blocking::Client::new();
let url = format!("https://api.cloudflare.com/client/v4/zones?name={}", zone); let url = format!("https://api.cloudflare.com/client/v4/zones?name={}", zone);
let response = client let response = client
.get(&url) .get(&url)
.header("X-Auth-Email", email) .header("Authorization", format!("Bearer {}", api_token))
.header("X-Auth-Key", key)
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.send()?; .send()?;
@ -68,8 +67,7 @@ pub fn get_zone_identifier(zone: &str, email: &str, key: &str) -> anyhow::Result
pub fn get_dns_record_id( pub fn get_dns_record_id(
zone_id: &str, zone_id: &str,
domain: &str, domain: &str,
email: &str, api_token: &str,
key: &str,
) -> anyhow::Result<String> { ) -> anyhow::Result<String> {
let client = reqwest::blocking::Client::new(); let client = reqwest::blocking::Client::new();
let url = format!( let url = format!(
@ -79,8 +77,7 @@ pub fn get_dns_record_id(
let response = client let response = client
.get(&url) .get(&url)
.header("X-Auth-Email", email) .header("Authorization", format!("Bearer {}", api_token))
.header("X-Auth-Key", key)
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.send()?; .send()?;
@ -132,8 +129,7 @@ pub fn update_ddns(
domain: &str, domain: &str,
zone_id: &str, zone_id: &str,
record_id: &str, record_id: &str,
email: &str, api_token: &str,
key: &str,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let client = reqwest::blocking::Client::new(); let client = reqwest::blocking::Client::new();
let url = format!( let url = format!(
@ -150,8 +146,7 @@ pub fn update_ddns(
let response = client let response = client
.put(&url) .put(&url)
.header("X-Auth-Email", email) .header("Authorization", format!("Bearer {}", api_token))
.header("X-Auth-Key", key)
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.json(&update_data) .json(&update_data)
.send()?; .send()?;