mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
Merge pull request #3079 from Start9Labs/hotfix/alpha.16
hotfixes for alpha.16
This commit is contained in:
2
Makefile
2
Makefile
@@ -161,7 +161,7 @@ results/$(REGISTRY_BASENAME).deb: dpkg-build.sh $(call ls-files,debian/start-reg
|
||||
|
||||
tunnel-deb: results/$(TUNNEL_BASENAME).deb
|
||||
|
||||
results/$(TUNNEL_BASENAME).deb: dpkg-build.sh $(call ls-files,debian/start-tunnel) $(TUNNEL_TARGETS)
|
||||
results/$(TUNNEL_BASENAME).deb: dpkg-build.sh $(call ls-files,debian/start-tunnel) $(TUNNEL_TARGETS) build/lib/scripts/forward-port
|
||||
PROJECT=start-tunnel PLATFORM=$(ARCH) REQUIRES=debian DEPENDS=wireguard-tools,iptables,conntrack ./build/os-compat/run-compat.sh ./dpkg-build.sh
|
||||
|
||||
$(IMAGE_TYPE): results/$(BASENAME).$(IMAGE_TYPE)
|
||||
|
||||
@@ -5,29 +5,47 @@ if [ -z "$sip" ] || [ -z "$dip" ] || [ -z "$sport" ] || [ -z "$dport" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rule_exists() {
|
||||
iptables -t nat -C "$@" 2>/dev/null
|
||||
}
|
||||
NAME="F$(echo "$sip:$sport -> $dip:$dport" | sha256sum | head -c 15)"
|
||||
|
||||
apply_rule() {
|
||||
if [ "$UNDO" = "1" ]; then
|
||||
if rule_exists "$@"; then
|
||||
iptables -t nat -D "$@"
|
||||
fi
|
||||
else
|
||||
if ! rule_exists "$@"; then
|
||||
iptables -t nat -A "$@"
|
||||
fi
|
||||
for kind in INPUT FORWARD ACCEPT; do
|
||||
if ! iptables -C $kind -j "${NAME}_${kind}" 2> /dev/null; then
|
||||
iptables -N "${NAME}_${kind}" 2> /dev/null
|
||||
iptables -A $kind -j "${NAME}_${kind}"
|
||||
fi
|
||||
}
|
||||
done
|
||||
for kind in PREROUTING INPUT OUTPUT POSTROUTING; do
|
||||
if ! iptables -t nat -C $kind -j "${NAME}_${kind}" 2> /dev/null; then
|
||||
iptables -t nat -N "${NAME}_${kind}" 2> /dev/null
|
||||
iptables -t nat -A $kind -j "${NAME}_${kind}"
|
||||
fi
|
||||
done
|
||||
|
||||
apply_rule PREROUTING -p tcp -d $sip --dport $sport -j DNAT --to-destination $dip:$dport
|
||||
apply_rule PREROUTING -p udp -d $sip --dport $sport -j DNAT --to-destination $dip:$dport
|
||||
apply_rule OUTPUT -p tcp -d $sip --dport $sport -j DNAT --to-destination $dip:$dport
|
||||
apply_rule OUTPUT -p udp -d $sip --dport $sport -j DNAT --to-destination $dip:$dport
|
||||
apply_rule POSTROUTING -p tcp -d $dip --dport $dport -j MASQUERADE
|
||||
apply_rule POSTROUTING -p udp -d $dip --dport $dport -j MASQUERADE
|
||||
err=0
|
||||
trap 'err=1' ERR
|
||||
|
||||
for kind in INPUT FORWARD ACCEPT; do
|
||||
iptables -F "${NAME}_${kind}" 2> /dev/null
|
||||
done
|
||||
for kind in PREROUTING INPUT OUTPUT POSTROUTING; do
|
||||
iptables -t nat -F "${NAME}_${kind}" 2> /dev/null
|
||||
done
|
||||
if [ "$UNDO" = 1 ]; then
|
||||
conntrack -D -p tcp -d $sip --dport $sport || true # conntrack returns exit 1 if no connections are active
|
||||
fi
|
||||
conntrack -D -p udp -d $sip --dport $sport || true # conntrack returns exit 1 if no connections are active
|
||||
exit $err
|
||||
fi
|
||||
|
||||
iptables -t nat -A ${NAME}_PREROUTING -d "$sip" -p tcp --dport "$sport" -j DNAT --to-destination "$dip:$dport"
|
||||
iptables -t nat -A ${NAME}_PREROUTING -d "$sip" -p udp --dport "$sport" -j DNAT --to-destination "$dip:$dport"
|
||||
iptables -t nat -A ${NAME}_OUTPUT -d "$sip" -p tcp --dport "$sport" -j DNAT --to-destination "$dip:$dport"
|
||||
iptables -t nat -A ${NAME}_OUTPUT -d "$sip" -p udp --dport "$sport" -j DNAT --to-destination "$dip:$dport"
|
||||
|
||||
iptables -t nat -A ${NAME}_PREROUTING -s "$dip/24" -d "$sip" -p tcp --dport "$sport" -j DNAT --to-destination "$dip:$dport"
|
||||
iptables -t nat -A ${NAME}_PREROUTING -s "$dip/24" -d "$sip" -p udp --dport "$sport" -j DNAT --to-destination "$dip:$dport"
|
||||
iptables -t nat -A ${NAME}_POSTROUTING -s "$dip/24" -d "$dip" -p tcp --dport "$dport" -j MASQUERADE
|
||||
iptables -t nat -A ${NAME}_POSTROUTING -s "$dip/24" -d "$dip" -p udp --dport "$dport" -j MASQUERADE
|
||||
|
||||
iptables -A ${NAME}_FORWARD -d $dip -p tcp --dport $dport -m state --state NEW -j ACCEPT
|
||||
iptables -A ${NAME}_FORWARD -d $dip -p udp --dport $dport -m state --state NEW -j ACCEPT
|
||||
|
||||
exit $err
|
||||
@@ -4,6 +4,7 @@ OnFailure=container-runtime-failure.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment=RUST_LOG=startos=debug
|
||||
ExecStart=/usr/bin/node --experimental-detect-module --trace-warnings --unhandled-rejections=warn /usr/lib/startos/init/index.js
|
||||
Restart=no
|
||||
|
||||
|
||||
@@ -68,7 +68,10 @@ export class SystemForStartOs implements System {
|
||||
try {
|
||||
if (this.runningMain || this.starting) return
|
||||
this.starting = true
|
||||
effects.constRetry = utils.once(() => effects.restart())
|
||||
effects.constRetry = utils.once(() => {
|
||||
console.debug(".const() triggered")
|
||||
effects.restart()
|
||||
})
|
||||
let mainOnTerm: () => Promise<void> | undefined
|
||||
const daemons = await (
|
||||
await this.abi.main({
|
||||
|
||||
15
core/Cargo.lock
generated
15
core/Cargo.lock
generated
@@ -5290,7 +5290,7 @@ dependencies = [
|
||||
"nix 0.30.1",
|
||||
"patch-db-macro",
|
||||
"serde",
|
||||
"serde_cbor",
|
||||
"serde_cbor 0.11.1",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -6477,7 +6477,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rpc-toolkit"
|
||||
version = "0.3.2"
|
||||
source = "git+https://github.com/Start9Labs/rpc-toolkit.git?rev=068db90#068db905ee38a7da97cc4a43b806409204e73723"
|
||||
source = "git+https://github.com/Start9Labs/rpc-toolkit.git#81d18147fd0ca9725b820c010c006e8a2cada322"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
@@ -6494,6 +6494,7 @@ dependencies = [
|
||||
"pin-project",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_cbor 0.11.2",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
@@ -6933,6 +6934,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_cbor"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
|
||||
dependencies = [
|
||||
"half 1.8.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
|
||||
@@ -212,8 +212,8 @@ reqwest = { version = "0.12.25", features = [
|
||||
] }
|
||||
reqwest_cookie_store = "0.9.0"
|
||||
rpassword = "7.2.0"
|
||||
rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git", rev = "068db90" }
|
||||
rust-argon2 = "3.0.0"
|
||||
rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git" }
|
||||
safelog = { version = "0.4.8", git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit", optional = true }
|
||||
semver = { version = "1.0.20", features = ["serde"] }
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
|
||||
@@ -7,7 +7,7 @@ use openssl::x509::X509;
|
||||
|
||||
use crate::db::model::DatabaseModel;
|
||||
use crate::hostname::{Hostname, generate_hostname, generate_id};
|
||||
use crate::net::ssl::{generate_key, make_root_cert};
|
||||
use crate::net::ssl::{gen_nistp256, make_root_cert};
|
||||
use crate::net::tor::TorSecretKey;
|
||||
use crate::prelude::*;
|
||||
use crate::util::serde::Pem;
|
||||
@@ -37,7 +37,7 @@ impl AccountInfo {
|
||||
let server_id = generate_id();
|
||||
let hostname = generate_hostname();
|
||||
let tor_key = vec![TorSecretKey::generate()];
|
||||
let root_ca_key = generate_key()?;
|
||||
let root_ca_key = gen_nistp256()?;
|
||||
let root_ca_cert = make_root_cert(&root_ca_key, &hostname, start_time)?;
|
||||
let ssh_key = ssh_key::PrivateKey::from(ssh_key::private::Ed25519Keypair::random(
|
||||
&mut ssh_key::rand_core::OsRng::default(),
|
||||
@@ -128,7 +128,7 @@ impl AccountInfo {
|
||||
cert_store
|
||||
.as_root_cert_mut()
|
||||
.ser(Pem::new_ref(&self.root_ca_cert))?;
|
||||
let int_key = crate::net::ssl::generate_key()?;
|
||||
let int_key = crate::net::ssl::gen_nistp256()?;
|
||||
let int_cert =
|
||||
crate::net::ssl::make_int_cert((&self.root_ca_key, &self.root_ca_cert), &int_key)?;
|
||||
cert_store.as_int_key_mut().ser(&Pem(int_key))?;
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use std::fmt;
|
||||
|
||||
use clap::{CommandFactory, FromArgMatches, Parser};
|
||||
pub use crate::ActionId;
|
||||
use crate::{PackageId, ReplayId};
|
||||
use qrcode::QrCode;
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::instrument;
|
||||
use ts_rs::TS;
|
||||
|
||||
pub use crate::ActionId;
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::package::TaskSeverity;
|
||||
use crate::prelude::*;
|
||||
@@ -16,6 +15,7 @@ use crate::rpc_continuations::Guid;
|
||||
use crate::util::serde::{
|
||||
HandlerExtSerde, StdinDeserializable, WithIoFormat, display_serializable,
|
||||
};
|
||||
use crate::{PackageId, ReplayId};
|
||||
|
||||
pub fn action_api<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
|
||||
@@ -14,8 +14,8 @@ use tracing::instrument;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::middleware::auth::{
|
||||
AsLogoutSessionId, AuthContext, HasLoggedOutSessions, HashSessionToken, LoginRes,
|
||||
use crate::middleware::auth::session::{
|
||||
AsLogoutSessionId, HasLoggedOutSessions, HashSessionToken, LoginRes, SessionAuthContext,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use crate::util::crypto::EncryptedWire;
|
||||
@@ -110,7 +110,7 @@ impl std::str::FromStr for PasswordType {
|
||||
})
|
||||
}
|
||||
}
|
||||
pub fn auth<C: Context, AC: AuthContext>() -> ParentHandler<C>
|
||||
pub fn auth<C: Context, AC: SessionAuthContext>() -> ParentHandler<C>
|
||||
where
|
||||
CliContext: CallRemote<AC>,
|
||||
{
|
||||
@@ -173,7 +173,7 @@ fn gen_pwd() {
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
async fn cli_login<C: AuthContext>(
|
||||
async fn cli_login<C: SessionAuthContext>(
|
||||
HandlerArgs {
|
||||
context: ctx,
|
||||
parent_method,
|
||||
@@ -227,7 +227,7 @@ pub struct LoginParams {
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn login_impl<C: AuthContext>(
|
||||
pub async fn login_impl<C: SessionAuthContext>(
|
||||
ctx: C,
|
||||
LoginParams {
|
||||
password,
|
||||
@@ -283,7 +283,7 @@ pub struct LogoutParams {
|
||||
session: InternedString,
|
||||
}
|
||||
|
||||
pub async fn logout<C: AuthContext>(
|
||||
pub async fn logout<C: SessionAuthContext>(
|
||||
ctx: C,
|
||||
LogoutParams { session }: LogoutParams,
|
||||
) -> Result<Option<HasLoggedOutSessions>, Error> {
|
||||
@@ -312,7 +312,7 @@ pub struct SessionList {
|
||||
sessions: Sessions,
|
||||
}
|
||||
|
||||
pub fn session<C: Context, AC: AuthContext>() -> ParentHandler<C>
|
||||
pub fn session<C: Context, AC: SessionAuthContext>() -> ParentHandler<C>
|
||||
where
|
||||
CliContext: CallRemote<AC>,
|
||||
{
|
||||
@@ -379,7 +379,7 @@ pub struct ListParams {
|
||||
|
||||
// #[command(display(display_sessions))]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn list<C: AuthContext>(
|
||||
pub async fn list<C: SessionAuthContext>(
|
||||
ctx: C,
|
||||
ListParams { session, .. }: ListParams,
|
||||
) -> Result<SessionList, Error> {
|
||||
@@ -418,7 +418,10 @@ pub struct KillParams {
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn kill<C: AuthContext>(ctx: C, KillParams { ids }: KillParams) -> Result<(), Error> {
|
||||
pub async fn kill<C: SessionAuthContext>(
|
||||
ctx: C,
|
||||
KillParams { ids }: KillParams,
|
||||
) -> Result<(), Error> {
|
||||
HasLoggedOutSessions::new(ids.into_iter().map(KillSessionId::new), &ctx).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use chrono::Utc;
|
||||
use clap::Parser;
|
||||
use color_eyre::eyre::eyre;
|
||||
use imbl::OrdSet;
|
||||
use crate::PackageId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tracing::instrument;
|
||||
@@ -14,6 +13,7 @@ use ts_rs::TS;
|
||||
|
||||
use super::PackageBackupReport;
|
||||
use super::target::{BackupTargetId, PackageBackupInfo};
|
||||
use crate::PackageId;
|
||||
use crate::backup::os::OsBackup;
|
||||
use crate::backup::{BackupReport, ServerBackupReport};
|
||||
use crate::context::RpcContext;
|
||||
@@ -22,7 +22,7 @@ use crate::db::model::{Database, DatabaseModel};
|
||||
use crate::disk::mount::backup::BackupMountGuard;
|
||||
use crate::disk::mount::filesystem::ReadWrite;
|
||||
use crate::disk::mount::guard::{GenericMountGuard, TmpMountGuard};
|
||||
use crate::middleware::auth::AuthContext;
|
||||
use crate::middleware::auth::session::SessionAuthContext;
|
||||
use crate::notifications::{NotificationLevel, notify};
|
||||
use crate::prelude::*;
|
||||
use crate::util::io::{AtomicFile, dir_copy};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::PackageId;
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::PackageId;
|
||||
use crate::context::CliContext;
|
||||
#[allow(unused_imports)]
|
||||
use crate::prelude::*;
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::sync::Arc;
|
||||
|
||||
use clap::Parser;
|
||||
use futures::{StreamExt, stream};
|
||||
use crate::PackageId;
|
||||
use patch_db::json_ptr::ROOT;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::Mutex;
|
||||
@@ -11,7 +10,6 @@ use tracing::instrument;
|
||||
use ts_rs::TS;
|
||||
|
||||
use super::target::BackupTargetId;
|
||||
use crate::PLATFORM;
|
||||
use crate::backup::os::OsBackup;
|
||||
use crate::context::setup::SetupResult;
|
||||
use crate::context::{RpcContext, SetupContext};
|
||||
@@ -27,6 +25,7 @@ use crate::service::service_map::DownloadInstallFuture;
|
||||
use crate::setup::SetupExecuteProgress;
|
||||
use crate::system::sync_kiosk;
|
||||
use crate::util::serde::IoFormat;
|
||||
use crate::{PLATFORM, PackageId};
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -9,8 +9,6 @@ use digest::OutputSizeUser;
|
||||
use digest::generic_array::GenericArray;
|
||||
use exver::Version;
|
||||
use imbl_value::InternedString;
|
||||
use crate::util::FromStrParser;
|
||||
use crate::PackageId;
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Sha256;
|
||||
@@ -19,6 +17,7 @@ use tracing::instrument;
|
||||
use ts_rs::TS;
|
||||
|
||||
use self::cifs::CifsBackupTarget;
|
||||
use crate::PackageId;
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::DatabaseModel;
|
||||
use crate::disk::mount::backup::BackupMountGuard;
|
||||
@@ -28,10 +27,10 @@ use crate::disk::mount::filesystem::{FileSystem, MountType, ReadWrite};
|
||||
use crate::disk::mount::guard::{GenericMountGuard, TmpMountGuard};
|
||||
use crate::disk::util::PartitionInfo;
|
||||
use crate::prelude::*;
|
||||
use crate::util::VersionString;
|
||||
use crate::util::serde::{
|
||||
HandlerExtSerde, WithIoFormat, deserialize_from_str, display_serializable, serialize_display,
|
||||
};
|
||||
use crate::util::{FromStrParser, VersionString};
|
||||
|
||||
pub mod cifs;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ use std::time::Duration;
|
||||
|
||||
use clap::Parser;
|
||||
use futures::FutureExt;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use rpc_toolkit::CliApp;
|
||||
use tokio::signal::unix::signal;
|
||||
use tracing::instrument;
|
||||
@@ -20,6 +19,7 @@ use crate::prelude::*;
|
||||
use crate::tunnel::context::{TunnelConfig, TunnelContext};
|
||||
use crate::tunnel::tunnel_router;
|
||||
use crate::tunnel::web::TunnelCertHandler;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use crate::util::logger::LOGGER;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
|
||||
@@ -24,7 +24,7 @@ use super::setup::CURRENT_SECRET;
|
||||
use crate::context::config::{ClientConfig, local_config_path};
|
||||
use crate::context::{DiagnosticContext, InitContext, InstallContext, RpcContext, SetupContext};
|
||||
use crate::developer::{OS_DEVELOPER_KEY_PATH, default_developer_key_path};
|
||||
use crate::middleware::auth::AuthContext;
|
||||
use crate::middleware::auth::local::LocalAuthContext;
|
||||
use crate::prelude::*;
|
||||
use crate::rpc_continuations::Guid;
|
||||
use crate::util::io::read_file_to_string;
|
||||
@@ -307,7 +307,7 @@ impl CallRemote<RpcContext> for CliContext {
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Network)?;
|
||||
}
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
self.rpc_url.clone(),
|
||||
HeaderMap::new(),
|
||||
@@ -320,7 +320,7 @@ impl CallRemote<RpcContext> for CliContext {
|
||||
}
|
||||
impl CallRemote<DiagnosticContext> for CliContext {
|
||||
async fn call_remote(&self, method: &str, params: Value, _: Empty) -> Result<Value, RpcError> {
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
self.rpc_url.clone(),
|
||||
HeaderMap::new(),
|
||||
@@ -333,7 +333,7 @@ impl CallRemote<DiagnosticContext> for CliContext {
|
||||
}
|
||||
impl CallRemote<InitContext> for CliContext {
|
||||
async fn call_remote(&self, method: &str, params: Value, _: Empty) -> Result<Value, RpcError> {
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
self.rpc_url.clone(),
|
||||
HeaderMap::new(),
|
||||
@@ -346,7 +346,7 @@ impl CallRemote<InitContext> for CliContext {
|
||||
}
|
||||
impl CallRemote<SetupContext> for CliContext {
|
||||
async fn call_remote(&self, method: &str, params: Value, _: Empty) -> Result<Value, RpcError> {
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
self.rpc_url.clone(),
|
||||
HeaderMap::new(),
|
||||
@@ -359,7 +359,7 @@ impl CallRemote<SetupContext> for CliContext {
|
||||
}
|
||||
impl CallRemote<InstallContext> for CliContext {
|
||||
async fn call_remote(&self, method: &str, params: Value, _: Empty) -> Result<Value, RpcError> {
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
self.rpc_url.clone(),
|
||||
HeaderMap::new(),
|
||||
|
||||
@@ -8,12 +8,10 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use chrono::{TimeDelta, Utc};
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use imbl::OrdMap;
|
||||
use imbl_value::InternedString;
|
||||
use itertools::Itertools;
|
||||
use josekit::jwk::Jwk;
|
||||
use crate::{ActionId, PackageId};
|
||||
use reqwest::{Client, Proxy};
|
||||
use rpc_toolkit::yajrc::RpcError;
|
||||
use rpc_toolkit::{CallRemote, Context, Empty};
|
||||
@@ -22,7 +20,6 @@ use tokio::time::Instant;
|
||||
use tracing::instrument;
|
||||
|
||||
use super::setup::CURRENT_SECRET;
|
||||
use crate::DATA_DIR;
|
||||
use crate::account::AccountInfo;
|
||||
use crate::auth::Sessions;
|
||||
use crate::context::config::ServerConfig;
|
||||
@@ -45,9 +42,11 @@ use crate::service::ServiceMap;
|
||||
use crate::service::action::update_tasks;
|
||||
use crate::service::effects::callbacks::ServiceCallbacks;
|
||||
use crate::shutdown::Shutdown;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use crate::util::io::delete_file;
|
||||
use crate::util::lshw::LshwDevice;
|
||||
use crate::util::sync::{SyncMutex, SyncRwLock, Watch};
|
||||
use crate::{ActionId, DATA_DIR, PackageId};
|
||||
|
||||
pub struct RpcContextSeed {
|
||||
is_closed: AtomicBool,
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::{Future, StreamExt};
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use imbl_value::InternedString;
|
||||
use josekit::jwk::Jwk;
|
||||
use patch_db::PatchDb;
|
||||
@@ -28,6 +27,7 @@ use crate::progress::FullProgressTracker;
|
||||
use crate::rpc_continuations::{Guid, RpcContinuation, RpcContinuations};
|
||||
use crate::setup::SetupProgress;
|
||||
use crate::shutdown::Shutdown;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use crate::util::net::WebSocketExt;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use clap::Parser;
|
||||
use crate::PackageId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::instrument;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::Error;
|
||||
use crate::context::RpcContext;
|
||||
use crate::prelude::*;
|
||||
use crate::{Error, PackageId};
|
||||
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -4,8 +4,6 @@ use std::path::PathBuf;
|
||||
use chrono::{DateTime, Utc};
|
||||
use exver::VersionRange;
|
||||
use imbl_value::InternedString;
|
||||
use crate::util::DataUrl;
|
||||
use crate::{ActionId, HealthCheckId, HostId, PackageId, ReplayId, ServiceInterfaceId};
|
||||
use patch_db::HasModel;
|
||||
use patch_db::json_ptr::JsonPointer;
|
||||
use reqwest::Url;
|
||||
@@ -18,7 +16,9 @@ use crate::prelude::*;
|
||||
use crate::progress::FullProgress;
|
||||
use crate::s9pk::manifest::Manifest;
|
||||
use crate::status::StatusInfo;
|
||||
use crate::util::DataUrl;
|
||||
use crate::util::serde::{Pem, is_partial_of};
|
||||
use crate::{ActionId, HealthCheckId, HostId, PackageId, ReplayId, ServiceInterfaceId};
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, TS)]
|
||||
#[ts(export)]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
use crate::PackageId;
|
||||
use patch_db::{HasModel, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::PackageId;
|
||||
use crate::auth::Sessions;
|
||||
use crate::backup::target::cifs::CifsTargets;
|
||||
use crate::net::forward::AvailablePorts;
|
||||
|
||||
@@ -9,7 +9,6 @@ use imbl_value::InternedString;
|
||||
use ipnet::IpNet;
|
||||
use isocountry::CountryCode;
|
||||
use itertools::Itertools;
|
||||
use crate::{GatewayId, PackageId};
|
||||
use openssl::hash::MessageDigest;
|
||||
use patch_db::{HasModel, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -31,7 +30,7 @@ use crate::util::cpupower::Governor;
|
||||
use crate::util::lshw::LshwDevice;
|
||||
use crate::util::serde::MaybeUtf8String;
|
||||
use crate::version::{Current, VersionT};
|
||||
use crate::{ARCH, PLATFORM};
|
||||
use crate::{ARCH, GatewayId, PLATFORM, PackageId};
|
||||
|
||||
pub static DB_UI_SEED_CELL: OnceLock<&'static str> = OnceLock::new();
|
||||
|
||||
|
||||
@@ -2,13 +2,12 @@ use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
|
||||
use imbl_value::InternedString;
|
||||
use crate::PackageId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::Error;
|
||||
use crate::prelude::*;
|
||||
use crate::util::PathOrUrl;
|
||||
use crate::{Error, PackageId};
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, HasModel, TS)]
|
||||
#[model = "Model<Self>"]
|
||||
|
||||
@@ -2,11 +2,11 @@ use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use crate::PackageId;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tracing::instrument;
|
||||
|
||||
use super::guard::{GenericMountGuard, TmpMountGuard};
|
||||
use crate::PackageId;
|
||||
use crate::auth::check_password;
|
||||
use crate::backup::target::BackupInfo;
|
||||
use crate::disk::mount::filesystem::ReadWrite;
|
||||
|
||||
@@ -8,7 +8,6 @@ use clap::Parser;
|
||||
use clap::builder::ValueParserFactory;
|
||||
use digest::generic_array::GenericArray;
|
||||
use digest::{Digest, OutputSizeUser};
|
||||
use crate::util::FromStrParser;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Sha256;
|
||||
use tokio::process::Command;
|
||||
@@ -16,7 +15,7 @@ use ts_rs::TS;
|
||||
|
||||
use super::FileSystem;
|
||||
use crate::prelude::*;
|
||||
use crate::util::Invoke;
|
||||
use crate::util::{FromStrParser, Invoke};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize, Parser, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -4,14 +4,13 @@ use std::sync::{Arc, Weak};
|
||||
|
||||
use futures::Future;
|
||||
use lazy_static::lazy_static;
|
||||
use crate::ResultExt;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::instrument;
|
||||
|
||||
use super::filesystem::{FileSystem, MountType, ReadOnly, ReadWrite};
|
||||
use super::util::unmount;
|
||||
use crate::Error;
|
||||
use crate::util::{Invoke, Never};
|
||||
use crate::{Error, ResultExt};
|
||||
|
||||
pub const TMP_MOUNTPOINT: &'static str = "/media/startos/tmp";
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
use axum::http::StatusCode;
|
||||
use tokio_rustls::rustls;
|
||||
use axum::http::uri::InvalidUri;
|
||||
use color_eyre::eyre::eyre;
|
||||
use num_enum::TryFromPrimitive;
|
||||
@@ -12,6 +11,7 @@ use rpc_toolkit::yajrc::{
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio_rustls::rustls;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::InvalidId;
|
||||
@@ -217,8 +217,9 @@ impl Error {
|
||||
source: E,
|
||||
kind: ErrorKind,
|
||||
) -> Self {
|
||||
let debug = (std::any::TypeId::of::<E>() == std::any::TypeId::of::<color_eyre::eyre::Error>())
|
||||
.then(|| eyre!("{source:?}"));
|
||||
let debug = (std::any::TypeId::of::<E>()
|
||||
== std::any::TypeId::of::<color_eyre::eyre::Error>())
|
||||
.then(|| eyre!("{source:?}"));
|
||||
Error {
|
||||
source: source.into(),
|
||||
debug,
|
||||
@@ -591,8 +592,9 @@ where
|
||||
fn with_ctx<F: FnOnce(&E) -> (ErrorKind, D), D: Display>(self, f: F) -> Result<T, Error> {
|
||||
self.map_err(|e| {
|
||||
let (kind, ctx) = f(&e);
|
||||
let debug = (std::any::TypeId::of::<E>() == std::any::TypeId::of::<color_eyre::eyre::Error>())
|
||||
.then(|| eyre!("{ctx}: {e:?}"));
|
||||
let debug = (std::any::TypeId::of::<E>()
|
||||
== std::any::TypeId::of::<color_eyre::eyre::Error>())
|
||||
.then(|| eyre!("{ctx}: {e:?}"));
|
||||
let source = color_eyre::eyre::Error::from(e);
|
||||
let with_ctx = format!("{ctx}: {source}");
|
||||
let source = source.wrap_err(with_ctx);
|
||||
|
||||
@@ -2,9 +2,9 @@ use std::convert::Infallible;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use imbl_value::InternedString;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
use imbl_value::InternedString;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, TS)]
|
||||
#[ts(type = "string")]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use imbl_value::InternedString;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use ts_rs::TS;
|
||||
use imbl_value::InternedString;
|
||||
|
||||
use crate::{Id, InvalidId};
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::str::FromStr;
|
||||
|
||||
use imbl_value::InternedString;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use imbl_value::InternedString;
|
||||
|
||||
mod action;
|
||||
mod gateway;
|
||||
|
||||
@@ -2,9 +2,9 @@ use std::borrow::Borrow;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use imbl_value::InternedString;
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
use ts_rs::TS;
|
||||
use imbl_value::InternedString;
|
||||
|
||||
use crate::{Id, InvalidId, SYSTEM_ID};
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ use std::convert::Infallible;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use imbl_value::InternedString;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
use imbl_value::InternedString;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, TS)]
|
||||
#[ts(type = "string")]
|
||||
|
||||
@@ -5,8 +5,8 @@ use rpc_toolkit::clap::builder::ValueParserFactory;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::util::FromStrParser;
|
||||
use crate::Id;
|
||||
use crate::util::FromStrParser;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, TS)]
|
||||
#[ts(export, type = "string")]
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::db::model::Database;
|
||||
use crate::db::model::public::ServerStatus;
|
||||
use crate::developer::OS_DEVELOPER_KEY_PATH;
|
||||
use crate::hostname::Hostname;
|
||||
use crate::middleware::auth::AuthContext;
|
||||
use crate::middleware::auth::local::LocalAuthContext;
|
||||
use crate::net::gateway::UpgradableListener;
|
||||
use crate::net::net_controller::{NetController, NetService};
|
||||
use crate::net::socks::DEFAULT_SOCKS_LISTEN;
|
||||
|
||||
@@ -10,7 +10,6 @@ use exver::VersionRange;
|
||||
use futures::StreamExt;
|
||||
use imbl_value::{InternedString, json};
|
||||
use itertools::Itertools;
|
||||
use crate::util::{FromStrParser, VersionString};
|
||||
use reqwest::Url;
|
||||
use reqwest::header::{CONTENT_LENGTH, HeaderMap};
|
||||
use rpc_toolkit::HandlerArgs;
|
||||
@@ -31,10 +30,10 @@ use crate::rpc_continuations::{Guid, RpcContinuation};
|
||||
use crate::s9pk::manifest::PackageId;
|
||||
use crate::s9pk::v2::SIG_CONTEXT;
|
||||
use crate::upload::upload;
|
||||
use crate::util::Never;
|
||||
use crate::util::io::open_file;
|
||||
use crate::util::net::WebSocketExt;
|
||||
use crate::util::tui::choose;
|
||||
use crate::util::{FromStrParser, Never, VersionString};
|
||||
|
||||
pub const PKG_ARCHIVE_DIR: &str = "package-data/archive";
|
||||
pub const PKG_PUBLIC_DIR: &str = "package-data/public";
|
||||
|
||||
@@ -43,8 +43,8 @@ pub mod diagnostic;
|
||||
pub mod disk;
|
||||
pub mod error;
|
||||
pub mod firmware;
|
||||
pub mod id;
|
||||
pub mod hostname;
|
||||
pub mod id;
|
||||
pub mod init;
|
||||
pub mod install;
|
||||
pub mod logs;
|
||||
|
||||
@@ -13,8 +13,6 @@ use color_eyre::eyre::eyre;
|
||||
use futures::stream::BoxStream;
|
||||
use futures::{Future, FutureExt, Stream, StreamExt, TryStreamExt};
|
||||
use itertools::Itertools;
|
||||
use crate::util::FromStrParser;
|
||||
use crate::PackageId;
|
||||
use rpc_toolkit::yajrc::RpcError;
|
||||
use rpc_toolkit::{
|
||||
CallRemote, Context, Empty, HandlerArgs, HandlerExt, HandlerFor, ParentHandler, from_fn_async,
|
||||
@@ -27,13 +25,14 @@ use tokio_stream::wrappers::LinesStream;
|
||||
use tokio_tungstenite::tungstenite::Message;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::PackageId;
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::error::ResultExt;
|
||||
use crate::prelude::*;
|
||||
use crate::rpc_continuations::{Guid, RpcContinuation, RpcContinuations};
|
||||
use crate::util::Invoke;
|
||||
use crate::util::net::WebSocketExt;
|
||||
use crate::util::serde::Reversible;
|
||||
use crate::util::{FromStrParser, Invoke};
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub struct LogStream {
|
||||
|
||||
@@ -7,8 +7,6 @@ use std::time::Duration;
|
||||
use clap::builder::ValueParserFactory;
|
||||
use futures::StreamExt;
|
||||
use imbl_value::InternedString;
|
||||
use crate::util::FromStrParser;
|
||||
use crate::{InvalidId, PackageId};
|
||||
use rpc_toolkit::yajrc::RpcError;
|
||||
use rpc_toolkit::{RpcRequest, RpcResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -31,7 +29,8 @@ use crate::rpc_continuations::{Guid, RpcContinuation};
|
||||
use crate::service::ServiceStats;
|
||||
use crate::util::io::open_file;
|
||||
use crate::util::rpc_client::UnixRpcClient;
|
||||
use crate::util::{Invoke, new_guid};
|
||||
use crate::util::{FromStrParser, Invoke, new_guid};
|
||||
use crate::{InvalidId, PackageId};
|
||||
|
||||
const LXC_CONTAINER_DIR: &str = "/var/lib/lxc";
|
||||
const RPC_DIR: &str = "media/startos/rpc"; // must not be absolute path
|
||||
|
||||
101
core/startos/src/middleware/auth/local.rs
Normal file
101
core/startos/src/middleware/auth/local.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use base64::Engine;
|
||||
use basic_cookies::Cookie;
|
||||
use http::HeaderValue;
|
||||
use http::header::COOKIE;
|
||||
use rand::random;
|
||||
use rpc_toolkit::yajrc::{RpcError, RpcResponse};
|
||||
use rpc_toolkit::{Context, Empty, Middleware};
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::context::RpcContext;
|
||||
use crate::prelude::*;
|
||||
use crate::util::Invoke;
|
||||
use crate::util::io::{create_file_mod, read_file_to_string};
|
||||
use crate::util::serde::BASE64;
|
||||
|
||||
pub trait LocalAuthContext: Context {
|
||||
const LOCAL_AUTH_COOKIE_PATH: &str;
|
||||
const LOCAL_AUTH_COOKIE_OWNERSHIP: &str;
|
||||
fn init_auth_cookie() -> impl Future<Output = Result<(), Error>> + Send {
|
||||
async {
|
||||
let mut file = create_file_mod(Self::LOCAL_AUTH_COOKIE_PATH, 0o640).await?;
|
||||
file.write_all(BASE64.encode(random::<[u8; 32]>()).as_bytes())
|
||||
.await?;
|
||||
file.sync_all().await?;
|
||||
drop(file);
|
||||
Command::new("chown")
|
||||
.arg(Self::LOCAL_AUTH_COOKIE_OWNERSHIP)
|
||||
.arg(Self::LOCAL_AUTH_COOKIE_PATH)
|
||||
.invoke(crate::ErrorKind::Filesystem)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalAuthContext for RpcContext {
|
||||
const LOCAL_AUTH_COOKIE_PATH: &str = "/run/startos/rpc.authcookie";
|
||||
const LOCAL_AUTH_COOKIE_OWNERSHIP: &str = "root:startos";
|
||||
}
|
||||
|
||||
fn unauthorized() -> Error {
|
||||
Error::new(eyre!("UNAUTHORIZED"), crate::ErrorKind::Authorization)
|
||||
}
|
||||
|
||||
async fn check_from_header<C: LocalAuthContext>(header: Option<&HeaderValue>) -> Result<(), Error> {
|
||||
if let Some(cookie_header) = header {
|
||||
let cookies = Cookie::parse(
|
||||
cookie_header
|
||||
.to_str()
|
||||
.with_kind(crate::ErrorKind::Authorization)?,
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Authorization)?;
|
||||
if let Some(cookie) = cookies.iter().find(|c| c.get_name() == "local") {
|
||||
return check_cookie::<C>(cookie).await;
|
||||
}
|
||||
}
|
||||
Err(unauthorized())
|
||||
}
|
||||
|
||||
async fn check_cookie<C: LocalAuthContext>(local: &Cookie<'_>) -> Result<(), Error> {
|
||||
if let Ok(token) = read_file_to_string(C::LOCAL_AUTH_COOKIE_PATH).await {
|
||||
if local.get_value() == &*token {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Err(unauthorized())
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LocalAuth {
|
||||
cookie: Option<HeaderValue>,
|
||||
}
|
||||
impl LocalAuth {
|
||||
pub fn new() -> Self {
|
||||
Self { cookie: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: LocalAuthContext> Middleware<C> for LocalAuth {
|
||||
type Metadata = Empty;
|
||||
async fn process_http_request(
|
||||
&mut self,
|
||||
_: &C,
|
||||
request: &mut axum::extract::Request,
|
||||
) -> Result<(), axum::response::Response> {
|
||||
self.cookie = request.headers().get(COOKIE).cloned();
|
||||
Ok(())
|
||||
}
|
||||
async fn process_rpc_request(
|
||||
&mut self,
|
||||
_: &C,
|
||||
_: Self::Metadata,
|
||||
_: &mut rpc_toolkit::RpcRequest,
|
||||
) -> Result<(), rpc_toolkit::RpcResponse> {
|
||||
check_from_header::<C>(self.cookie.as_ref())
|
||||
.await
|
||||
.map_err(|e| RpcResponse::from(RpcError::from(e)))
|
||||
}
|
||||
}
|
||||
113
core/startos/src/middleware/auth/mod.rs
Normal file
113
core/startos/src/middleware/auth/mod.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
use axum::extract::Request;
|
||||
use axum::response::Response;
|
||||
use rpc_toolkit::{Context, DynMiddleware, Middleware, RpcRequest, RpcResponse};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::context::RpcContext;
|
||||
use crate::db::model::Database;
|
||||
use crate::middleware::auth::local::{LocalAuth, LocalAuthContext};
|
||||
use crate::middleware::auth::session::{SessionAuth, SessionAuthContext};
|
||||
use crate::middleware::auth::signature::{SignatureAuth, SignatureAuthContext};
|
||||
use crate::prelude::*;
|
||||
use crate::util::serde::const_true;
|
||||
|
||||
pub mod local;
|
||||
pub mod session;
|
||||
pub mod signature;
|
||||
|
||||
pub trait DbContext: Context {
|
||||
type Database: HasModel<Model = Model<Self::Database>> + Send + Sync;
|
||||
fn db(&self) -> &TypedPatchDb<Self::Database>;
|
||||
}
|
||||
impl DbContext for RpcContext {
|
||||
type Database = Database;
|
||||
fn db(&self) -> &TypedPatchDb<Self::Database> {
|
||||
&self.db
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Metadata {
|
||||
#[serde(default = "const_true")]
|
||||
authenticated: bool,
|
||||
}
|
||||
|
||||
pub struct Auth<C: Context>(Vec<DynMiddleware<C>>);
|
||||
impl<C: Context> Clone for Auth<C> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
impl<C: Context> Auth<C> {
|
||||
pub fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
}
|
||||
impl<C: LocalAuthContext> Auth<C> {
|
||||
pub fn with_local_auth(mut self) -> Self {
|
||||
self.0.push(DynMiddleware::new(LocalAuth::new()));
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<C: SignatureAuthContext> Auth<C> {
|
||||
pub fn with_signature_auth(mut self) -> Self {
|
||||
self.0.push(DynMiddleware::new(SignatureAuth::new()));
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<C: SessionAuthContext> Auth<C> {
|
||||
pub fn with_session_auth(mut self) -> Self {
|
||||
self.0.push(DynMiddleware::new(SessionAuth::new()));
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<C: Context> Middleware<C> for Auth<C> {
|
||||
type Metadata = Value;
|
||||
async fn process_http_request(
|
||||
&mut self,
|
||||
context: &C,
|
||||
request: &mut Request,
|
||||
) -> Result<(), Response> {
|
||||
for middleware in self.0.iter_mut() {
|
||||
middleware.process_http_request(context, request).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
async fn process_rpc_request(
|
||||
&mut self,
|
||||
context: &C,
|
||||
metadata: Self::Metadata,
|
||||
request: &mut RpcRequest,
|
||||
) -> Result<(), RpcResponse> {
|
||||
let m: Metadata =
|
||||
from_value(metadata.clone()).map_err(|e| RpcResponse::from_result(Err(e)))?;
|
||||
let mut err = None;
|
||||
for middleware in self.0.iter_mut() {
|
||||
if let Err(e) = middleware
|
||||
.process_rpc_request(context, metadata.clone(), request)
|
||||
.await
|
||||
{
|
||||
if m.authenticated {
|
||||
err = Some(e);
|
||||
}
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
if let Some(e) = err {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
async fn process_rpc_response(&mut self, context: &C, response: &mut RpcResponse) {
|
||||
for middleware in self.0.iter_mut() {
|
||||
middleware.process_rpc_response(context, response).await;
|
||||
}
|
||||
}
|
||||
async fn process_http_response(&mut self, context: &C, response: &mut Response) {
|
||||
for middleware in self.0.iter_mut() {
|
||||
middleware.process_http_response(context, response).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,23 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::BTreeSet;
|
||||
use std::future::Future;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use axum::extract::Request;
|
||||
use axum::response::Response;
|
||||
use base64::Engine;
|
||||
use basic_cookies::Cookie;
|
||||
use chrono::Utc;
|
||||
use color_eyre::eyre::eyre;
|
||||
use digest::Digest;
|
||||
use http::HeaderValue;
|
||||
use http::header::{COOKIE, USER_AGENT};
|
||||
use imbl_value::{InternedString, json};
|
||||
use rand::random;
|
||||
use rpc_toolkit::yajrc::INTERNAL_ERROR;
|
||||
use rpc_toolkit::{Middleware, RpcRequest, RpcResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Sha256;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::Mutex;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::auth::{Sessions, check_password, write_shadow};
|
||||
use crate::context::RpcContext;
|
||||
use crate::middleware::signature::{SignatureAuth, SignatureAuthContext};
|
||||
use crate::middleware::auth::DbContext;
|
||||
use crate::prelude::*;
|
||||
use crate::rpc_continuations::OpenAuthedContinuations;
|
||||
use crate::util::Invoke;
|
||||
@@ -34,24 +25,7 @@ use crate::util::io::{create_file_mod, read_file_to_string};
|
||||
use crate::util::serde::{BASE64, const_true};
|
||||
use crate::util::sync::SyncMutex;
|
||||
|
||||
pub trait AuthContext: SignatureAuthContext {
|
||||
const LOCAL_AUTH_COOKIE_PATH: &str;
|
||||
const LOCAL_AUTH_COOKIE_OWNERSHIP: &str;
|
||||
fn init_auth_cookie() -> impl Future<Output = Result<(), Error>> + Send {
|
||||
async {
|
||||
let mut file = create_file_mod(Self::LOCAL_AUTH_COOKIE_PATH, 0o640).await?;
|
||||
file.write_all(BASE64.encode(random::<[u8; 32]>()).as_bytes())
|
||||
.await?;
|
||||
file.sync_all().await?;
|
||||
drop(file);
|
||||
Command::new("chown")
|
||||
.arg(Self::LOCAL_AUTH_COOKIE_OWNERSHIP)
|
||||
.arg(Self::LOCAL_AUTH_COOKIE_PATH)
|
||||
.invoke(crate::ErrorKind::Filesystem)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
pub trait SessionAuthContext: DbContext {
|
||||
fn ephemeral_sessions(&self) -> &SyncMutex<Sessions>;
|
||||
fn open_authed_continuations(&self) -> &OpenAuthedContinuations<Option<InternedString>>;
|
||||
fn access_sessions(db: &mut Model<Self::Database>) -> &mut Model<Sessions>;
|
||||
@@ -62,9 +36,7 @@ pub trait AuthContext: SignatureAuthContext {
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthContext for RpcContext {
|
||||
const LOCAL_AUTH_COOKIE_PATH: &str = "/run/startos/rpc.authcookie";
|
||||
const LOCAL_AUTH_COOKIE_OWNERSHIP: &str = "root:startos";
|
||||
impl SessionAuthContext for RpcContext {
|
||||
fn ephemeral_sessions(&self) -> &SyncMutex<Sessions> {
|
||||
&self.ephemeral_sessions
|
||||
}
|
||||
@@ -103,7 +75,7 @@ pub trait AsLogoutSessionId {
|
||||
pub struct HasLoggedOutSessions(());
|
||||
|
||||
impl HasLoggedOutSessions {
|
||||
pub async fn new<C: AuthContext>(
|
||||
pub async fn new<C: SessionAuthContext>(
|
||||
sessions: impl IntoIterator<Item = impl AsLogoutSessionId>,
|
||||
ctx: &C,
|
||||
) -> Result<Self, Error> {
|
||||
@@ -134,90 +106,6 @@ impl HasLoggedOutSessions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Used when we need to know that we have logged in with a valid user
|
||||
#[derive(Clone)]
|
||||
pub struct HasValidSession(SessionType);
|
||||
|
||||
#[derive(Clone)]
|
||||
enum SessionType {
|
||||
Local,
|
||||
Session(HashSessionToken),
|
||||
}
|
||||
|
||||
impl HasValidSession {
|
||||
pub async fn from_header<C: AuthContext>(
|
||||
header: Option<&HeaderValue>,
|
||||
ctx: &C,
|
||||
) -> Result<Self, Error> {
|
||||
if let Some(cookie_header) = header {
|
||||
let cookies = Cookie::parse(
|
||||
cookie_header
|
||||
.to_str()
|
||||
.with_kind(crate::ErrorKind::Authorization)?,
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Authorization)?;
|
||||
if let Some(cookie) = cookies.iter().find(|c| c.get_name() == "local") {
|
||||
if let Ok(s) = Self::from_local::<C>(cookie).await {
|
||||
return Ok(s);
|
||||
}
|
||||
}
|
||||
if let Some(cookie) = cookies.iter().find(|c| c.get_name() == "session") {
|
||||
if let Ok(s) = Self::from_session(HashSessionToken::from_cookie(cookie), ctx).await
|
||||
{
|
||||
return Ok(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Error::new(
|
||||
eyre!("UNAUTHORIZED"),
|
||||
crate::ErrorKind::Authorization,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn from_session<C: AuthContext>(
|
||||
session_token: HashSessionToken,
|
||||
ctx: &C,
|
||||
) -> Result<Self, Error> {
|
||||
let session_hash = session_token.hashed();
|
||||
if !ctx.ephemeral_sessions().mutate(|s| {
|
||||
if let Some(session) = s.0.get_mut(session_hash) {
|
||||
session.last_active = Utc::now();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
ctx.db()
|
||||
.mutate(|db| {
|
||||
C::access_sessions(db)
|
||||
.as_idx_mut(session_hash)
|
||||
.ok_or_else(|| {
|
||||
Error::new(eyre!("UNAUTHORIZED"), crate::ErrorKind::Authorization)
|
||||
})?
|
||||
.mutate(|s| {
|
||||
s.last_active = Utc::now();
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
}
|
||||
Ok(Self(SessionType::Session(session_token)))
|
||||
}
|
||||
|
||||
pub async fn from_local<C: AuthContext>(local: &Cookie<'_>) -> Result<Self, Error> {
|
||||
let token = read_file_to_string(C::LOCAL_AUTH_COOKIE_PATH).await?;
|
||||
if local.get_value() == &*token {
|
||||
Ok(Self(SessionType::Local))
|
||||
} else {
|
||||
Err(Error::new(
|
||||
eyre!("UNAUTHORIZED"),
|
||||
crate::ErrorKind::Authorization,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// When we have a need to create a new session,
|
||||
/// Or when we are using internal valid authenticated service.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -312,51 +200,97 @@ impl Borrow<str> for HashSessionToken {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ValidSessionToken(pub HashSessionToken);
|
||||
impl ValidSessionToken {
|
||||
pub async fn from_header<C: SessionAuthContext>(
|
||||
header: Option<&HeaderValue>,
|
||||
ctx: &C,
|
||||
) -> Result<Self, Error> {
|
||||
if let Some(cookie_header) = header {
|
||||
let cookies = Cookie::parse(
|
||||
cookie_header
|
||||
.to_str()
|
||||
.with_kind(crate::ErrorKind::Authorization)?,
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Authorization)?;
|
||||
if let Some(cookie) = cookies.iter().find(|c| c.get_name() == "session") {
|
||||
if let Ok(s) = Self::from_session(HashSessionToken::from_cookie(cookie), ctx).await
|
||||
{
|
||||
return Ok(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Error::new(
|
||||
eyre!("UNAUTHORIZED"),
|
||||
crate::ErrorKind::Authorization,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn from_session<C: SessionAuthContext>(
|
||||
session_token: HashSessionToken,
|
||||
ctx: &C,
|
||||
) -> Result<Self, Error> {
|
||||
let session_hash = session_token.hashed();
|
||||
if !ctx.ephemeral_sessions().mutate(|s| {
|
||||
if let Some(session) = s.0.get_mut(session_hash) {
|
||||
session.last_active = Utc::now();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
ctx.db()
|
||||
.mutate(|db| {
|
||||
C::access_sessions(db)
|
||||
.as_idx_mut(session_hash)
|
||||
.ok_or_else(|| {
|
||||
Error::new(eyre!("UNAUTHORIZED"), crate::ErrorKind::Authorization)
|
||||
})?
|
||||
.mutate(|s| {
|
||||
s.last_active = Utc::now();
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
}
|
||||
Ok(Self(session_token))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Metadata {
|
||||
#[serde(default = "const_true")]
|
||||
authenticated: bool,
|
||||
#[serde(default)]
|
||||
login: bool,
|
||||
#[serde(default)]
|
||||
get_session: bool,
|
||||
#[serde(default)]
|
||||
get_signer: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Auth {
|
||||
rate_limiter: Arc<Mutex<(usize, Instant)>>,
|
||||
cookie: Option<HeaderValue>,
|
||||
pub struct SessionAuth {
|
||||
rate_limiter: Arc<SyncMutex<(usize, Instant)>>,
|
||||
is_login: bool,
|
||||
cookie: Option<HeaderValue>,
|
||||
set_cookie: Option<HeaderValue>,
|
||||
user_agent: Option<HeaderValue>,
|
||||
signature_auth: SignatureAuth,
|
||||
}
|
||||
impl Auth {
|
||||
impl SessionAuth {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
rate_limiter: Arc::new(Mutex::new((0, Instant::now()))),
|
||||
cookie: None,
|
||||
rate_limiter: Arc::new(SyncMutex::new((0, Instant::now()))),
|
||||
is_login: false,
|
||||
cookie: None,
|
||||
set_cookie: None,
|
||||
user_agent: None,
|
||||
signature_auth: SignatureAuth::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<C: AuthContext> Middleware<C> for Auth {
|
||||
|
||||
impl<C: SessionAuthContext> Middleware<C> for SessionAuth {
|
||||
type Metadata = Metadata;
|
||||
async fn process_http_request(
|
||||
&mut self,
|
||||
context: &C,
|
||||
request: &mut Request,
|
||||
) -> Result<(), Response> {
|
||||
self.cookie = request.headers_mut().remove(COOKIE);
|
||||
self.user_agent = request.headers_mut().remove(USER_AGENT);
|
||||
self.signature_auth
|
||||
.process_http_request(context, request)
|
||||
.await?;
|
||||
async fn process_http_request(&mut self, _: &C, request: &mut Request) -> Result<(), Response> {
|
||||
self.cookie = request.headers().get(COOKIE).cloned();
|
||||
self.user_agent = request.headers().get(USER_AGENT).cloned();
|
||||
Ok(())
|
||||
}
|
||||
async fn process_rpc_request(
|
||||
@@ -368,56 +302,37 @@ impl<C: AuthContext> Middleware<C> for Auth {
|
||||
async {
|
||||
if metadata.login {
|
||||
self.is_login = true;
|
||||
let guard = self.rate_limiter.lock().await;
|
||||
if guard.1.elapsed() < Duration::from_secs(20) && guard.0 >= 3 {
|
||||
return Err(Error::new(
|
||||
eyre!("Please limit login attempts to 3 per 20 seconds."),
|
||||
crate::ErrorKind::RateLimited,
|
||||
));
|
||||
}
|
||||
self.rate_limiter.mutate(|(count, time)| {
|
||||
if time.elapsed() < Duration::from_secs(20) && *count >= 3 {
|
||||
Err(Error::new(
|
||||
eyre!("Please limit login attempts to 3 per 20 seconds."),
|
||||
crate::ErrorKind::RateLimited,
|
||||
))
|
||||
} else {
|
||||
*count += 1;
|
||||
*time = Instant::now();
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
if let Some(user_agent) = self.user_agent.as_ref().and_then(|h| h.to_str().ok()) {
|
||||
request.params["__Auth_userAgent"] =
|
||||
Value::String(Arc::new(user_agent.to_owned()))
|
||||
// TODO: will this panic?
|
||||
}
|
||||
} else if metadata.authenticated {
|
||||
if self
|
||||
.signature_auth
|
||||
.process_rpc_request(
|
||||
context,
|
||||
from_value(json!({
|
||||
"get_signer": metadata.get_signer
|
||||
}))?,
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
match HasValidSession::from_header(self.cookie.as_ref(), context).await? {
|
||||
HasValidSession(SessionType::Session(s)) if metadata.get_session => {
|
||||
request.params["__Auth_session"] =
|
||||
Value::String(Arc::new(s.hashed().deref().to_owned()));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
} else {
|
||||
let ValidSessionToken(s) =
|
||||
ValidSessionToken::from_header(self.cookie.as_ref(), context).await?;
|
||||
if metadata.get_session {
|
||||
request.params["__Auth_session"] =
|
||||
Value::String(Arc::new(s.hashed().deref().to_owned()));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
Ok::<_, Error>(())
|
||||
}
|
||||
.await
|
||||
.map_err(|e| RpcResponse::from_result(Err(e)))
|
||||
}
|
||||
async fn process_rpc_response(&mut self, _: &C, response: &mut RpcResponse) {
|
||||
if self.is_login {
|
||||
let mut guard = self.rate_limiter.lock().await;
|
||||
if guard.1.elapsed() < Duration::from_secs(20) {
|
||||
if response.result.is_err() {
|
||||
guard.0 += 1;
|
||||
}
|
||||
} else {
|
||||
guard.0 = 0;
|
||||
}
|
||||
guard.1 = Instant::now();
|
||||
if response.result.is_ok() {
|
||||
let res = std::mem::replace(&mut response.result, Err(INTERNAL_ERROR));
|
||||
response.result = async {
|
||||
@@ -8,14 +8,14 @@ use axum::extract::Request;
|
||||
use http::{HeaderMap, HeaderValue};
|
||||
use reqwest::Client;
|
||||
use rpc_toolkit::yajrc::RpcError;
|
||||
use rpc_toolkit::{Context, Middleware, RpcRequest, RpcResponse};
|
||||
use rpc_toolkit::{Middleware, RpcRequest, RpcResponse};
|
||||
use serde::Deserialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use tokio::sync::Mutex;
|
||||
use url::Url;
|
||||
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::Database;
|
||||
use crate::middleware::auth::DbContext;
|
||||
use crate::prelude::*;
|
||||
use crate::sign::commitment::Commitment;
|
||||
use crate::sign::commitment::request::RequestCommitment;
|
||||
@@ -25,11 +25,9 @@ use crate::util::serde::Base64;
|
||||
|
||||
pub const AUTH_SIG_HEADER: &str = "X-StartOS-Auth-Sig";
|
||||
|
||||
pub trait SignatureAuthContext: Context {
|
||||
type Database: HasModel<Model = Model<Self::Database>> + Send + Sync;
|
||||
pub trait SignatureAuthContext: DbContext {
|
||||
type AdditionalMetadata: DeserializeOwned + Send;
|
||||
type CheckPubkeyRes: Send;
|
||||
fn db(&self) -> &TypedPatchDb<Self::Database>;
|
||||
fn sig_context(
|
||||
&self,
|
||||
) -> impl Future<Output = impl IntoIterator<Item = Result<impl AsRef<str> + Send, Error>> + Send>
|
||||
@@ -47,12 +45,8 @@ pub trait SignatureAuthContext: Context {
|
||||
}
|
||||
|
||||
impl SignatureAuthContext for RpcContext {
|
||||
type Database = Database;
|
||||
type AdditionalMetadata = ();
|
||||
type CheckPubkeyRes = ();
|
||||
fn db(&self) -> &TypedPatchDb<Self::Database> {
|
||||
&self.db
|
||||
}
|
||||
async fn sig_context(
|
||||
&self,
|
||||
) -> impl IntoIterator<Item = Result<impl AsRef<str> + Send, Error>> + Send {
|
||||
@@ -96,7 +90,7 @@ impl SignatureAuthContext for RpcContext {
|
||||
}
|
||||
|
||||
Err(Error::new(
|
||||
eyre!("Developer Key is not authorized"),
|
||||
eyre!("Key is not authorized"),
|
||||
ErrorKind::IncorrectPassword,
|
||||
))
|
||||
}
|
||||
@@ -2,4 +2,3 @@ pub mod auth;
|
||||
pub mod connect_info;
|
||||
pub mod cors;
|
||||
pub mod db;
|
||||
pub mod signature;
|
||||
|
||||
@@ -9,8 +9,6 @@ use clap::builder::ValueParserFactory;
|
||||
use futures::StreamExt;
|
||||
use imbl_value::InternedString;
|
||||
use itertools::Itertools;
|
||||
use crate::error::ErrorData;
|
||||
use crate::util::FromStrParser;
|
||||
use openssl::pkey::{PKey, Private};
|
||||
use openssl::x509::X509;
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
||||
@@ -27,10 +25,12 @@ use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::Database;
|
||||
use crate::db::model::public::AcmeSettings;
|
||||
use crate::db::{DbAccess, DbAccessByKey, DbAccessMut};
|
||||
use crate::error::ErrorData;
|
||||
use crate::net::ssl::should_use_cert;
|
||||
use crate::net::tls::{SingleCertResolver, TlsHandler};
|
||||
use crate::net::web_server::Accept;
|
||||
use crate::prelude::*;
|
||||
use crate::util::FromStrParser;
|
||||
use crate::util::serde::{Pem, Pkcs8Doc};
|
||||
use crate::util::sync::{SyncMutex, Watch};
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ use clap::Parser;
|
||||
use color_eyre::eyre::eyre;
|
||||
use futures::future::BoxFuture;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use hickory_client::client::Client;
|
||||
use hickory_client::proto::DnsHandle;
|
||||
use hickory_client::proto::runtime::TokioRuntimeProvider;
|
||||
@@ -23,7 +22,6 @@ use hickory_server::proto::rr::{Name, Record, RecordType};
|
||||
use hickory_server::server::{Request, RequestHandler, ResponseHandler, ResponseInfo};
|
||||
use imbl::OrdMap;
|
||||
use imbl_value::InternedString;
|
||||
use crate::{GatewayId, OptionExt, PackageId};
|
||||
use patch_db::json_ptr::JsonPointer;
|
||||
use rpc_toolkit::{
|
||||
Context, HandlerArgs, HandlerExt, ParentHandler, from_fn_async, from_fn_blocking,
|
||||
@@ -38,9 +36,11 @@ use crate::db::model::public::NetworkInterfaceInfo;
|
||||
use crate::net::gateway::NetworkInterfaceWatcher;
|
||||
use crate::prelude::*;
|
||||
use crate::util::actor::background::BackgroundJobQueue;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use crate::util::io::file_string_stream;
|
||||
use crate::util::serde::{HandlerExtSerde, display_serializable};
|
||||
use crate::util::sync::{SyncRwLock, Watch};
|
||||
use crate::{GatewayId, OptionExt, PackageId};
|
||||
|
||||
pub fn dns_api<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
|
||||
@@ -4,21 +4,21 @@ use std::sync::{Arc, Weak};
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::channel::oneshot;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use id_pool::IdPool;
|
||||
use iddqd::{IdOrdItem, IdOrdMap};
|
||||
use imbl::OrdMap;
|
||||
use crate::GatewayId;
|
||||
use rpc_toolkit::{Context, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::GatewayId;
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::public::NetworkInterfaceInfo;
|
||||
use crate::net::gateway::{DynInterfaceFilter, InterfaceFilter};
|
||||
use crate::prelude::*;
|
||||
use crate::util::Invoke;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use crate::util::serde::{HandlerExtSerde, display_serializable};
|
||||
use crate::util::sync::Watch;
|
||||
|
||||
@@ -180,11 +180,51 @@ pub struct PortForwardController {
|
||||
_thread: NonDetachingJoinHandle<()>,
|
||||
}
|
||||
|
||||
pub async fn add_iptables_rule(nat: bool, undo: bool, args: &[&str]) -> Result<(), Error> {
|
||||
let mut cmd = Command::new("iptables");
|
||||
if nat {
|
||||
cmd.arg("-t").arg("nat");
|
||||
}
|
||||
if undo != !cmd.arg("-C").args(args).status().await?.success() {
|
||||
let mut cmd = Command::new("iptables");
|
||||
if nat {
|
||||
cmd.arg("-t").arg("nat");
|
||||
}
|
||||
if undo {
|
||||
cmd.arg("-D");
|
||||
} else {
|
||||
cmd.arg("-A");
|
||||
}
|
||||
cmd.args(args).invoke(ErrorKind::Network).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl PortForwardController {
|
||||
pub fn new() -> Self {
|
||||
let (req_send, mut req_recv) = mpsc::unbounded_channel::<PortForwardCommand>();
|
||||
let thread = NonDetachingJoinHandle::from(tokio::spawn(async move {
|
||||
while let Err(e) = async {
|
||||
Command::new("iptables")
|
||||
.arg("-P")
|
||||
.arg("FORWARD")
|
||||
.arg("DROP")
|
||||
.invoke(ErrorKind::Network)
|
||||
.await?;
|
||||
add_iptables_rule(
|
||||
false,
|
||||
false,
|
||||
&[
|
||||
"FORWARD",
|
||||
"-m",
|
||||
"state",
|
||||
"--state",
|
||||
"ESTABLISHED,RELATED",
|
||||
"-j",
|
||||
"ACCEPT",
|
||||
],
|
||||
)
|
||||
.await?;
|
||||
Command::new("sysctl")
|
||||
.arg("-w")
|
||||
.arg("net.ipv4.ip_forward=1")
|
||||
|
||||
@@ -10,12 +10,10 @@ use std::time::Duration;
|
||||
use clap::Parser;
|
||||
use futures::future::Either;
|
||||
use futures::{FutureExt, Stream, StreamExt, TryStreamExt};
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use imbl::{OrdMap, OrdSet};
|
||||
use imbl_value::InternedString;
|
||||
use ipnet::IpNet;
|
||||
use itertools::Itertools;
|
||||
use crate::GatewayId;
|
||||
use nix::net::if_::if_nametoindex;
|
||||
use patch_db::json_ptr::JsonPointer;
|
||||
use rpc_toolkit::{Context, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
|
||||
@@ -32,6 +30,7 @@ use zbus::zvariant::{
|
||||
};
|
||||
use zbus::{Connection, proxy};
|
||||
|
||||
use crate::GatewayId;
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::Database;
|
||||
use crate::db::model::public::{IpInfo, NetworkInterfaceInfo, NetworkInterfaceType};
|
||||
@@ -42,7 +41,7 @@ use crate::net::web_server::{Accept, AcceptStream, Acceptor, MetadataVisitor};
|
||||
use crate::prelude::*;
|
||||
use crate::util::Invoke;
|
||||
use crate::util::collections::OrdMapIterMut;
|
||||
use crate::util::future::Until;
|
||||
use crate::util::future::{NonDetachingJoinHandle, Until};
|
||||
use crate::util::io::open_file;
|
||||
use crate::util::serde::{HandlerExtSerde, display_serializable};
|
||||
use crate::util::sync::{SyncMutex, Watch};
|
||||
@@ -65,13 +64,15 @@ pub fn gateway_api<C: Context>() -> ParentHandler<C> {
|
||||
for (iface, info) in res {
|
||||
table.add_row(row![
|
||||
iface,
|
||||
info.ip_info.as_ref()
|
||||
info.ip_info
|
||||
.as_ref()
|
||||
.and_then(|ip_info| ip_info.device_type)
|
||||
.map_or_else(|| "UNKNOWN".to_owned(), |ty| format!("{ty:?}")),
|
||||
info.public(),
|
||||
info.ip_info.as_ref().map_or_else(
|
||||
|| "<DISCONNECTED>".to_owned(),
|
||||
|ip_info| ip_info.subnets
|
||||
|ip_info| ip_info
|
||||
.subnets
|
||||
.iter()
|
||||
.map(|ipnet| match ipnet.addr() {
|
||||
IpAddr::V4(ip) => format!("{ip}/{}", ipnet.prefix_len()),
|
||||
@@ -81,8 +82,10 @@ pub fn gateway_api<C: Context>() -> ParentHandler<C> {
|
||||
ipnet.prefix_len()
|
||||
),
|
||||
})
|
||||
.join(", ")),
|
||||
info.ip_info.as_ref()
|
||||
.join(", ")
|
||||
),
|
||||
info.ip_info
|
||||
.as_ref()
|
||||
.and_then(|ip_info| ip_info.wan_ip)
|
||||
.map_or_else(|| "N/A".to_owned(), |ip| ip.to_string())
|
||||
]);
|
||||
@@ -102,26 +105,34 @@ pub fn gateway_api<C: Context>() -> ParentHandler<C> {
|
||||
.no_display()
|
||||
.with_about("Indicate whether this gateway has inbound access from the WAN")
|
||||
.with_call_remote::<CliContext>(),
|
||||
).subcommand(
|
||||
)
|
||||
.subcommand(
|
||||
"unset-public",
|
||||
from_fn_async(unset_public)
|
||||
.with_metadata("sync_db", Value::Bool(true))
|
||||
.no_display()
|
||||
.with_about("Allow this gateway to infer whether it has inbound access from the WAN based on its IPv4 address")
|
||||
.with_about(concat!(
|
||||
"Allow this gateway to infer whether it has",
|
||||
" inbound access from the WAN based on its IPv4 address"
|
||||
))
|
||||
.with_call_remote::<CliContext>(),
|
||||
).subcommand("forget",
|
||||
)
|
||||
.subcommand(
|
||||
"forget",
|
||||
from_fn_async(forget_iface)
|
||||
.with_metadata("sync_db", Value::Bool(true))
|
||||
.no_display()
|
||||
.with_about("Forget a disconnected gateway")
|
||||
.with_call_remote::<CliContext>()
|
||||
).subcommand("set-name",
|
||||
from_fn_async(set_name)
|
||||
.with_metadata("sync_db", Value::Bool(true))
|
||||
.no_display()
|
||||
.with_about("Rename a gateway")
|
||||
.with_call_remote::<CliContext>()
|
||||
)
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
.subcommand(
|
||||
"set-name",
|
||||
from_fn_async(set_name)
|
||||
.with_metadata("sync_db", Value::Bool(true))
|
||||
.no_display()
|
||||
.with_about("Rename a gateway")
|
||||
.with_call_remote::<CliContext>(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn list_interfaces(
|
||||
@@ -266,6 +277,9 @@ trait Ip4Config {
|
||||
#[zbus(property)]
|
||||
fn address_data(&self) -> Result<Vec<AddressData>, Error>;
|
||||
|
||||
#[zbus(property)]
|
||||
fn route_data(&self) -> Result<Vec<RouteData>, Error>;
|
||||
|
||||
#[zbus(property)]
|
||||
fn gateway(&self) -> Result<String, Error>;
|
||||
|
||||
@@ -301,6 +315,14 @@ impl TryFrom<AddressData> for IpNet {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, DeserializeDict, ZValue, ZType)]
|
||||
#[zvariant(signature = "dict")]
|
||||
struct RouteData {
|
||||
dest: String,
|
||||
prefix: u32,
|
||||
table: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, DeserializeDict, ZValue, ZType)]
|
||||
#[zvariant(signature = "dict")]
|
||||
struct NameserverData {
|
||||
@@ -613,6 +635,7 @@ async fn watch_ip(
|
||||
Ip6ConfigProxy::new(&connection, ip6_config.clone()).await?;
|
||||
let mut until = Until::new()
|
||||
.with_stream(ip4_proxy.receive_address_data_changed().await.stub())
|
||||
.with_stream(ip4_proxy.receive_route_data_changed().await.stub())
|
||||
.with_stream(ip4_proxy.receive_gateway_changed().await.stub())
|
||||
.with_stream(
|
||||
ip4_proxy.receive_nameserver_data_changed().await.stub(),
|
||||
@@ -680,6 +703,22 @@ async fn watch_ip(
|
||||
.into_iter()
|
||||
.map(IpNet::try_from)
|
||||
.try_collect()?;
|
||||
let tables = ip4_proxy.route_data().await?.into_iter().filter_map(|d|d.table).collect::<Vec<_>>();
|
||||
if !tables.is_empty() {
|
||||
let rules = String::from_utf8(Command::new("ip").arg("rule").arg("list").invoke(ErrorKind::Network).await?)?;
|
||||
for table in tables {
|
||||
for subnet in subnets.iter().filter(|s| s.addr().is_ipv4()) {
|
||||
let subnet_string = subnet.trunc().to_string();
|
||||
let rule = ["from", &subnet_string, "lookup", &table.to_string()];
|
||||
if !rules.contains(&rule.join(" ")) {
|
||||
if rules.contains(&rule[..2].join(" ")) {
|
||||
Command::new("ip").arg("rule").arg("del").args(&rule[..2]).invoke(ErrorKind::Network).await?;
|
||||
}
|
||||
Command::new("ip").arg("rule").arg("add").args(rule).invoke(ErrorKind::Network).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let wan_ip = if !subnets.is_empty()
|
||||
&& !matches!(
|
||||
device_type,
|
||||
@@ -1405,12 +1444,8 @@ impl<B: Bind> ListenerMap<B> {
|
||||
&mut self,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> Poll<Result<(SocketAddr, <B::Accept as Accept>::Metadata, AcceptStream), Error>> {
|
||||
for (addr, listener) in self.listeners.iter_mut() {
|
||||
if let Poll::Ready((metadata, stream)) = listener.poll_accept(cx)? {
|
||||
return Poll::Ready(Ok((*addr, metadata, stream)));
|
||||
}
|
||||
}
|
||||
Poll::Pending
|
||||
let (metadata, stream) = ready!(self.listeners.poll_accept(cx)?);
|
||||
Poll::Ready(Ok((metadata.key, metadata.inner, stream)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ use std::net::Ipv4Addr;
|
||||
|
||||
use clap::Parser;
|
||||
use imbl_value::InternedString;
|
||||
use crate::GatewayId;
|
||||
use rpc_toolkit::{Context, Empty, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::GatewayId;
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::DatabaseModel;
|
||||
use crate::net::acme::AcmeProvider;
|
||||
|
||||
@@ -4,8 +4,6 @@ use std::str::FromStr;
|
||||
use clap::Parser;
|
||||
use clap::builder::ValueParserFactory;
|
||||
use imbl::OrdSet;
|
||||
use crate::util::FromStrParser;
|
||||
use crate::{GatewayId, HostId};
|
||||
use rpc_toolkit::{Context, Empty, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
@@ -17,7 +15,9 @@ use crate::net::gateway::InterfaceFilter;
|
||||
use crate::net::host::HostApiKind;
|
||||
use crate::net::vhost::AlpnInfo;
|
||||
use crate::prelude::*;
|
||||
use crate::util::FromStrParser;
|
||||
use crate::util::serde::{HandlerExtSerde, display_serializable};
|
||||
use crate::{GatewayId, HostId};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, TS)]
|
||||
#[ts(export)]
|
||||
|
||||
@@ -5,7 +5,6 @@ use std::panic::RefUnwindSafe;
|
||||
use clap::Parser;
|
||||
use imbl_value::InternedString;
|
||||
use itertools::Itertools;
|
||||
use crate::{HostId, PackageId};
|
||||
use rpc_toolkit::{Context, Empty, HandlerExt, OrEmpty, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
@@ -18,6 +17,7 @@ use crate::net::host::binding::{BindInfo, BindOptions, binding};
|
||||
use crate::net::service_interface::HostnameInfo;
|
||||
use crate::net::tor::OnionAddress;
|
||||
use crate::prelude::*;
|
||||
use crate::{HostId, PackageId};
|
||||
|
||||
pub mod address;
|
||||
pub mod binding;
|
||||
|
||||
@@ -6,19 +6,17 @@ use color_eyre::eyre::eyre;
|
||||
use imbl::{OrdMap, vector};
|
||||
use imbl_value::InternedString;
|
||||
use ipnet::IpNet;
|
||||
use crate::{GatewayId, HostId, OptionExt, PackageId};
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio_rustls::rustls::ClientConfig as TlsClientConfig;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::HOST_IP;
|
||||
use crate::db::model::Database;
|
||||
use crate::db::model::public::NetworkInterfaceType;
|
||||
use crate::error::ErrorCollection;
|
||||
use crate::hostname::Hostname;
|
||||
use crate::net::dns::DnsController;
|
||||
use crate::net::forward::{InterfacePortForwardController, START9_BRIDGE_IFACE};
|
||||
use crate::net::forward::{InterfacePortForwardController, START9_BRIDGE_IFACE, add_iptables_rule};
|
||||
use crate::net::gateway::{
|
||||
AndFilter, DynInterfaceFilter, IdFilter, InterfaceFilter, NetworkInterfaceController, OrFilter,
|
||||
PublicFilter, SecureFilter, TypeFilter,
|
||||
@@ -34,6 +32,7 @@ use crate::net::vhost::{AlpnInfo, DynVHostTarget, ProxyTarget, VHostController};
|
||||
use crate::prelude::*;
|
||||
use crate::service::effects::callbacks::ServiceCallbacks;
|
||||
use crate::util::serde::MaybeUtf8String;
|
||||
use crate::{GatewayId, HOST_IP, HostId, OptionExt, PackageId};
|
||||
|
||||
pub struct NetController {
|
||||
pub(crate) db: TypedPatchDb<Database>,
|
||||
@@ -70,6 +69,22 @@ impl NetController {
|
||||
.de()?
|
||||
.0],
|
||||
)?);
|
||||
add_iptables_rule(
|
||||
false,
|
||||
false,
|
||||
&[
|
||||
"FORWARD",
|
||||
"-i",
|
||||
START9_BRIDGE_IFACE,
|
||||
"-m",
|
||||
"state",
|
||||
"--state",
|
||||
"NEW",
|
||||
"-j",
|
||||
"ACCEPT",
|
||||
],
|
||||
)
|
||||
.await?;
|
||||
Ok(Self {
|
||||
db: db.clone(),
|
||||
tor,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
use imbl_value::InternedString;
|
||||
use crate::{GatewayId, HostId, ServiceInterfaceId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::{GatewayId, HostId, ServiceInterfaceId};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use socks5_impl::protocol::{Address, Reply};
|
||||
use socks5_impl::server::auth::NoAuth;
|
||||
use socks5_impl::server::{AuthAdaptor, ClientConnection, Server};
|
||||
@@ -12,6 +11,7 @@ use crate::HOST_IP;
|
||||
use crate::net::tor::TorController;
|
||||
use crate::prelude::*;
|
||||
use crate::util::actor::background::BackgroundJobQueue;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
|
||||
pub const DEFAULT_SOCKS_LISTEN: SocketAddr = SocketAddr::V4(SocketAddrV4::new(
|
||||
Ipv4Addr::new(HOST_IP[0], HOST_IP[1], HOST_IP[2], HOST_IP[3]),
|
||||
|
||||
@@ -13,7 +13,6 @@ use openssl::bn::{BigNum, MsbOption};
|
||||
use openssl::ec::{EcGroup, EcKey};
|
||||
use openssl::error::ErrorStack;
|
||||
use openssl::hash::MessageDigest;
|
||||
use openssl::nid::Nid;
|
||||
use openssl::pkey::{PKey, Private};
|
||||
use openssl::x509::extension::{
|
||||
AuthorityKeyIdentifier, BasicConstraints, KeyUsage, SubjectAlternativeName,
|
||||
@@ -42,12 +41,6 @@ use crate::net::web_server::{Accept, ExtractVisitor, TcpMetadata, extract};
|
||||
use crate::prelude::*;
|
||||
use crate::util::serde::Pem;
|
||||
|
||||
pub fn gen_nistp256() -> Result<PKey<Private>, ErrorStack> {
|
||||
PKey::from_ec_key(EcKey::generate(&*EcGroup::from_curve_name(
|
||||
Nid::X9_62_PRIME256V1,
|
||||
)?)?)
|
||||
}
|
||||
|
||||
pub fn should_use_cert(cert: &X509Ref) -> Result<bool, ErrorStack> {
|
||||
Ok(cert
|
||||
.not_before()
|
||||
@@ -71,7 +64,7 @@ pub struct CertStore {
|
||||
}
|
||||
impl CertStore {
|
||||
pub fn new(account: &AccountInfo) -> Result<Self, Error> {
|
||||
let int_key = generate_key()?;
|
||||
let int_key = gen_nistp256()?;
|
||||
let int_cert = make_int_cert((&account.root_ca_key, &account.root_ca_cert), &int_key)?;
|
||||
Ok(Self {
|
||||
root_key: Pem::new(account.root_ca_key.clone()),
|
||||
@@ -283,10 +276,8 @@ fn rand_serial() -> Result<Asn1Integer, Error> {
|
||||
Ok(asn1)
|
||||
}
|
||||
#[instrument(skip_all)]
|
||||
pub fn generate_key() -> Result<PKey<Private>, Error> {
|
||||
let new_key = EcKey::generate(EC_GROUP.as_ref())?;
|
||||
let key = PKey::from_ec_key(new_key)?;
|
||||
Ok(key)
|
||||
pub fn gen_nistp256() -> Result<PKey<Private>, Error> {
|
||||
Ok(PKey::from_ec_key(EcKey::generate(EC_GROUP.as_ref())?)?)
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
@@ -324,6 +315,11 @@ pub fn make_root_cert(
|
||||
let ctx = builder.x509v3_context(None, Some(&cfg));
|
||||
// subjectKeyIdentifier = hash
|
||||
let subject_key_identifier = SubjectKeyIdentifier::new().build(&ctx)?;
|
||||
// authorityKeyIdentifier = keyid,issuer:always
|
||||
let authority_key_identifier = AuthorityKeyIdentifier::new()
|
||||
.keyid(false)
|
||||
.issuer(true)
|
||||
.build(&ctx)?;
|
||||
// basicConstraints = critical, CA:true, pathlen:0
|
||||
let basic_constraints = BasicConstraints::new().critical().ca().build()?;
|
||||
// keyUsage = critical, digitalSignature, cRLSign, keyCertSign
|
||||
@@ -334,6 +330,7 @@ pub fn make_root_cert(
|
||||
.key_cert_sign()
|
||||
.build()?;
|
||||
builder.append_extension(subject_key_identifier)?;
|
||||
builder.append_extension(authority_key_identifier)?;
|
||||
builder.append_extension(basic_constraints)?;
|
||||
builder.append_extension(key_usage)?;
|
||||
builder.sign(&root_key, MessageDigest::sha256())?;
|
||||
@@ -370,9 +367,9 @@ pub fn make_int_cert(
|
||||
|
||||
// subjectKeyIdentifier = hash
|
||||
let subject_key_identifier = SubjectKeyIdentifier::new().build(&ctx)?;
|
||||
// authorityKeyIdentifier = keyid:always,issuer
|
||||
// authorityKeyIdentifier = keyid:always,issuer:always
|
||||
let authority_key_identifier = AuthorityKeyIdentifier::new()
|
||||
.keyid(false)
|
||||
.keyid(true)
|
||||
.issuer(true)
|
||||
.build(&ctx)?;
|
||||
// basicConstraints = critical, CA:true, pathlen:0
|
||||
@@ -478,7 +475,7 @@ pub fn make_leaf_cert(
|
||||
|
||||
// Google Apple and Mozilla reject certificate horizons longer than 398 days
|
||||
// https://techbeacon.com/security/google-apple-mozilla-enforce-1-year-max-security-certifications
|
||||
let expiration = Asn1Time::days_from_now(365)?;
|
||||
let expiration = Asn1Time::days_from_now(397)?;
|
||||
builder.set_not_after(&expiration)?;
|
||||
|
||||
builder.set_serial_number(&*rand_serial()?)?;
|
||||
@@ -508,8 +505,8 @@ pub fn make_leaf_cert(
|
||||
|
||||
let subject_key_identifier = SubjectKeyIdentifier::new().build(&ctx)?;
|
||||
let authority_key_identifier = AuthorityKeyIdentifier::new()
|
||||
.keyid(false)
|
||||
.issuer(true)
|
||||
.keyid(true)
|
||||
.issuer(false)
|
||||
.build(&ctx)?;
|
||||
let subject_alt_name = applicant.1.x509_extension().build(&ctx)?;
|
||||
let basic_constraints = BasicConstraints::new().build()?;
|
||||
|
||||
@@ -22,7 +22,6 @@ use http::request::Parts as RequestParts;
|
||||
use http::{HeaderValue, Method, StatusCode};
|
||||
use imbl_value::InternedString;
|
||||
use include_dir::Dir;
|
||||
use crate::PackageId;
|
||||
use new_mime_guess::MimeGuess;
|
||||
use openssl::hash::MessageDigest;
|
||||
use openssl::x509::X509;
|
||||
@@ -33,8 +32,8 @@ use url::Url;
|
||||
|
||||
use crate::context::{DiagnosticContext, InitContext, InstallContext, RpcContext, SetupContext};
|
||||
use crate::hostname::Hostname;
|
||||
use crate::main_api;
|
||||
use crate::middleware::auth::{Auth, HasValidSession};
|
||||
use crate::middleware::auth::Auth;
|
||||
use crate::middleware::auth::session::ValidSessionToken;
|
||||
use crate::middleware::cors::Cors;
|
||||
use crate::middleware::db::SyncDb;
|
||||
use crate::net::gateway::GatewayInfo;
|
||||
@@ -49,6 +48,7 @@ use crate::sign::commitment::merkle_archive::MerkleArchiveCommitment;
|
||||
use crate::util::io::open_file;
|
||||
use crate::util::net::SyncBody;
|
||||
use crate::util::serde::BASE64;
|
||||
use crate::{PackageId, main_api};
|
||||
|
||||
const NOT_FOUND: &[u8] = b"Not Found";
|
||||
const METHOD_NOT_ALLOWED: &[u8] = b"Method Not Allowed";
|
||||
@@ -80,7 +80,12 @@ impl UiContext for RpcContext {
|
||||
fn middleware(server: Server<Self>) -> HttpServer<Self> {
|
||||
server
|
||||
.middleware(Cors::new())
|
||||
.middleware(Auth::new())
|
||||
.middleware(
|
||||
Auth::new()
|
||||
.with_local_auth()
|
||||
.with_signature_auth()
|
||||
.with_session_auth(),
|
||||
)
|
||||
.middleware(SyncDb::new())
|
||||
}
|
||||
fn extend_router(self, router: Router) -> Router {
|
||||
@@ -405,8 +410,9 @@ async fn if_authorized<
|
||||
f: F,
|
||||
) -> Result<Response, Error> {
|
||||
if let Err(e) =
|
||||
HasValidSession::from_header(request.headers().get(http::header::COOKIE), ctx).await
|
||||
ValidSessionToken::from_header(request.headers().get(http::header::COOKIE), ctx).await
|
||||
{
|
||||
// TODO: other auth methods
|
||||
Ok(unauthorized(e, request.uri().path()))
|
||||
} else {
|
||||
f(request).await
|
||||
|
||||
@@ -11,7 +11,6 @@ use base64::Engine;
|
||||
use clap::Parser;
|
||||
use color_eyre::eyre::eyre;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use imbl_value::InternedString;
|
||||
use itertools::Itertools;
|
||||
use rpc_toolkit::{Context, Empty, HandlerExt, ParentHandler, from_fn_async};
|
||||
@@ -31,7 +30,7 @@ use ts_rs::TS;
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::prelude::*;
|
||||
use crate::util::actor::background::BackgroundJobQueue;
|
||||
use crate::util::future::Until;
|
||||
use crate::util::future::{NonDetachingJoinHandle, Until};
|
||||
use crate::util::io::ReadWriter;
|
||||
use crate::util::serde::{
|
||||
BASE64, Base64, HandlerExtSerde, WithIoFormat, deserialize_from_str, display_serializable,
|
||||
|
||||
@@ -10,7 +10,6 @@ use clap::Parser;
|
||||
use color_eyre::eyre::eyre;
|
||||
use futures::future::BoxFuture;
|
||||
use futures::{FutureExt, TryFutureExt, TryStreamExt};
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use imbl::OrdMap;
|
||||
use imbl_value::InternedString;
|
||||
use lazy_static::lazy_static;
|
||||
@@ -31,6 +30,7 @@ use crate::logs::{LogSource, LogsParams, journalctl};
|
||||
use crate::prelude::*;
|
||||
use crate::util::Invoke;
|
||||
use crate::util::collections::ordmap_retain;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use crate::util::io::{ReadWriter, write_file_atomic};
|
||||
use crate::util::serde::{
|
||||
BASE64, Base64, HandlerExtSerde, WithIoFormat, deserialize_from_str, display_serializable,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use clap::Parser;
|
||||
use imbl_value::InternedString;
|
||||
use crate::GatewayId;
|
||||
use patch_db::json_ptr::JsonPointer;
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::process::Command;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::GatewayId;
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::public::{NetworkInterfaceInfo, NetworkInterfaceType};
|
||||
use crate::net::host::all_hosts;
|
||||
|
||||
@@ -8,11 +8,11 @@ use futures::stream::BoxStream;
|
||||
use futures::{StreamExt, TryStreamExt};
|
||||
use imbl_value::InternedString;
|
||||
use ipnet::{IpNet, Ipv4Net, Ipv6Net};
|
||||
use crate::GatewayId;
|
||||
use nix::net::if_::if_nametoindex;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::GatewayId;
|
||||
use crate::db::model::public::{IpInfo, NetworkInterfaceType};
|
||||
use crate::prelude::*;
|
||||
use crate::util::Invoke;
|
||||
|
||||
@@ -9,9 +9,7 @@ use async_acme::acme::ACME_TLS_ALPN_NAME;
|
||||
use color_eyre::eyre::eyre;
|
||||
use futures::FutureExt;
|
||||
use futures::future::BoxFuture;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use imbl_value::{InOMap, InternedString};
|
||||
use crate::ResultExt;
|
||||
use rpc_toolkit::{Context, HandlerArgs, HandlerExt, ParentHandler, from_fn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::net::TcpStream;
|
||||
@@ -24,6 +22,7 @@ use tracing::instrument;
|
||||
use ts_rs::TS;
|
||||
use visit_rs::Visit;
|
||||
|
||||
use crate::ResultExt;
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::Database;
|
||||
use crate::db::model::public::AcmeSettings;
|
||||
@@ -42,7 +41,7 @@ use crate::net::tls::{
|
||||
use crate::net::web_server::{Accept, AcceptStream, ExtractVisitor, TcpMetadata, extract};
|
||||
use crate::prelude::*;
|
||||
use crate::util::collections::EqSet;
|
||||
use crate::util::future::WeakFuture;
|
||||
use crate::util::future::{NonDetachingJoinHandle, WeakFuture};
|
||||
use crate::util::serde::{HandlerExtSerde, MaybeUtf8String, display_serializable};
|
||||
use crate::util::sync::{SyncMutex, Watch};
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ use std::time::Duration;
|
||||
use axum::Router;
|
||||
use futures::future::Either;
|
||||
use futures::{FutureExt, TryFutureExt};
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use http::Extensions;
|
||||
use hyper_util::rt::{TokioIo, TokioTimer};
|
||||
use tokio::net::TcpListener;
|
||||
@@ -22,6 +21,7 @@ use visit_rs::{Visit, VisitFields, Visitor};
|
||||
use crate::net::static_server::{UiContext, ui_router};
|
||||
use crate::prelude::*;
|
||||
use crate::util::actor::background::BackgroundJobQueue;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use crate::util::io::ReadWriter;
|
||||
use crate::util::sync::{SyncRwLock, Watch};
|
||||
|
||||
@@ -483,7 +483,7 @@ where
|
||||
.http2()
|
||||
.timer(TokioTimer::new())
|
||||
.enable_connect_protocol()
|
||||
.keep_alive_interval(Duration::from_secs(60))
|
||||
.keep_alive_interval(Duration::from_secs(25))
|
||||
.keep_alive_timeout(Duration::from_secs(300));
|
||||
let (queue, mut runner) = BackgroundJobQueue::new();
|
||||
queue_cell.replace(Some(queue.clone()));
|
||||
|
||||
@@ -7,17 +7,17 @@ use clap::Parser;
|
||||
use clap::builder::ValueParserFactory;
|
||||
use color_eyre::eyre::eyre;
|
||||
use imbl_value::InternedString;
|
||||
use crate::util::FromStrParser;
|
||||
use crate::PackageId;
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::instrument;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::PackageId;
|
||||
use crate::backup::BackupReport;
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::DatabaseModel;
|
||||
use crate::prelude::*;
|
||||
use crate::util::FromStrParser;
|
||||
use crate::util::serde::{HandlerExtSerde, const_true};
|
||||
|
||||
// #[command(subcommands(list, delete, delete_before, create))]
|
||||
|
||||
@@ -2,13 +2,11 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
use clap::Parser;
|
||||
use color_eyre::eyre::eyre;
|
||||
use crate::Error;
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::process::Command;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::ARCH;
|
||||
use crate::context::config::ServerConfig;
|
||||
use crate::context::{CliContext, InstallContext};
|
||||
use crate::disk::OsPartitionInfo;
|
||||
@@ -25,6 +23,7 @@ use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile;
|
||||
use crate::util::Invoke;
|
||||
use crate::util::io::{TmpDir, delete_file, open_file};
|
||||
use crate::util::serde::IoFormat;
|
||||
use crate::{ARCH, Error};
|
||||
|
||||
mod gpt;
|
||||
mod mbr;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub use color_eyre::eyre::eyre;
|
||||
pub use imbl_value::InternedString;
|
||||
pub use lazy_format::lazy_format;
|
||||
pub use tracing::instrument;
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use reqwest::Client;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::io::AsyncWrite;
|
||||
@@ -20,6 +19,7 @@ use crate::sign::commitment::merkle_archive::MerkleArchiveCommitment;
|
||||
use crate::sign::commitment::{Commitment, Digestable};
|
||||
use crate::sign::{AnySignature, AnyVerifyingKey};
|
||||
use crate::upload::UploadingFile;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::sync::Arc;
|
||||
|
||||
use chrono::Utc;
|
||||
use clap::Parser;
|
||||
use cookie::{Cookie, Expiration, SameSite};
|
||||
use http::HeaderMap;
|
||||
use imbl_value::InternedString;
|
||||
use patch_db::PatchDb;
|
||||
@@ -21,7 +22,9 @@ use url::Url;
|
||||
|
||||
use crate::context::config::{CONFIG_PATH, ContextConfig};
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::middleware::signature::SignatureAuthContext;
|
||||
use crate::middleware::auth::DbContext;
|
||||
use crate::middleware::auth::local::LocalAuthContext;
|
||||
use crate::middleware::auth::signature::SignatureAuthContext;
|
||||
use crate::prelude::*;
|
||||
use crate::registry::RegistryDatabase;
|
||||
use crate::registry::device_info::{DEVICE_INFO_HEADER, DeviceInfo};
|
||||
@@ -29,7 +32,7 @@ use crate::registry::migrations::run_migrations;
|
||||
use crate::registry::signer::SignerInfo;
|
||||
use crate::rpc_continuations::RpcContinuations;
|
||||
use crate::sign::AnyVerifyingKey;
|
||||
use crate::util::io::append_file;
|
||||
use crate::util::io::{append_file, read_file_to_string};
|
||||
|
||||
const DEFAULT_REGISTRY_LISTEN: SocketAddr =
|
||||
SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::LOCALHOST), 5959);
|
||||
@@ -104,6 +107,8 @@ impl RegistryContext {
|
||||
}
|
||||
db.mutate(|db| run_migrations(db)).await.result?;
|
||||
|
||||
Self::init_auth_cookie().await?;
|
||||
|
||||
let tor_proxy_url = config
|
||||
.tor_proxy
|
||||
.clone()
|
||||
@@ -169,9 +174,11 @@ impl CallRemote<RegistryContext> for CliContext {
|
||||
params: Value,
|
||||
_: Empty,
|
||||
) -> Result<Value, RpcError> {
|
||||
let cookie = read_file_to_string(RegistryContext::LOCAL_AUTH_COOKIE_PATH).await;
|
||||
|
||||
let url = if let Some(url) = self.registry_url.clone() {
|
||||
url
|
||||
} else if !self.registry_hostname.is_empty() {
|
||||
} else if cookie.is_ok() || !self.registry_hostname.is_empty() {
|
||||
let mut url: Url = format!(
|
||||
"http://{}",
|
||||
self.registry_listen.unwrap_or(DEFAULT_REGISTRY_LISTEN)
|
||||
@@ -179,7 +186,8 @@ impl CallRemote<RegistryContext> for CliContext {
|
||||
.parse()
|
||||
.map_err(Error::from)?;
|
||||
url.path_segments_mut()
|
||||
.map_err(|_| Error::new(eyre!("cannot extend URL path"), ErrorKind::ParseUrl))?
|
||||
.map_err(|_| eyre!("Url cannot be base"))
|
||||
.with_kind(crate::ErrorKind::ParseUrl)?
|
||||
.push("rpc")
|
||||
.push("v0");
|
||||
url
|
||||
@@ -189,6 +197,26 @@ impl CallRemote<RegistryContext> for CliContext {
|
||||
);
|
||||
};
|
||||
|
||||
if let Ok(local) = cookie {
|
||||
let cookie_url = match url.host() {
|
||||
Some(url::Host::Ipv4(ip)) if ip.is_loopback() => url.clone(),
|
||||
Some(url::Host::Ipv6(ip)) if ip.is_loopback() => url.clone(),
|
||||
_ => format!("http://{DEFAULT_REGISTRY_LISTEN}").parse()?,
|
||||
};
|
||||
self.cookie_store
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert_raw(
|
||||
&Cookie::build(("local", local))
|
||||
.domain(cookie_url.host_str().unwrap_or("localhost"))
|
||||
.expires(Expiration::Session)
|
||||
.same_site(SameSite::Strict)
|
||||
.build(),
|
||||
&cookie_url,
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Network)?;
|
||||
}
|
||||
|
||||
method = method.strip_prefix("registry.").unwrap_or(method);
|
||||
let sig_context = self
|
||||
.registry_hostname
|
||||
@@ -196,7 +224,7 @@ impl CallRemote<RegistryContext> for CliContext {
|
||||
.cloned()
|
||||
.or_else(|| url.host().as_ref().map(InternedString::from_display));
|
||||
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
url,
|
||||
HeaderMap::new(),
|
||||
@@ -230,7 +258,7 @@ impl CallRemote<RegistryContext, RegistryUrlParams> for RpcContext {
|
||||
method = method.strip_prefix("registry.").unwrap_or(method);
|
||||
let sig_context = registry.host_str().map(InternedString::from);
|
||||
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
registry,
|
||||
headers,
|
||||
@@ -257,13 +285,19 @@ pub struct AdminLogRecord {
|
||||
pub key: AnyVerifyingKey,
|
||||
}
|
||||
|
||||
impl SignatureAuthContext for RegistryContext {
|
||||
impl DbContext for RegistryContext {
|
||||
type Database = RegistryDatabase;
|
||||
type AdditionalMetadata = RegistryAuthMetadata;
|
||||
type CheckPubkeyRes = Option<(AnyVerifyingKey, SignerInfo)>;
|
||||
fn db(&self) -> &TypedPatchDb<Self::Database> {
|
||||
&self.db
|
||||
}
|
||||
}
|
||||
impl LocalAuthContext for RegistryContext {
|
||||
const LOCAL_AUTH_COOKIE_PATH: &str = "/run/startos/registry.authcookie";
|
||||
const LOCAL_AUTH_COOKIE_OWNERSHIP: &str = "root:root";
|
||||
}
|
||||
impl SignatureAuthContext for RegistryContext {
|
||||
type AdditionalMetadata = RegistryAuthMetadata;
|
||||
type CheckPubkeyRes = Option<(AnyVerifyingKey, SignerInfo)>;
|
||||
async fn sig_context(
|
||||
&self,
|
||||
) -> impl IntoIterator<Item = Result<impl AsRef<str> + Send, Error>> + Send {
|
||||
@@ -274,17 +308,14 @@ impl SignatureAuthContext for RegistryContext {
|
||||
pubkey: Option<&AnyVerifyingKey>,
|
||||
metadata: Self::AdditionalMetadata,
|
||||
) -> Result<Self::CheckPubkeyRes, Error> {
|
||||
if metadata.admin {
|
||||
if let Some(pubkey) = pubkey {
|
||||
let (guid, admin) = db.as_index().as_signers().get_signer_info(pubkey)?;
|
||||
if db.as_admins().de()?.contains(&guid) {
|
||||
return Ok(Some((pubkey.clone(), admin)));
|
||||
}
|
||||
if let Some(pubkey) = pubkey {
|
||||
let (guid, admin) = db.as_index().as_signers().get_signer_info(pubkey)?;
|
||||
if !metadata.admin || db.as_admins().de()?.contains(&guid) {
|
||||
return Ok(Some((pubkey.clone(), admin)));
|
||||
}
|
||||
Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization))
|
||||
}
|
||||
async fn post_auth_hook(
|
||||
&self,
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::path::PathBuf;
|
||||
use clap::Parser;
|
||||
use imbl_value::InternedString;
|
||||
use itertools::Itertools;
|
||||
use crate::util::DataUrl;
|
||||
use rpc_toolkit::{Context, Empty, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
@@ -13,12 +12,14 @@ use crate::context::CliContext;
|
||||
use crate::prelude::*;
|
||||
use crate::registry::context::RegistryContext;
|
||||
use crate::registry::package::index::Category;
|
||||
use crate::util::DataUrl;
|
||||
use crate::util::serde::{HandlerExtSerde, WithIoFormat};
|
||||
|
||||
pub fn info_api<C: Context>() -> ParentHandler<C, WithIoFormat<Empty>> {
|
||||
ParentHandler::<C, WithIoFormat<Empty>>::new()
|
||||
.root_handler(
|
||||
from_fn_async(get_info)
|
||||
.with_metadata("authenticated", Value::Bool(false))
|
||||
.with_display_serializable()
|
||||
.with_about("Display registry name, icon, and package categories")
|
||||
.with_call_remote::<CliContext>(),
|
||||
|
||||
@@ -3,14 +3,13 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||
use axum::Router;
|
||||
use futures::future::ready;
|
||||
use imbl_value::InternedString;
|
||||
use crate::util::DataUrl;
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, Server, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::context::CliContext;
|
||||
use crate::middleware::auth::Auth;
|
||||
use crate::middleware::cors::Cors;
|
||||
use crate::middleware::signature::SignatureAuth;
|
||||
use crate::net::static_server::{bad_request, not_found, server_error};
|
||||
use crate::prelude::*;
|
||||
use crate::registry::context::RegistryContext;
|
||||
@@ -19,6 +18,7 @@ use crate::registry::os::index::OsIndex;
|
||||
use crate::registry::package::index::PackageIndex;
|
||||
use crate::registry::signer::SignerInfo;
|
||||
use crate::rpc_continuations::Guid;
|
||||
use crate::util::DataUrl;
|
||||
use crate::util::serde::HandlerExtSerde;
|
||||
|
||||
pub mod admin;
|
||||
@@ -108,7 +108,7 @@ pub fn registry_router(ctx: RegistryContext) -> Router {
|
||||
any(
|
||||
Server::new(move || ready(Ok(ctx.clone())), registry_api())
|
||||
.middleware(Cors::new())
|
||||
.middleware(SignatureAuth::new())
|
||||
.middleware(Auth::new().with_local_auth().with_signature_auth())
|
||||
.middleware(DeviceInfoMiddleware::new()),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -24,21 +24,36 @@ use crate::util::io::{AtomicFile, open_file};
|
||||
|
||||
pub fn get_api<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
.subcommand("iso", from_fn_async(get_iso).no_cli())
|
||||
.subcommand(
|
||||
"iso",
|
||||
from_fn_async(get_iso)
|
||||
.with_metadata("authenticated", Value::Bool(false))
|
||||
.no_cli(),
|
||||
)
|
||||
.subcommand(
|
||||
"iso",
|
||||
from_fn_async(cli_get_os_asset)
|
||||
.no_display()
|
||||
.with_about("Download iso"),
|
||||
)
|
||||
.subcommand("img", from_fn_async(get_img).no_cli())
|
||||
.subcommand(
|
||||
"img",
|
||||
from_fn_async(get_img)
|
||||
.with_metadata("authenticated", Value::Bool(false))
|
||||
.no_cli(),
|
||||
)
|
||||
.subcommand(
|
||||
"img",
|
||||
from_fn_async(cli_get_os_asset)
|
||||
.no_display()
|
||||
.with_about("Download img"),
|
||||
)
|
||||
.subcommand("squashfs", from_fn_async(get_squashfs).no_cli())
|
||||
.subcommand(
|
||||
"squashfs",
|
||||
from_fn_async(get_squashfs)
|
||||
.with_metadata("authenticated", Value::Bool(false))
|
||||
.no_cli(),
|
||||
)
|
||||
.subcommand(
|
||||
"squashfs",
|
||||
from_fn_async(cli_get_os_asset)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
||||
|
||||
use crate::context::CliContext;
|
||||
use crate::prelude::*;
|
||||
use crate::util::serde::HandlerExtSerde;
|
||||
|
||||
pub const SIG_CONTEXT: &str = "startos";
|
||||
@@ -14,6 +15,7 @@ pub fn os_api<C: Context>() -> ParentHandler<C> {
|
||||
.subcommand(
|
||||
"index",
|
||||
from_fn_async(index::get_os_index)
|
||||
.with_metadata("authenticated", Value::Bool(false))
|
||||
.with_display_serializable()
|
||||
.with_about("List index of OS versions")
|
||||
.with_call_remote::<CliContext>(),
|
||||
|
||||
@@ -45,6 +45,7 @@ pub fn version_api<C: Context>() -> ParentHandler<C> {
|
||||
.subcommand(
|
||||
"get",
|
||||
from_fn_async(get_version)
|
||||
.with_metadata("authenticated", Value::Bool(false))
|
||||
.with_metadata("get_device_info", Value::Bool(true))
|
||||
.with_display_serializable()
|
||||
.with_custom_display_fn(|handle, result| {
|
||||
|
||||
@@ -35,6 +35,7 @@ pub fn signer_api<C: Context>() -> ParentHandler<C> {
|
||||
.subcommand(
|
||||
"list",
|
||||
from_fn_async(list_version_signers)
|
||||
.with_metadata("authenticated", Value::Bool(false))
|
||||
.with_display_serializable()
|
||||
.with_custom_display_fn(|handle, result| display_signers(handle.params, result))
|
||||
.with_about("List version signers and related signer info")
|
||||
|
||||
@@ -4,13 +4,12 @@ use std::sync::Arc;
|
||||
use clap::Parser;
|
||||
use imbl_value::InternedString;
|
||||
use itertools::Itertools;
|
||||
use crate::util::VersionString;
|
||||
use crate::PackageId;
|
||||
use rpc_toolkit::HandlerArgs;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
use url::Url;
|
||||
|
||||
use crate::PackageId;
|
||||
use crate::context::CliContext;
|
||||
use crate::prelude::*;
|
||||
use crate::progress::{FullProgressTracker, ProgressTrackerWriter, ProgressUnits};
|
||||
@@ -23,6 +22,7 @@ use crate::s9pk::v2::SIG_CONTEXT;
|
||||
use crate::sign::commitment::merkle_archive::MerkleArchiveCommitment;
|
||||
use crate::sign::ed25519::Ed25519;
|
||||
use crate::sign::{AnySignature, AnyVerifyingKey, SignatureScheme};
|
||||
use crate::util::VersionString;
|
||||
use crate::util::io::TrackingIO;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, TS)]
|
||||
|
||||
@@ -2,11 +2,11 @@ use std::collections::BTreeMap;
|
||||
|
||||
use clap::Parser;
|
||||
use imbl_value::InternedString;
|
||||
use crate::PackageId;
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::PackageId;
|
||||
use crate::context::CliContext;
|
||||
use crate::prelude::*;
|
||||
use crate::registry::context::RegistryContext;
|
||||
@@ -50,6 +50,7 @@ pub fn category_api<C: Context>() -> ParentHandler<C> {
|
||||
.subcommand(
|
||||
"list",
|
||||
from_fn_async(list_categories)
|
||||
.with_metadata("authenticated", Value::Bool(false))
|
||||
.with_display_serializable()
|
||||
.with_custom_display_fn(|params, categories| {
|
||||
display_categories(params.params, categories)
|
||||
|
||||
@@ -5,10 +5,10 @@ use clap::{Parser, ValueEnum};
|
||||
use exver::{ExtendedVersion, VersionRange};
|
||||
use imbl_value::{InternedString, json};
|
||||
use itertools::Itertools;
|
||||
use crate::PackageId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::PackageId;
|
||||
use crate::context::CliContext;
|
||||
use crate::prelude::*;
|
||||
use crate::progress::{FullProgressTracker, ProgressUnits};
|
||||
|
||||
@@ -3,12 +3,11 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||
use chrono::Utc;
|
||||
use exver::{Version, VersionRange};
|
||||
use imbl_value::InternedString;
|
||||
use crate::util::{DataUrl, VersionString};
|
||||
use crate::PackageId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
use url::Url;
|
||||
|
||||
use crate::PackageId;
|
||||
use crate::prelude::*;
|
||||
use crate::registry::asset::RegistryAsset;
|
||||
use crate::registry::context::RegistryContext;
|
||||
@@ -20,6 +19,7 @@ use crate::s9pk::manifest::{Alerts, Description, HardwareRequirements};
|
||||
use crate::s9pk::merkle_archive::source::FileSource;
|
||||
use crate::sign::commitment::merkle_archive::MerkleArchiveCommitment;
|
||||
use crate::sign::{AnySignature, AnyVerifyingKey};
|
||||
use crate::util::{DataUrl, VersionString};
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, HasModel, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -15,6 +15,7 @@ pub fn package_api<C: Context>() -> ParentHandler<C> {
|
||||
.subcommand(
|
||||
"index",
|
||||
from_fn_async(index::get_package_index)
|
||||
.with_metadata("authenticated", Value::Bool(false))
|
||||
.with_display_serializable()
|
||||
.with_about("List packages and categories")
|
||||
.with_call_remote::<CliContext>(),
|
||||
@@ -46,6 +47,7 @@ pub fn package_api<C: Context>() -> ParentHandler<C> {
|
||||
.subcommand(
|
||||
"get",
|
||||
from_fn_async(get::get_package)
|
||||
.with_metadata("authenticated", Value::Bool(false))
|
||||
.with_metadata("get_device_info", Value::Bool(true))
|
||||
.with_display_serializable()
|
||||
.with_custom_display_fn(|handle, result| {
|
||||
|
||||
@@ -2,11 +2,11 @@ use std::collections::BTreeMap;
|
||||
|
||||
use clap::Parser;
|
||||
use exver::VersionRange;
|
||||
use crate::PackageId;
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::PackageId;
|
||||
use crate::context::CliContext;
|
||||
use crate::prelude::*;
|
||||
use crate::registry::admin::display_package_signers;
|
||||
@@ -36,6 +36,7 @@ pub fn signer_api<C: Context>() -> ParentHandler<C> {
|
||||
.subcommand(
|
||||
"list",
|
||||
from_fn_async(list_package_signers)
|
||||
.with_metadata("authenticated", Value::Bool(false))
|
||||
.with_display_serializable()
|
||||
.with_custom_display_fn(|handle, result| {
|
||||
display_package_signers(handle.params, result)
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::str::FromStr;
|
||||
|
||||
use clap::builder::ValueParserFactory;
|
||||
use itertools::Itertools;
|
||||
use crate::util::FromStrParser;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
use url::Url;
|
||||
@@ -11,6 +10,7 @@ use url::Url;
|
||||
use crate::prelude::*;
|
||||
use crate::sign::commitment::Digestable;
|
||||
use crate::sign::{AnySignature, AnyVerifyingKey, SignatureScheme};
|
||||
use crate::util::FromStrParser;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -12,14 +12,13 @@ use clap::builder::ValueParserFactory;
|
||||
use futures::future::BoxFuture;
|
||||
use futures::{Future, FutureExt};
|
||||
use imbl_value::InternedString;
|
||||
use crate::util::FromStrParser;
|
||||
use tokio::sync::{Mutex as AsyncMutex, broadcast};
|
||||
use ts_rs::TS;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::prelude::*;
|
||||
use crate::util::future::TimedResource;
|
||||
use crate::util::new_guid;
|
||||
use crate::util::{FromStrParser, new_guid};
|
||||
|
||||
#[derive(
|
||||
Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize, TS,
|
||||
|
||||
@@ -2,11 +2,11 @@ use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use clap::Parser;
|
||||
use crate::ImageId;
|
||||
use rpc_toolkit::{Empty, HandlerExt, ParentHandler, from_fn_async};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::ImageId;
|
||||
use crate::context::CliContext;
|
||||
use crate::prelude::*;
|
||||
use crate::s9pk::manifest::Manifest;
|
||||
|
||||
@@ -4,15 +4,15 @@ use std::path::{Path, PathBuf};
|
||||
use exver::{Version, VersionRange};
|
||||
use imbl_value::InternedString;
|
||||
use indexmap::IndexMap;
|
||||
pub use crate::PackageId;
|
||||
use crate::{ActionId, HealthCheckId, ImageId, VolumeId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
pub use crate::PackageId;
|
||||
use crate::prelude::*;
|
||||
use crate::s9pk::git_hash::GitHash;
|
||||
use crate::s9pk::manifest::{Alerts, Description};
|
||||
use crate::util::serde::{Duration, IoFormat, Regex};
|
||||
use crate::{ActionId, HealthCheckId, ImageId, VolumeId};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
|
||||
@@ -9,7 +9,6 @@ use std::task::{Context, Poll};
|
||||
use color_eyre::eyre::eyre;
|
||||
use digest::Output;
|
||||
use ed25519_dalek::VerifyingKey;
|
||||
use crate::{ImageId, PackageId};
|
||||
use sha2::{Digest, Sha512};
|
||||
use tokio::fs::File;
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, BufReader, ReadBuf};
|
||||
@@ -21,6 +20,7 @@ use crate::prelude::*;
|
||||
use crate::s9pk::v1::docker::DockerReader;
|
||||
use crate::util::VersionString;
|
||||
use crate::util::io::open_file;
|
||||
use crate::{ImageId, PackageId};
|
||||
|
||||
#[pin_project::pin_project]
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -4,22 +4,21 @@ use std::path::Path;
|
||||
use color_eyre::eyre::eyre;
|
||||
use exver::{Version, VersionRange};
|
||||
use imbl_value::InternedString;
|
||||
pub use crate::PackageId;
|
||||
use crate::util::mime;
|
||||
use crate::{ImageId, VolumeId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
use url::Url;
|
||||
|
||||
pub use crate::PackageId;
|
||||
use crate::dependencies::Dependencies;
|
||||
use crate::prelude::*;
|
||||
use crate::s9pk::git_hash::GitHash;
|
||||
use crate::s9pk::merkle_archive::directory_contents::DirectoryContents;
|
||||
use crate::s9pk::merkle_archive::expected::{Expected, Filter};
|
||||
use crate::s9pk::v2::pack::ImageConfig;
|
||||
use crate::util::VersionString;
|
||||
use crate::util::serde::Regex;
|
||||
use crate::util::{VersionString, mime};
|
||||
use crate::version::{Current, VersionT};
|
||||
use crate::{ImageId, VolumeId};
|
||||
|
||||
fn current_version() -> Version {
|
||||
Current::default().semver()
|
||||
|
||||
@@ -3,10 +3,9 @@ use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use imbl_value::InternedString;
|
||||
use crate::util::{DataUrl, mime};
|
||||
use crate::PackageId;
|
||||
use tokio::fs::File;
|
||||
|
||||
use crate::PackageId;
|
||||
use crate::dependencies::DependencyMetadata;
|
||||
use crate::prelude::*;
|
||||
use crate::s9pk::manifest::Manifest;
|
||||
@@ -20,6 +19,7 @@ use crate::s9pk::v2::pack::{ImageSource, PackSource};
|
||||
use crate::sign::commitment::merkle_archive::MerkleArchiveCommitment;
|
||||
use crate::util::io::{TmpDir, open_file};
|
||||
use crate::util::serde::IoFormat;
|
||||
use crate::util::{DataUrl, mime};
|
||||
|
||||
const MAGIC_AND_VERSION: &[u8] = &[0x3b, 0x3b, 0x02];
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@ use std::collections::BTreeMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use imbl_value::json;
|
||||
use crate::service::ProcedureName;
|
||||
use crate::{ActionId, PackageId, ReplayId};
|
||||
|
||||
use crate::action::{ActionInput, ActionResult};
|
||||
use crate::db::model::package::{
|
||||
@@ -11,10 +9,11 @@ use crate::db::model::package::{
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use crate::rpc_continuations::Guid;
|
||||
use crate::service::{Service, ServiceActor};
|
||||
use crate::service::{ProcedureName, Service, ServiceActor};
|
||||
use crate::util::actor::background::BackgroundJobQueue;
|
||||
use crate::util::actor::{ConflictBuilder, Handler};
|
||||
use crate::util::serde::is_partial_of;
|
||||
use crate::{ActionId, PackageId, ReplayId};
|
||||
|
||||
pub(super) struct GetActionInput {
|
||||
id: ActionId,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::{ActionId, PackageId, ReplayId};
|
||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
||||
|
||||
use crate::action::{ActionInput, ActionResult, display_action_result};
|
||||
@@ -11,6 +10,7 @@ use crate::rpc_continuations::Guid;
|
||||
use crate::service::cli::ContainerCliContext;
|
||||
use crate::service::effects::prelude::*;
|
||||
use crate::util::serde::HandlerExtSerde;
|
||||
use crate::{ActionId, PackageId, ReplayId};
|
||||
|
||||
pub fn action_api<C: Context>() -> ParentHandler<C> {
|
||||
ParentHandler::new()
|
||||
|
||||
@@ -5,10 +5,8 @@ use std::time::{Duration, SystemTime};
|
||||
|
||||
use clap::Parser;
|
||||
use futures::future::join_all;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use imbl::{Vector, vector};
|
||||
use imbl_value::InternedString;
|
||||
use crate::{HostId, PackageId, ServiceInterfaceId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::warn;
|
||||
use ts_rs::TS;
|
||||
@@ -20,6 +18,8 @@ use crate::service::effects::net::ssl::Algorithm;
|
||||
use crate::service::rpc::{CallbackHandle, CallbackId};
|
||||
use crate::service::{Service, ServiceActorSeed};
|
||||
use crate::util::collections::EqMap;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use crate::{HostId, PackageId, ServiceInterfaceId};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ServiceCallbacks(Mutex<ServiceCallbackMap>);
|
||||
|
||||
@@ -2,13 +2,13 @@ use std::str::FromStr;
|
||||
|
||||
use chrono::Utc;
|
||||
use clap::builder::ValueParserFactory;
|
||||
use crate::util::FromStrParser;
|
||||
use crate::PackageId;
|
||||
|
||||
use crate::PackageId;
|
||||
use crate::service::RebuildParams;
|
||||
use crate::service::effects::prelude::*;
|
||||
use crate::service::rpc::CallbackId;
|
||||
use crate::status::{DesiredStatus, StatusInfo};
|
||||
use crate::util::FromStrParser;
|
||||
|
||||
pub async fn rebuild(context: EffectContext) -> Result<(), Error> {
|
||||
let seed = context.deref()?.seed.clone();
|
||||
|
||||
@@ -5,10 +5,7 @@ use std::str::FromStr;
|
||||
use clap::builder::ValueParserFactory;
|
||||
use exver::VersionRange;
|
||||
use imbl_value::InternedString;
|
||||
use crate::util::{FromStrParser, VersionString};
|
||||
use crate::{HealthCheckId, PackageId, ReplayId, VolumeId};
|
||||
|
||||
use crate::DATA_DIR;
|
||||
use crate::db::model::package::{
|
||||
CurrentDependencies, CurrentDependencyInfo, CurrentDependencyKind, ManifestPreference,
|
||||
TaskEntry,
|
||||
@@ -19,7 +16,9 @@ use crate::disk::mount::filesystem::{FileSystem, MountType};
|
||||
use crate::disk::mount::util::{is_mountpoint, unmount};
|
||||
use crate::service::effects::prelude::*;
|
||||
use crate::status::health_check::NamedHealthCheckResult;
|
||||
use crate::util::{FromStrParser, VersionString};
|
||||
use crate::volume::data_dir;
|
||||
use crate::{DATA_DIR, HealthCheckId, PackageId, ReplayId, VolumeId};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[ts(export)]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::HealthCheckId;
|
||||
|
||||
use crate::service::effects::prelude::*;
|
||||
use crate::status::health_check::NamedHealthCheckResult;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::{HostId, PackageId};
|
||||
|
||||
use crate::net::host::binding::{BindId, BindOptions, NetInfo};
|
||||
use crate::service::effects::prelude::*;
|
||||
use crate::{HostId, PackageId};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use crate::{HostId, PackageId};
|
||||
|
||||
use crate::net::host::Host;
|
||||
use crate::service::effects::callbacks::CallbackHandler;
|
||||
use crate::service::effects::prelude::*;
|
||||
use crate::service::rpc::CallbackId;
|
||||
use crate::{HostId, PackageId};
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::PackageId;
|
||||
|
||||
use crate::service::effects::callbacks::CallbackHandler;
|
||||
use crate::service::effects::prelude::*;
|
||||
use crate::service::rpc::CallbackId;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use imbl::vector;
|
||||
use crate::{PackageId, ServiceInterfaceId};
|
||||
|
||||
use crate::net::service_interface::{AddressInfo, ServiceInterface, ServiceInterfaceType};
|
||||
use crate::service::effects::callbacks::CallbackHandler;
|
||||
use crate::service::effects::prelude::*;
|
||||
use crate::service::rpc::CallbackId;
|
||||
use crate::{PackageId, ServiceInterfaceId};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[ts(export)]
|
||||
|
||||
@@ -13,10 +13,8 @@ use clap::Parser;
|
||||
use futures::future::BoxFuture;
|
||||
use futures::stream::FusedStream;
|
||||
use futures::{FutureExt, SinkExt, StreamExt, TryStreamExt};
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use imbl_value::{InternedString, json};
|
||||
use itertools::Itertools;
|
||||
use crate::{ActionId, HostId, ImageId, PackageId};
|
||||
use nix::sys::signal::Signal;
|
||||
use persistent_container::{PersistentContainer, Subcontainer};
|
||||
use rpc_toolkit::HandlerArgs;
|
||||
@@ -47,12 +45,13 @@ use crate::service::service_map::InstallProgressHandles;
|
||||
use crate::service::uninstall::cleanup;
|
||||
use crate::util::Never;
|
||||
use crate::util::actor::concurrent::ConcurrentActor;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use crate::util::io::{AsyncReadStream, AtomicFile, TermSize, delete_file};
|
||||
use crate::util::net::WebSocketExt;
|
||||
use crate::util::serde::Pem;
|
||||
use crate::util::sync::SyncMutex;
|
||||
use crate::volume::data_dir;
|
||||
use crate::{CAP_1_KiB, DATA_DIR};
|
||||
use crate::{ActionId, CAP_1_KiB, DATA_DIR, HostId, ImageId, PackageId};
|
||||
|
||||
pub mod action;
|
||||
pub mod cli;
|
||||
|
||||
@@ -5,11 +5,8 @@ use std::time::Duration;
|
||||
|
||||
use futures::Future;
|
||||
use futures::future::ready;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use imbl::{Vector, vector};
|
||||
use imbl_value::InternedString;
|
||||
use crate::service::ProcedureName;
|
||||
use crate::{ImageId, VolumeId};
|
||||
use rpc_toolkit::{Empty, Server, ShutdownHandle};
|
||||
use serde::de::DeserializeOwned;
|
||||
use tokio::process::Command;
|
||||
@@ -34,12 +31,13 @@ use crate::service::effects::handler;
|
||||
use crate::service::rpc::{
|
||||
CallbackHandle, CallbackId, CallbackParams, ExitParams, InitKind, InitParams,
|
||||
};
|
||||
use crate::service::{Service, rpc};
|
||||
use crate::service::{ProcedureName, Service, rpc};
|
||||
use crate::util::Invoke;
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use crate::util::io::create_file;
|
||||
use crate::util::rpc_client::UnixRpcClient;
|
||||
use crate::volume::data_dir;
|
||||
use crate::{ARCH, DATA_DIR, PACKAGE_DATA};
|
||||
use crate::{ARCH, DATA_DIR, ImageId, PACKAGE_DATA, VolumeId};
|
||||
|
||||
const RPC_CONNECT_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
|
||||
@@ -7,16 +7,15 @@ use clap::builder::ValueParserFactory;
|
||||
use exver::{ExtendedVersion, VersionRange};
|
||||
use imbl::Vector;
|
||||
use imbl_value::{InternedString, Value};
|
||||
use crate::service::ProcedureName;
|
||||
use crate::util::FromStrParser;
|
||||
use rpc_toolkit::Empty;
|
||||
use rpc_toolkit::yajrc::RpcMethod;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::rpc_continuations::Guid;
|
||||
use crate::service::ProcedureName;
|
||||
use crate::service::persistent_container::PersistentContainer;
|
||||
use crate::util::Never;
|
||||
use crate::util::{FromStrParser, Never};
|
||||
|
||||
#[derive(Clone, serde::Deserialize, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
|
||||
@@ -132,9 +132,7 @@ async fn service_actor_loop<'a>(
|
||||
.filter(|task| task.kind == TransitionKind::BackingUp);
|
||||
*transition = task.or_else(|| Some(seed.backup()));
|
||||
}
|
||||
_ => {
|
||||
*transition = None;
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -8,9 +8,7 @@ use exver::VersionRange;
|
||||
use futures::future::{BoxFuture, Fuse};
|
||||
use futures::stream::FuturesUnordered;
|
||||
use futures::{Future, FutureExt, StreamExt, TryFutureExt};
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use imbl::OrdMap;
|
||||
use crate::error::ErrorData;
|
||||
use tokio::sync::{OwnedRwLockReadGuard, OwnedRwLockWriteGuard, RwLock, oneshot};
|
||||
use tracing::instrument;
|
||||
use url::Url;
|
||||
@@ -21,6 +19,7 @@ use crate::db::model::package::{
|
||||
InstallingInfo, InstallingState, PackageDataEntry, PackageState, UpdatingState,
|
||||
};
|
||||
use crate::disk::mount::guard::GenericMountGuard;
|
||||
use crate::error::ErrorData;
|
||||
use crate::install::PKG_ARCHIVE_DIR;
|
||||
use crate::notifications::{NotificationLevel, notify};
|
||||
use crate::prelude::*;
|
||||
@@ -32,6 +31,7 @@ use crate::service::rpc::{ExitParams, InitKind};
|
||||
use crate::service::{LoadDisposition, Service, ServiceRef};
|
||||
use crate::sign::commitment::merkle_archive::MerkleArchiveCommitment;
|
||||
use crate::status::{DesiredStatus, StatusInfo};
|
||||
use crate::util::future::NonDetachingJoinHandle;
|
||||
use crate::util::serde::{Base32, Pem};
|
||||
use crate::util::sync::SyncMutex;
|
||||
|
||||
@@ -115,7 +115,18 @@ impl ServiceMap {
|
||||
shutdown_err = service.shutdown(None).await;
|
||||
}
|
||||
match Service::load(ctx, id, disposition).await {
|
||||
Ok(s) => *service = s.into(),
|
||||
Ok(s) => {
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
if let Some(pde) = db.as_public_mut().as_package_data_mut().as_idx_mut(id) {
|
||||
pde.as_status_info_mut().as_error_mut().ser(&None)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
*service = s.into();
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Error loading service: {e}");
|
||||
tracing::debug!("{e:?}");
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::path::PathBuf;
|
||||
|
||||
use futures::future::BoxFuture;
|
||||
use futures::{FutureExt, TryFutureExt};
|
||||
use crate::service::ProcedureName;
|
||||
use rpc_toolkit::yajrc::RpcError;
|
||||
|
||||
use crate::disk::mount::filesystem::ReadWrite;
|
||||
@@ -11,7 +10,7 @@ use crate::rpc_continuations::Guid;
|
||||
use crate::service::action::GetActionInput;
|
||||
use crate::service::start_stop::StartStop;
|
||||
use crate::service::transition::{Transition, TransitionKind};
|
||||
use crate::service::{ServiceActor, ServiceActorSeed};
|
||||
use crate::service::{ProcedureName, ServiceActor, ServiceActorSeed};
|
||||
use crate::status::DesiredStatus;
|
||||
use crate::util::actor::background::BackgroundJobQueue;
|
||||
use crate::util::actor::{ConflictBuilder, Handler};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user