feat: add domain check

This commit is contained in:
2021-05-05 16:01:54 +08:00
parent 2f8f5d4c9d
commit e3d9979e48
4 changed files with 675 additions and 4 deletions

View File

@@ -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
View 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());
}