fix(cd): accept bounded partial SSH route proof
This commit is contained in:
@@ -3223,7 +3223,8 @@ jobs:
|
||||
# NetworkPolicy boundary and the target replied. Keep host
|
||||
# service readiness separate from this security-boundary
|
||||
# verifier; a stopped sshd must not make a healthy rollout
|
||||
# look like a policy failure.
|
||||
# look like a policy failure. This is bounded to the fixed
|
||||
# targets whose exact CIDRs were verified immediately above.
|
||||
refused.append(host)
|
||||
except OSError:
|
||||
# Endpoint availability is a separate runtime signal. The
|
||||
@@ -3243,7 +3244,13 @@ jobs:
|
||||
"broker_ssh_allowlisted_endpoint_unavailable="
|
||||
+ ",".join(unavailable)
|
||||
)
|
||||
raise SystemExit(0 if connected else 1)
|
||||
# Static verification already proves the exact five-destination
|
||||
# allowlist. The live probe only needs one positive route signal;
|
||||
# requiring every host to answer confuses host/service availability
|
||||
# (for example a booted host with sshd down) with NetworkPolicy
|
||||
# correctness. Arbitrary OSError values remain non-evidence, so an
|
||||
# all-timeout/unreachable result still fails closed.
|
||||
raise SystemExit(0 if connected or refused else 1)
|
||||
PY
|
||||
}
|
||||
verify_ssh_denied awoooi-api api || {
|
||||
@@ -3253,7 +3260,7 @@ jobs:
|
||||
echo "❌ signal worker can still establish host SSH connections"; exit 1;
|
||||
}
|
||||
verify_broker_ssh_allowed || {
|
||||
echo "❌ execution broker cannot reach any allowlisted SSH endpoint"; exit 1;
|
||||
echo "❌ execution broker has no live route evidence for allowlisted SSH endpoints"; exit 1;
|
||||
}
|
||||
echo "executor_boundary_stage=live_socket_boundary_verified"
|
||||
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import yaml
|
||||
|
||||
_BROKER_PROBE_HOSTS = (
|
||||
"192.168.0.110",
|
||||
"192.168.0.112",
|
||||
"192.168.0.120",
|
||||
"192.168.0.121",
|
||||
"192.168.0.188",
|
||||
)
|
||||
|
||||
|
||||
def _network_policies() -> dict[str, dict[str, Any]]:
|
||||
repo_root = Path(__file__).resolve().parents[3]
|
||||
@@ -24,6 +38,64 @@ def _ports(policy: dict[str, Any]) -> set[int]:
|
||||
}
|
||||
|
||||
|
||||
def _broker_probe_script() -> str:
|
||||
repo_root = Path(__file__).resolve().parents[3]
|
||||
workflow = (repo_root / ".gitea/workflows/cd.yaml").read_text()
|
||||
function = re.search(
|
||||
r"verify_broker_ssh_allowed\(\) \{.*?<<'PY'\n"
|
||||
r"(?P<script>.*?)\n\s+PY\n\s+\}",
|
||||
workflow,
|
||||
re.DOTALL,
|
||||
)
|
||||
assert function is not None
|
||||
return textwrap.dedent(function.group("script"))
|
||||
|
||||
|
||||
def _run_broker_probe(
|
||||
tmp_path: Path,
|
||||
*,
|
||||
outcomes: dict[str, str],
|
||||
) -> subprocess.CompletedProcess[str]:
|
||||
fake_socket = tmp_path / "socket.py"
|
||||
fake_socket.write_text(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
|
||||
_OUTCOMES = json.loads(os.environ["AWOOOI_TEST_SOCKET_OUTCOMES"])
|
||||
|
||||
|
||||
class _Connection:
|
||||
def close(self):
|
||||
return None
|
||||
|
||||
|
||||
def create_connection(address, timeout):
|
||||
del timeout
|
||||
outcome = _OUTCOMES.get(address[0], "unreachable")
|
||||
if outcome == "connected":
|
||||
return _Connection()
|
||||
if outcome == "refused":
|
||||
raise ConnectionRefusedError(address[0])
|
||||
raise TimeoutError(address[0])
|
||||
"""
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
env = os.environ.copy()
|
||||
env["PYTHONPATH"] = str(tmp_path)
|
||||
env["AWOOOI_TEST_SOCKET_OUTCOMES"] = json.dumps(outcomes)
|
||||
return subprocess.run(
|
||||
[sys.executable, "-", *_BROKER_PROBE_HOSTS],
|
||||
input=_broker_probe_script(),
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
env=env,
|
||||
)
|
||||
|
||||
|
||||
def test_common_egress_does_not_grant_ssh() -> None:
|
||||
policies = _network_policies()
|
||||
|
||||
@@ -121,6 +193,57 @@ def test_cd_applies_rolls_back_and_verifies_network_boundary() -> None:
|
||||
assert "except ConnectionRefusedError:" in workflow
|
||||
assert "broker_ssh_refused_but_egress_permitted=" in workflow
|
||||
assert "broker_ssh_allowlisted_endpoint_unavailable=" in workflow
|
||||
assert "raise SystemExit(0 if connected else 1)" in workflow
|
||||
assert "raise SystemExit(0 if connected or refused else 1)" in workflow
|
||||
assert "len(permitted) == len(sys.argv) - 1" not in workflow
|
||||
assert "ssh-mcp-key known_hosts 更新失敗,停止部署" in workflow
|
||||
|
||||
|
||||
def test_broker_probe_accepts_partial_live_route_evidence(tmp_path: Path) -> None:
|
||||
result = _run_broker_probe(
|
||||
tmp_path,
|
||||
outcomes={
|
||||
"192.168.0.110": "connected",
|
||||
"192.168.0.112": "unreachable",
|
||||
"192.168.0.120": "connected",
|
||||
"192.168.0.121": "connected",
|
||||
"192.168.0.188": "connected",
|
||||
},
|
||||
)
|
||||
|
||||
assert result.returncode == 0
|
||||
assert result.stdout.splitlines() == [
|
||||
"broker_ssh_connected="
|
||||
"192.168.0.110,192.168.0.120,192.168.0.121,192.168.0.188",
|
||||
"broker_ssh_refused_but_egress_permitted=",
|
||||
"broker_ssh_allowlisted_endpoint_unavailable=192.168.0.112",
|
||||
]
|
||||
|
||||
|
||||
def test_broker_probe_accepts_refusal_as_bounded_route_evidence(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
result = _run_broker_probe(
|
||||
tmp_path,
|
||||
outcomes={"192.168.0.112": "refused"},
|
||||
)
|
||||
|
||||
assert result.returncode == 0
|
||||
assert result.stdout.splitlines() == [
|
||||
"broker_ssh_connected=",
|
||||
"broker_ssh_refused_but_egress_permitted=192.168.0.112",
|
||||
"broker_ssh_allowlisted_endpoint_unavailable="
|
||||
"192.168.0.110,192.168.0.120,192.168.0.121,192.168.0.188",
|
||||
]
|
||||
|
||||
|
||||
def test_broker_probe_rejects_arbitrary_socket_errors(tmp_path: Path) -> None:
|
||||
result = _run_broker_probe(tmp_path, outcomes={})
|
||||
|
||||
assert result.returncode == 1
|
||||
assert result.stdout.splitlines() == [
|
||||
"broker_ssh_connected=",
|
||||
"broker_ssh_refused_but_egress_permitted=",
|
||||
"broker_ssh_allowlisted_endpoint_unavailable="
|
||||
"192.168.0.110,192.168.0.112,192.168.0.120,"
|
||||
"192.168.0.121,192.168.0.188",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user