Allow no-tunnel passthrough mode
This commit is contained in:
parent
450e9c6fcd
commit
7ade60646b
3 changed files with 76 additions and 38 deletions
|
|
@ -71,20 +71,19 @@ impl DaemonRPCServer {
|
||||||
self.network_update_chan.0.send(()).map_err(proc_err)
|
self.network_update_chan.0.send(()).map_err(proc_err)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn resolve_tunnel(&self) -> Result<Option<ResolvedTunnel>, RspStatus> {
|
async fn resolve_tunnel(&self) -> Result<ResolvedTunnel, RspStatus> {
|
||||||
let conn = self.get_connection()?;
|
let conn = self.get_connection()?;
|
||||||
let networks = list_networks(&conn).map_err(proc_err)?;
|
let networks = list_networks(&conn).map_err(proc_err)?;
|
||||||
ResolvedTunnel::from_networks(&networks).map_err(proc_err)
|
ResolvedTunnel::from_networks(&networks).map_err(proc_err)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn current_tunnel_configuration(&self) -> Result<TunnelConfigurationResponse, RspStatus> {
|
async fn current_tunnel_configuration(&self) -> Result<TunnelConfigurationResponse, RspStatus> {
|
||||||
match self.resolve_tunnel().await? {
|
let config = self
|
||||||
Some(config) => {
|
.resolve_tunnel()
|
||||||
let config = config.server_config().map_err(proc_err)?;
|
.await?
|
||||||
Ok(configuration_rsp(config))
|
.server_config()
|
||||||
}
|
.map_err(proc_err)?;
|
||||||
None => Ok(empty_configuration_rsp()),
|
Ok(configuration_rsp(config))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn stop_active_tunnel(&self) -> Result<bool, RspStatus> {
|
async fn stop_active_tunnel(&self) -> Result<bool, RspStatus> {
|
||||||
|
|
@ -114,10 +113,6 @@ impl DaemonRPCServer {
|
||||||
|
|
||||||
async fn reconcile_runtime(&self) -> Result<(), RspStatus> {
|
async fn reconcile_runtime(&self) -> Result<(), RspStatus> {
|
||||||
let desired = self.resolve_tunnel().await?;
|
let desired = self.resolve_tunnel().await?;
|
||||||
let Some(desired) = desired else {
|
|
||||||
let _ = self.stop_active_tunnel().await?;
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
let needs_restart = {
|
let needs_restart = {
|
||||||
let guard = self.active_tunnel.read().await;
|
let guard = self.active_tunnel.read().await;
|
||||||
guard
|
guard
|
||||||
|
|
@ -163,10 +158,7 @@ impl Tunnel for DaemonRPCServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tunnel_start(&self, _request: Request<Empty>) -> Result<Response<Empty>, RspStatus> {
|
async fn tunnel_start(&self, _request: Request<Empty>) -> Result<Response<Empty>, RspStatus> {
|
||||||
let desired = self
|
let desired = self.resolve_tunnel().await?;
|
||||||
.resolve_tunnel()
|
|
||||||
.await?
|
|
||||||
.ok_or_else(|| RspStatus::failed_precondition("no stored network configured"))?;
|
|
||||||
let already_running = {
|
let already_running = {
|
||||||
let guard = self.active_tunnel.read().await;
|
let guard = self.active_tunnel.read().await;
|
||||||
guard
|
guard
|
||||||
|
|
@ -285,13 +277,6 @@ fn configuration_rsp(config: ServerConfig) -> TunnelConfigurationResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty_configuration_rsp() -> TunnelConfigurationResponse {
|
|
||||||
TunnelConfigurationResponse {
|
|
||||||
mtu: 1500,
|
|
||||||
addresses: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn status_rsp(state: RunState) -> TunnelStatusResponse {
|
fn status_rsp(state: RunState) -> TunnelStatusResponse {
|
||||||
TunnelStatusResponse {
|
TunnelStatusResponse {
|
||||||
state: state.to_rpc().into(),
|
state: state.to_rpc().into(),
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ mod tests {
|
||||||
client::BurrowClient,
|
client::BurrowClient,
|
||||||
grpc_defs::{
|
grpc_defs::{
|
||||||
Empty, Network, NetworkListResponse, NetworkReorderRequest, NetworkType,
|
Empty, Network, NetworkListResponse, NetworkReorderRequest, NetworkType,
|
||||||
TunnelConfigurationResponse,
|
TunnelConfigurationResponse, TunnelStatusResponse,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -111,6 +111,11 @@ mod tests {
|
||||||
.network_list(Empty {})
|
.network_list(Empty {})
|
||||||
.await?
|
.await?
|
||||||
.into_inner();
|
.into_inner();
|
||||||
|
let mut status_stream = client
|
||||||
|
.tunnel_client
|
||||||
|
.tunnel_status(Empty {})
|
||||||
|
.await?
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
let initial_config = next_configuration(&mut config_stream).await?;
|
let initial_config = next_configuration(&mut config_stream).await?;
|
||||||
assert!(initial_config.addresses.is_empty());
|
assert!(initial_config.addresses.is_empty());
|
||||||
|
|
@ -119,12 +124,27 @@ mod tests {
|
||||||
let initial_networks = next_networks(&mut network_stream).await?;
|
let initial_networks = next_networks(&mut network_stream).await?;
|
||||||
assert!(initial_networks.network.is_empty());
|
assert!(initial_networks.network.is_empty());
|
||||||
|
|
||||||
let start_err = client
|
let initial_status = next_status(&mut status_stream).await?;
|
||||||
.tunnel_client
|
assert_eq!(
|
||||||
.tunnel_start(Empty {})
|
initial_status.state(),
|
||||||
.await
|
crate::daemon::rpc::grpc_defs::State::Stopped
|
||||||
.expect_err("starting without a stored network should fail");
|
);
|
||||||
assert_eq!(start_err.code(), tonic::Code::FailedPrecondition);
|
|
||||||
|
client.tunnel_client.tunnel_start(Empty {}).await?;
|
||||||
|
|
||||||
|
let passthrough_status = next_status(&mut status_stream).await?;
|
||||||
|
assert_eq!(
|
||||||
|
passthrough_status.state(),
|
||||||
|
crate::daemon::rpc::grpc_defs::State::Running
|
||||||
|
);
|
||||||
|
|
||||||
|
client.tunnel_client.tunnel_stop(Empty {}).await?;
|
||||||
|
|
||||||
|
let stopped_status = next_status(&mut status_stream).await?;
|
||||||
|
assert_eq!(
|
||||||
|
stopped_status.state(),
|
||||||
|
crate::daemon::rpc::grpc_defs::State::Stopped
|
||||||
|
);
|
||||||
|
|
||||||
client
|
client
|
||||||
.networks_client
|
.networks_client
|
||||||
|
|
@ -246,6 +266,14 @@ Endpoint = wg.burrow.rs:51820
|
||||||
.ok_or_else(|| anyhow!("network stream ended unexpectedly"))
|
.ok_or_else(|| anyhow!("network stream ended unexpectedly"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn next_status(
|
||||||
|
stream: &mut tonic::Streaming<TunnelStatusResponse>,
|
||||||
|
) -> Result<TunnelStatusResponse> {
|
||||||
|
timeout(Duration::from_secs(5), stream.message())
|
||||||
|
.await??
|
||||||
|
.ok_or_else(|| anyhow!("status stream ended unexpectedly"))
|
||||||
|
}
|
||||||
|
|
||||||
fn network_ids(response: &NetworkListResponse) -> Vec<(i32, NetworkType)> {
|
fn network_ids(response: &NetworkListResponse) -> Vec<(i32, NetworkType)> {
|
||||||
response
|
response
|
||||||
.network
|
.network
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ use crate::{
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum RuntimeIdentity {
|
pub enum RuntimeIdentity {
|
||||||
|
Passthrough,
|
||||||
Network {
|
Network {
|
||||||
id: i32,
|
id: i32,
|
||||||
network_type: NetworkType,
|
network_type: NetworkType,
|
||||||
|
|
@ -24,6 +25,9 @@ pub enum RuntimeIdentity {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ResolvedTunnel {
|
pub enum ResolvedTunnel {
|
||||||
|
Passthrough {
|
||||||
|
identity: RuntimeIdentity,
|
||||||
|
},
|
||||||
WireGuard {
|
WireGuard {
|
||||||
identity: RuntimeIdentity,
|
identity: RuntimeIdentity,
|
||||||
config: Config,
|
config: Config,
|
||||||
|
|
@ -35,9 +39,11 @@ pub enum ResolvedTunnel {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResolvedTunnel {
|
impl ResolvedTunnel {
|
||||||
pub fn from_networks(networks: &[Network]) -> Result<Option<Self>> {
|
pub fn from_networks(networks: &[Network]) -> Result<Self> {
|
||||||
let Some(network) = networks.first() else {
|
let Some(network) = networks.first() else {
|
||||||
return Ok(None);
|
return Ok(Self::Passthrough {
|
||||||
|
identity: RuntimeIdentity::Passthrough,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let identity = RuntimeIdentity::Network {
|
let identity = RuntimeIdentity::Network {
|
||||||
|
|
@ -51,23 +57,30 @@ impl ResolvedTunnel {
|
||||||
let payload = String::from_utf8(network.payload.clone())
|
let payload = String::from_utf8(network.payload.clone())
|
||||||
.context("wireguard payload must be valid UTF-8")?;
|
.context("wireguard payload must be valid UTF-8")?;
|
||||||
let config = Config::from_content_fmt(&payload, "ini")?;
|
let config = Config::from_content_fmt(&payload, "ini")?;
|
||||||
Ok(Some(Self::WireGuard { identity, config }))
|
Ok(Self::WireGuard { identity, config })
|
||||||
}
|
}
|
||||||
NetworkType::HackClub => {
|
NetworkType::HackClub => {
|
||||||
let config = HackClubNetworkConfig::from_payload(&network.payload)?;
|
let config = HackClubNetworkConfig::from_payload(&network.payload)?;
|
||||||
Ok(Some(Self::HackClub { identity, config }))
|
Ok(Self::HackClub { identity, config })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn identity(&self) -> &RuntimeIdentity {
|
pub fn identity(&self) -> &RuntimeIdentity {
|
||||||
match self {
|
match self {
|
||||||
Self::WireGuard { identity, .. } | Self::HackClub { identity, .. } => identity,
|
Self::Passthrough { identity }
|
||||||
|
| Self::WireGuard { identity, .. }
|
||||||
|
| Self::HackClub { identity, .. } => identity,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn server_config(&self) -> Result<ServerConfig> {
|
pub fn server_config(&self) -> Result<ServerConfig> {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Passthrough { .. } => Ok(ServerConfig {
|
||||||
|
address: Vec::new(),
|
||||||
|
name: None,
|
||||||
|
mtu: Some(1500),
|
||||||
|
}),
|
||||||
Self::WireGuard { config, .. } => ServerConfig::try_from(config),
|
Self::WireGuard { config, .. } => ServerConfig::try_from(config),
|
||||||
Self::HackClub { config, .. } => Ok(ServerConfig {
|
Self::HackClub { config, .. } => Ok(ServerConfig {
|
||||||
address: config.local_addresses.clone(),
|
address: config.local_addresses.clone(),
|
||||||
|
|
@ -82,6 +95,7 @@ impl ResolvedTunnel {
|
||||||
tun_interface: Arc<RwLock<Option<TunInterface>>>,
|
tun_interface: Arc<RwLock<Option<TunInterface>>>,
|
||||||
) -> Result<ActiveTunnel> {
|
) -> Result<ActiveTunnel> {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Passthrough { identity } => Ok(ActiveTunnel::Passthrough { identity }),
|
||||||
Self::WireGuard { identity, config } => {
|
Self::WireGuard { identity, config } => {
|
||||||
let tun = TunOptions::new().open()?;
|
let tun = TunOptions::new().open()?;
|
||||||
tun_interface.write().await.replace(tun);
|
tun_interface.write().await.replace(tun);
|
||||||
|
|
@ -118,6 +132,9 @@ impl ResolvedTunnel {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ActiveTunnel {
|
pub enum ActiveTunnel {
|
||||||
|
Passthrough {
|
||||||
|
identity: RuntimeIdentity,
|
||||||
|
},
|
||||||
WireGuard {
|
WireGuard {
|
||||||
identity: RuntimeIdentity,
|
identity: RuntimeIdentity,
|
||||||
interface: Arc<RwLock<WireGuardInterface>>,
|
interface: Arc<RwLock<WireGuardInterface>>,
|
||||||
|
|
@ -132,12 +149,15 @@ pub enum ActiveTunnel {
|
||||||
impl ActiveTunnel {
|
impl ActiveTunnel {
|
||||||
pub fn identity(&self) -> &RuntimeIdentity {
|
pub fn identity(&self) -> &RuntimeIdentity {
|
||||||
match self {
|
match self {
|
||||||
Self::WireGuard { identity, .. } | Self::HackClub { identity, .. } => identity,
|
Self::Passthrough { identity }
|
||||||
|
| Self::WireGuard { identity, .. }
|
||||||
|
| Self::HackClub { identity, .. } => identity,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn shutdown(self, tun_interface: &Arc<RwLock<Option<TunInterface>>>) -> Result<()> {
|
pub async fn shutdown(self, tun_interface: &Arc<RwLock<Option<TunInterface>>>) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Passthrough { .. } => Ok(()),
|
||||||
Self::WireGuard { interface, task, .. } => {
|
Self::WireGuard { interface, task, .. } => {
|
||||||
interface.read().await.remove_tun().await;
|
interface.read().await.remove_tun().await;
|
||||||
let task_result = task.await;
|
let task_result = task.await;
|
||||||
|
|
@ -174,7 +194,12 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_networks_resolves_to_no_tunnel() {
|
fn no_networks_resolve_to_passthrough() {
|
||||||
assert!(ResolvedTunnel::from_networks(&[]).unwrap().is_none());
|
let resolved = ResolvedTunnel::from_networks(&[]).unwrap();
|
||||||
|
assert_eq!(resolved.identity(), &RuntimeIdentity::Passthrough);
|
||||||
|
assert_eq!(
|
||||||
|
resolved.server_config().unwrap().address,
|
||||||
|
Vec::<String>::new()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue