Полный продакшн-фикс: parity с old bash, drift-recovery, status/runtime и builder separation

This commit is contained in:
2026-05-10 17:09:39 +05:00
parent b8d2c4592b
commit 76c6b52f4e
14 changed files with 558 additions and 62 deletions
+95 -13
View File
@@ -11,6 +11,12 @@ struct FirewallActual {
counter_exists: bool,
chain_exists: bool,
rule_matches: bool,
hook_forward_present: bool,
policy_accept_present: bool,
ct_state_new_present: bool,
meter_present: bool,
counter_name_used: bool,
drop_present: bool,
}
pub fn apply_firewall(cfg: &Config) -> anyhow::Result<()> {
@@ -25,7 +31,7 @@ pub fn apply_firewall(cfg: &Config) -> anyhow::Result<()> {
}
let actual = inspect_firewall(transport.as_ref(), cfg)?;
if actual.table_exists && actual.counter_exists && actual.chain_exists && actual.rule_matches {
if !firewall_has_drift(&actual) {
return Ok(());
}
@@ -57,23 +63,44 @@ pub fn apply_firewall(cfg: &Config) -> anyhow::Result<()> {
)?;
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");
if firewall_has_drift(&verify) {
anyhow::bail!(
"firewall verify-after-apply failed: {}",
firewall_drift_reason(&verify)
);
}
Ok(())
}
fn inspect_firewall(transport: &dyn NsTransport, cfg: &Config) -> anyhow::Result<FirewallActual> {
let table = inspect_cmd(
transport,
&["nft", "list", "table", "inet", "awg2_harness"],
"firewall table",
)?;
let table = match transport.run(&["nft", "list", "table", "inet", "awg2_harness"]) {
Ok(out) => out,
Err(e) if is_nft_not_found(&e.to_string()) => {
return Ok(FirewallActual {
table_exists: false,
counter_exists: false,
chain_exists: false,
rule_matches: false,
hook_forward_present: false,
policy_accept_present: false,
ct_state_new_present: false,
meter_present: false,
counter_name_used: false,
drop_present: false,
});
}
Err(e) => return Err(anyhow::anyhow!("nft table inspect failed: {e}")),
};
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 hook_forward_present = table.contains("hook forward");
let policy_accept_present = table.contains("policy accept");
let ct_state_new_present = table.contains("ct state new");
let meter_present = table.contains("meter awg2_peer_connlimit_v4");
let counter_name_used = table.contains("counter name antitorrent_dropped_v4");
let drop_present = table.contains(" drop") || table.contains("\ndrop");
let rule_matches = firewall_rule_contract_matches(
&table,
@@ -86,6 +113,12 @@ fn inspect_firewall(transport: &dyn NsTransport, cfg: &Config) -> anyhow::Result
counter_exists,
chain_exists,
rule_matches,
hook_forward_present,
policy_accept_present,
ct_state_new_present,
meter_present,
counter_name_used,
drop_present,
})
}
@@ -179,10 +212,59 @@ fn run_allow_notfound(transport: &dyn NsTransport, args: &[&str]) -> anyhow::Res
}
}
fn inspect_cmd(transport: &dyn NsTransport, args: &[&str], what: &str) -> anyhow::Result<String> {
transport
.run(args)
.map_err(|e| anyhow::anyhow!("{what} inspect failed: {e}"))
fn is_nft_not_found(msg: &str) -> bool {
let s = msg.to_ascii_lowercase();
s.contains("no such file")
|| s.contains("not found")
|| s.contains("not exist")
|| s.contains("no such table")
}
fn firewall_has_drift(actual: &FirewallActual) -> bool {
!(actual.table_exists
&& actual.counter_exists
&& actual.chain_exists
&& actual.rule_matches
&& actual.hook_forward_present
&& actual.policy_accept_present
&& actual.ct_state_new_present
&& actual.meter_present
&& actual.counter_name_used
&& actual.drop_present)
}
fn firewall_drift_reason(actual: &FirewallActual) -> String {
if !actual.table_exists {
return "missing nft table".to_string();
}
if !actual.counter_exists {
return "missing nft counter".to_string();
}
if !actual.chain_exists {
return "missing nft chain".to_string();
}
if !actual.hook_forward_present {
return "missing nft forward hook".to_string();
}
if !actual.policy_accept_present {
return "missing nft policy accept".to_string();
}
if !actual.ct_state_new_present {
return "missing ct state new".to_string();
}
if !actual.meter_present {
return "missing nft meter".to_string();
}
if !actual.counter_name_used {
return "missing nft counter name usage".to_string();
}
if !actual.drop_present {
return "missing drop verdict".to_string();
}
if !actual.rule_matches {
return "wrong anti-torrent threshold".to_string();
}
"unknown firewall drift".to_string()
}
fn firewall_rule_contract_matches(table: &str, cidr: &str, max_conn: u32) -> bool {