mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-07 10:14:35 +00:00
cli for port forward and tcp whitelist (#1165)
This commit is contained in:
@@ -6,8 +6,9 @@ use std::{
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use crate::common::token_bucket::TokenBucket;
|
||||
use crate::common::{config::ConfigLoader, global_ctx::ArcGlobalCtx, token_bucket::TokenBucket};
|
||||
use crate::proto::acl::*;
|
||||
use anyhow::Context as _;
|
||||
use dashmap::DashMap;
|
||||
use tokio::task::JoinSet;
|
||||
|
||||
@@ -993,6 +994,146 @@ impl AclStatKey {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AclRuleBuilder {
|
||||
pub acl: Option<Acl>,
|
||||
pub tcp_whitelist: Vec<String>,
|
||||
pub udp_whitelist: Vec<String>,
|
||||
pub whitelist_priority: Option<u32>,
|
||||
}
|
||||
|
||||
impl AclRuleBuilder {
|
||||
fn parse_port_list(port_list: &[String]) -> anyhow::Result<Vec<String>> {
|
||||
let mut ports = Vec::new();
|
||||
|
||||
for port_spec in port_list {
|
||||
if port_spec.contains('-') {
|
||||
// Handle port range like "8000-9000"
|
||||
let parts: Vec<&str> = port_spec.split('-').collect();
|
||||
if parts.len() != 2 {
|
||||
return Err(anyhow::anyhow!("Invalid port range format: {}", port_spec));
|
||||
}
|
||||
|
||||
let start: u16 = parts[0]
|
||||
.parse()
|
||||
.with_context(|| format!("Invalid start port in range: {}", port_spec))?;
|
||||
let end: u16 = parts[1]
|
||||
.parse()
|
||||
.with_context(|| format!("Invalid end port in range: {}", port_spec))?;
|
||||
|
||||
if start > end {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Start port must be <= end port in range: {}",
|
||||
port_spec
|
||||
));
|
||||
}
|
||||
|
||||
// acl can handle port range
|
||||
ports.push(port_spec.clone());
|
||||
} else {
|
||||
// Handle single port
|
||||
let port: u16 = port_spec
|
||||
.parse()
|
||||
.with_context(|| format!("Invalid port number: {}", port_spec))?;
|
||||
ports.push(port.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ports)
|
||||
}
|
||||
|
||||
fn generate_acl_from_whitelists(&mut self) -> anyhow::Result<()> {
|
||||
if self.tcp_whitelist.is_empty() && self.udp_whitelist.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Create inbound chain for whitelist rules
|
||||
let mut inbound_chain = Chain {
|
||||
name: "inbound_whitelist".to_string(),
|
||||
chain_type: ChainType::Inbound as i32,
|
||||
description: "Auto-generated inbound whitelist from CLI".to_string(),
|
||||
enabled: true,
|
||||
rules: vec![],
|
||||
default_action: Action::Drop as i32, // Default deny
|
||||
};
|
||||
|
||||
let mut rule_priority = self.whitelist_priority.unwrap_or(1000u32);
|
||||
|
||||
// Add TCP whitelist rules
|
||||
if !self.tcp_whitelist.is_empty() {
|
||||
let tcp_ports = Self::parse_port_list(&self.tcp_whitelist)?;
|
||||
let tcp_rule = Rule {
|
||||
name: "tcp_whitelist".to_string(),
|
||||
description: "Auto-generated TCP whitelist rule".to_string(),
|
||||
priority: rule_priority,
|
||||
enabled: true,
|
||||
protocol: Protocol::Tcp as i32,
|
||||
ports: tcp_ports,
|
||||
source_ips: vec![],
|
||||
destination_ips: vec![],
|
||||
source_ports: vec![],
|
||||
action: Action::Allow as i32,
|
||||
rate_limit: 0,
|
||||
burst_limit: 0,
|
||||
stateful: true,
|
||||
};
|
||||
inbound_chain.rules.push(tcp_rule);
|
||||
rule_priority -= 1;
|
||||
}
|
||||
|
||||
// Add UDP whitelist rules
|
||||
if !self.udp_whitelist.is_empty() {
|
||||
let udp_ports = Self::parse_port_list(&self.udp_whitelist)?;
|
||||
let udp_rule = Rule {
|
||||
name: "udp_whitelist".to_string(),
|
||||
description: "Auto-generated UDP whitelist rule".to_string(),
|
||||
priority: rule_priority,
|
||||
enabled: true,
|
||||
protocol: Protocol::Udp as i32,
|
||||
ports: udp_ports,
|
||||
source_ips: vec![],
|
||||
destination_ips: vec![],
|
||||
source_ports: vec![],
|
||||
action: Action::Allow as i32,
|
||||
rate_limit: 0,
|
||||
burst_limit: 0,
|
||||
stateful: false,
|
||||
};
|
||||
inbound_chain.rules.push(udp_rule);
|
||||
}
|
||||
|
||||
if self.acl.is_none() {
|
||||
self.acl = Some(Acl::default());
|
||||
}
|
||||
|
||||
let acl = self.acl.as_mut().unwrap();
|
||||
|
||||
if let Some(ref mut acl_v1) = acl.acl_v1 {
|
||||
acl_v1.chains.push(inbound_chain);
|
||||
} else {
|
||||
acl.acl_v1 = Some(AclV1 {
|
||||
chains: vec![inbound_chain],
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_build(mut self) -> anyhow::Result<Option<Acl>> {
|
||||
self.generate_acl_from_whitelists()?;
|
||||
Ok(self.acl.clone())
|
||||
}
|
||||
|
||||
pub fn build(global_ctx: &ArcGlobalCtx) -> anyhow::Result<Option<Acl>> {
|
||||
let builder = AclRuleBuilder {
|
||||
acl: global_ctx.config.get_acl(),
|
||||
tcp_whitelist: global_ctx.config.get_tcp_whitelist(),
|
||||
udp_whitelist: global_ctx.config.get_udp_whitelist(),
|
||||
whitelist_priority: None,
|
||||
};
|
||||
builder.do_build()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum AclStatType {
|
||||
Total,
|
||||
|
||||
@@ -122,6 +122,12 @@ pub trait ConfigLoader: Send + Sync {
|
||||
fn get_acl(&self) -> Option<Acl>;
|
||||
fn set_acl(&self, acl: Option<Acl>);
|
||||
|
||||
fn get_tcp_whitelist(&self) -> Vec<String>;
|
||||
fn set_tcp_whitelist(&self, whitelist: Vec<String>);
|
||||
|
||||
fn get_udp_whitelist(&self) -> Vec<String>;
|
||||
fn set_udp_whitelist(&self, whitelist: Vec<String>);
|
||||
|
||||
fn dump(&self) -> String;
|
||||
}
|
||||
|
||||
@@ -230,7 +236,7 @@ pub struct VpnPortalConfig {
|
||||
pub wireguard_listen: SocketAddr,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
|
||||
pub struct PortForwardConfig {
|
||||
pub bind_addr: SocketAddr,
|
||||
pub dst_addr: SocketAddr,
|
||||
@@ -299,6 +305,9 @@ struct Config {
|
||||
flags_struct: Option<Flags>,
|
||||
|
||||
acl: Option<Acl>,
|
||||
|
||||
tcp_whitelist: Option<Vec<String>>,
|
||||
udp_whitelist: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -665,6 +674,32 @@ impl ConfigLoader for TomlConfigLoader {
|
||||
self.config.lock().unwrap().acl = acl;
|
||||
}
|
||||
|
||||
fn get_tcp_whitelist(&self) -> Vec<String> {
|
||||
self.config
|
||||
.lock()
|
||||
.unwrap()
|
||||
.tcp_whitelist
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn set_tcp_whitelist(&self, whitelist: Vec<String>) {
|
||||
self.config.lock().unwrap().tcp_whitelist = Some(whitelist);
|
||||
}
|
||||
|
||||
fn get_udp_whitelist(&self) -> Vec<String> {
|
||||
self.config
|
||||
.lock()
|
||||
.unwrap()
|
||||
.udp_whitelist
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn set_udp_whitelist(&self, whitelist: Vec<String>) {
|
||||
self.config.lock().unwrap().udp_whitelist = Some(whitelist);
|
||||
}
|
||||
|
||||
fn dump(&self) -> String {
|
||||
let default_flags_json = serde_json::to_string(&gen_default_flags()).unwrap();
|
||||
let default_flags_hashmap =
|
||||
|
||||
Reference in New Issue
Block a user