Merge pull request #1587 from RealOrangeOne/request-proxy

Allow outbound requests to go via a proxy
This commit is contained in:
Daniel García 2021-04-15 17:40:39 +02:00 committed by GitHub
commit 4bb0d7bc05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 73 additions and 24 deletions

39
Cargo.lock generated
View File

@ -563,6 +563,12 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.28" version = "0.8.28"
@ -2213,6 +2219,7 @@ dependencies = [
"serde_urlencoded", "serde_urlencoded",
"tokio", "tokio",
"tokio-native-tls", "tokio-native-tls",
"tokio-socks",
"tokio-util", "tokio-util",
"url 2.2.1", "url 2.2.1",
"wasm-bindgen", "wasm-bindgen",
@ -2767,6 +2774,26 @@ dependencies = [
"utf-8", "utf-8",
] ]
[[package]]
name = "thiserror"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
dependencies = [
"proc-macro2 1.0.24",
"quote 1.0.9",
"syn 1.0.65",
]
[[package]] [[package]]
name = "threadpool" name = "threadpool"
version = "1.8.1" version = "1.8.1"
@ -2865,6 +2892,18 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "tokio-socks"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0"
dependencies = [
"either",
"futures-util",
"thiserror",
"tokio",
]
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.6.5" version = "0.6.5"

View File

@ -32,7 +32,7 @@ rocket = { version = "0.5.0-dev", features = ["tls"], default-features = false }
rocket_contrib = "0.5.0-dev" rocket_contrib = "0.5.0-dev"
# HTTP client # HTTP client
reqwest = { version = "0.11.2", features = ["blocking", "json", "gzip", "brotli"] } reqwest = { version = "0.11.2", features = ["blocking", "json", "gzip", "brotli", "socks"] }
# multipart/form-data support # multipart/form-data support
multipart = { version = "0.17.1", features = ["server"], default-features = false } multipart = { version = "0.17.1", features = ["server"], default-features = false }

View File

