wip: debugging tor

This commit is contained in:
Aiden McClelland
2025-08-27 15:10:54 -06:00
parent c5fa09c4d4
commit b3b031ed47
31 changed files with 521 additions and 607 deletions

View File

@@ -40,6 +40,8 @@ sudo chown 0:0 tmp/combined/lib/systemd/system/container-runtime.service
sudo cp container-runtime-failure.service tmp/combined/lib/systemd/system/container-runtime-failure.service
sudo chown 0:0 tmp/combined/lib/systemd/system/container-runtime-failure.service
sudo cp ../core/target/$ARCH-unknown-linux-musl/release/containerbox tmp/combined/usr/bin/start-container
echo -e '#!/bin/bash\nexec start-container $@' | sudo tee tmp/combined/usr/bin/start-cli # TODO: remove
sudo chmod +x tmp/combined/usr/bin/start-cli
sudo chown 0:0 tmp/combined/usr/bin/start-container
echo container-runtime | sha256sum | head -c 32 | cat - <(echo) | sudo tee tmp/combined/etc/machine-id
cat deb-install.sh | sudo systemd-nspawn --console=pipe -D tmp/combined $QEMU /bin/bash

738
core/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -26,7 +26,7 @@ if [[ "${ENVIRONMENT}" =~ (^|-)unstable($|-) ]]; then
RUSTFLAGS="--cfg tokio_unstable"
fi
alias 'rust-musl-builder'='docker run $USE_TTY --rm -e "RUSTFLAGS=$RUSTFLAGS" -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$HOME/.cargo/git":/root/.cargo/git -v "$(pwd)":/home/rust/src -w /home/rust/src -P messense/rust-musl-cross:$ARCH-musl'
source ./core/builder-alias.sh
echo "FEATURES=\"$FEATURES\""
echo "RUSTFLAGS=\"$RUSTFLAGS\""

View File

@@ -26,7 +26,7 @@ if [[ "${ENVIRONMENT}" =~ (^|-)unstable($|-) ]]; then
RUSTFLAGS="--cfg tokio_unstable"
fi
alias 'rust-musl-builder'='docker run $USE_TTY --rm -e "RUSTFLAGS=$RUSTFLAGS" -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$HOME/.cargo/git":/root/.cargo/git -v "$(pwd)":/home/rust/src -w /home/rust/src -P messense/rust-musl-cross:$ARCH-musl'
source ./core/builder-alias.sh
echo "FEATURES=\"$FEATURES\""
echo "RUSTFLAGS=\"$RUSTFLAGS\""

View File

@@ -31,7 +31,7 @@ if [[ "${ENVIRONMENT}" =~ (^|-)unstable($|-) ]]; then
RUSTFLAGS="--cfg tokio_unstable"
fi
alias 'rust-musl-builder'='docker run $USE_TTY --rm -e "RUSTFLAGS=$RUSTFLAGS" -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$HOME/.cargo/git":/root/.cargo/git -v "$(pwd)":/home/rust/src -w /home/rust/src -P messense/rust-musl-cross:$ARCH-musl'
source ./core/builder-alias.sh
echo "FEATURES=\"$FEATURES\""
echo "RUSTFLAGS=\"$RUSTFLAGS\""

View File

@@ -26,7 +26,7 @@ if [[ "${ENVIRONMENT}" =~ (^|-)unstable($|-) ]]; then
RUSTFLAGS="--cfg tokio_unstable"
fi
alias 'rust-musl-builder'='docker run $USE_TTY --rm -e "RUSTFLAGS=$RUSTFLAGS" -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$HOME/.cargo/git":/root/.cargo/git -v "$(pwd)":/home/rust/src -w /home/rust/src -P messense/rust-musl-cross:$ARCH-musl'
source ./core/builder-alias.sh
echo "FEATURES=\"$FEATURES\""
echo "RUSTFLAGS=\"$RUSTFLAGS\""

View File

@@ -26,7 +26,7 @@ if [[ "${ENVIRONMENT}" =~ (^|-)unstable($|-) ]]; then
RUSTFLAGS="--cfg tokio_unstable"
fi
alias 'rust-musl-builder'='docker run $USE_TTY --rm -e "RUSTFLAGS=$RUSTFLAGS" -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$HOME/.cargo/git":/root/.cargo/git -v "$(pwd)":/home/rust/src -w /home/rust/src -P messense/rust-musl-cross:$ARCH-musl'
source ./core/builder-alias.sh
echo "FEATURES=\"$FEATURES\""
echo "RUSTFLAGS=\"$RUSTFLAGS\""

