From b1049c4dccb1ce7c65687bbbcbc1d4e979c3a1ee Mon Sep 17 00:00:00 2001 From: Ramil_Algayev Date: Fri, 27 Dec 2024 23:04:17 +0400 Subject: [PATCH] improved /install --- dtrfs_api/Cargo.lock | 38 ++++++++++++++++++++---- dtrfs_api/Cargo.toml | 3 ++ dtrfs_api/src/main.rs | 4 +-- dtrfs_api/src/os.rs | 68 ++++++++++++++++++++++++++++--------------- 4 files changed, 81 insertions(+), 32 deletions(-) diff --git a/dtrfs_api/Cargo.lock b/dtrfs_api/Cargo.lock index f72473f..d73b5bd 100644 --- a/dtrfs_api/Cargo.lock +++ b/dtrfs_api/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "actix-codec" @@ -678,6 +678,9 @@ dependencies = [ "serde", "sev", "sha3", + "tokio", + "tokio-stream", + "tokio-util", ] [[package]] @@ -2104,9 +2107,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.41.1" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -2116,9 +2119,21 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "socket2", + "tokio-macros", "windows-sys 0.52.0", ] +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio-rustls" version = "0.26.0" @@ -2131,10 +2146,21 @@ dependencies = [ ] [[package]] -name = "tokio-util" -version = "0.7.12" +name = "tokio-stream" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", diff --git a/dtrfs_api/Cargo.toml b/dtrfs_api/Cargo.toml index f01b59f..2f66da4 100644 --- a/dtrfs_api/Cargo.toml +++ b/dtrfs_api/Cargo.toml @@ -11,6 +11,9 @@ regex = "1.11.1" sev = { version = "4.0", default-features = false, features = ['crypto_nossl','snp'] } ed25519-dalek = { version = "2.1.1", features = ["pem", "pkcs8"] } lazy_static = "1.5.0" +tokio = { version = "1.42.0", features = ["full"] } +tokio-util = "0.7.13" +tokio-stream = { version = "0.1.17", features = ["io-util"] } actix-web = { version = "4.9.0", features = ["rustls-0_23"] } sha3 = "0.10.8" rustls = "0.23.18" diff --git a/dtrfs_api/src/main.rs b/dtrfs_api/src/main.rs index cd11718..b174538 100644 --- a/dtrfs_api/src/main.rs +++ b/dtrfs_api/src/main.rs @@ -93,8 +93,8 @@ async fn post_install_form(req: HttpRequest, form: web::Form) -> Ht if let Err(e) = verify(&req) { return HttpResponse::BadRequest().body(format!("Signature verification failed: {}", e)); }; - match os::encrypt_and_install_os(&form.url, &form.sha, &form.keyfile) { - Ok(s) => HttpResponse::Ok().body(s), + match os::encrypt_and_install_os(&form.url, &form.sha, &form.keyfile).await { + Ok(s) => s, Err(e) => HttpResponse::InternalServerError().body(format!("{e:?}")), } } diff --git a/dtrfs_api/src/os.rs b/dtrfs_api/src/os.rs index 7ed90b9..32d3c1d 100644 --- a/dtrfs_api/src/os.rs +++ b/dtrfs_api/src/os.rs @@ -1,47 +1,67 @@ use crate::snp::get_derived_key; +use actix_web::{web::Bytes, HttpResponse}; use anyhow::{anyhow, Result}; use base64::prelude::{Engine, BASE64_URL_SAFE}; +use std::process::Command; use std::{ fs::File, io::{BufRead, BufReader, Write}, path::Path, - process::Command, + process::Stdio, }; const SNP_KEYFILE_PATH: &str = "/tmp/detee_snp_keyfile"; const BACKUP_KEYFILE_PATH: &str = "/tmp/detee_backup_keyfile"; -pub fn encrypt_and_install_os( +pub async fn encrypt_and_install_os( install_url: &str, install_sha: &str, keyfile: &str, -) -> Result { - let binary_keyfile = BASE64_URL_SAFE.decode(keyfile)?; - std::fs::write(BACKUP_KEYFILE_PATH, binary_keyfile)?; - // this path is hardcoded also in the initrd creation script - let install_result = Command::new("/usr/lib/dtrfs/install_os.sh") +) -> Result { + use tokio::process::Command; + use tokio::io::{BufReader, AsyncBufReadExt}; + use tokio_stream::{StreamExt, wrappers::LinesStream}; + + let binary_keyfile = base64::engine::general_purpose::URL_SAFE.decode(keyfile) + .map_err(|e| actix_web::error::ErrorBadRequest(e.to_string()))?; + + // Write the decoded keyfile to the backup path asynchronously + tokio::fs::write(BACKUP_KEYFILE_PATH, &binary_keyfile) + .await + .map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?; + + // Spawn the installation script as a child process + let mut child = Command::new("/usr/lib/dtrfs/install_os.sh") .env("INSTALL_URL", install_url) .env("INSTALL_SHA", install_sha) .env("SNP_KEY_FILE", SNP_KEYFILE_PATH) .env("ROOT_KEYFILE", BACKUP_KEYFILE_PATH) - .output()?; + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?; - if !install_result.status.success() { - return Err(anyhow!( - "OS installation script failed.\nScript stdout:\n{}\nScript stderr:\n{}", - String::from_utf8(install_result.stdout) - .unwrap_or("Could not grab stdout from installation script.".to_string()), - String::from_utf8(install_result.stderr) - .unwrap_or("Could not grab stderr from installation script.".to_string()), - )); - } - Ok(format!( - "Successfully installed OS. Script stdout:\n{}\nScript stderr:\n{}", - String::from_utf8(install_result.stdout) - .unwrap_or("Could not grab stdout from installation script.".to_string()), - String::from_utf8(install_result.stderr) - .unwrap_or("Could not grab stderr from installation script.".to_string()), - )) + // Take stdout and stderr from the child process + let stdout = child.stdout.take().ok_or_else(|| { + actix_web::error::ErrorInternalServerError("Failed to capture stdout".to_string()) + })?; + let stderr = child.stderr.take().ok_or_else(|| { + actix_web::error::ErrorInternalServerError("Failed to capture stderr".to_string()) + })?; + + // Merge stdout and stderr into a single stream + let stdout_lines = LinesStream::new(BufReader::new(stdout).lines()); + let stderr_lines = LinesStream::new(BufReader::new(stderr).lines()); + let merged_stream = stdout_lines.merge(stderr_lines).map(|line_result| { + line_result + .map(|line| Bytes::from(line + "\n")) + .map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string())) + }); + + // Return the merged stream as the HTTP response + Ok(HttpResponse::Ok() + .content_type("text/plain; charset=utf-8") + .streaming(merged_stream)) } pub fn try_hot_keyfile() -> Result<()> {