mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 19:59:26 +00:00
plane(2.3): egress -- one TLS rule over store host, Git hosts and Model hosts; no http rule
policy_for(task, workspaces, model, store_host, receiver_host=None) emits one tlsPassthrough rule on 443 over the sorted union of the memory store's host, the bound Workspaces' https Git hosts and the bound Model's egress hosts. There is no http rule: an http Git repo is refused by name (it could not be reached), a store given as an address is refused naming S3_ENDPOINT (the gateway allows hostnames only). receiver_host adds the old receiver's http rule for the path group 2 keeps until 2.6; the reconciler passes it by keyword (passing the receiver's address positionally would have raised outside the worker's try and hung the run on the queue -- 2.4 hardens the worker). Tests: the store-only policy, the Model-bound policy, the two refusals, the receiver exception, and every committed template under plane/tasks/ rendering exactly the store plus, for verify and roles, the provider host. Gates: pytest 2027 passed, 42 skipped, exit 0; ruff check 0; ruff format 0; mypy 0. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
9506a18b56
commit
c49eae4f30
@@ -84,17 +84,32 @@ def git_hosts(workspaces: Iterable[Workspace]) -> tuple[list[str], list[str]]:
|
||||
return sorted(tls), sorted(plain)
|
||||
|
||||
|
||||
def policy_for(task: Task, workspaces: Iterable[Workspace], model: Model | None, receiver_host: str | None) -> dict[str, Any]:
|
||||
"""The task's EgressPolicy body: the receiver over HTTP/80, Git and Model hosts as TLS passthrough on 443."""
|
||||
def policy_for(
|
||||
task: Task,
|
||||
workspaces: Iterable[Workspace],
|
||||
model: Model | None,
|
||||
store_host: str | None,
|
||||
receiver_host: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""The task's EgressPolicy body (plane-on-kubernetes design section 3): one ``tlsPassthrough`` rule on 443 over
|
||||
the sorted union of the store's hostname (``store_host``: where the runner PUTs its tar and GETs its inputs), the
|
||||
bound Workspaces' https Git hosts and the bound Model's ``openultrasast.io/egress-hosts``. No ``http`` rule, unless
|
||||
``receiver_host`` names the old receiver (the path task 2.6 removes). An http Git repo cannot be reached under
|
||||
this policy and is refused; a store given as an address is refused naming ``S3_ENDPOINT`` (the gateway allows
|
||||
hostnames only)."""
|
||||
tls, plain = git_hosts(workspaces)
|
||||
tls = sorted(set(tls) | set(model.egress_hosts if model else ()))
|
||||
if receiver_host and not _is_ip(receiver_host): # an address cannot be allowed; only the direct test mode has one
|
||||
plain = sorted({*plain, receiver_host.lower()})
|
||||
rules: list[dict[str, Any]] = []
|
||||
if plain:
|
||||
rules.append({"http": {"hostnames": plain, "ports": {"numbers": [80]}}})
|
||||
if tls:
|
||||
rules.append({"tlsPassthrough": {"hostnames": tls, "ports": {"numbers": [443]}}})
|
||||
raise ValueError(f"Task/{task.metadata.name}: http Git repos ({', '.join(plain)}) cannot be reached: the policy grants TLS only")
|
||||
hosts = set(tls) | set(model.egress_hosts if model else ())
|
||||
if store_host:
|
||||
if _is_ip(store_host):
|
||||
raise ValueError(f"Task/{task.metadata.name}: the store endpoint is an address ({store_host}); S3_ENDPOINT must be a hostname")
|
||||
hosts.add(store_host.lower())
|
||||
rules: list[dict[str, Any]] = []
|
||||
if receiver_host and not _is_ip(receiver_host): # the receiver path, kept for group 2 only (task 2.6 deletes it)
|
||||
rules.append({"http": {"hostnames": [receiver_host.lower()], "ports": {"numbers": [80]}}})
|
||||
if hosts:
|
||||
rules.append({"tlsPassthrough": {"hostnames": sorted(hosts), "ports": {"numbers": [443]}}})
|
||||
return {"rules": rules}
|
||||
|
||||
|
||||
|
||||
@@ -341,7 +341,7 @@ def _execute(
|
||||
docs = render_task(run, entry, manifests, receiver.url, receiver.dial)
|
||||
manifest.write_text(yaml.safe_dump_all(docs, sort_keys=False), encoding="utf-8")
|
||||
model = manifests.models.get(task.metadata.annotations.get(MODEL_ANNOTATION) or "")
|
||||
policy = policy_for(task, [manifests.workspaces[b.name] for b in task.workspaces], model, urlsplit(receiver.url).hostname)
|
||||
policy = policy_for(task, [manifests.workspaces[b.name] for b in task.workspaces], model, None, urlsplit(receiver.url).hostname)
|
||||
try: # Req 4.6: the bound Model's credential is read here and only ever sent in the start request
|
||||
start = router.starter(atespace, ax_name, run.metadata.name, credentials(model))
|
||||
ax.apply(manifest) # creates the Substrate actor; its egress is denied until the policy exists
|
||||
|
||||
Reference in New Issue
Block a user