3
core/builder-alias.sh Normal file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
alias 'rust-musl-builder'='docker run $USE_TTY --rm -e "RUSTFLAGS=$RUSTFLAGS" -e SCCACHE_GHA_ENABLED -e SCCACHE_GHA_VERSION -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$HOME/.cargo/git":/root/.cargo/git -v "$HOME/.cache/sccache":/root/.cache/sccache -v "$(pwd)":/home/rust/src -w /home/rust/src -P start9/rust-musl-cross:$ARCH-musl'

View File

@@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
arti-client = { version = "0.33", features = ["full"] }
arti-client = { version = "0.33", default-features = false, git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit" }
axum = "0.8.4"
base64 = "0.22.1"
color-eyre = "0.6.2"

View File

@@ -64,7 +64,9 @@ arti-client = { version = "0.33", features = [
"static",
"tokio",
"ephemeral-keystore",
], default-features = false }
"onion-service-client",
"onion-service-service",
], default-features = false, git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit" }
aes = { version = "0.7.5", features = ["ctr"] }
async-acme = { version = "0.6.0", git = "https://github.com/dr-bonez/async-acme.git", features = [
"use_rustls",
@@ -195,7 +197,7 @@ rpassword = "7.2.0"
rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git", branch = "master" }
rust-argon2 = "2.0.0"
rustyline-async = "0.4.1"
safelog = "0.4.8"
safelog = { version = "0.4.8", git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit" }
semver = { version = "1.0.20", features = ["serde"] }
serde = { version = "1.0", features = ["derive", "rc"] }
serde_cbor = { package = "ciborium", version = "0.2.1" }
@@ -227,13 +229,22 @@ tokio-stream = { version = "0.1.14", features = ["io-util", "sync", "net"] }
tokio-tar = { git = "https://github.com/dr-bonez/tokio-tar.git" }
tokio-tungstenite = { version = "0.26.2", features = ["native-tls", "url"] }
tokio-util = { version = "0.7.9", features = ["io"] }
tor-cell = { version = "0.33" }
tor-hscrypto = { version = "0.33", features = ["full"] }
tor-hsservice = { version = "0.33" }
tor-keymgr = { version = "0.33", features = ["ephemeral-keystore"] }
tor-llcrypto = { version = "0.33", features = ["full"] }
tor-proto = { version = "0.33" }
tor-rtcompat = { version = "0.33", features = ["tokio", "rustls"] }
tor-cell = { version = "0.33", git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit" }
tor-hscrypto = { version = "0.33", features = [
"full",
], git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit" }
tor-hsservice = { version = "0.33", git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit" }
tor-keymgr = { version = "0.33", features = [
"ephemeral-keystore",
], git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit" }
tor-llcrypto = { version = "0.33", features = [
"full",
], git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit" }
tor-proto = { version = "0.33", git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit" }
tor-rtcompat = { version = "0.33", features = [
"tokio",
"rustls",
], git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit" }
tower-service = "0.3.3"
tracing = "0.1.39"
tracing-error = "0.2.0"

View File

@@ -18,7 +18,7 @@ use models::{ActionId, PackageId};
use reqwest::{Client, Proxy};
use rpc_toolkit::yajrc::RpcError;
use rpc_toolkit::{CallRemote, Context, Empty};
use tokio::sync::{Mutex, RwLock, broadcast, oneshot, watch};
use tokio::sync::{RwLock, broadcast, oneshot, watch};
use tokio::time::Instant;
use tracing::instrument;
@@ -32,7 +32,7 @@ use crate::db::model::package::TaskSeverity;
use crate::disk::OsPartitionInfo;
use crate::init::{InitResult, check_time_is_synchronized};
use crate::install::PKG_ARCHIVE_DIR;
use crate::lxc::{ContainerId, LxcContainer, LxcManager};
use crate::lxc::LxcManager;
use crate::net::net_controller::{NetController, NetService};
use crate::net::utils::{find_eth_iface, find_wifi_iface};
use crate::net::web_server::{UpgradableListener, WebServerAcceptorSetter};
@@ -74,12 +74,6 @@ pub struct RpcContextSeed {
pub client: Client,
pub start_time: Instant,
pub crons: SyncMutex<BTreeMap<Guid, NonDetachingJoinHandle<()>>>,
// #[cfg(feature = "dev")]
pub dev: Dev,
}
pub struct Dev {
pub lxc: Mutex<BTreeMap<ContainerId, LxcContainer>>,
}
pub struct Hardware {
@@ -262,10 +256,6 @@ impl RpcContext {
.with_kind(crate::ErrorKind::ParseUrl)?,
start_time: Instant::now(),
crons,
// #[cfg(feature = "dev")]
dev: Dev {
lxc: Mutex::new(BTreeMap::new()),
},
});
let res = Self(seed.clone());

View File

@@ -19,8 +19,8 @@ use crate::account::AccountInfo;
use crate::db::model::package::AllPackageData;
use crate::net::acme::AcmeProvider;
use crate::net::forward::START9_BRIDGE_IFACE;
use crate::net::host::binding::{AddSslOptions, BindInfo, BindOptions, NetInfo};
use crate::net::host::Host;
use crate::net::host::binding::{AddSslOptions, BindInfo, BindOptions, NetInfo};
use crate::net::utils::ipv6_is_local;
use crate::net::vhost::AlpnInfo;
use crate::prelude::*;

View File

@@ -10,34 +10,34 @@ use futures::future::BoxFuture;
use futures::{FutureExt, StreamExt, TryStreamExt};
use helpers::NonDetachingJoinHandle;
use hickory_client::client::Client;
use hickory_client::proto::DnsHandle;
use hickory_client::proto::runtime::TokioRuntimeProvider;
use hickory_client::proto::tcp::TcpClientStream;
use hickory_client::proto::udp::UdpClientStream;
use hickory_client::proto::xfer::{DnsExchangeBackground, DnsRequestOptions};
use hickory_client::proto::DnsHandle;
use hickory_server::ServerFuture;
use hickory_server::authority::MessageResponseBuilder;
use hickory_server::proto::op::{Header, ResponseCode};
use hickory_server::proto::rr::{Name, Record, RecordType};
use hickory_server::server::{Request, RequestHandler, ResponseHandler, ResponseInfo};
use hickory_server::ServerFuture;
use imbl::OrdMap;
use imbl_value::InternedString;
use itertools::Itertools;
use models::{GatewayId, OptionExt, PackageId};
use rpc_toolkit::{
from_fn_async, from_fn_blocking, Context, HandlerArgs, HandlerExt, ParentHandler,
Context, HandlerArgs, HandlerExt, ParentHandler, from_fn_async, from_fn_blocking,
};
use serde::{Deserialize, Serialize};
use tokio::net::{TcpListener, UdpSocket};
use tracing::instrument;
use crate::context::RpcContext;
use crate::db::model::public::NetworkInterfaceInfo;
use crate::db::model::Database;
use crate::db::model::public::NetworkInterfaceInfo;
use crate::net::gateway::NetworkInterfaceWatcher;
use crate::prelude::*;
use crate::util::io::file_string_stream;
use crate::util::serde::{display_serializable, HandlerExtSerde};
use crate::util::serde::{HandlerExtSerde, display_serializable};
use crate::util::sync::{SyncRwLock, Watch};
pub fn dns_api<C: Context>() -> ParentHandler<C> {
@@ -349,11 +349,7 @@ impl RequestHandler for Resolver {
Header::response_from_request(request.header()),
&ip.into_iter()
.filter_map(|a| {
if let IpAddr::V4(a) = a {
Some(a)
} else {
None
}
if let IpAddr::V4(a) = a { Some(a) } else { None }
})
.map(|ip| {
Record::from_rdata(
@@ -377,11 +373,7 @@ impl RequestHandler for Resolver {
Header::response_from_request(request.header()),
&ip.into_iter()
.filter_map(|a| {
if let IpAddr::V6(a) = a {
Some(a)
} else {
None
}
if let IpAddr::V6(a) = a { Some(a) } else { None }
})
.map(|ip| {
Record::from_rdata(
@@ -415,9 +407,7 @@ impl RequestHandler for Resolver {
}
} else {
let query = query.original().clone();
let mut streams = self
.client
.lookup(query, DnsRequestOptions::default());
let mut streams = self.client.lookup(query, DnsRequestOptions::default());
let mut err = None;
for stream in streams.iter_mut() {
match tokio::time::timeout(Duration::from_secs(5), stream.next()).await {

View File

@@ -16,7 +16,7 @@ use itertools::Itertools;
use models::GatewayId;
use nix::net::if_::if_nametoindex;
use patch_db::json_ptr::JsonPointer;
use rpc_toolkit::{from_fn_async, Context, HandlerArgs, HandlerExt, ParentHandler};
use rpc_toolkit::{Context, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
use serde::{Deserialize, Serialize};
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::net::{TcpListener, TcpStream};
@@ -24,25 +24,25 @@ use tokio::process::Command;
use ts_rs::TS;
use zbus::proxy::{PropertyChanged, PropertyStream, SignalStream};
use zbus::zvariant::{
DeserializeDict, Dict, OwnedObjectPath, OwnedValue, Type as ZType, Value as ZValue,
DICT_ENTRY_SIG_END_STR,
DICT_ENTRY_SIG_END_STR, DeserializeDict, Dict, OwnedObjectPath, OwnedValue, Type as ZType,
Value as ZValue,
};
use zbus::{proxy, Connection};
use zbus::{Connection, proxy};
use crate::context::{CliContext, RpcContext};
use crate::db::model::public::{IpInfo, NetworkInterfaceInfo, NetworkInterfaceType};
use crate::db::model::Database;
use crate::db::model::public::{IpInfo, NetworkInterfaceInfo, NetworkInterfaceType};
use crate::net::forward::START9_BRIDGE_IFACE;
use crate::net::gateway::device::DeviceProxy;
use crate::net::utils::ipv6_is_link_local;
use crate::net::web_server::Accept;
use crate::prelude::*;
use crate::util::Invoke;
use crate::util::collections::OrdMapIterMut;
use crate::util::future::Until;
use crate::util::io::open_file;
use crate::util::serde::{display_serializable, HandlerExtSerde};
use crate::util::serde::{HandlerExtSerde, display_serializable};
use crate::util::sync::{SyncMutex, Watch};
use crate::util::Invoke;
pub fn gateway_api<C: Context>() -> ParentHandler<C> {
ParentHandler::new()

View File

@@ -1,19 +1,20 @@
use std::collections::BTreeSet;
use std::net::Ipv4Addr;
use clap::Parser;
use imbl_value::InternedString;
use models::GatewayId;
use rpc_toolkit::{from_fn_async, Context, Empty, HandlerArgs, HandlerExt, ParentHandler};
use rpc_toolkit::{Context, Empty, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use crate::context::{CliContext, RpcContext};
use crate::db::model::DatabaseModel;
use crate::net::acme::AcmeProvider;
use crate::net::host::{all_hosts, HostApiKind};
use crate::net::host::{HostApiKind, all_hosts};
use crate::net::tor::OnionAddress;
use crate::prelude::*;
use crate::util::serde::{display_serializable, HandlerExtSerde};
use crate::util::serde::{HandlerExtSerde, display_serializable};
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
@@ -35,10 +36,10 @@ pub struct PublicDomainConfig {
pub acme: Option<AcmeProvider>,
}
fn check_duplicates(db: &DatabaseModel) -> Result<(), Error> {
fn handle_duplicates(db: &mut DatabaseModel) -> Result<(), Error> {
let mut onions = BTreeSet::<OnionAddress>::new();
let mut domains = BTreeSet::<InternedString>::new();
let mut check_onion = |onion: OnionAddress| {
let check_onion = |onions: &mut BTreeSet<OnionAddress>, onion: OnionAddress| {
if onions.contains(&onion) {
return Err(Error::new(
eyre!("onion address {onion} is already in use"),
@@ -48,7 +49,7 @@ fn check_duplicates(db: &DatabaseModel) -> Result<(), Error> {
onions.insert(onion);
Ok(())
};
let mut check_domain = |domain: InternedString| {
let check_domain = |domains: &mut BTreeSet<InternedString>, domain: InternedString| {
if domains.contains(&domain) {
return Err(Error::new(
eyre!("domain {domain} is already in use"),
@@ -58,23 +59,47 @@ fn check_duplicates(db: &DatabaseModel) -> Result<(), Error> {
domains.insert(domain);
Ok(())
};
let mut not_in_use = Vec::new();
for host in all_hosts(db) {
let host = host?;
let in_use = host.as_bindings().de()?.values().any(|v| v.enabled);
if !in_use {
not_in_use.push(host);
continue;
}
for onion in host.as_onions().de()? {
check_onion(onion)?;
check_onion(&mut onions, onion)?;
}
for domain in host.as_public_domains().keys()? {
check_domain(domain)?;
check_domain(&mut domains, domain)?;
}
for domain in host.as_private_domains().de()? {
check_domain(domain)?;
check_domain(&mut domains, domain)?;
}
}
for host in not_in_use {
host.as_onions_mut()
.mutate(|o| Ok(o.retain(|o| !onions.contains(o))))?;
host.as_public_domains_mut()
.mutate(|d| Ok(d.retain(|d, _| !domains.contains(d))))?;
host.as_private_domains_mut()
.mutate(|d| Ok(d.retain(|d| !domains.contains(d))))?;
for onion in host.as_onions().de()? {
check_onion(&mut onions, onion)?;
}
for domain in host.as_public_domains().keys()? {
check_domain(&mut domains, domain)?;
}
for domain in host.as_private_domains().de()? {
check_domain(&mut domains, domain)?;
}
}
Ok(())
}
pub fn address_api<C: Context, Kind: HostApiKind>(
) -> ParentHandler<C, Kind::Params, Kind::InheritedParams> {
pub fn address_api<C: Context, Kind: HostApiKind>()
-> ParentHandler<C, Kind::Params, Kind::InheritedParams> {
ParentHandler::<C, Kind::Params, Kind::InheritedParams>::new()
.subcommand(
"domain",
@@ -209,12 +234,12 @@ pub struct AddPublicDomainParams {
pub async fn add_public_domain<Kind: HostApiKind>(
ctx: RpcContext,
AddPublicDomainParams {
ref fqdn,
fqdn,
acme,
gateway,
}: AddPublicDomainParams,
inheritance: Kind::Inheritance,
) -> Result<(), Error> {
) -> Result<Option<Ipv4Addr>, Error> {
ctx.db
.mutate(|db| {
if let Some(acme) = &acme {
@@ -231,31 +256,35 @@ pub async fn add_public_domain<Kind: HostApiKind>(
Kind::host_for(&inheritance, db)?
.as_public_domains_mut()
.insert(fqdn, &PublicDomainConfig { acme, gateway })?;
check_duplicates(db)
.insert(&fqdn, &PublicDomainConfig { acme, gateway })?;
handle_duplicates(db)
})
.await
.result?;
Kind::sync_host(&ctx, inheritance).await?;
Ok(())
tokio::task::spawn_blocking(|| {
crate::net::dns::query_dns(ctx, crate::net::dns::QueryDnsParams { fqdn })
})
.await
.with_kind(ErrorKind::Unknown)?
}
#[derive(Deserialize, Serialize, Parser)]
pub struct RemoveDomainParams {
pub domain: InternedString,
pub fqdn: InternedString,
}
pub async fn remove_public_domain<Kind: HostApiKind>(
ctx: RpcContext,
RemoveDomainParams { domain }: RemoveDomainParams,
RemoveDomainParams { fqdn }: RemoveDomainParams,
inheritance: Kind::Inheritance,
) -> Result<(), Error> {
ctx.db
.mutate(|db| {
Kind::host_for(&inheritance, db)?
.as_public_domains_mut()
.remove(&domain)
.remove(&fqdn)
})
.await
.result?;
@@ -279,7 +308,7 @@ pub async fn add_private_domain<Kind: HostApiKind>(
Kind::host_for(&inheritance, db)?
.as_private_domains_mut()
.mutate(|d| Ok(d.insert(fqdn)))?;
check_duplicates(db)
handle_duplicates(db)
})
.await
.result?;
@@ -290,7 +319,7 @@ pub async fn add_private_domain<Kind: HostApiKind>(
pub async fn remove_private_domain<Kind: HostApiKind>(
ctx: RpcContext,
RemoveDomainParams { domain }: RemoveDomainParams,
RemoveDomainParams { fqdn: domain }: RemoveDomainParams,
inheritance: Kind::Inheritance,
) -> Result<(), Error> {
ctx.db
@@ -332,7 +361,7 @@ pub async fn add_onion<Kind: HostApiKind>(
Kind::host_for(&inheritance, db)?
.as_onions_mut()
.mutate(|a| Ok(a.insert(onion)))?;
check_duplicates(db)
handle_duplicates(db)
})
.await
.result?;

View File

@@ -1,11 +1,11 @@
use std::collections::{BTreeMap, BTreeSet};
use std::str::FromStr;
use clap::builder::ValueParserFactory;
use clap::Parser;
use clap::builder::ValueParserFactory;
use imbl::OrdSet;
use models::{FromStrParser, GatewayId, HostId};
use rpc_toolkit::{from_fn_async, Context, Empty, HandlerArgs, HandlerExt, ParentHandler};
use rpc_toolkit::{Context, Empty, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
@@ -16,7 +16,7 @@ use crate::net::gateway::InterfaceFilter;
use crate::net::host::HostApiKind;
use crate::net::vhost::AlpnInfo;
use crate::prelude::*;
use crate::util::serde::{display_serializable, HandlerExtSerde};
use crate::util::serde::{HandlerExtSerde, display_serializable};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, TS)]
#[ts(export)]
@@ -169,8 +169,8 @@ pub struct AddSslOptions {
pub alpn: Option<AlpnInfo>,
}
pub fn binding<C: Context, Kind: HostApiKind>(
) -> ParentHandler<C, Kind::Params, Kind::InheritedParams> {
pub fn binding<C: Context, Kind: HostApiKind>()
-> ParentHandler<C, Kind::Params, Kind::InheritedParams> {
ParentHandler::<C, Kind::Params, Kind::InheritedParams>::new()
.subcommand(
"list",

View File

@@ -127,14 +127,16 @@ pub fn host_for<'a>(
})
}
pub fn all_hosts(db: &DatabaseModel) -> impl Iterator<Item = Result<&Model<Host>, Error>> {
[Ok(db.as_public().as_server_info().as_network().as_host())]
pub fn all_hosts(db: &mut DatabaseModel) -> impl Iterator<Item = Result<&mut Model<Host>, Error>> {
use patch_db::DestructureMut;
let destructured = db.as_public_mut().destructure_mut();
[Ok(destructured.server_info.as_network_mut().as_host_mut())]
.into_iter()
.chain(
[db.as_public().as_package_data().as_entries()]
[destructured.package_data.as_entries_mut()]
.into_iter()
.flatten_ok()
.map(|entry| entry.and_then(|(_, v)| v.as_hosts().as_entries()))
.map(|entry| entry.and_then(|(_, v)| v.as_hosts_mut().as_entries_mut()))
.flatten_ok()
.map_ok(|(_, v)| v),
)

View File

@@ -3,7 +3,7 @@ use std::net::{Ipv4Addr, SocketAddr};
use std::sync::{Arc, Weak};
use color_eyre::eyre::eyre;
use imbl::{vector, OrdMap};
use imbl::{OrdMap, vector};
use imbl_value::InternedString;
use ipnet::IpNet;
use models::{HostId, OptionExt, PackageId};
@@ -11,8 +11,9 @@ use tokio::sync::Mutex;
use tokio::task::JoinHandle;
use tracing::instrument;
use crate::db::model::public::NetworkInterfaceInfo;
use crate::HOST_IP;
use crate::db::model::Database;
use crate::db::model::public::NetworkInterfaceInfo;
use crate::error::ErrorCollection;
use crate::hostname::Hostname;
use crate::net::dns::DnsController;
@@ -23,7 +24,7 @@ use crate::net::gateway::{
};
use crate::net::host::address::HostAddress;
use crate::net::host::binding::{AddSslOptions, BindId, BindOptions};
use crate::net::host::{host_for, Host, Hosts};
use crate::net::host::{Host, Hosts, host_for};
use crate::net::service_interface::{HostnameInfo, IpHostname, OnionHostname};
use crate::net::tor::{OnionAddress, TorController, TorSecretKey};
use crate::net::utils::ipv6_is_local;
@@ -31,7 +32,6 @@ use crate::net::vhost::{AlpnInfo, TargetInfo, VHostController};
use crate::prelude::*;
use crate::service::effects::callbacks::ServiceCallbacks;
use crate::util::serde::MaybeUtf8String;
use crate::HOST_IP;
pub struct NetController {
pub(crate) db: TypedPatchDb<Database>,

View File

@@ -9,24 +9,19 @@ use arti_client::{TorClient, TorClientConfig};
use base64::Engine;
use clap::Parser;
use color_eyre::eyre::eyre;
use futures::{FutureExt, Stream, StreamExt};
use futures::StreamExt;
use helpers::NonDetachingJoinHandle;
use imbl_value::InternedString;
use itertools::Itertools;
use lazy_static::lazy_static;
use regex::Regex;
use rpc_toolkit::{from_fn_async, Context, Empty, HandlerExt, ParentHandler};
use safelog::DisplayRedacted;
use serde::{Deserialize, Serialize};
use tokio::io::AsyncWriteExt;
use tokio::net::TcpStream;
use tokio::sync::oneshot;
use tor_cell::relaycell::msg::Connected;
use tor_hscrypto::pk::{HsId, HsIdKeypair};
use tor_hsservice::status::State as ArtiOnionServiceState;
use tor_hsservice::{HsNickname, RendRequest, RunningOnionService};
use tor_hsservice::{HsNickname, RunningOnionService};
use tor_keymgr::config::ArtiKeystoreKind;
use tor_proto::stream::IncomingStreamRequest;
use tor_proto::client::stream::IncomingStreamRequest;
use tor_rtcompat::tokio::TokioRustlsRuntime;
use ts_rs::TS;
@@ -39,13 +34,11 @@ use crate::util::serde::{
};
use crate::util::sync::{SyncMutex, SyncRwLock};
const STARTING_HEALTH_TIMEOUT: u64 = 120; // 2min
#[derive(Debug, Clone, Copy)]
pub struct OnionAddress(pub HsId);
impl std::fmt::Display for OnionAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt_unredacted(f)
safelog::DisplayRedacted::fmt_unredacted(&self.0, f)
}
}
impl FromStr for OnionAddress {
@@ -201,7 +194,7 @@ impl std::fmt::Debug for OnionStore {
impl<'a> std::fmt::Debug for OnionStoreMap<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
#[derive(Debug)]
struct KeyFor(OnionAddress);
struct KeyFor(#[allow(unused)] OnionAddress);
let mut map = f.debug_map();
for (k, v) in self.0 {
map.key(k);
@@ -451,7 +444,6 @@ impl TorController {
}
pub async fn reset(&self, wipe_state: bool) -> Result<(), Error> {
// todo!()
Ok(())
}
@@ -488,7 +480,7 @@ impl OnionService {
.run_while(async {
loop {
if let Err(e) = async {
let (new_service, mut stream) = client.peek(|c| {
let (new_service, stream) = client.peek(|c| {
c.launch_onion_service_with_hsid(
OnionServiceConfigBuilder::default()
.nickname(
@@ -504,40 +496,54 @@ impl OnionService {
)
.with_kind(ErrorKind::Tor)
})?;
let addr = new_service.onion_address().map(|a| safelog::DisplayRedacted::display_unredacted(&a).to_string());
let mut status_stream = new_service.status_events();
bg.add_job(async move {
while let Some(status) = status_stream.next().await {
tracing::debug!("{addr:?} status: {status:?}");
if let Some(err) = status.current_problem() {
tracing::error!("{err:?}");
}
}
});
service.replace(Some(new_service));
let mut stream = tor_hsservice::handle_rend_requests(stream);
while let Some(req) = stream.next().await {
bg.add_job({
let bg = bg.clone();
let bindings = bindings.clone();
async move {
if let Err(e) = async {
let mut stream =
req.accept().await.with_kind(ErrorKind::Tor)?;
while let Some(req) = stream.next().await {
let IncomingStreamRequest::Begin(begin) =
req.request()
else {
continue; // TODO: reject instead?
return req
.reject(tor_cell::relaycell::msg::End::new_with_reason(
tor_cell::relaycell::msg::EndReason::DONE,
))
.await
.with_kind(ErrorKind::Tor);
};
let Some(target) = bindings.peek(|b| {
b.get(&begin.port()).and_then(|a| {
a.iter()
.find(|(_, rc)| {
rc.strong_count() > 0
})
.find(|(_, rc)| rc.strong_count() > 0)
.map(|(addr, _)| *addr)
})
}) else {
continue; // TODO: reject instead?
return req
.reject(tor_cell::relaycell::msg::End::new_with_reason(
tor_cell::relaycell::msg::EndReason::DONE,
))
.await
.with_kind(ErrorKind::Tor);
};
bg.add_job(async move {
if let Err(e) = async {
let mut outgoing =
TcpStream::connect(target)
.await
.with_kind(
ErrorKind::Network,
)?;
.with_kind(ErrorKind::Network)?;
let mut incoming = req
.accept(Connected::new_empty())
.await
@@ -565,7 +571,6 @@ impl OnionService {
tracing::debug!("{e:?}");
}
});
}
Ok::<_, Error>(())
}
.await
@@ -623,6 +628,7 @@ impl OnionService {
}
pub async fn shutdown(self) -> Result<(), Error> {
self.0.service.replace(None);
self.0._thread.abort();
Ok(())
}

View File

@@ -6,8 +6,8 @@ use std::task::Poll;
use std::time::Duration;
use axum::Router;
use futures::future::Either;
use futures::FutureExt;
use futures::future::Either;
use helpers::NonDetachingJoinHandle;
use hyper_util::rt::{TokioIo, TokioTimer};
use tokio::net::{TcpListener, TcpStream};
@@ -15,7 +15,7 @@ use tokio::sync::oneshot;
use crate::context::{DiagnosticContext, InitContext, InstallContext, RpcContext, SetupContext};
use crate::net::gateway::{
lookup_info_by_addr, NetworkInterfaceListener, SelfContainedNetworkInterfaceListener,
NetworkInterfaceListener, SelfContainedNetworkInterfaceListener, lookup_info_by_addr,
};
use crate::net::static_server::{
diagnostic_ui_router, init_ui_router, install_ui_router, main_ui_router, redirecter, refresher,

View File

@@ -11,14 +11,14 @@ use crate::service::effects::prelude::*;
use crate::service::persistent_container::Subcontainer;
use crate::util::Invoke;
#[cfg(feature = "container-runtime")]
#[cfg(feature = "start-container")]
mod sync;
#[cfg(not(feature = "container-runtime"))]
#[cfg(not(feature = "start-container"))]
mod sync_dummy;
pub use sync::*;
#[cfg(not(feature = "container-runtime"))]
#[cfg(not(feature = "start-container"))]
use sync_dummy as sync;
#[derive(Debug, Deserialize, Serialize, Parser, TS)]

View File

@@ -82,7 +82,7 @@ impl BackgroundJobRunner {
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
let this = self.project();
this.runner.poll(cx);
let _ = this.runner.poll(cx);
this.fut.poll(cx)
}
}

View File

@@ -6,8 +6,8 @@ use std::os::unix::prelude::MetadataExt;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::str::FromStr;
use std::sync::atomic::AtomicU64;
use std::sync::Arc;
use std::sync::atomic::AtomicU64;
use std::task::{Poll, Waker};
use std::time::Duration;
@@ -22,7 +22,7 @@ use nix::unistd::{Gid, Uid};
use serde::{Deserialize, Serialize};
use tokio::fs::{File, OpenOptions};
use tokio::io::{
duplex, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, DuplexStream, ReadBuf, WriteHalf,
AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, DuplexStream, ReadBuf, WriteHalf, duplex,
};
use tokio::net::TcpStream;
use tokio::sync::{Notify, OwnedMutexGuard};

View File

@@ -5,8 +5,8 @@ use std::sync::atomic::AtomicUsize;
use std::sync::{Arc, Weak};
use std::task::{Poll, Waker};
use futures::stream::BoxStream;
use futures::Stream;
use futures::stream::BoxStream;
use crate::prelude::*;

View File

@@ -193,7 +193,7 @@ export class PublicDomainService {
),
250,
)
} else if (ip === wanIp) {
} else if (ip !== wanIp) {
setTimeout(
() =>
this.showDns(

View File

@@ -167,10 +167,9 @@ export class InterfaceTorDomainsComponent {
const loader = this.loader.open('Saving').subscribe()
try {
let onion = key
const onion = key
? await this.api.addTorKey({ key })
: await this.api.generateTorKey({})
onion = `${onion}.onion`
if (this.interface.packageId) {
await this.api.pkgAddOnion({

View File

@@ -114,14 +114,16 @@ export default class ServiceInterfaceRoute {
const { serviceInterfaces, hosts } = pkg
const iFace = serviceInterfaces[this.interfaceId()]
const key = iFace?.addressInfo.hostId || ''
const key = iFace!.addressInfo.hostId || ''
const host = hosts[key]
const port = iFace?.addressInfo.internalPort
const port = iFace!.addressInfo.internalPort
if (!host || !iFace || !port) {
return
}
const binding = host.bindings[port]
const gateways = this.gatewayService.gateways() || []
return {
@@ -129,10 +131,13 @@ export default class ServiceInterfaceRoute {
addresses: this.interfaceService.getAddresses(iFace, host, gateways),
gateways:
gateways.map(g => ({
enabled: true,
enabled:
(g.public
? binding?.net.publicEnabled.includes(g.id)
: !binding?.net.privateDisabled.includes(g.id)) ?? false,
...g,
})) || [],
torDomains: host.onions.map(o => `${o}.onion`),
torDomains: host.onions,
publicDomains: getPublicDomains(host.publicDomains, gateways),
privateDomains: host.privateDomains,
}

View File

@@ -84,6 +84,8 @@ export default class StartOsUiComponent {
if (!network || !gateways) return
const binding = network.host.bindings['80']
return {
...this.iface,
addresses: this.interfaceService.getAddresses(
@@ -92,10 +94,13 @@ export default class StartOsUiComponent {
gateways,
),
gateways: gateways.map(g => ({
enabled: true,
enabled:
(g.public
? binding?.net.publicEnabled.includes(g.id)
: !binding?.net.privateDisabled.includes(g.id)) ?? false,
...g,
})),
torDomains: network.host.onions.map(o => `${o}.onion`),
torDomains: network.host.onions,
publicDomains: getPublicDomains(network.host.publicDomains, gateways),
privateDomains: network.host.privateDomains,
}

View File

@@ -280,7 +280,7 @@ export namespace RR {
key: string
}
export type GenerateTorKeyReq = {} // net.tor.key.generate
export type AddTorKeyRes = string // onion address without .onion suffix
export type AddTorKeyRes = string // onion address *with* .onion suffix
export type ServerBindingToggleGatewayReq = {
// server.host.binding.set-gateway-enabled

View File

@@ -1339,12 +1339,12 @@ export class MockApiService extends ApiService {
async addTorKey(params: RR.AddTorKeyReq): Promise<RR.AddTorKeyRes> {
await pauseFor(2000)
return 'vanityabcdefghijklmnop'
return 'vanityabcdefghijklmnop.onion'
}
async generateTorKey(params: RR.GenerateTorKeyReq): Promise<RR.AddTorKeyRes> {
await pauseFor(2000)
return 'abcdefghijklmnopqrstuv'
return 'abcdefghijklmnopqrstuv.onion'
}
async serverBindingToggleGateway(