Bugfix/alpha.13 (#3053)

* bugfixes for alpha.13

* minor fixes

* version bump

* start-tunnel workflow

* sdk beta 44

* defaultFilter

* fix reset-password on tunnel auth

* explicitly rebuild types

* fix typo

* ubuntu-latest runner

* add cleanup steps

* fix env on attach
This commit is contained in:
Aiden McClelland
2025-11-19 22:48:49 -07:00
committed by GitHub
parent ad0632892e
commit e3e0b85e0c
36 changed files with 361 additions and 160 deletions

2
core/Cargo.lock generated
View File

@@ -7908,7 +7908,7 @@ dependencies = [
[[package]]
name = "start-os"
version = "0.4.0-alpha.13"
version = "0.4.0-alpha.14"
dependencies = [
"aes 0.7.5",
"arti-client",

View File

@@ -12,7 +12,7 @@ if [ "${PROFILE}" = "release" ]; then
BUILD_FLAGS="--release"
else
if [ "$PROFILE" != "debug"]; then
>&2 echo "Unknonw profile $PROFILE: falling back to debug..."
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
PROFILE=debug
fi
fi

View File

@@ -12,7 +12,7 @@ if [ "${PROFILE}" = "release" ]; then
BUILD_FLAGS="--release"
else
if [ "$PROFILE" != "debug"]; then
>&2 echo "Unknonw profile $PROFILE: falling back to debug..."
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
PROFILE=debug
fi
fi

View File

@@ -12,7 +12,7 @@ if [ "${PROFILE}" = "release" ]; then
BUILD_FLAGS="--release"
else
if [ "$PROFILE" != "debug"]; then
>&2 echo "Unknonw profile $PROFILE: falling back to debug..."
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
PROFILE=debug
fi
fi

View File

@@ -12,7 +12,7 @@ if [ "${PROFILE}" = "release" ]; then
BUILD_FLAGS="--release"
else
if [ "$PROFILE" != "debug"]; then
>&2 echo "Unknonw profile $PROFILE: falling back to debug..."
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
PROFILE=debug
fi
fi

View File

@@ -12,7 +12,7 @@ if [ "${PROFILE}" = "release" ]; then
BUILD_FLAGS="--release"
else
if [ "$PROFILE" != "debug"]; then
>&2 echo "Unknonw profile $PROFILE: falling back to debug..."
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
PROFILE=debug
fi
fi

View File

@@ -12,7 +12,7 @@ if [ "${PROFILE}" = "release" ]; then
BUILD_FLAGS="--release"
else
if [ "$PROFILE" != "debug"]; then
>&2 echo "Unknonw profile $PROFILE: falling back to debug..."
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
PROFILE=debug
fi
fi

View File

@@ -5,4 +5,4 @@ if tty -s; then
USE_TTY="-it"
fi
alias 'rust-zig-builder'='docker run '"$USE_TTY"' --rm -e "RUSTFLAGS=$RUSTFLAGS" -e "CFLAGS=-D_FORTIFY_SOURCE=2" -e "CXXFLAGS=-D_FORTIFY_SOURCE=2" -e SCCACHE_GHA_ENABLED -e SCCACHE_GHA_VERSION -e ACTIONS_RESULTS_URL -e ACTIONS_RUNTIME_TOKEN -v "$HOME/.cargo/registry":/usr/local/cargo/registry -v "$HOME/.cargo/git":/root/.cargo/git -v "$HOME/.cache/sccache":/root/.cache/sccache -v "$(pwd)":/workdir -w /workdir -P start9/cargo-zigbuild'
alias 'rust-zig-builder'='docker run '"$USE_TTY"' --rm -e "RUSTFLAGS=$RUSTFLAGS" -e "AWS_LC_SYS_CMAKE_TOOLCHAIN_FILE_riscv64gc_unknown_linux_musl=/root/cmake-overrides/toolchain-riscv64-musl-clang.cmake" -e SCCACHE_GHA_ENABLED -e SCCACHE_GHA_VERSION -e ACTIONS_RESULTS_URL -e ACTIONS_RUNTIME_TOKEN -v "$HOME/.cargo/registry":/usr/local/cargo/registry -v "$HOME/.cargo/git":/root/.cargo/git -v "$HOME/.cache/sccache":/root/.cache/sccache -v "$(pwd)":/workdir -w /workdir -P start9/cargo-zigbuild'

View File

@@ -12,7 +12,7 @@ if [ "${PROFILE}" = "release" ]; then
BUILD_FLAGS="--release"
else
if [ "$PROFILE" != "debug"]; then
>&2 echo "Unknonw profile $PROFILE: falling back to debug..."
>&2 echo "Unknown profile $PROFILE: falling back to debug..."
PROFILE=debug
fi
fi

View File

@@ -15,7 +15,7 @@ license = "MIT"
name = "start-os"
readme = "README.md"
repository = "https://github.com/Start9Labs/start-os"
version = "0.4.0-alpha.13" # VERSION_BUMP
version = "0.4.0-alpha.14" # VERSION_BUMP
[lib]
name = "startos"

View File

@@ -260,11 +260,7 @@ impl NetworkInterfaceInfo {
}
pub fn secure(&self) -> bool {
self.secure.unwrap_or_else(|| {
self.ip_info.as_ref().map_or(false, |ip_info| {
ip_info.device_type == Some(NetworkInterfaceType::Wireguard)
}) && !self.public()
})
self.secure.unwrap_or(false)
}
}

View File

@@ -366,6 +366,7 @@ impl LxcContainer {
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
tracing::info!("Connected to socket in {:?}", started.elapsed());
Ok(UnixRpcClient::new(sock_path))
}
}

View File

@@ -649,16 +649,6 @@ async fn torctl(
.invoke(ErrorKind::Tor)
.await?;
let logs = journalctl(
LogSource::Unit(SYSTEMD_UNIT),
Some(0),
None,
Some("0"),
false,
true,
)
.await?;
let mut tcp_stream = None;
for _ in 0..60 {
if let Ok(conn) = TcpStream::connect(tor_control).await {
@@ -720,7 +710,7 @@ async fn torctl(
ErrorKind::Tor,
));
}
Ok((connection, logs))
Ok(connection)
};
let pre_handler = async {
while let Some(command) = recv.recv().await {
@@ -745,7 +735,7 @@ async fn torctl(
Ok(())
};
let (mut connection, mut logs) = tokio::select! {
let mut connection = tokio::select! {
res = bootstrap => res?,
res = pre_handler => return res,
};
@@ -851,46 +841,59 @@ async fn torctl(
Ok(())
};
let log_parser = async {
while let Some(log) = logs.try_next().await? {
for (regex, severity) in &*LOG_REGEXES {
if regex.is_match(&log.message) {
let (check, wipe_state) = match severity {
ErrorLogSeverity::Fatal { wipe_state } => (false, *wipe_state),
ErrorLogSeverity::Unknown { wipe_state } => (true, *wipe_state),
};
let addr = hck_key.public().get_onion_address().to_string();
if !check
|| TcpStream::connect(tor_socks)
.map_err(|e| Error::new(e, ErrorKind::Tor))
.and_then(|mut tor_socks| async move {
tokio::time::timeout(
Duration::from_secs(30),
socks5_impl::client::connect(&mut tor_socks, (addr, 80), None)
.map_err(|e| Error::new(e, ErrorKind::Tor)),
)
loop {
let mut logs = journalctl(
LogSource::Unit(SYSTEMD_UNIT),
Some(0),
None,
Some("0"),
false,
true,
)
.await?;
while let Some(log) = logs.try_next().await? {
for (regex, severity) in &*LOG_REGEXES {
if regex.is_match(&log.message) {
let (check, wipe_state) = match severity {
ErrorLogSeverity::Fatal { wipe_state } => (false, *wipe_state),
ErrorLogSeverity::Unknown { wipe_state } => (true, *wipe_state),
};
let addr = hck_key.public().get_onion_address().to_string();
if !check
|| TcpStream::connect(tor_socks)
.map_err(|e| Error::new(e, ErrorKind::Tor))
.await?
})
.await
.with_ctx(|_| (ErrorKind::Tor, "Tor is confirmed to be down"))
.log_err()
.is_some()
{
if wipe_state {
Command::new("systemctl")
.arg("stop")
.arg("tor")
.invoke(ErrorKind::Tor)
.await?;
tokio::fs::remove_dir_all("/var/lib/tor").await?;
.and_then(|mut tor_socks| async move {
tokio::time::timeout(
Duration::from_secs(30),
socks5_impl::client::connect(
&mut tor_socks,
(addr, 80),
None,
)
.map_err(|e| Error::new(e, ErrorKind::Tor)),
)
.map_err(|e| Error::new(e, ErrorKind::Tor))
.await?
})
.await
.with_ctx(|_| (ErrorKind::Tor, "Tor is confirmed to be down"))
.log_err()
.is_some()
{
if wipe_state {
Command::new("systemctl")
.arg("stop")
.arg("tor")
.invoke(ErrorKind::Tor)
.await?;
tokio::fs::remove_dir_all("/var/lib/tor").await?;
}
return Err(Error::new(eyre!("{}", log.message), ErrorKind::Tor));
}
return Err(Error::new(eyre!("{}", log.message), ErrorKind::Tor));
}
}
}
}
// Err(Error::new(eyre!("Log stream terminated"), ErrorKind::Tor))
Ok(())
};
let health_checker = async {
let mut last_success = Instant::now();
@@ -960,20 +963,23 @@ impl TorControl {
_thread: tokio::spawn(async move {
let wipe_state = AtomicBool::new(false);
let mut health_timeout = Duration::from_secs(STARTING_HEALTH_TIMEOUT);
while let Err(e) = torctl(
tor_control,
tor_socks,
&mut recv,
&mut thread_services,
&wipe_state,
&mut health_timeout,
)
.await
{
tracing::error!("{e}: Restarting tor");
tracing::debug!("{e:?}");
loop {
if let Err(e) = torctl(
tor_control,
tor_socks,
&mut recv,
&mut thread_services,
&wipe_state,
&mut health_timeout,
)
.await
{
tracing::error!("TorControl : {e}");
tracing::debug!("{e:?}");
}
tracing::info!("Restarting Tor");
tokio::time::sleep(Duration::from_secs(1)).await;
}
tracing::info!("TorControl is shut down.")
})
.into(),
send,

View File

@@ -106,7 +106,9 @@ pub struct ExecParams {
#[arg(long)]
pty_size: Option<TermSize>,
#[arg(short, long)]
env: Option<PathBuf>,
env: Vec<String>,
#[arg(long)]
env_file: Option<PathBuf>,
#[arg(short, long)]
workdir: Option<PathBuf>,
#[arg(short, long)]
@@ -119,6 +121,7 @@ impl ExecParams {
fn exec(&self) -> Result<(), Error> {
let ExecParams {
env,
env_file,
workdir,
user,
chroot,
@@ -131,14 +134,15 @@ impl ExecParams {
ErrorKind::InvalidRequest,
));
};
let env_string = if let Some(env) = &env {
std::fs::read_to_string(env)
let env_string = if let Some(env_file) = &env_file {
std::fs::read_to_string(env_file)
.with_ctx(|_| (ErrorKind::Filesystem, lazy_format!("read {env:?}")))?
} else {
Default::default()
};
let env = env_string
.lines()
.chain(env.iter().map(|l| l.as_str()))
.map(|l| l.trim())
.filter_map(|l| l.split_once("="))
.collect::<BTreeMap<_, _>>();
@@ -199,6 +203,7 @@ pub fn launch(
force_stderr_tty,
pty_size,
env,
env_file,
workdir,
user,
chroot,
@@ -294,8 +299,11 @@ pub fn launch(
let (pty, pts) = pty_process::open().with_kind(ErrorKind::Filesystem)?;
let mut cmd = pty_process::Command::new("/usr/bin/start-container");
cmd = cmd.arg("subcontainer").arg("launch-init");
if let Some(env) = env {
cmd = cmd.arg("--env").arg(env);
for env in env {
cmd = cmd.arg("-e").arg(env)
}
if let Some(env_file) = env_file {
cmd = cmd.arg("--env-file").arg(env_file);
}
if let Some(workdir) = workdir {
cmd = cmd.arg("--workdir").arg(workdir);
@@ -349,8 +357,11 @@ pub fn launch(
} else {
let mut cmd = StdCommand::new("/usr/bin/start-container");
cmd.arg("subcontainer").arg("launch-init");
if let Some(env) = env {
cmd.arg("--env").arg(env);
for env in env {
cmd.arg("-e").arg(env);
}
if let Some(env_file) = env_file {
cmd.arg("--env-file").arg(env_file);
}
if let Some(workdir) = workdir {
cmd.arg("--workdir").arg(workdir);
@@ -441,6 +452,7 @@ pub fn exec(
force_stderr_tty,
pty_size,
env,
env_file,
workdir,
user,
chroot,
@@ -544,8 +556,11 @@ pub fn exec(
let (pty, pts) = pty_process::open().with_kind(ErrorKind::Filesystem)?;
let mut cmd = pty_process::Command::new("/usr/bin/start-container");
cmd = cmd.arg("subcontainer").arg("exec-command");
if let Some(env) = env {
cmd = cmd.arg("--env").arg(env);
for env in env {
cmd = cmd.arg("-e").arg(env);
}
if let Some(env_file) = env_file {
cmd = cmd.arg("--env-file").arg(env_file);
}
if let Some(workdir) = workdir {
cmd = cmd.arg("--workdir").arg(workdir);
@@ -599,8 +614,11 @@ pub fn exec(
} else {
let mut cmd = StdCommand::new("/usr/bin/start-container");
cmd.arg("subcontainer").arg("exec-command");
if let Some(env) = env {
cmd.arg("--env").arg(env);
for env in env {
cmd.arg("-e").arg(env);
}
if let Some(env_file) = env_file {
cmd.arg("--env-file").arg(env_file);
}
if let Some(workdir) = workdir {
cmd.arg("--workdir").arg(workdir);

View File

@@ -885,7 +885,7 @@ pub async fn attach(
.arg("start-container")
.arg("subcontainer")
.arg("exec")
.arg("--env")
.arg("--env-file")
.arg(
Path::new("/media/startos/images")
.join(image_id)

View File

@@ -43,7 +43,7 @@ use crate::util::rpc_client::UnixRpcClient;
use crate::volume::data_dir;
use crate::{ARCH, DATA_DIR, PACKAGE_DATA};
const RPC_CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
const RPC_CONNECT_TIMEOUT: Duration = Duration::from_secs(30);
#[derive(Debug)]
pub struct ServiceState {

View File

@@ -117,6 +117,8 @@ impl ServiceMap {
match Service::load(ctx, id, disposition).await {
Ok(s) => *service = s.into(),
Err(e) => {
tracing::error!("Error loading service: {e}");
tracing::debug!("{e:?}");
let e = ErrorData::from(e);
ctx.db
.mutate(|db| {

View File

@@ -3,7 +3,7 @@ use imbl::HashMap;
use imbl_value::InternedString;
use itertools::Itertools;
use patch_db::HasModel;
use rpc_toolkit::{Context, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
use rpc_toolkit::{Context, Empty, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
@@ -113,27 +113,12 @@ impl AuthContext for TunnelContext {
#[derive(Clone, Debug, Deserialize, Serialize, HasModel, TS, Parser)]
#[serde(rename_all = "camelCase")]
#[model = "Model<Self>"]
#[ts(export)]
pub struct SignerInfo {
pub name: InternedString,
}
pub fn auth_api<C: Context>() -> ParentHandler<C> {
ParentHandler::new()
.subcommand(
"login",
from_fn_async(crate::auth::login_impl::<TunnelContext>)
.with_metadata("login", Value::Bool(true))
.no_cli(),
)
.subcommand(
"logout",
from_fn_async(crate::auth::logout::<TunnelContext>)
.with_metadata("get_session", Value::Bool(true))
.no_display()
.with_about("Log out of current auth session")
.with_call_remote::<CliContext>(),
)
crate::auth::auth::<C, TunnelContext>()
.subcommand("set-password", from_fn_async(set_password_rpc).no_cli())
.subcommand(
"set-password",
@@ -173,19 +158,15 @@ pub fn auth_api<C: Context>() -> ParentHandler<C> {
.with_display_serializable()
.with_custom_display_fn(|HandlerArgs { params, .. }, res| {
use prettytable::*;
if let Some(format) = params.format {
return display_serializable(format, res);
}
let mut table = Table::new();
table.add_row(row![bc => "NAME", "KEY"]);
for (key, info) in res {
table.add_row(row![info.name, key]);
}
table.print_tty(false)?;
Ok(())
})
.with_about("List authorized keys")
@@ -194,7 +175,7 @@ pub fn auth_api<C: Context>() -> ParentHandler<C> {
)
}
#[derive(Debug, Deserialize, Serialize, Parser)]
#[derive(Debug, Deserialize, Serialize, Parser, TS)]
#[serde(rename_all = "camelCase")]
pub struct AddKeyParams {
pub name: InternedString,
@@ -216,7 +197,7 @@ pub async fn add_key(
.result
}
#[derive(Debug, Deserialize, Serialize, Parser)]
#[derive(Debug, Deserialize, Serialize, Parser, TS)]
#[serde(rename_all = "camelCase")]
pub struct RemoveKeyParams {
pub key: AnyVerifyingKey,
@@ -240,7 +221,7 @@ pub async fn list_keys(ctx: TunnelContext) -> Result<HashMap<AnyVerifyingKey, Si
ctx.db.peek().await.into_auth_pubkeys().de()
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[derive(Debug, Clone, Deserialize, Serialize, TS)]
pub struct SetPasswordParams {
pub password: String,
}

View File

@@ -53,8 +53,9 @@ mod v0_4_0_alpha_10;
mod v0_4_0_alpha_11;
mod v0_4_0_alpha_12;
mod v0_4_0_alpha_13;
mod v0_4_0_alpha_14;
pub type Current = v0_4_0_alpha_13::Version; // VERSION_BUMP
pub type Current = v0_4_0_alpha_14::Version; // VERSION_BUMP
impl Current {
#[instrument(skip(self, db))]
@@ -169,7 +170,8 @@ enum Version {
V0_4_0_alpha_10(Wrapper<v0_4_0_alpha_10::Version>),
V0_4_0_alpha_11(Wrapper<v0_4_0_alpha_11::Version>),
V0_4_0_alpha_12(Wrapper<v0_4_0_alpha_12::Version>),
V0_4_0_alpha_13(Wrapper<v0_4_0_alpha_13::Version>), // VERSION_BUMP
V0_4_0_alpha_13(Wrapper<v0_4_0_alpha_13::Version>),
V0_4_0_alpha_14(Wrapper<v0_4_0_alpha_14::Version>), // VERSION_BUMP
Other(exver::Version),
}
@@ -225,7 +227,8 @@ impl Version {
Self::V0_4_0_alpha_10(v) => DynVersion(Box::new(v.0)),
Self::V0_4_0_alpha_11(v) => DynVersion(Box::new(v.0)),
Self::V0_4_0_alpha_12(v) => DynVersion(Box::new(v.0)),
Self::V0_4_0_alpha_13(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP
Self::V0_4_0_alpha_13(v) => DynVersion(Box::new(v.0)),
Self::V0_4_0_alpha_14(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP
Self::Other(v) => {
return Err(Error::new(
eyre!("unknown version {v}"),
@@ -273,7 +276,8 @@ impl Version {
Version::V0_4_0_alpha_10(Wrapper(x)) => x.semver(),
Version::V0_4_0_alpha_11(Wrapper(x)) => x.semver(),
Version::V0_4_0_alpha_12(Wrapper(x)) => x.semver(),
Version::V0_4_0_alpha_13(Wrapper(x)) => x.semver(), // VERSION_BUMP
Version::V0_4_0_alpha_13(Wrapper(x)) => x.semver(),
Version::V0_4_0_alpha_14(Wrapper(x)) => x.semver(), // VERSION_BUMP
Version::Other(x) => x.clone(),
}
}

View File

@@ -4,7 +4,7 @@ use exver::{PreReleaseSegment, VersionRange};
use imbl_value::InternedString;
use super::v0_3_5::V0_3_0_COMPAT;
use super::{VersionT, v0_4_0_alpha_11};
use super::{v0_4_0_alpha_11, VersionT};
use crate::net::tor::TorSecretKey;
use crate::prelude::*;
@@ -75,7 +75,10 @@ impl VersionT for Version {
}
fix_host(&mut db["public"]["serverInfo"]["network"]["host"])?;
db["private"]["keyStore"]["localCerts"] = db["private"]["keyStore"]["local_certs"].clone();
if db["private"]["keyStore"]["localCerts"].is_null() {
db["private"]["keyStore"]["localCerts"] =
db["private"]["keyStore"]["local_certs"].clone();
}
Ok(Value::Null)
}

View File

@@ -0,0 +1,37 @@
use exver::{PreReleaseSegment, VersionRange};
use super::v0_3_5::V0_3_0_COMPAT;
use super::{VersionT, v0_4_0_alpha_13};
use crate::prelude::*;
lazy_static::lazy_static! {
static ref V0_4_0_alpha_14: exver::Version = exver::Version::new(
[0, 4, 0],
[PreReleaseSegment::String("alpha".into()), 14.into()]
);
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Version;
impl VersionT for Version {
type Previous = v0_4_0_alpha_13::Version;
type PreUpRes = ();
async fn pre_up(self) -> Result<Self::PreUpRes, Error> {
Ok(())
}
fn semver(self) -> exver::Version {
V0_4_0_alpha_14.clone()
}
fn compat(self) -> &'static VersionRange {
&V0_3_0_COMPAT
}
#[instrument(skip_all)]
fn up(self, _db: &mut Value, _: Self::PreUpRes) -> Result<Value, Error> {
Ok(Value::Null)
}
fn down(self, _db: &mut Value) -> Result<(), Error> {
Ok(())
}
}