mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
implement cert download endpoint (#794)
* implement cert download endpoint * Apply suggestions from code review Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com>
This commit is contained in:
committed by
Aiden McClelland
parent
e6ba378c05
commit
c2513f0dba
@@ -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()?;
|
||||
|
||||
@@ -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<Body>, ctx: RpcContext) -> Result<Respo
|
||||
.unwrap());
|
||||
}
|
||||
(Ok(valid_session), Method::GET, Some(("package-data", path))) => {
|
||||
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<Body> {
|
||||
async fn file_send(
|
||||
_valid_session: HasValidSession,
|
||||
ctx: &RpcContext,
|
||||
filename: PathBuf,
|
||||
path: impl AsRef<Path>,
|
||||
) -> Result<Response<Body>, 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<Self> {
|
||||
fn new(metadata: &Metadata, path: &Path) -> Option<Self> {
|
||||
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<Builder, Error> {
|
||||
fn with_e_tag(path: &Path, metadata: &Metadata, builder: Builder) -> Result<Builder, Error> {
|
||||
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<B
|
||||
))
|
||||
}
|
||||
///https://en.wikipedia.org/wiki/Media_type
|
||||
fn with_content_type(path: &PathBuf, builder: Builder) -> 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",
|
||||
|
||||
Reference in New Issue
Block a user