@@ -7,6 +7,7 @@ | |||||
extern crate rand; | extern crate rand; | ||||
use std::sync::Arc; | use std::sync::Arc; | ||||
use std::time::{SystemTime, UNIX_EPOCH}; | |||||
use rand::Rng; | use rand::Rng; | ||||
use rand::distributions::Alphanumeric; | use rand::distributions::Alphanumeric; | ||||
@@ -17,32 +18,49 @@ use sha2::{Sha256, Digest}; | |||||
#[derive(Debug, Deserialize, Serialize, Clone)] | #[derive(Debug, Deserialize, Serialize, Clone)] | ||||
pub struct Ping { | pub struct Ping { | ||||
time : u128, | |||||
salt : String, | salt : String, | ||||
hash : String | hash : String | ||||
} | } | ||||
const MAX_PING_AGE: i64 = 30000; | |||||
const MIN_PING_AGE: i64 = -5000; | |||||
impl Ping { | impl Ping { | ||||
pub fn new (auth_token : Arc<String>) -> Ping { | pub fn new (auth_token : Arc<String>) -> Ping { | ||||
let salt = rand::thread_rng() | let salt = rand::thread_rng() | ||||
.sample_iter(&Alphanumeric) | .sample_iter(&Alphanumeric) | ||||
.take(30) | |||||
.take(50) | |||||
.collect::<String>(); | .collect::<String>(); | ||||
let hash = hash_token_with_salt(auth_token, &salt); | |||||
Ping { salt, hash } | |||||
let time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis(); | |||||
let hash = hash_token_with_salt(auth_token, time, &salt); | |||||
Ping { time, salt, hash } | |||||
} | } | ||||
pub fn verify(&self, auth_token : Arc<String>) -> bool { | pub fn verify(&self, auth_token : Arc<String>) -> bool { | ||||
let hash = hash_token_with_salt(auth_token, &self.salt); | |||||
eprintln!("Ping.verify: INFO: comparing provided hash={} with calculated hash={}", self.hash, hash); | |||||
self.hash.eq(&hash) | |||||
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis(); | |||||
let age : i64 = (now - self.time) as i64; | |||||
return if age > MAX_PING_AGE { | |||||
eprintln!("Ping.verify: ERROR: ping was too old"); | |||||
false | |||||
} else if age < MIN_PING_AGE { | |||||
eprintln!("Ping.verify: ERROR: ping was too young"); | |||||
false | |||||
} else { | |||||
let hash = hash_token_with_salt(auth_token, self.time, &self.salt); | |||||
eprintln!("Ping.verify: INFO: comparing provided hash={} with calculated hash={}", self.hash, hash); | |||||
self.hash.eq(&hash) | |||||
} | |||||
} | } | ||||
} | } | ||||
fn hash_token_with_salt(auth_token: Arc<String>, salt: &String) -> String { | |||||
fn hash_token_with_salt(auth_token: Arc<String>, time : u128, salt: &String) -> String { | |||||
let mut hasher = Sha256::new(); | let mut hasher = Sha256::new(); | ||||
hasher.update(salt.as_bytes()); | hasher.update(salt.as_bytes()); | ||||
hasher.update(b":"); | hasher.update(b":"); | ||||
hasher.update(time.to_string()); | |||||
hasher.update(b":"); | |||||
hasher.update(auth_token.to_string()); | hasher.update(auth_token.to_string()); | ||||
let digest = hasher.finalize(); | let digest = hasher.finalize(); | ||||
let hash = hex::encode(digest); | let hash = hex::encode(digest); | ||||
@@ -82,6 +82,8 @@ pub async fn start_proxy (dns1_ip : &str, | |||||
eprintln!("start_proxy: INFO Proxy await result: {:?}", result); | eprintln!("start_proxy: INFO Proxy await result: {:?}", result); | ||||
} | } | ||||
const HEADER_FLEX_AUTH: &'static str = "X-Bubble-Flex-Auth"; | |||||
async fn proxy(client: Client<HttpsConnector<HttpConnector<CacheResolver>>>, | async fn proxy(client: Client<HttpsConnector<HttpConnector<CacheResolver>>>, | ||||
gateway: Arc<String>, | gateway: Arc<String>, | ||||
resolver: Arc<TokioAsyncResolver>, | resolver: Arc<TokioAsyncResolver>, | ||||
@@ -97,8 +99,8 @@ async fn proxy(client: Client<HttpsConnector<HttpConnector<CacheResolver>>>, | |||||
let ping : Ping = serde_json::from_str(body.as_str()).unwrap(); | let ping : Ping = serde_json::from_str(body.as_str()).unwrap(); | ||||
eprintln!("proxy: INFO: received body: {:?}", ping); | eprintln!("proxy: INFO: received body: {:?}", ping); | ||||
if !ping.verify(auth_token.clone()) { | if !ping.verify(auth_token.clone()) { | ||||
eprintln!("proxy: INFO: ping hash not valid"); | |||||
bad_request("ping hash not valid") | |||||
eprintln!("proxy: ERROR: invalid ping hash"); | |||||
bad_request("invalid ping hash") | |||||
} else { | } else { | ||||
let pong = Ping::new(auth_token.clone()); | let pong = Ping::new(auth_token.clone()); | ||||
let pong_json = serde_json::to_string(&pong).unwrap(); | let pong_json = serde_json::to_string(&pong).unwrap(); | ||||
@@ -106,9 +108,28 @@ async fn proxy(client: Client<HttpsConnector<HttpConnector<CacheResolver>>>, | |||||
} | } | ||||
} else { | } else { | ||||
eprintln!("proxy: ERROR: no host"); | eprintln!("proxy: ERROR: no host"); | ||||
bad_request("No host!") | |||||
bad_request("No host") | |||||
} | } | ||||
} | } | ||||
let headers = req.headers(); | |||||
let flex_auth_header = headers.get(HEADER_FLEX_AUTH); | |||||
if flex_auth_header.is_none() { | |||||
eprintln!("proxy: ERROR: no auth"); | |||||
return bad_request("No auth"); | |||||
} | |||||
let flex_auth = flex_auth_header.unwrap().to_str(); | |||||
if flex_auth.is_err() { | |||||
eprintln!("proxy: ERROR: auth not found"); | |||||
return bad_request("auth not found"); | |||||
} | |||||
let auth : Ping = serde_json::from_str(flex_auth.unwrap().to_string().as_str()).unwrap(); | |||||
if !auth.verify(auth_token.clone()) { | |||||
eprintln!("proxy: ERROR: invalid auth"); | |||||
return bad_request("invalid auth"); | |||||
} | |||||
let host = host.unwrap(); | let host = host.unwrap(); | ||||
let ip_string = resolve_with_cache(host, &resolver, resolver_cache).await; | let ip_string = resolve_with_cache(host, &resolver, resolver_cache).await; | ||||
eprintln!("proxy: INFO req(host {} resolved to: {}): {:?}", host, ip_string, req); | eprintln!("proxy: INFO req(host {} resolved to: {}): {:?}", host, ip_string, req); | ||||