Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HTTPS/TLS support to the auth proxy #2430

Merged
merged 13 commits into from
Nov 16, 2023
Merged
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion crates/common/axum_tls/src/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,9 @@ mod tests {
}

fn test_data(file_name: &str) -> String {
std::fs::read_to_string(format!("./test_data/{file_name}")).with_context(|| format!("opening file {file_name} from test_data")).unwrap()
std::fs::read_to_string(format!("./test_data/{file_name}"))
.with_context(|| format!("opening file {file_name} from test_data"))
.unwrap()
}

fn config_from_pem(
Expand Down
1 change: 1 addition & 0 deletions crates/common/axum_tls/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod maybe_tls;
mod redirect_http;

use crate::acceptor::Acceptor;
pub use crate::acceptor::TlsData;
pub use crate::files::*;
use crate::redirect_http::redirect_http_to_https;
use axum::middleware::map_request;
Expand Down
2 changes: 2 additions & 0 deletions crates/common/download/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ repository = { workspace = true }
[dependencies]
anyhow = { workspace = true, features = ["backtrace"] }
backoff = { workspace = true }
hyper = { workspace = true }
log = { workspace = true }
nix = { workspace = true }
reqwest = { workspace = true, features = [
"rustls-tls-native-roots",
] }
rustls = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
tedge_utils = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion crates/common/download/examples/simple_download.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ async fn main() -> Result<()> {

// Create downloader instance with desired file path and target directory.
#[allow(deprecated)]
let downloader = Downloader::new("/tmp/test_download".into());
let downloader = Downloader::new("/tmp/test_download".into(), None);

// Call `download` method to get data from url.
downloader.download(&url_data).await?;
Expand Down
75 changes: 46 additions & 29 deletions crates/common/download/src/download.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ use log::warn;
use nix::sys::statvfs;
pub use partial_response::InvalidResponseError;
use reqwest::header;
use reqwest::Identity;
use serde::Deserialize;
use serde::Serialize;
use std::error::Error;
use std::fs;
use std::fs::File;
use std::io::Seek;
Expand Down Expand Up @@ -100,36 +102,33 @@ pub struct Downloader {
target_filename: PathBuf,
target_permission: PermissionEntry,
backoff: ExponentialBackoff,
}

impl From<PathBuf> for Downloader {
fn from(path: PathBuf) -> Self {
Self {
target_filename: path,
target_permission: PermissionEntry::default(),
backoff: default_backoff(),
}
}
identity: Option<Identity>,
}

impl Downloader {
/// Creates a new downloader which downloads to a target directory and uses
/// default permissions.
pub fn new(target_path: PathBuf) -> Self {
pub fn new(target_path: PathBuf, identity: Option<Identity>) -> Self {
Self {
target_filename: target_path,
target_permission: PermissionEntry::default(),
backoff: default_backoff(),
identity,
}
}

/// Creates a new downloader which downloads to a target directory and sets
/// specified permissions the downloaded file.
pub fn with_permission(target_path: PathBuf, target_permission: PermissionEntry) -> Self {
pub fn with_permission(
target_path: PathBuf,
target_permission: PermissionEntry,
identity: Option<Identity>,
) -> Self {
Self {
target_filename: target_path,
target_permission,
backoff: default_backoff(),
identity,
}
}

Expand Down Expand Up @@ -386,20 +385,26 @@ impl Downloader {
let backoff = self.backoff.clone();

let operation = || async {
let mut client = reqwest::Client::new().get(url.url());
let mut client = reqwest::Client::builder();
if let Some(identity) = &self.identity {
client = client.identity(identity.clone());
}
let mut request = client.build()?.get(url.url());
if let Some(Auth::Bearer(token)) = &url.auth {
client = client.bearer_auth(token)
request = request.bearer_auth(token)
}

if range_start != 0 {
client = client.header("Range", format!("bytes={range_start}-"));
request = request.header("Range", format!("bytes={range_start}-"));
}

client
request
.send()
.await
.map_err(|err| {
if err.is_builder() || err.is_connect() {
// rustls errors are caused by e.g. CertificateRequired
// If this is the case, retrying won't help us
if err.is_builder() || err.is_connect() || is_rustls(&err) {
backoff::Error::Permanent(err)
} else {
backoff::Error::transient(err)
Expand Down Expand Up @@ -476,6 +481,18 @@ fn try_pre_allocate_space(file: &File, path: &Path, file_len: u64) -> Result<(),
Ok(())
}

fn is_rustls(err: &reqwest::Error) -> bool {
(|| {
err.source()?
.downcast_ref::<hyper::Error>()?
.source()?
.downcast_ref::<std::io::Error>()?
.get_ref()?
.downcast_ref::<rustls::Error>()
})()
.is_some()
}

#[cfg(test)]
#[allow(deprecated)]
mod tests {
Expand Down Expand Up @@ -508,7 +525,7 @@ mod tests {

let url = DownloadInfo::new(&target_url);

let mut downloader = Downloader::new(target_path);
let mut downloader = Downloader::new(target_path, None);
downloader.set_backoff(ExponentialBackoff {
current_interval: Duration::ZERO,
..Default::default()
Expand Down Expand Up @@ -538,7 +555,7 @@ mod tests {

let url = DownloadInfo::new(&target_url);

let downloader = Downloader::new(target_path.clone());
let downloader = Downloader::new(target_path.clone(), None);
downloader.download(&url).await.unwrap();

let file_content = std::fs::read(target_path).unwrap();
Expand Down Expand Up @@ -567,7 +584,7 @@ mod tests {

let url = DownloadInfo::new(&target_url);

let downloader = Downloader::new(target_path);
let downloader = Downloader::new(target_path, None);
let err = downloader.download(&url).await.unwrap_err();
assert!(matches!(err, DownloadError::InsufficientSpace));
}
Expand All @@ -590,7 +607,7 @@ mod tests {
let url = DownloadInfo::new(&target_url);

// empty filename
let downloader = Downloader::new("".into());
let downloader = Downloader::new("".into(), None);
let err = downloader.download(&url).await.unwrap_err();
assert!(matches!(
err,
Expand All @@ -599,15 +616,15 @@ mod tests {

// invalid unicode filename
let path = unsafe { String::from_utf8_unchecked(b"\xff".to_vec()) };
let downloader = Downloader::new(path.into());
let downloader = Downloader::new(path.into(), None);
let err = downloader.download(&url).await.unwrap_err();
assert!(matches!(
err,
DownloadError::FromFileError(FileError::InvalidFileName { .. })
));

// relative path filename
let downloader = Downloader::new("myfile.txt".into());
let downloader = Downloader::new("myfile.txt".into(), None);
let err = downloader.download(&url).await.unwrap_err();
assert!(matches!(
err,
Expand All @@ -634,7 +651,7 @@ mod tests {

let url = DownloadInfo::new(&target_url);

let downloader = Downloader::new(target_file_path.clone());
let downloader = Downloader::new(target_file_path.clone(), None);
downloader.download(&url).await.unwrap();

let file_content = std::fs::read(target_file_path).unwrap();
Expand All @@ -661,7 +678,7 @@ mod tests {

let url = DownloadInfo::new(&target_url);

let downloader = Downloader::new(target_path);
let downloader = Downloader::new(target_path, None);

downloader.download(&url).await.unwrap();

Expand All @@ -688,7 +705,7 @@ mod tests {

let url = DownloadInfo::new(&target_url);

let downloader = Downloader::new(target_path);
let downloader = Downloader::new(target_path, None);
downloader.download(&url).await.unwrap();

let log_content = std::fs::read(downloader.filename()).unwrap();
Expand All @@ -709,7 +726,7 @@ mod tests {

let url = DownloadInfo::new(&target_url);

let downloader = Downloader::new(target_path);
let downloader = Downloader::new(target_path, None);
downloader.download(&url).await.unwrap();

assert_eq!("".as_bytes(), std::fs::read(downloader.filename()).unwrap());
Expand Down Expand Up @@ -799,7 +816,7 @@ mod tests {
let tmpdir = TempDir::new().unwrap();
let target_path = tmpdir.path().join("partial_download");

let downloader = Downloader::new(target_path);
let downloader = Downloader::new(target_path, None);
let url = DownloadInfo::new(&format!("http://localhost:{port}/"));

downloader.download(&url).await.unwrap();
Expand Down Expand Up @@ -907,7 +924,7 @@ mod tests {
};

let target_path = target_dir_path.path().join("test_download");
let mut downloader = Downloader::new(target_path);
let mut downloader = Downloader::new(target_path, None);
downloader.set_backoff(ExponentialBackoff {
current_interval: Duration::ZERO,
..Default::default()
Expand Down
2 changes: 1 addition & 1 deletion crates/common/download/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
//! );
//!
//! // Create downloader instance with desired file path and target directory.
//! let downloader = Downloader::new("/tmp/test_download".into());
//! let downloader = Downloader::new("/tmp/test_download".into(), None);
//!
//! // Call `download` method to get data from url.
//! downloader.download(&url_data).await?;
Expand Down
3 changes: 2 additions & 1 deletion crates/common/tedge_config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ homepage = { workspace = true }
repository = { workspace = true }

[dependencies]
anyhow = { workspace = true }
camino = { workspace = true, features = ["serde", "serde1"] }
certificate = { workspace = true }
doku = { workspace = true }
figment = { workspace = true, features = ["env", "toml"] }
mqtt_channel = { workspace = true }
once_cell = { workspace = true }
reqwest = { workspace = true, features = ["rustls-tls-native-roots"] }
serde = { workspace = true, features = ["rc"] }
serde_ignored = { workspace = true }
strum_macros = { workspace = true }
Expand All @@ -27,7 +29,6 @@ tracing-subscriber = { workspace = true }
url = { workspace = true }

[dev-dependencies]
anyhow = { workspace = true }
assert_matches = { workspace = true }
figment = { workspace = true, features = ["test"] }
tedge_test_utils = { workspace = true }
Expand Down
1 change: 1 addition & 0 deletions crates/common/tedge_config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub use self::tedge_config_cli::tedge_config_repository::*;
pub use camino::Utf8Path as Path;
pub use camino::Utf8PathBuf as PathBuf;
pub use certificate::CertificateError;
pub use tedge_config_macros::all_or_nothing;

/// loads the new tedge config from system default
pub fn get_new_tedge_config() -> Result<TEdgeConfig, TEdgeConfigError> {
Expand Down
Loading
Loading