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",