src/client/http_client.rs: implement fingerprint cache
This commit is contained in:
parent
d59dbeca1b
commit
5a74756c15
@ -167,6 +167,7 @@ fn connect(server: &str, userid: &str) -> Result<HttpClient, Error> {
|
|||||||
|
|
||||||
let options = HttpClientOptions::new()
|
let options = HttpClientOptions::new()
|
||||||
.interactive(true)
|
.interactive(true)
|
||||||
|
.fingerprint_cache(true)
|
||||||
.ticket_cache(true);
|
.ticket_cache(true);
|
||||||
|
|
||||||
HttpClient::new(server, userid, options)
|
HttpClient::new(server, userid, options)
|
||||||
@ -1472,10 +1473,9 @@ async fn status(param: Value) -> Result<Value, Error> {
|
|||||||
// like get, but simply ignore errors and return Null instead
|
// like get, but simply ignore errors and return Null instead
|
||||||
async fn try_get(repo: &BackupRepository, url: &str) -> Value {
|
async fn try_get(repo: &BackupRepository, url: &str) -> Value {
|
||||||
|
|
||||||
|
|
||||||
let options = HttpClientOptions::new()
|
let options = HttpClientOptions::new()
|
||||||
.verify_cert(false) // fixme: set verify to true, but howto handle fingerprint ??
|
|
||||||
.interactive(false)
|
.interactive(false)
|
||||||
|
.fingerprint_cache(true)
|
||||||
.ticket_cache(true);
|
.ticket_cache(true);
|
||||||
|
|
||||||
let client = match HttpClient::new(repo.host(), repo.user(), options) {
|
let client = match HttpClient::new(repo.host(), repo.user(), options) {
|
||||||
|
@ -35,6 +35,7 @@ pub struct HttpClientOptions {
|
|||||||
fingerprint: Option<String>,
|
fingerprint: Option<String>,
|
||||||
interactive: bool,
|
interactive: bool,
|
||||||
ticket_cache: bool,
|
ticket_cache: bool,
|
||||||
|
fingerprint_cache: bool,
|
||||||
verify_cert: bool,
|
verify_cert: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,6 +47,7 @@ impl HttpClientOptions {
|
|||||||
fingerprint: None,
|
fingerprint: None,
|
||||||
interactive: false,
|
interactive: false,
|
||||||
ticket_cache: false,
|
ticket_cache: false,
|
||||||
|
fingerprint_cache: false,
|
||||||
verify_cert: true,
|
verify_cert: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,6 +72,11 @@ impl HttpClientOptions {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fingerprint_cache(mut self, fingerprint_cache: bool) -> Self {
|
||||||
|
self.fingerprint_cache = fingerprint_cache;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn verify_cert(mut self, verify_cert: bool) -> Self {
|
pub fn verify_cert(mut self, verify_cert: bool) -> Self {
|
||||||
self.verify_cert = verify_cert;
|
self.verify_cert = verify_cert;
|
||||||
self
|
self
|
||||||
@ -106,6 +113,69 @@ pub fn delete_ticket_info(server: &str, username: &str) -> Result<(), Error> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn store_fingerprint(server: &str, fingerprint: &str) -> Result<(), Error> {
|
||||||
|
|
||||||
|
let base = BaseDirectories::with_prefix("proxmox-backup")?;
|
||||||
|
|
||||||
|
// usually ~/.config/proxmox-backup/fingerprints
|
||||||
|
let path = base.place_config_file("fingerprints")?;
|
||||||
|
|
||||||
|
let raw = match std::fs::read_to_string(&path) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => {
|
||||||
|
if err.kind() == std::io::ErrorKind::NotFound {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
bail!("unable to read fingerprints from {:?} - {}", path, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut result = String::new();
|
||||||
|
|
||||||
|
raw.split('\n').for_each(|line| {
|
||||||
|
let items: Vec<String> = line.split_whitespace().map(String::from).collect();
|
||||||
|
if items.len() == 2 {
|
||||||
|
if &items[0] == server {
|
||||||
|
// found, add later with new fingerprint
|
||||||
|
} else {
|
||||||
|
result.push_str(line);
|
||||||
|
result.push('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
result.push_str(server);
|
||||||
|
result.push(' ');
|
||||||
|
result.push_str(fingerprint);
|
||||||
|
result.push('\n');
|
||||||
|
|
||||||
|
replace_file(path, result.as_bytes(), CreateOptions::new())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_fingerprint(server: &str) -> Option<String> {
|
||||||
|
|
||||||
|
let base = BaseDirectories::with_prefix("proxmox-backup").ok()?;
|
||||||
|
|
||||||
|
// usually ~/.config/proxmox-backup/fingerprints
|
||||||
|
let path = base.place_config_file("fingerprints").ok()?;
|
||||||
|
|
||||||
|
let raw = std::fs::read_to_string(&path).ok()?;
|
||||||
|
|
||||||
|
for line in raw.split('\n') {
|
||||||
|
let items: Vec<String> = line.split_whitespace().map(String::from).collect();
|
||||||
|
if items.len() == 2 {
|
||||||
|
if &items[0] == server {
|
||||||
|
return Some(items[1].clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn store_ticket_info(server: &str, username: &str, ticket: &str, token: &str) -> Result<(), Error> {
|
fn store_ticket_info(server: &str, username: &str, ticket: &str, token: &str) -> Result<(), Error> {
|
||||||
|
|
||||||
let base = BaseDirectories::with_prefix("proxmox-backup")?;
|
let base = BaseDirectories::with_prefix("proxmox-backup")?;
|
||||||
@ -169,11 +239,18 @@ impl HttpClient {
|
|||||||
|
|
||||||
let verified_fingerprint = Arc::new(Mutex::new(None));
|
let verified_fingerprint = Arc::new(Mutex::new(None));
|
||||||
|
|
||||||
|
let mut fingerprint = options.fingerprint.take();
|
||||||
|
if options.fingerprint_cache && fingerprint.is_none() {
|
||||||
|
fingerprint = load_fingerprint(server);
|
||||||
|
}
|
||||||
|
|
||||||
let client = Self::build_client(
|
let client = Self::build_client(
|
||||||
options.fingerprint.clone(),
|
server.to_string(),
|
||||||
|
fingerprint,
|
||||||
options.interactive,
|
options.interactive,
|
||||||
verified_fingerprint.clone(),
|
verified_fingerprint.clone(),
|
||||||
options.verify_cert,
|
options.verify_cert,
|
||||||
|
options.fingerprint_cache,
|
||||||
);
|
);
|
||||||
|
|
||||||
let password = options.password.take();
|
let password = options.password.take();
|
||||||
@ -243,9 +320,11 @@ impl HttpClient {
|
|||||||
fn verify_callback(
|
fn verify_callback(
|
||||||
valid: bool, ctx:
|
valid: bool, ctx:
|
||||||
&mut X509StoreContextRef,
|
&mut X509StoreContextRef,
|
||||||
|
server: String,
|
||||||
expected_fingerprint: Option<String>,
|
expected_fingerprint: Option<String>,
|
||||||
interactive: bool,
|
interactive: bool,
|
||||||
verified_fingerprint: Arc<Mutex<Option<String>>>,
|
verified_fingerprint: Arc<Mutex<Option<String>>>,
|
||||||
|
fingerprint_cache: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if valid { return true; }
|
if valid { return true; }
|
||||||
|
|
||||||
@ -285,7 +364,11 @@ impl HttpClient {
|
|||||||
match std::io::stdin().read_exact(&mut buf) {
|
match std::io::stdin().read_exact(&mut buf) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
if buf[0] == b'y' || buf[0] == b'Y' {
|
if buf[0] == b'y' || buf[0] == b'Y' {
|
||||||
println!("TRUST {}", fp_string);
|
if fingerprint_cache {
|
||||||
|
if let Err(err) = store_fingerprint(&server, &fp_string) {
|
||||||
|
eprintln!("{}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
*verified_fingerprint.lock().unwrap() = Some(fp_string);
|
*verified_fingerprint.lock().unwrap() = Some(fp_string);
|
||||||
return true;
|
return true;
|
||||||
} else if buf[0] == b'n' || buf[0] == b'N' {
|
} else if buf[0] == b'n' || buf[0] == b'N' {
|
||||||
@ -302,17 +385,21 @@ impl HttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_client(
|
fn build_client(
|
||||||
|
server: String,
|
||||||
fingerprint: Option<String>,
|
fingerprint: Option<String>,
|
||||||
interactive: bool,
|
interactive: bool,
|
||||||
verified_fingerprint: Arc<Mutex<Option<String>>>,
|
verified_fingerprint: Arc<Mutex<Option<String>>>,
|
||||||
verify_cert: bool,
|
verify_cert: bool,
|
||||||
|
fingerprint_cache: bool,
|
||||||
) -> Client<HttpsConnector> {
|
) -> Client<HttpsConnector> {
|
||||||
|
|
||||||
let mut ssl_connector_builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
let mut ssl_connector_builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||||
|
|
||||||
if verify_cert {
|
if verify_cert {
|
||||||
ssl_connector_builder.set_verify_callback(openssl::ssl::SslVerifyMode::PEER, move |valid, ctx| {
|
ssl_connector_builder.set_verify_callback(openssl::ssl::SslVerifyMode::PEER, move |valid, ctx| {
|
||||||
Self::verify_callback(valid, ctx, fingerprint.clone(), interactive, verified_fingerprint.clone())
|
Self::verify_callback(
|
||||||
|
valid, ctx, server.clone(), fingerprint.clone(), interactive,
|
||||||
|
verified_fingerprint.clone(), fingerprint_cache)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
ssl_connector_builder.set_verify(openssl::ssl::SslVerifyMode::NONE);
|
ssl_connector_builder.set_verify(openssl::ssl::SslVerifyMode::NONE);
|
||||||
|
Loading…
Reference in New Issue
Block a user