diff --git a/appmgr/src/net/ssl.rs b/appmgr/src/net/ssl.rs index eff30c64d..675634945 100644 --- a/appmgr/src/net/ssl.rs +++ b/appmgr/src/net/ssl.rs @@ -1,5 +1,5 @@ use std::cmp::Ordering; -use std::path::Path; +use std::path::{Path, PathBuf}; use color_eyre::eyre::eyre; use futures::FutureExt; @@ -18,6 +18,7 @@ use tracing::instrument; use crate::{Error, ErrorKind, ResultExt}; static CERTIFICATE_VERSION: i32 = 2; // X509 version 3 is actually encoded as '2' in the cert because fuck you. +pub const ROOT_CA_STATIC_PATH: &str = "/var/lib/embassy/ssl/root-ca.crt"; #[derive(Debug)] pub struct SslManager { @@ -168,6 +169,15 @@ impl SslManager { } Some((key, cert)) => Ok((key, cert)), }?; + // generate static file for download, this will get blown up on embassy restart so it's good to write it on + // every ssl manager init + tokio::fs::create_dir_all( + Path::new(ROOT_CA_STATIC_PATH) + .parent() + .unwrap_or(Path::new("/")), + ) + .await?; + tokio::fs::write(ROOT_CA_STATIC_PATH, root_cert.to_pem()?).await?; let (int_key, int_cert) = match store.load_intermediate_certificate().await? { None => { let int_key = generate_key()?; diff --git a/appmgr/src/static_server.rs b/appmgr/src/static_server.rs index 0117e1e49..d3950004c 100644 --- a/appmgr/src/static_server.rs +++ b/appmgr/src/static_server.rs @@ -1,6 +1,6 @@ use std::fs::Metadata; use std::future::Future; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::time::UNIX_EPOCH; use digest::Digest; @@ -69,7 +69,20 @@ async fn file_server_router(req: Request, ctx: RpcContext) -> Result { - file_send(valid_session, &ctx, PathBuf::from(path)).await + file_send( + valid_session, + &ctx, + ctx.datadir.join(PKG_PUBLIC_DIR).join(path), + ) + .await + } + (Ok(valid_session), Method::GET, Some(("eos", "local.crt"))) => { + file_send( + valid_session, + &ctx, + PathBuf::from(crate::net::ssl::ROOT_CA_STATIC_PATH), + ) + .await } _ => Ok(not_found()), } @@ -93,21 +106,22 @@ fn server_error() -> Response { async fn file_send( _valid_session: HasValidSession, ctx: &RpcContext, - filename: PathBuf, + path: impl AsRef, ) -> Result, Error> { // Serve a file by asynchronously reading it by chunks using tokio-util crate. - let path = ctx.datadir.join(PKG_PUBLIC_DIR).join(filename); - if let Ok(file) = File::open(path.clone()).await { + let path = path.as_ref(); + + if let Ok(file) = File::open(path).await { let metadata = file.metadata().await.with_kind(ErrorKind::Filesystem)?; - let _is_non_empty = match IsNonEmptyFile::new(&metadata, &path) { + let _is_non_empty = match IsNonEmptyFile::new(&metadata, path) { Some(a) => a, None => return Ok(not_found()), }; let mut builder = Response::builder().status(StatusCode::OK); - builder = with_e_tag(&path, &metadata, builder)?; - builder = with_content_type(&path, builder); + builder = with_e_tag(path, &metadata, builder)?; + builder = with_content_type(path, builder); builder = with_content_length(&metadata, builder); let stream = FramedRead::new(file, BytesCodec::new()); let body = Body::wrap_stream(stream); @@ -120,7 +134,7 @@ async fn file_send( struct IsNonEmptyFile(()); impl IsNonEmptyFile { - fn new(metadata: &Metadata, path: &PathBuf) -> Option { + fn new(metadata: &Metadata, path: &Path) -> Option { let length = metadata.len(); if !metadata.is_file() || length == 0 { tracing::debug!("File is empty: {:?}", path); @@ -130,7 +144,7 @@ impl IsNonEmptyFile { } } -fn with_e_tag(path: &PathBuf, metadata: &Metadata, builder: Builder) -> Result { +fn with_e_tag(path: &Path, metadata: &Metadata, builder: Builder) -> Result { let modified = metadata.modified().with_kind(ErrorKind::Filesystem)?; let mut hasher = sha2::Sha256::new(); hasher.update(format!("{:?}", path).as_bytes()); @@ -151,7 +165,7 @@ fn with_e_tag(path: &PathBuf, metadata: &Metadata, builder: Builder) -> Result Builder { +fn with_content_type(path: &Path, builder: Builder) -> Builder { let content_type = match path.extension() { Some(os_str) => match os_str.to_str() { Some("apng") => "image/apng",