Полностью доведена production-логика AWG2 harness по up2: strict execution, tc parser, anti-torrent, status и документация
This commit is contained in:
+133
-15
@@ -1,12 +1,13 @@
|
||||
use crate::{
|
||||
config::Config,
|
||||
docker,
|
||||
netns::{build_transport, NsTransport},
|
||||
netns::{NsTransport, build_transport},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct FirewallActual {
|
||||
table_exists: bool,
|
||||
counter_exists: bool,
|
||||
chain_exists: bool,
|
||||
rule_matches: bool,
|
||||
}
|
||||
@@ -16,47 +17,164 @@ pub fn apply_firewall(cfg: &Config) -> anyhow::Result<()> {
|
||||
let transport = build_transport(target.container_pid, &target.container_name);
|
||||
|
||||
if !cfg.anti_torrent.enabled {
|
||||
let _ = transport.run(&["sh", "-c", "nft delete table inet awg2_harness 2>/dev/null || true"])?;
|
||||
let _ = transport.run(&["nft", "delete", "table", "inet", "awg2_harness"]);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let actual = inspect_firewall(transport.as_ref(), cfg)?;
|
||||
if actual.table_exists && actual.chain_exists && actual.rule_matches {
|
||||
if actual.table_exists && actual.counter_exists && actual.chain_exists && actual.rule_matches {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let rule = format!(
|
||||
"nft 'add table inet awg2_harness' 2>/dev/null || true; \
|
||||
nft 'add chain inet awg2_harness forward_antitorrent {{ type filter hook forward priority filter; policy accept; }}' 2>/dev/null || true; \
|
||||
nft 'add rule inet awg2_harness forward_antitorrent ip saddr {cidr} ct state new meter awg2_peer_connlimit_v4 {{ ip saddr ct count over {max} }} counter name antitorrent_dropped_v4 drop' 2>/dev/null || true",
|
||||
cidr = cfg.anti_torrent.peer_ipv4_cidr,
|
||||
max = cfg.anti_torrent.max_connections_per_peer
|
||||
);
|
||||
apply_rule(transport.as_ref(), &rule)?;
|
||||
run_allow_exists(
|
||||
transport.as_ref(),
|
||||
&["nft", "add", "table", "inet", "awg2_harness"],
|
||||
)?;
|
||||
run_allow_exists(
|
||||
transport.as_ref(),
|
||||
&[
|
||||
"nft",
|
||||
"add",
|
||||
"counter",
|
||||
"inet",
|
||||
"awg2_harness",
|
||||
"antitorrent_dropped_v4",
|
||||
],
|
||||
)?;
|
||||
run_allow_exists_chain(transport.as_ref())?;
|
||||
apply_rule(
|
||||
transport.as_ref(),
|
||||
&cfg.anti_torrent.peer_ipv4_cidr,
|
||||
cfg.anti_torrent.max_connections_per_peer,
|
||||
)?;
|
||||
|
||||
let verify = inspect_firewall(transport.as_ref(), cfg)?;
|
||||
if !(verify.table_exists && verify.counter_exists && verify.chain_exists && verify.rule_matches)
|
||||
{
|
||||
anyhow::bail!("firewall verify-after-apply failed");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn inspect_firewall(transport: &dyn NsTransport, cfg: &Config) -> anyhow::Result<FirewallActual> {
|
||||
let table = transport
|
||||
.run(&["sh", "-c", "nft list table inet awg2_harness 2>/dev/null"])
|
||||
.run(&["nft", "list", "table", "inet", "awg2_harness"])
|
||||
.unwrap_or_default();
|
||||
|
||||
let table_exists = !table.trim().is_empty();
|
||||
let counter_exists = table.contains("counter antitorrent_dropped_v4");
|
||||
let chain_exists = table.contains("chain forward_antitorrent");
|
||||
|
||||
let needle_cidr = format!("ip saddr {}", cfg.anti_torrent.peer_ipv4_cidr);
|
||||
let needle_max = format!("ct count over {}", cfg.anti_torrent.max_connections_per_peer);
|
||||
let needle_max = format!(
|
||||
"ct count over {}",
|
||||
cfg.anti_torrent.max_connections_per_peer
|
||||
);
|
||||
let rule_matches = table.contains(&needle_cidr) && table.contains(&needle_max);
|
||||
|
||||
Ok(FirewallActual {
|
||||
table_exists,
|
||||
counter_exists,
|
||||
chain_exists,
|
||||
rule_matches,
|
||||
})
|
||||
}
|
||||
|
||||
fn apply_rule(transport: &dyn NsTransport, rule: &str) -> anyhow::Result<()> {
|
||||
let _ = transport.run(&["sh", "-c", rule])?;
|
||||
fn apply_rule(transport: &dyn NsTransport, cidr: &str, max_conn: u32) -> anyhow::Result<()> {
|
||||
let _ = transport.run(&[
|
||||
"nft",
|
||||
"add",
|
||||
"rule",
|
||||
"inet",
|
||||
"awg2_harness",
|
||||
"forward_antitorrent",
|
||||
"ip",
|
||||
"saddr",
|
||||
cidr,
|
||||
"ct",
|
||||
"state",
|
||||
"new",
|
||||
"meter",
|
||||
"awg2_peer_connlimit_v4",
|
||||
"{",
|
||||
"ip",
|
||||
"saddr",
|
||||
"ct",
|
||||
"count",
|
||||
"over",
|
||||
&max_conn.to_string(),
|
||||
"}",
|
||||
"counter",
|
||||
"name",
|
||||
"antitorrent_dropped_v4",
|
||||
"drop",
|
||||
])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_allow_exists(transport: &dyn NsTransport, args: &[&str]) -> anyhow::Result<()> {
|
||||
match transport.run(args) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
let msg = e.to_string();
|
||||
if msg.contains("File exists") || msg.contains("exists") {
|
||||
return Ok(());
|
||||
}
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_allow_exists_chain(transport: &dyn NsTransport) -> anyhow::Result<()> {
|
||||
run_allow_exists(
|
||||
transport,
|
||||
&[
|
||||
"nft",
|
||||
"add",
|
||||
"chain",
|
||||
"inet",
|
||||
"awg2_harness",
|
||||
"forward_antitorrent",
|
||||
"{",
|
||||
"type",
|
||||
"filter",
|
||||
"hook",
|
||||
"forward",
|
||||
"priority",
|
||||
"filter;",
|
||||
"policy",
|
||||
"accept;",
|
||||
"}",
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn antitorrent_drop_counter(cfg: &Config) -> anyhow::Result<Option<(u64, u64)>> {
|
||||
if !cfg.anti_torrent.enabled {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let target = docker::discover_target(cfg)?;
|
||||
let transport = build_transport(target.container_pid, &target.container_name);
|
||||
let table = transport
|
||||
.run(&["nft", "list", "table", "inet", "awg2_harness"])
|
||||
.unwrap_or_default();
|
||||
|
||||
for line in table.lines() {
|
||||
if line.contains("counter antitorrent_dropped_v4") && line.contains("packets") {
|
||||
let toks: Vec<&str> = line.split_whitespace().collect();
|
||||
if let Some(idx) = toks.iter().position(|t| *t == "packets") {
|
||||
if let Some(v) = toks.get(idx + 1).and_then(|s| s.parse::<u64>().ok()) {
|
||||
let bytes = toks
|
||||
.iter()
|
||||
.position(|t| *t == "bytes")
|
||||
.and_then(|i| toks.get(i + 1))
|
||||
.and_then(|s| s.parse::<u64>().ok())
|
||||
.unwrap_or(0);
|
||||
return Ok(Some((v, bytes)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user