mirror of
https://mirror.suhoan.cn/https://github.com/EasyTier/EasyTier.git
synced 2025-12-14 13:47:24 +08:00
Implement ACL (#1140)
1. get acl stats
```
./easytier-cli acl stats
AclStats:
Global:
CacheHits: 4
CacheMaxSize: 10000
CacheSize: 5
DefaultAllows: 3
InboundPacketsAllowed: 2
InboundPacketsTotal: 2
OutboundPacketsAllowed: 7
OutboundPacketsTotal: 7
PacketsAllowed: 9
PacketsTotal: 9
RuleMatches: 2
ConnTrack:
[src: 10.14.11.1:57444, dst: 10.14.11.2:1000, proto: Tcp, state: New, pkts: 1, bytes: 60, created: 2025-07-24 10:13:39 +08:00, last_seen: 2025-07-24 10:13:39 +08:00]
Rules:
[name: 'tcp_whitelist', prio: 1000, action: Allow, enabled: true, proto: Tcp, ports: ["1000"], src_ports: [], src_ips: [], dst_ips: [], stateful: true, rate: 0, burst: 0] [pkts: 2, bytes: 120]
```
2. use tcp/udp whitelist to block unexpected traffic.
`sudo ./easytier-core -d --tcp-whitelist 1000`
3. use complete acl ability with config file:
```
[[acl.acl_v1.chains]]
name = "inbound_whitelist"
chain_type = 1
description = "Auto-generated inbound whitelist from CLI"
enabled = true
default_action = 2
[[acl.acl_v1.chains.rules]]
name = "tcp_whitelist"
description = "Auto-generated TCP whitelist rule"
priority = 1000
enabled = true
protocol = 1
ports = ["1000"]
source_ips = []
destination_ips = []
source_ports = []
action = 1
rate_limit = 0
burst_limit = 0
stateful = true
```
This commit is contained in:
@@ -1328,3 +1328,183 @@ async fn avoid_tunnel_loop_back_to_virtual_network() {
|
||||
|
||||
drop_insts(insts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial_test::serial]
|
||||
pub async fn acl_rule_test_inbound() {
|
||||
use crate::tunnel::{
|
||||
common::tests::_tunnel_pingpong_netns,
|
||||
tcp::{TcpTunnelConnector, TcpTunnelListener},
|
||||
udp::{UdpTunnelConnector, UdpTunnelListener},
|
||||
};
|
||||
use rand::Rng;
|
||||
let insts = init_three_node("udp").await;
|
||||
|
||||
// 构造 ACL 配置
|
||||
use crate::proto::acl::*;
|
||||
let mut acl = Acl::default();
|
||||
let mut acl_v1 = AclV1::default();
|
||||
|
||||
let mut chain = Chain::default();
|
||||
chain.name = "test_inbound".to_string();
|
||||
chain.chain_type = ChainType::Inbound as i32;
|
||||
chain.enabled = true;
|
||||
|
||||
// 禁止 8080
|
||||
let mut deny_rule = Rule::default();
|
||||
deny_rule.name = "deny_8080".to_string();
|
||||
deny_rule.priority = 200;
|
||||
deny_rule.enabled = true;
|
||||
deny_rule.action = Action::Drop as i32;
|
||||
deny_rule.protocol = Protocol::Any as i32;
|
||||
deny_rule.ports = vec!["8080".to_string()];
|
||||
chain.rules.push(deny_rule);
|
||||
|
||||
// 允许其他
|
||||
let mut allow_rule = Rule::default();
|
||||
allow_rule.name = "allow_all".to_string();
|
||||
allow_rule.priority = 100;
|
||||
allow_rule.enabled = true;
|
||||
allow_rule.action = Action::Allow as i32;
|
||||
allow_rule.protocol = Protocol::Any as i32;
|
||||
allow_rule.stateful = true;
|
||||
chain.rules.push(allow_rule);
|
||||
|
||||
// 禁止 src ip 为 10.144.144.2 的流量
|
||||
let mut deny_rule = Rule::default();
|
||||
deny_rule.name = "deny_10.144.144.2".to_string();
|
||||
deny_rule.priority = 200;
|
||||
deny_rule.enabled = true;
|
||||
deny_rule.action = Action::Drop as i32;
|
||||
deny_rule.protocol = Protocol::Any as i32;
|
||||
deny_rule.source_ips = vec!["10.144.144.2/32".to_string()];
|
||||
chain.rules.push(deny_rule);
|
||||
|
||||
acl_v1.chains.push(chain);
|
||||
acl.acl_v1 = Some(acl_v1);
|
||||
|
||||
// convert acl to to toml
|
||||
let acl_toml = toml::to_string(&acl).unwrap();
|
||||
println!("ACL TOML: {}", acl_toml);
|
||||
|
||||
insts[2]
|
||||
.get_global_ctx()
|
||||
.get_acl_filter()
|
||||
.reload_rules(Some(&acl));
|
||||
|
||||
// TCP 测试部分
|
||||
{
|
||||
// 2. 在 inst2 上监听 8080 和 8081
|
||||
let listener_8080 = TcpTunnelListener::new("tcp://0.0.0.0:8080".parse().unwrap());
|
||||
let listener_8081 = TcpTunnelListener::new("tcp://0.0.0.0:8081".parse().unwrap());
|
||||
let listener_8082 = TcpTunnelListener::new("tcp://0.0.0.0:8082".parse().unwrap());
|
||||
|
||||
// 3. inst1 作为客户端,尝试连接 inst2 的 8080(应被拒绝)和 8081(应被允许)
|
||||
let connector_8080 =
|
||||
TcpTunnelConnector::new(format!("tcp://{}:8080", "10.144.144.3").parse().unwrap());
|
||||
let connector_8081 =
|
||||
TcpTunnelConnector::new(format!("tcp://{}:8081", "10.144.144.3").parse().unwrap());
|
||||
let connector_8082 =
|
||||
TcpTunnelConnector::new(format!("tcp://{}:8082", "10.144.144.3").parse().unwrap());
|
||||
|
||||
// 4. 构造测试数据
|
||||
let mut buf = vec![0; 32];
|
||||
rand::thread_rng().fill(&mut buf[..]);
|
||||
|
||||
// 5. 8081 应该可以 pingpong 成功
|
||||
_tunnel_pingpong_netns(
|
||||
listener_8081,
|
||||
connector_8081,
|
||||
NetNS::new(Some("net_c".into())),
|
||||
NetNS::new(Some("net_a".into())),
|
||||
buf.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
// 6. 8080 应该连接失败(被 ACL 拦截)
|
||||
let result = tokio::time::timeout(
|
||||
std::time::Duration::from_millis(200),
|
||||
_tunnel_pingpong_netns(
|
||||
listener_8080,
|
||||
connector_8080,
|
||||
NetNS::new(Some("net_c".into())),
|
||||
NetNS::new(Some("net_a".into())),
|
||||
buf.clone(),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(result.is_err(), "TCP 连接 8080 应被 ACL 拦截,不能成功");
|
||||
|
||||
// 7. 从 10.144.144.2 连接 8082 应该连接失败(被 ACL 拦截)
|
||||
let result = tokio::time::timeout(
|
||||
std::time::Duration::from_millis(200),
|
||||
_tunnel_pingpong_netns(
|
||||
listener_8082,
|
||||
connector_8082,
|
||||
NetNS::new(Some("net_c".into())),
|
||||
NetNS::new(Some("net_b".into())),
|
||||
buf.clone(),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(result.is_err(), "TCP 连接 8082 应被 ACL 拦截,不能成功");
|
||||
|
||||
let stats = insts[2].get_global_ctx().get_acl_filter().get_stats();
|
||||
println!("stats: {:?}", stats);
|
||||
}
|
||||
|
||||
// UDP 测试部分
|
||||
{
|
||||
// 1. 在 inst2 上监听 UDP 8080 和 8081
|
||||
let listener_8080 = UdpTunnelListener::new("udp://0.0.0.0:8080".parse().unwrap());
|
||||
let listener_8081 = UdpTunnelListener::new("udp://0.0.0.0:8081".parse().unwrap());
|
||||
|
||||
// 2. inst1 作为客户端,尝试连接 inst2 的 8080(应被拒绝)和 8081(应被允许)
|
||||
let connector_8080 =
|
||||
UdpTunnelConnector::new(format!("udp://{}:8080", "10.144.144.3").parse().unwrap());
|
||||
let connector_8081 =
|
||||
UdpTunnelConnector::new(format!("udp://{}:8081", "10.144.144.3").parse().unwrap());
|
||||
|
||||
// 3. 构造测试数据
|
||||
let mut buf = vec![0; 32];
|
||||
rand::thread_rng().fill(&mut buf[..]);
|
||||
|
||||
// 4. 8081 应该可以 pingpong 成功
|
||||
_tunnel_pingpong_netns(
|
||||
listener_8081,
|
||||
connector_8081,
|
||||
NetNS::new(Some("net_c".into())),
|
||||
NetNS::new(Some("net_a".into())),
|
||||
buf.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
// 5. 8080 应该连接失败(被 ACL 拦截)
|
||||
let result = tokio::time::timeout(
|
||||
std::time::Duration::from_millis(200),
|
||||
_tunnel_pingpong_netns(
|
||||
listener_8080,
|
||||
connector_8080,
|
||||
NetNS::new(Some("net_c".into())),
|
||||
NetNS::new(Some("net_a".into())),
|
||||
buf.clone(),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(result.is_err(), "UDP 连接 8080 应被 ACL 拦截,不能成功");
|
||||
|
||||
let stats = insts[2].get_global_ctx().get_acl_filter().get_stats();
|
||||
println!("stats: {}", stats);
|
||||
}
|
||||
|
||||
// remove acl, 8080 should succ
|
||||
insts[2]
|
||||
.get_global_ctx()
|
||||
.get_acl_filter()
|
||||
.reload_rules(None);
|
||||
|
||||
drop_insts(insts).await;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user