mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
feat(plane): per-task egress policy; receiver by name through the gateway
Agent Substrate's egress gateway denies an actor without an EgressPolicy, so no task could deliver its artifacts or reach a model. egress.py grants each task exactly: http/80 to ousast-receiver.ax-system.svc.cluster.local, and TLS passthrough/443 to the bound Workspaces' https Git hosts and the bound Model's openultrasast.io/egress-hosts (api.deepseek.com for deepseek-flash). No IPs, no wildcards, no https (MITM) rules. The reconciler writes the policy with kubectl-ate between ax apply and resume, reads it back, waits OUSAST_EGRESS_SETTLE (11 s, the gateway's cached deny), and after ax delete task confirms the policy went with its actor (kubectl-ate has no delete verb; the store cascades). The receiver is a selector-less Service (ops/ax/receiver-service.yaml.tmpl, applied by up.sh together with the no-build agentgateway egress gateway) on port 18090 of the host. Measured live: the gateway checks the policy on Host but connects to the address the actor dialled, so the runner dials the Service's ClusterIP (OUSAST_ARTIFACT_DIAL) with the receiver's name as Host. The runner now labels its delivery with the Run's task name (OUSAST_TASK), which is what the receiver accepts; the ax Task name was refused with 403. The Receiver moved to egress.py to keep reconciler.py at 460 lines. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0744a157bf
commit
137c6c8ed5
@@ -0,0 +1,31 @@
|
||||
# The reconciler's artifact receiver by name (ai-service-plane task 0). Actors reach nothing but through Agent
|
||||
# Substrate's egress gateway, and the gateway allows HTTP by hostname only (never an IP address): the runner dials
|
||||
# this Service's ClusterIP on port 80 with Host `ousast-receiver.ax-system.svc.cluster.local`; the gateway allows
|
||||
# it on that Host and connects to the ClusterIP, which kube-proxy maps to the endpoint below, the host's receiver
|
||||
# on ${OUSAST_ARTIFACT_PORT} at the kind gateway. Rendered by up.sh.
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ousast-receiver
|
||||
namespace: ax-system
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: ${OUSAST_ARTIFACT_PORT}
|
||||
protocol: TCP
|
||||
---
|
||||
apiVersion: discovery.k8s.io/v1
|
||||
kind: EndpointSlice
|
||||
metadata:
|
||||
name: ousast-receiver-host
|
||||
namespace: ax-system
|
||||
labels:
|
||||
kubernetes.io/service-name: ousast-receiver
|
||||
addressType: IPv4
|
||||
ports:
|
||||
- name: http
|
||||
port: ${OUSAST_ARTIFACT_PORT}
|
||||
protocol: TCP
|
||||
endpoints:
|
||||
- addresses: ["${KIND_GATEWAY}"]
|
||||
+4
-2
@@ -4,8 +4,10 @@ set -euo pipefail
|
||||
export PATH="$HOME/go/bin:$HOME/.local/bin:$PATH"
|
||||
SRC="${OUSAST_AX_SRC:-$HOME/.cache/ousast/ax-src}"
|
||||
RUNNER_IMAGE="$(cat "$SRC/runner-image")"; export RUNNER_IMAGE
|
||||
KIND_GW="$(docker network inspect kind --format '{{range .IPAM.Config}}{{if .Gateway}}{{.Gateway}} {{end}}{{end}}' | tr ' ' '\n' | grep -m1 '\.')"
|
||||
export OUSAST_ARTIFACT_HOST="${OUSAST_ARTIFACT_HOST:-$KIND_GW}" # sandboxes reach the host via the kind gateway
|
||||
# Actors reach the receiver by its Service name through the egress gateway (ops/ax/receiver-service.yaml.tmpl,
|
||||
# applied by up.sh, endpoint this host at OUSAST_ARTIFACT_PORT): the runner dials the Service's ClusterIP on port
|
||||
# 80 with that name as Host; the reconciler looks the ClusterIP up unless OUSAST_ARTIFACT_DIAL is set.
|
||||
export OUSAST_ARTIFACT_PORT="${OUSAST_ARTIFACT_PORT:-18090}"
|
||||
envsubst < "$(dirname "$0")/smoke-run.yaml.tmpl" > "$SRC/smoke-run.yaml"
|
||||
REPO="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
"$REPO/.venv/bin/ousast" plane run "$SRC/smoke-run.yaml" "$@"
|
||||
|
||||
@@ -35,6 +35,15 @@ if ready ate-system; then echo "substrate ready; kept"; else
|
||||
fi
|
||||
kubectl --context "$CTX" -n ate-system get pods
|
||||
|
||||
step "egress gateway (atenet-egress, agentgateway variant: one prebuilt image, nothing built)"
|
||||
# Every actor connection goes through this gateway; without it an actor has no network at all. The Substrate
|
||||
# install can die before deploying it (e.g. a full disk), so it is applied on its own when absent.
|
||||
if kubectl --context "$CTX" -n ate-system get deploy atenet-egress >/dev/null 2>&1; then echo "gateway present; kept"; else
|
||||
kubectl kustomize --load-restrictor=LoadRestrictionsNone "$SRC/substrate/manifests/ate-install/agentgateway-egress" \
|
||||
| kubectl --context "$CTX" apply -f -
|
||||
kubectl --context "$CTX" -n ate-system rollout status deploy/atenet-egress --timeout=300s
|
||||
fi
|
||||
|
||||
step "ax control plane (ax-system)"
|
||||
if ready ax-system; then echo "ax ready; kept"; else
|
||||
(cd "$SRC/ax" && make deploy AX_IMAGE_REPO="$KO_DOCKER_REPO")
|
||||
@@ -42,6 +51,11 @@ if ready ax-system; then echo "ax ready; kept"; else
|
||||
fi
|
||||
kubectl --context "$CTX" -n ax-system get pods
|
||||
|
||||
step "artifact receiver Service (ousast-receiver.ax-system -> this host)"
|
||||
KIND_GATEWAY="$(docker network inspect kind --format '{{range .IPAM.Config}}{{if .Gateway}}{{.Gateway}} {{end}}{{end}}' | tr ' ' '\n' | grep -m1 '\.')"
|
||||
export KIND_GATEWAY OUSAST_ARTIFACT_PORT="${OUSAST_ARTIFACT_PORT:-18090}" # the reconciler's fixed receiver port
|
||||
envsubst < "$(dirname "$0")/receiver-service.yaml.tmpl" | kubectl --context "$CTX" apply -f -
|
||||
|
||||
step "worker pool (gVisor)"
|
||||
SUBSTRATE_VERSION="$(kubectl --context "$CTX" get nodes -o jsonpath='{.items[0].metadata.labels.ate\.dev/substrate-version}')"
|
||||
export SUBSTRATE_VERSION
|
||||
|
||||
Reference in New Issue
Block a user