Feature/sdk improvements (#2879)

* sdk improvements

* subcontainer fixes, disable wifi on migration if not in use, filterable interfaces
This commit is contained in:
Aiden McClelland
2025-04-18 14:11:13 -06:00
committed by GitHub
parent dcfbaa9243
commit 2c65033c0a
19 changed files with 426 additions and 136 deletions

13
core/Cargo.lock generated
View File

@@ -5510,6 +5510,18 @@ dependencies = [
"tempfile",
]
[[package]]
name = "sha-crypt"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88e79009728d8311d42d754f2f319a975f9e38f156fd5e422d2451486c78b286"
dependencies = [
"base64ct",
"rand 0.8.5",
"sha2 0.10.8",
"subtle",
]
[[package]]
name = "sha1"
version = "0.10.6"
@@ -6091,6 +6103,7 @@ dependencies = [
"serde_urlencoded",
"serde_with",
"serde_yml",
"sha-crypt",
"sha2 0.10.8",
"shell-words",
"signal-hook",

View File

@@ -183,6 +183,7 @@ serde_toml = { package = "toml", version = "0.8.2" }
serde_urlencoded = "0.7"
serde_with = { version = "3.4.0", features = ["macros", "json"] }
serde_yaml = { package = "serde_yml", version = "0.0.10" }
sha-crypt = "0.5.0"
sha2 = "0.10.2"
shell-words = "1"
signal-hook = "0.3.17"

View File

@@ -9,6 +9,7 @@ use josekit::jwk::Jwk;
use rpc_toolkit::yajrc::RpcError;
use rpc_toolkit::{from_fn_async, Context, HandlerArgs, HandlerExt, ParentHandler};
use serde::{Deserialize, Serialize};
use tokio::io::AsyncWriteExt;
use tracing::instrument;
use ts_rs::TS;
@@ -19,6 +20,7 @@ use crate::middleware::auth::{
};
use crate::prelude::*;
use crate::util::crypto::EncryptedWire;
use crate::util::io::create_file_mod;
use crate::util::serde::{display_serializable, HandlerExtSerde, WithIoFormat};
use crate::{ensure_code, Error, ResultExt};
@@ -41,6 +43,30 @@ impl Map for Sessions {
}
}
pub async fn write_shadow(password: &str) -> Result<(), Error> {
let shadow_contents = tokio::fs::read_to_string("/etc/shadow").await?;
let mut shadow_file =
create_file_mod("/media/startos/config/overlay/etc/shadow", 0o640).await?;
for line in shadow_contents.lines() {
if line.starts_with("start9:") {
let rest = line.splitn(3, ":").nth(2).ok_or_else(|| {
Error::new(eyre!("malformed /etc/shadow"), ErrorKind::ParseSysInfo)
})?;
let pw = sha_crypt::sha512_simple(password, &sha_crypt::Sha512Params::default())
.map_err(|e| Error::new(eyre!("{e:?}"), ErrorKind::Serialization))?;
shadow_file
.write_all(format!("start9:{pw}:{rest}\n").as_bytes())
.await?;
} else {
shadow_file.write_all(line.as_bytes()).await?;
shadow_file.write_all(b"\n").await?;
}
}
shadow_file.sync_all().await?;
tokio::fs::copy("/media/startos/config/overlay/etc/shadow", "/etc/shadow").await?;
Ok(())
}
#[derive(Clone, Serialize, Deserialize, TS)]
#[serde(untagged)]
#[ts(export)]
@@ -210,7 +236,7 @@ pub async fn login_impl(
) -> Result<LoginRes, Error> {
let password = password.unwrap_or_default().decrypt(&ctx)?;
if ephemeral {
let tok = if ephemeral {
check_password_against_db(&ctx.db.peek().await, &password)?;
let hash_token = HashSessionToken::new();
ctx.ephemeral_sessions.mutate(|s| {
@@ -242,7 +268,16 @@ pub async fn login_impl(
})
.await
.result
}?;
if tokio::fs::metadata("/media/startos/config/overlay/etc/shadow")
.await
.is_err()
{
write_shadow(&password).await?;
}
Ok(tok)
}
#[derive(Deserialize, Serialize, Parser, TS)]

View File

@@ -17,6 +17,7 @@ use tracing::instrument;
use ts_rs::TS;
use crate::account::AccountInfo;
use crate::auth::write_shadow;
use crate::backup::restore::recover_full_embassy;
use crate::backup::target::BackupTargetFS;
use crate::context::rpc::InitRpcContextPhases;
@@ -88,8 +89,8 @@ async fn setup_init(
.db
.mutate(|m| {
let mut account = AccountInfo::load(m)?;
if let Some(password) = password {
account.set_password(&password)?;
if let Some(password) = &password {
account.set_password(password)?;
}
account.save(m)?;
m.as_public_mut()
@@ -101,6 +102,10 @@ async fn setup_init(
.await
.result?;
if let Some(password) = &password {
write_shadow(&password).await?;
}
Ok((account, init_result))
}
@@ -346,6 +351,8 @@ pub async fn complete(ctx: SetupContext) -> Result<SetupResult, Error> {
.arg(format!("--hostname={}", res.hostname.0))
.invoke(ErrorKind::ParseSysInfo)
.await?;
Command::new("sync").invoke(ErrorKind::Filesystem).await?;
Ok(res.clone())
}
Some(Err(e)) => Err(e.clone_output()),
@@ -465,6 +472,8 @@ async fn fresh_setup(
)
.await?;
write_shadow(start_os_password).await?;
Ok(((&account).try_into()?, rpc_ctx))
}

View File

@@ -944,6 +944,23 @@ pub async fn create_file(path: impl AsRef<Path>) -> Result<File, Error> {
.with_ctx(|_| (ErrorKind::Filesystem, lazy_format!("create {path:?}")))
}
pub async fn create_file_mod(path: impl AsRef<Path>, mode: u32) -> Result<File, Error> {
let path = path.as_ref();
if let Some(parent) = path.parent() {
tokio::fs::create_dir_all(parent)
.await
.with_ctx(|_| (ErrorKind::Filesystem, lazy_format!("mkdir -p {parent:?}")))?;
}
OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.mode(mode)
.open(path)
.await
.with_ctx(|_| (ErrorKind::Filesystem, lazy_format!("create {path:?}")))
}
pub async fn append_file(path: impl AsRef<Path>) -> Result<File, Error> {
let path = path.as_ref();
if let Some(parent) = path.parent() {

View File

@@ -31,7 +31,7 @@ impl VersionT for Version {
fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> {
let host = db["public"]["serverInfo"]["host"].clone();
let mut wifi = db["public"]["serverInfo"]["wifi"].clone();
wifi["enabled"] = Value::Bool(true);
wifi["enabled"] = Value::Bool(!wifi["selected"].is_null());
let mut network_interfaces = db["public"]["serverInfo"]["networkInterfaces"].clone();
for (k, v) in network_interfaces
.as_object_mut()