@ -3,7 +3,7 @@ use serde::de::DeserializeOwned;
use serde_json::Value; use serde_json::Value;
use std::{env, time::Duration}; use std::{env, time::Duration};
use reqwest::{blocking::Client, header::USER_AGENT};
use rocket::{ use rocket::{
http::{Cookie, Cookies, SameSite}, http::{Cookie, Cookies, SameSite},
request::{self, FlashMessage, Form, FromRequest, Outcome, Request}, request::{self, FlashMessage, Form, FromRequest, Outcome, Request},
@ -19,7 +19,7 @@ use crate::{
db::{backup_database, get_sql_server_version, models::*, DbConn, DbConnType}, db::{backup_database, get_sql_server_version, models::*, DbConn, DbConnType},
error::{Error, MapResult}, error::{Error, MapResult},
mail, mail,
util::{format_naive_datetime_local, get_display_size, is_running_in_docker}, util::{format_naive_datetime_local, get_display_size, is_running_in_docker, get_reqwest_client},
CONFIG, CONFIG,
}; };
@ -469,24 +469,22 @@ struct GitCommit {
} }
fn get_github_api<T: DeserializeOwned>(url: &str) -> Result<T, Error> { fn get_github_api<T: DeserializeOwned>(url: &str) -> Result<T, Error> {
let github_api = Client::builder().build()?; let github_api = get_reqwest_client();
Ok(github_api Ok(github_api
.get(url) .get(url)
.timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(10))
.header(USER_AGENT, "Bitwarden_RS")
.send()? .send()?
.error_for_status()? .error_for_status()?
.json::<T>()?) .json::<T>()?)
} }
fn has_http_access() -> bool { fn has_http_access() -> bool {
let http_access = Client::builder().build().unwrap(); let http_access = get_reqwest_client();
match http_access match http_access
.head("https://github.com/dani-garcia/bitwarden_rs") .head("https://github.com/dani-garcia/bitwarden_rs")
.timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(10))
.header(USER_AGENT, "Bitwarden_RS")
.send() .send()
{ {
Ok(r) => r.status().is_success(), Ok(r) => r.status().is_success(),

View File

@ -43,6 +43,7 @@ use crate::{
auth::Headers, auth::Headers,
db::DbConn, db::DbConn,
error::Error, error::Error,
util::get_reqwest_client,
}; };
#[put("/devices/identifier/<uuid>/clear-token")] #[put("/devices/identifier/<uuid>/clear-token")]
@ -147,20 +148,16 @@ fn put_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, conn: DbC
#[get("/hibp/breach?<username>")] #[get("/hibp/breach?<username>")]
fn hibp_breach(username: String) -> JsonResult { fn hibp_breach(username: String) -> JsonResult {
let user_agent = "Bitwarden_RS";
let url = format!( let url = format!(
"https://haveibeenpwned.com/api/v3/breachedaccount/{}?truncateResponse=false&includeUnverified=false", "https://haveibeenpwned.com/api/v3/breachedaccount/{}?truncateResponse=false&includeUnverified=false",
username username
); );
use reqwest::{blocking::Client, header::USER_AGENT};
if let Some(api_key) = crate::CONFIG.hibp_api_key() { if let Some(api_key) = crate::CONFIG.hibp_api_key() {
let hibp_client = Client::builder().build()?; let hibp_client = get_reqwest_client();
let res = hibp_client let res = hibp_client
.get(&url) .get(&url)
.header(USER_AGENT, user_agent)
.header("hibp-api-key", api_key) .header("hibp-api-key", api_key)
.send()?; .send()?;

View File

@ -12,6 +12,7 @@ use crate::{
DbConn, DbConn,
}, },
error::MapResult, error::MapResult,
util::get_reqwest_client,
CONFIG, CONFIG,
}; };
@ -185,9 +186,7 @@ fn activate_duo_put(data: JsonUpcase<EnableDuoData>, headers: Headers, conn: DbC
} }
fn duo_api_request(method: &str, path: &str, params: &str, data: &DuoData) -> EmptyResult { fn duo_api_request(method: &str, path: &str, params: &str, data: &DuoData) -> EmptyResult {
const AGENT: &str = "bitwarden_rs:Duo/1.0 (Rust)"; use reqwest::{header, Method};
use reqwest::{blocking::Client, header::*, Method};
use std::str::FromStr; use std::str::FromStr;
// https://duo.com/docs/authapi#api-details // https://duo.com/docs/authapi#api-details
@ -199,11 +198,12 @@ fn duo_api_request(method: &str, path: &str, params: &str, data: &DuoData) -> Em
let m = Method::from_str(method).unwrap_or_default(); let m = Method::from_str(method).unwrap_or_default();
Client::new() let client = get_reqwest_client();
.request(m, &url)
client.request(m, &url)
.basic_auth(username, Some(password)) .basic_auth(username, Some(password))
.header(USER_AGENT, AGENT) .header(header::USER_AGENT, "bitwarden_rs:Duo/1.0 (Rust)")
.header(DATE, date) .header(header::DATE, date)
.send()? .send()?
.error_for_status()?; .error_for_status()?;

View File

@ -12,7 +12,7 @@ use regex::Regex;
use reqwest::{blocking::Client, blocking::Response, header, Url}; use reqwest::{blocking::Client, blocking::Response, header, Url};
use rocket::{http::ContentType, http::Cookie, response::Content, Route}; use rocket::{http::ContentType, http::Cookie, response::Content, Route};
use crate::{error::Error, util::Cached, CONFIG}; use crate::{error::Error, util::{Cached, get_reqwest_client_builder}, CONFIG};
pub fn routes() -> Vec<Route> { pub fn routes() -> Vec<Route> {
routes![icon] routes![icon]
@ -28,11 +28,11 @@ static CLIENT: Lazy<Client> = Lazy::new(|| {
default_headers.insert(header::ACCEPT, header::HeaderValue::from_static("text/html,application/xhtml+xml,application/xml; q=0.9,image/webp,image/apng,*/*;q=0.8")); default_headers.insert(header::ACCEPT, header::HeaderValue::from_static("text/html,application/xhtml+xml,application/xml; q=0.9,image/webp,image/apng,*/*;q=0.8"));
// Reuse the client between requests // Reuse the client between requests
Client::builder() get_reqwest_client_builder()
.timeout(Duration::from_secs(CONFIG.icon_download_timeout())) .timeout(Duration::from_secs(CONFIG.icon_download_timeout()))
.default_headers(default_headers) .default_headers(default_headers)
.build() .build()
.unwrap() .expect("Failed to build icon client")
}); });
// Build Regex only once since this takes a lot of time. // Build Regex only once since this takes a lot of time.

View File

@ -478,7 +478,6 @@ pub fn retry<F, T, E>(func: F, max_tries: u32) -> Result<T, E>
where where
F: Fn() -> Result<T, E>, F: Fn() -> Result<T, E>,
{ {
use std::{thread::sleep, time::Duration};
let mut tries = 0; let mut tries = 0;
loop { loop {
@ -497,12 +496,13 @@ where
} }
} }
use std::{thread::sleep, time::Duration};
pub fn retry_db<F, T, E>(func: F, max_tries: u32) -> Result<T, E> pub fn retry_db<F, T, E>(func: F, max_tries: u32) -> Result<T, E>
where where
F: Fn() -> Result<T, E>, F: Fn() -> Result<T, E>,
E: std::error::Error, E: std::error::Error,
{ {
use std::{thread::sleep, time::Duration};
let mut tries = 0; let mut tries = 0;
loop { loop {
@ -522,3 +522,18 @@ where
} }
} }
} }
use reqwest::{blocking::{Client, ClientBuilder}, header};
pub fn get_reqwest_client() -> Client {
get_reqwest_client_builder().build().expect("Failed to build client")
}
pub fn get_reqwest_client_builder() -> ClientBuilder {
let mut headers = header::HeaderMap::new();
headers.insert(header::USER_AGENT, header::HeaderValue::from_static("Bitwarden_RS"));
Client::builder()
.default_headers(headers)
.timeout(Duration::from_secs(10))
}