Added RPC portal whitelist function, allowing only local access by default to enhance security (#929)
This commit is contained in:
@@ -304,6 +304,15 @@ const bool_flags: BoolFlag[] = [
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-x-9 flex-wrap w-full">
|
||||||
|
<div class="flex flex-col gap-2 grow p-fluid">
|
||||||
|
<label for="">{{ t('rpc_portal_whitelists') }}</label>
|
||||||
|
<AutoComplete id="rpc_portal_whitelists" v-model="curNetwork.rpc_portal_whitelists"
|
||||||
|
:placeholder="t('chips_placeholder', ['127.0.0.0/8'])" class="w-full" multiple fluid
|
||||||
|
:suggestions="inetSuggestions" @complete="searchInetSuggestions" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||||
<div class="flex flex-col gap-2 basis-5/12 grow">
|
<div class="flex flex-col gap-2 basis-5/12 grow">
|
||||||
<label for="dev_name">{{ t('dev_name') }}</label>
|
<label for="dev_name">{{ t('dev_name') }}</label>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ advanced_settings: 高级设置
|
|||||||
basic_settings: 基础设置
|
basic_settings: 基础设置
|
||||||
listener_urls: 监听地址
|
listener_urls: 监听地址
|
||||||
rpc_port: RPC端口
|
rpc_port: RPC端口
|
||||||
|
rpc_portal_whitelists: RPC白名单
|
||||||
config_network: 配置网络
|
config_network: 配置网络
|
||||||
running: 运行中
|
running: 运行中
|
||||||
error_msg: 错误信息
|
error_msg: 错误信息
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ advanced_settings: Advanced Settings
|
|||||||
basic_settings: Basic Settings
|
basic_settings: Basic Settings
|
||||||
listener_urls: Listener URLs
|
listener_urls: Listener URLs
|
||||||
rpc_port: RPC Port
|
rpc_port: RPC Port
|
||||||
|
rpc_portal_whitelists: RPC Whitelist
|
||||||
config_network: Config Network
|
config_network: Config Network
|
||||||
running: Running
|
running: Running
|
||||||
error_msg: Error Message
|
error_msg: Error Message
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ export interface NetworkConfig {
|
|||||||
|
|
||||||
enable_magic_dns?: boolean
|
enable_magic_dns?: boolean
|
||||||
enable_private_mode?: boolean
|
enable_private_mode?: boolean
|
||||||
|
|
||||||
|
rpc_portal_whitelists: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DEFAULT_NETWORK_CONFIG(): NetworkConfig {
|
export function DEFAULT_NETWORK_CONFIG(): NetworkConfig {
|
||||||
@@ -123,6 +125,7 @@ export function DEFAULT_NETWORK_CONFIG(): NetworkConfig {
|
|||||||
mapped_listeners: [],
|
mapped_listeners: [],
|
||||||
enable_magic_dns: false,
|
enable_magic_dns: false,
|
||||||
enable_private_mode: false,
|
enable_private_mode: false,
|
||||||
|
rpc_portal_whitelists: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ core_clap:
|
|||||||
rpc_portal:
|
rpc_portal:
|
||||||
en: "rpc portal address to listen for management. 0 means random port, 12345 means listen on 12345 of localhost, 0.0.0.0:12345 means listen on 12345 of all interfaces. default is 0 and will try 15888 first"
|
en: "rpc portal address to listen for management. 0 means random port, 12345 means listen on 12345 of localhost, 0.0.0.0:12345 means listen on 12345 of all interfaces. default is 0 and will try 15888 first"
|
||||||
zh-CN: "用于管理的RPC门户地址。0表示随机端口,12345表示在localhost的12345上监听,0.0.0.0:12345表示在所有接口的12345上监听。默认是0,首先尝试15888"
|
zh-CN: "用于管理的RPC门户地址。0表示随机端口,12345表示在localhost的12345上监听,0.0.0.0:12345表示在所有接口的12345上监听。默认是0,首先尝试15888"
|
||||||
|
rpc_portal_whitelist:
|
||||||
|
en: "rpc portal whitelist, only allow these addresses to access rpc portal, e.g.: 127.0.0.1,127.0.0.0/8,::1/128"
|
||||||
|
zh-CN: "RPC门户白名单,仅允许这些地址访问RPC门户,例如:127.0.0.1/32,127.0.0.0/8,::1/128"
|
||||||
listeners:
|
listeners:
|
||||||
en: |+
|
en: |+
|
||||||
listeners to accept connections, allow format:
|
listeners to accept connections, allow format:
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use cidr::IpCidr;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -87,6 +88,9 @@ pub trait ConfigLoader: Send + Sync {
|
|||||||
fn get_rpc_portal(&self) -> Option<SocketAddr>;
|
fn get_rpc_portal(&self) -> Option<SocketAddr>;
|
||||||
fn set_rpc_portal(&self, addr: SocketAddr);
|
fn set_rpc_portal(&self, addr: SocketAddr);
|
||||||
|
|
||||||
|
fn get_rpc_portal_whitelist(&self) -> Option<Vec<IpCidr>>;
|
||||||
|
fn set_rpc_portal_whitelist(&self, whitelist: Option<Vec<IpCidr>>);
|
||||||
|
|
||||||
fn get_vpn_portal_config(&self) -> Option<VpnPortalConfig>;
|
fn get_vpn_portal_config(&self) -> Option<VpnPortalConfig>;
|
||||||
fn set_vpn_portal_config(&self, config: VpnPortalConfig);
|
fn set_vpn_portal_config(&self, config: VpnPortalConfig);
|
||||||
|
|
||||||
@@ -243,6 +247,7 @@ struct Config {
|
|||||||
console_logger: Option<ConsoleLoggerConfig>,
|
console_logger: Option<ConsoleLoggerConfig>,
|
||||||
|
|
||||||
rpc_portal: Option<SocketAddr>,
|
rpc_portal: Option<SocketAddr>,
|
||||||
|
rpc_portal_whitelist: Option<Vec<IpCidr>>,
|
||||||
|
|
||||||
vpn_portal_config: Option<VpnPortalConfig>,
|
vpn_portal_config: Option<VpnPortalConfig>,
|
||||||
|
|
||||||
@@ -544,6 +549,14 @@ impl ConfigLoader for TomlConfigLoader {
|
|||||||
self.config.lock().unwrap().rpc_portal = Some(addr);
|
self.config.lock().unwrap().rpc_portal = Some(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_rpc_portal_whitelist(&self) -> Option<Vec<IpCidr>> {
|
||||||
|
self.config.lock().unwrap().rpc_portal_whitelist.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_rpc_portal_whitelist(&self, whitelist: Option<Vec<IpCidr>>) {
|
||||||
|
self.config.lock().unwrap().rpc_portal_whitelist = whitelist;
|
||||||
|
}
|
||||||
|
|
||||||
fn get_vpn_portal_config(&self) -> Option<VpnPortalConfig> {
|
fn get_vpn_portal_config(&self) -> Option<VpnPortalConfig> {
|
||||||
self.config.lock().unwrap().vpn_portal_config.clone()
|
self.config.lock().unwrap().vpn_portal_config.clone()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use cidr::IpCidr;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use easytier::{
|
use easytier::{
|
||||||
@@ -176,6 +177,14 @@ struct Cli {
|
|||||||
)]
|
)]
|
||||||
rpc_portal: Option<String>,
|
rpc_portal: Option<String>,
|
||||||
|
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
env = "ET_RPC_PORTAL_WHITELIST",
|
||||||
|
value_delimiter = ',',
|
||||||
|
help = t!("core_clap.rpc_portal_whitelist").to_string(),
|
||||||
|
)]
|
||||||
|
rpc_portal_whitelist: Option<Vec<IpCidr>>,
|
||||||
|
|
||||||
#[arg(
|
#[arg(
|
||||||
short,
|
short,
|
||||||
long,
|
long,
|
||||||
@@ -616,6 +625,8 @@ impl TryFrom<&Cli> for TomlConfigLoader {
|
|||||||
};
|
};
|
||||||
cfg.set_rpc_portal(rpc_portal);
|
cfg.set_rpc_portal(rpc_portal);
|
||||||
|
|
||||||
|
cfg.set_rpc_portal_whitelist(cli.rpc_portal_whitelist.clone());
|
||||||
|
|
||||||
if let Some(external_nodes) = cli.external_node.as_ref() {
|
if let Some(external_nodes) = cli.external_node.as_ref() {
|
||||||
let mut old_peers = cfg.get_peers();
|
let mut old_peers = cfg.get_peers();
|
||||||
old_peers.push(PeerConfig {
|
old_peers.push(PeerConfig {
|
||||||
|
|||||||
@@ -298,12 +298,13 @@ impl NicPacketFilter for MagicDnsServerInstanceData {
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl RpcServerHook for MagicDnsServerInstanceData {
|
impl RpcServerHook for MagicDnsServerInstanceData {
|
||||||
async fn on_new_client(&self, tunnel_info: Option<TunnelInfo>) {
|
async fn on_new_client(&self, tunnel_info: Option<TunnelInfo>)-> Result<Option<TunnelInfo>, anyhow::Error> {
|
||||||
println!("New client connected: {:?}", tunnel_info);
|
tracing::info!(?tunnel_info, "New client connected");
|
||||||
|
Ok(tunnel_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_client_disconnected(&self, tunnel_info: Option<TunnelInfo>) {
|
async fn on_client_disconnected(&self, tunnel_info: Option<TunnelInfo>) {
|
||||||
println!("Client disconnected: {:?}", tunnel_info);
|
tracing::info!(?tunnel_info, "Client disconnected");
|
||||||
let Some(tunnel_info) = tunnel_info else {
|
let Some(tunnel_info) = tunnel_info else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::net::Ipv4Addr;
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use cidr::Ipv4Inet;
|
use cidr::{IpCidr, Ipv4Inet};
|
||||||
|
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tokio::{sync::Mutex, task::JoinSet};
|
use tokio::{sync::Mutex, task::JoinSet};
|
||||||
@@ -29,8 +29,9 @@ use crate::peers::rpc_service::PeerManagerRpcService;
|
|||||||
use crate::peers::{create_packet_recv_chan, recv_packet_from_chan, PacketRecvChanReceiver};
|
use crate::peers::{create_packet_recv_chan, recv_packet_from_chan, PacketRecvChanReceiver};
|
||||||
use crate::proto::cli::VpnPortalRpc;
|
use crate::proto::cli::VpnPortalRpc;
|
||||||
use crate::proto::cli::{GetVpnPortalInfoRequest, GetVpnPortalInfoResponse, VpnPortalInfo};
|
use crate::proto::cli::{GetVpnPortalInfoRequest, GetVpnPortalInfoResponse, VpnPortalInfo};
|
||||||
|
use crate::proto::common::TunnelInfo;
|
||||||
use crate::proto::peer_rpc::PeerCenterRpcServer;
|
use crate::proto::peer_rpc::PeerCenterRpcServer;
|
||||||
use crate::proto::rpc_impl::standalone::StandAloneServer;
|
use crate::proto::rpc_impl::standalone::{RpcServerHook, StandAloneServer};
|
||||||
use crate::proto::rpc_types;
|
use crate::proto::rpc_types;
|
||||||
use crate::proto::rpc_types::controller::BaseController;
|
use crate::proto::rpc_types::controller::BaseController;
|
||||||
use crate::tunnel::tcp::TcpTunnelListener;
|
use crate::tunnel::tcp::TcpTunnelListener;
|
||||||
@@ -155,6 +156,58 @@ impl NicCtxContainer {
|
|||||||
|
|
||||||
type ArcNicCtx = Arc<Mutex<Option<NicCtxContainer>>>;
|
type ArcNicCtx = Arc<Mutex<Option<NicCtxContainer>>>;
|
||||||
|
|
||||||
|
pub struct InstanceRpcServerHook {
|
||||||
|
rpc_portal_whitelist: Vec<IpCidr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstanceRpcServerHook {
|
||||||
|
pub fn new(rpc_portal_whitelist: Option<Vec<IpCidr>>) -> Self {
|
||||||
|
let rpc_portal_whitelist = rpc_portal_whitelist
|
||||||
|
.unwrap_or_else(|| vec!["127.0.0.0/8".parse().unwrap(), "::1/128".parse().unwrap()]);
|
||||||
|
InstanceRpcServerHook {
|
||||||
|
rpc_portal_whitelist,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl RpcServerHook for InstanceRpcServerHook {
|
||||||
|
async fn on_new_client(
|
||||||
|
&self,
|
||||||
|
tunnel_info: Option<TunnelInfo>,
|
||||||
|
) -> Result<Option<TunnelInfo>, anyhow::Error> {
|
||||||
|
let tunnel_info = tunnel_info.ok_or_else(|| anyhow::anyhow!("tunnel info is None"))?;
|
||||||
|
|
||||||
|
let remote_url = tunnel_info
|
||||||
|
.remote_addr
|
||||||
|
.clone()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("remote_addr is None"))?;
|
||||||
|
|
||||||
|
let url_str = &remote_url.url;
|
||||||
|
let url = url::Url::parse(url_str)
|
||||||
|
.map_err(|e| anyhow::anyhow!("Failed to parse remote URL '{}': {}", url_str, e))?;
|
||||||
|
|
||||||
|
let host = url
|
||||||
|
.host_str()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("No host found in remote URL '{}'", url_str))?;
|
||||||
|
|
||||||
|
let ip_addr: IpAddr = host
|
||||||
|
.parse()
|
||||||
|
.map_err(|e| anyhow::anyhow!("Failed to parse IP address '{}': {}", host, e))?;
|
||||||
|
|
||||||
|
for cidr in &self.rpc_portal_whitelist {
|
||||||
|
if cidr.contains(&ip_addr) {
|
||||||
|
return Ok(Some(tunnel_info));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Rpc portal client IP {} not in whitelist: {:?}, ignoring client.",
|
||||||
|
ip_addr,
|
||||||
|
self.rpc_portal_whitelist
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Instance {
|
pub struct Instance {
|
||||||
inst_name: String,
|
inst_name: String,
|
||||||
|
|
||||||
@@ -674,6 +727,10 @@ impl Instance {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.set_hook(Arc::new(InstanceRpcServerHook::new(
|
||||||
|
self.global_ctx.config.get_rpc_portal_whitelist(),
|
||||||
|
)));
|
||||||
|
|
||||||
let _g = self.global_ctx.net_ns.guard();
|
let _g = self.global_ctx.net_ns.guard();
|
||||||
Ok(s.serve().await.with_context(|| "rpc server start failed")?)
|
Ok(s.serve().await.with_context(|| "rpc server start failed")?)
|
||||||
}
|
}
|
||||||
@@ -726,3 +783,129 @@ impl Instance {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{instance::instance::InstanceRpcServerHook, proto::rpc_impl::standalone::RpcServerHook};
|
||||||
|
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_rpc_portal_whitelist() {
|
||||||
|
use cidr::IpCidr;
|
||||||
|
|
||||||
|
struct TestCase {
|
||||||
|
remote_url: String,
|
||||||
|
whitelist: Option<Vec<IpCidr>>,
|
||||||
|
expected_result: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
let test_cases:Vec<TestCase> = vec![
|
||||||
|
// Test default whitelist (127.0.0.0/8, ::1/128)
|
||||||
|
TestCase {
|
||||||
|
remote_url: "tcp://127.0.0.1:15888".to_string(),
|
||||||
|
whitelist: None,
|
||||||
|
expected_result: true,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
remote_url: "tcp://127.1.2.3:15888".to_string(),
|
||||||
|
whitelist: None,
|
||||||
|
expected_result: true,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
remote_url: "tcp://192.168.1.1:15888".to_string(),
|
||||||
|
whitelist: None,
|
||||||
|
expected_result: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Test custom whitelist
|
||||||
|
TestCase {
|
||||||
|
remote_url: "tcp://192.168.1.10:15888".to_string(),
|
||||||
|
whitelist: Some(vec![
|
||||||
|
"192.168.1.0/24".parse().unwrap(),
|
||||||
|
"10.0.0.0/8".parse().unwrap(),
|
||||||
|
]),
|
||||||
|
expected_result: true,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
remote_url: "tcp://10.1.2.3:15888".to_string(),
|
||||||
|
whitelist: Some(vec![
|
||||||
|
"192.168.1.0/24".parse().unwrap(),
|
||||||
|
"10.0.0.0/8".parse().unwrap(),
|
||||||
|
]),
|
||||||
|
expected_result: true,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
remote_url: "tcp://172.16.0.1:15888".to_string(),
|
||||||
|
whitelist: Some(vec![
|
||||||
|
"192.168.1.0/24".parse().unwrap(),
|
||||||
|
"10.0.0.0/8".parse().unwrap(),
|
||||||
|
]),
|
||||||
|
expected_result: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Test empty whitelist (should reject all connections)
|
||||||
|
TestCase {
|
||||||
|
remote_url: "tcp://127.0.0.1:15888".to_string(),
|
||||||
|
whitelist: Some(vec![]),
|
||||||
|
expected_result: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Test broad whitelist (0.0.0.0/0 and ::/0 accept all IP addresses)
|
||||||
|
TestCase {
|
||||||
|
remote_url: "tcp://8.8.8.8:15888".to_string(),
|
||||||
|
whitelist: Some(vec![
|
||||||
|
"0.0.0.0/0".parse().unwrap(),
|
||||||
|
]),
|
||||||
|
expected_result: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Test edge case: specific IP whitelist
|
||||||
|
TestCase {
|
||||||
|
remote_url: "tcp://192.168.1.5:15888".to_string(),
|
||||||
|
whitelist: Some(vec![
|
||||||
|
"192.168.1.5/32".parse().unwrap(),
|
||||||
|
]),
|
||||||
|
expected_result: true,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
remote_url: "tcp://192.168.1.6:15888".to_string(),
|
||||||
|
whitelist: Some(vec![
|
||||||
|
"192.168.1.5/32".parse().unwrap(),
|
||||||
|
]),
|
||||||
|
expected_result: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Test invalid URL (this case will fail during URL parsing)
|
||||||
|
TestCase {
|
||||||
|
remote_url: "invalid-url".to_string(),
|
||||||
|
whitelist: None,
|
||||||
|
expected_result: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Test URL without IP address (this case will fail during IP parsing)
|
||||||
|
TestCase {
|
||||||
|
remote_url: "tcp://localhost:15888".to_string(),
|
||||||
|
whitelist: None,
|
||||||
|
expected_result: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for case in test_cases {
|
||||||
|
let hook = InstanceRpcServerHook::new(case.whitelist.clone());
|
||||||
|
let tunnel_info = Some(crate::proto::common::TunnelInfo {
|
||||||
|
remote_addr: Some(crate::proto::common::Url {
|
||||||
|
url: case.remote_url.clone(),
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = hook.on_new_client(tunnel_info).await;
|
||||||
|
if case.expected_result {
|
||||||
|
assert!(result.is_ok(), "Expected success for remote_url:{},whitelist:{:?},but got: {:?}", case.remote_url, case.whitelist, result);
|
||||||
|
} else {
|
||||||
|
assert!(result.is_err(), "Expected failure for remote_url:{},whitelist:{:?},but got: {:?}", case.remote_url, case.whitelist, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -527,6 +527,20 @@ impl NetworkConfig {
|
|||||||
.with_context(|| format!("failed to parse rpc portal port: {:?}", self.rpc_port))?,
|
.with_context(|| format!("failed to parse rpc portal port: {:?}", self.rpc_port))?,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if self.rpc_portal_whitelists.is_empty() {
|
||||||
|
cfg.set_rpc_portal_whitelist(None);
|
||||||
|
} else {
|
||||||
|
cfg.set_rpc_portal_whitelist(Some(
|
||||||
|
self.rpc_portal_whitelists
|
||||||
|
.iter()
|
||||||
|
.map(|s| {
|
||||||
|
s.parse()
|
||||||
|
.with_context(|| format!("failed to parse rpc portal whitelist: {}", s))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if self.enable_vpn_portal.unwrap_or_default() {
|
if self.enable_vpn_portal.unwrap_or_default() {
|
||||||
let cidr = format!(
|
let cidr = format!(
|
||||||
"{}/{}",
|
"{}/{}",
|
||||||
|
|||||||
@@ -21,7 +21,12 @@ use super::service_registry::ServiceRegistry;
|
|||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
#[auto_impl::auto_impl(Arc, Box)]
|
#[auto_impl::auto_impl(Arc, Box)]
|
||||||
pub trait RpcServerHook: Send + Sync {
|
pub trait RpcServerHook: Send + Sync {
|
||||||
async fn on_new_client(&self, _tunnel_info: Option<TunnelInfo>) {}
|
async fn on_new_client(
|
||||||
|
&self,
|
||||||
|
tunnel_info: Option<TunnelInfo>,
|
||||||
|
) -> Result<Option<TunnelInfo>, anyhow::Error> {
|
||||||
|
Ok(tunnel_info)
|
||||||
|
}
|
||||||
async fn on_client_disconnected(&self, _tunnel_info: Option<TunnelInfo>) {}
|
async fn on_client_disconnected(&self, _tunnel_info: Option<TunnelInfo>) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +77,13 @@ impl<L: TunnelListener + 'static> StandAloneServer<L> {
|
|||||||
let inflight_server = inflight.clone();
|
let inflight_server = inflight.clone();
|
||||||
let hook = hook.clone();
|
let hook = hook.clone();
|
||||||
|
|
||||||
hook.on_new_client(tunnel_info.clone()).await;
|
let tunnel_info = match hook.on_new_client(tunnel_info).await {
|
||||||
|
Ok(info) => info,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!(?e, "standalone hook.on_new_client failed");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
inflight_server.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
inflight_server.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||||
tasks.lock().unwrap().spawn(async move {
|
tasks.lock().unwrap().spawn(async move {
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ message NetworkConfig {
|
|||||||
|
|
||||||
optional bool enable_magic_dns = 42;
|
optional bool enable_magic_dns = 42;
|
||||||
optional bool enable_private_mode = 43;
|
optional bool enable_private_mode = 43;
|
||||||
|
|
||||||
|
repeated string rpc_portal_whitelists = 44;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MyNodeInfo {
|
message MyNodeInfo {
|
||||||
|
|||||||
Reference in New Issue
Block a user