feat: add domain check
This commit is contained in:
36
src/main.rs
36
src/main.rs
@@ -5,6 +5,7 @@ extern crate rust_util;
|
||||
|
||||
mod config;
|
||||
mod x509;
|
||||
mod network;
|
||||
// mod simple_thread_pool;
|
||||
|
||||
use std::env;
|
||||
@@ -27,6 +28,7 @@ use config::AcmeMode;
|
||||
use crate::config::{CertConfig, CERT_NAME, KEY_NAME};
|
||||
use crate::x509::{X509PublicKeyAlgo, X509EcPublicKeyAlgo};
|
||||
use std::path::PathBuf;
|
||||
use crate::network::{get_local_public_ip, get_resolver, resolve_first_ipv4};
|
||||
|
||||
const NAME: &str = env!("CARGO_PKG_NAME");
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
@@ -46,6 +48,7 @@ struct AcmeRequest<'a> {
|
||||
mode: AcmeMode,
|
||||
account_dir: &'a str,
|
||||
timeout: u64,
|
||||
local_public_ip: Option<&'a str>,
|
||||
key_file: Option<String>,
|
||||
cert_file: Option<String>,
|
||||
}
|
||||
@@ -69,6 +72,7 @@ async fn main() -> tide::Result<()> {
|
||||
.arg(Arg::with_name("config").short("c").long("config").takes_value(true).help("Cert config"))
|
||||
.arg(Arg::with_name("check").long("check").help("Check cert config"))
|
||||
.arg(Arg::with_name("hide-logo").long("hide-logo").help("Hide logo"))
|
||||
.arg(Arg::with_name("skip-verify-ip").long("skip-verify-ip").help("Verify public ip"))
|
||||
.get_matches();
|
||||
|
||||
if matches.is_present("verbose") {
|
||||
@@ -85,6 +89,16 @@ async fn main() -> tide::Result<()> {
|
||||
println!("{}", include_str!("logo.txt"));
|
||||
}
|
||||
|
||||
let skip_verify_ip = matches.is_present("skip-verify-ip");
|
||||
let local_public_ip = if skip_verify_ip {
|
||||
None
|
||||
} else {
|
||||
Some(get_local_public_ip().unwrap_or_else(|e| {
|
||||
failure!("Get local public ip failed: {}", e);
|
||||
exit(1);
|
||||
}))
|
||||
};
|
||||
|
||||
debugging!("Clap matches: {:?}", matches);
|
||||
|
||||
let account_dir = matches.value_of("dir").unwrap_or("acme_dir");
|
||||
@@ -197,6 +211,7 @@ async fn main() -> tide::Result<()> {
|
||||
mode,
|
||||
account_dir,
|
||||
timeout,
|
||||
local_public_ip: local_public_ip.as_ref().map(|ip| ip.as_str()),
|
||||
..Default::default()
|
||||
};
|
||||
if let Err(e) = request_acme_certificate(acme_request) {
|
||||
@@ -233,6 +248,7 @@ async fn main() -> tide::Result<()> {
|
||||
mode,
|
||||
account_dir,
|
||||
timeout,
|
||||
local_public_ip: local_public_ip.as_ref().map(|ip| ip.as_str()),
|
||||
cert_file: Some(format!("{}/{}", item.path, CERT_NAME)),
|
||||
key_file: Some(format!("{}/{}", item.path, KEY_NAME)),
|
||||
};
|
||||
@@ -277,6 +293,26 @@ fn check_cert_config(cert_config: &CertConfig) {
|
||||
}
|
||||
|
||||
fn request_acme_certificate(acme_request: AcmeRequest) -> XResult<()> {
|
||||
if let Some(local_public_ip) = acme_request.local_public_ip {
|
||||
let mut all_domains = vec![acme_request.primary_name.to_string()];
|
||||
for alt_name in acme_request.alt_names {
|
||||
all_domains.push(alt_name.to_string());
|
||||
}
|
||||
information!("Checking domain dns records, domains: {:?}", all_domains);
|
||||
let resolver = opt_result!(get_resolver(), "Get resolver failed: {}");
|
||||
|
||||
for domain in &all_domains {
|
||||
debugging!("Checking domain: {}", domain);
|
||||
let ipv4 = opt_result!(resolve_first_ipv4(&resolver, domain), "{}");
|
||||
match ipv4 {
|
||||
None => return simple_error!("Resolve domain ip failed: {}", domain),
|
||||
Some(ipv4) => if local_public_ip != &ipv4 {
|
||||
return simple_error!("Check domain ip: {}, mis-match, local: {} vs domain: {}", domain, local_public_ip, ipv4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
information!("Acme mode: {:?}", acme_request.mode);
|
||||
let url = acme_request.mode.directory_url();
|
||||
information!("Acme dir: {}", acme_request.account_dir);
|
||||
|
||||
35
src/network.rs
Normal file
35
src/network.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use trust_dns_resolver::Resolver;
|
||||
use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};
|
||||
use rust_util::XResult;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PublicIpResponse {
|
||||
pub status: i32,
|
||||
pub message: String,
|
||||
pub ip: String,
|
||||
pub user_agent: Option<String>,
|
||||
}
|
||||
|
||||
pub fn get_local_public_ip() -> XResult<String> {
|
||||
let response = opt_result!(reqwest::blocking::get("https://hatter.ink/ip/ip.jsonp"), "Get local public ip failed: {}");
|
||||
let response_text = opt_result!(response.text(), "Get local public ip failed: {}");
|
||||
let response_json: PublicIpResponse = opt_result!(deser_hjson::from_str(&response_text), "Parse get public ip response failed: {}");
|
||||
Ok(response_json.ip)
|
||||
}
|
||||
|
||||
pub fn get_resolver() -> XResult<Resolver> {
|
||||
Ok(Resolver::new(ResolverConfig::default(), ResolverOpts::default())?)
|
||||
}
|
||||
|
||||
pub fn resolve_first_ipv4(resolver: &Resolver, domain: &str) -> XResult<Option<String>> {
|
||||
let ip = opt_result!(resolver.ipv4_lookup(domain), "Resolve domain: {}, failed: {}", domain);
|
||||
Ok(ip.iter().next().map(|i| i.to_string()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
println!("{:?}", resolve_first_ipv4(&get_resolver().unwrap(),"hatter.ink"));
|
||||
println!("{:?}", get_local_public_ip());
|
||||
}
|
||||
Reference in New Issue
Block a user