Learn how to design a secure multi-agent network: private versus public agent topologies, egress and ingress policies, using a service mesh, and hardening like rate limiting, input sanitization, and sandboxing tool execution on remote agents.

In episode 12 we built bridges between frameworks: exposing an ADK agent via to_a2a, using remote agents as sub-agents, and wrapping other frameworks with adapters. Now your agents are starting to communicate. The next question follows: how do you secure that entire network?
The problem differs from securing a single ordinary service. An A2A agent is a peer that can initiate actions, not just an endpoint serving requests. A publicly exposed agent can be called by anyone, asked to run tools, and its results affect other systems. This episode's roadmap: we start by distinguishing private and public agents, designing ingress and egress policies, leveraging a service mesh for mTLS, then close with three hardening techniques — rate limiting, input sanitization, and sandboxing tool execution.
The first decision in a multi-agent architecture is classifying each agent:
The principle is simple: default-private. The fewer agents exposed publicly, the smaller the attack surface. If a public agent must call an internal agent, don't open the internal agent's port to the internet — use a relay or a strict allowlist. One common pattern is to separate the two both physically and logically:
DMZ / Public Zone
└── pricing-agent (publik, AgentCard di domain publik)
Internal Zone
└── orchestrator (pribadi)
├── sales-agent (pribadi)
└── reporting-agent (pribadi)A public agent only serves receiving tasks from partners; the heavy work is done by internal agents. This is how you minimize the risk when one public agent gets compromised.
Once the topology is defined, we need to enforce those boundaries with network policy. In Kubernetes, NetworkPolicy lets us declare who may send traffic to an agent (ingress) and where an agent may send traffic (egress). The following example combines both: only the orchestrator may call the internal agent, and the internal agent may only send to the internal range 10.0.0.0/8 — simultaneously serving as a first defense against SSRF. Apply it with kubectl apply -f network-policy.yaml.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: agent-policy
spec:
podSelector:
matchLabels:
app: internal-agent
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: orchestrator
ports:
- port: 8000
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/8
ports:
- port: 443
- port: 8000Warning
Egress policies are often overlooked, even though they're a key defense. An LLM receiving a malicious prompt can order the agent to call an internal URL. With egress restricted to a specific internal range, the damage from prompt injection can be drastically reduced.
As the number of agents grows, managing certificates and observability manually becomes unreasonable. This is where the service mesh comes in. A service mesh like Istio or Linkerd provides:
Illustration of the mTLS flow in a service mesh:
orchestrator ──TLS──▶ Envoy sidecar ──mTLS──▶ Envoy sidecar ──TLS──▶ sales-agent
│ │
└───── otel telemetry & access log diteruskan ke mesh ─────────┘Because A2A runs over HTTP, the service mesh "doesn't care" about the contents of JSON-RPC payloads. It just sees ordinary HTTP traffic, so integrating a service mesh requires no changes to the agent code at all. Its biggest advantage: mTLS-based identity replaces ad hoc IP checks, and inter-agent traffic is automatically encrypted.
Public agents must have their request rate limited, both to prevent abuse and to protect the backend LLM from ballooning costs. Rate limiting can be done at the reverse proxy or in application middleware.
Example in Nginx, limiting to 10 requests per second per client with a small burst:
limit_req_zone $binary_remote_addr zone=a2a:10m rate=10r/s;
server {
listen 443 ssl;
location / {
limit_req zone=a2a burst=20 nodelay;
proxy_pass http://a2a-agent:8000;
}
}Info
When called by other agents, a fairer rate limit key is the client agent's identity from authentication, not just the IP. Two partner agents behind the same NAT will block each other if the key is only the IP; combine rate limiting with token identity for accurate results.
Input from other agents must not be trusted wholesale. An A2A Message contains text that eventually gets passed to an LLM, a file path, or a shell command. The required sanitization steps:
file parts only contain allowed mimeTypes and allowlisted URLs, not file:// schemes or internal IPs.Example of simple validation in Python:
MAX_TEXT_LEN = 20_000
ALLOWED_MIME = {"text/plain", "application/json", "image/png"}
def validate_message(part):
if part.kind == "text" and len(part.text) > MAX_TEXT_LEN:
raise ValueError("text part too large")
if part.kind == "file":
if part.mimeType not in ALLOWED_MIME:
raise ValueError("mime type not allowed")
if part.uri.startswith(("file:", "http://10.", "http://169.254.")):
raise ValueError("uri not allowed")
return TrueRemember: input sanitization here isn't a replacement for authentication, but a second line of defense if authentication is bypassed or if a partner turns out to be untrusted.
The most dangerous part of an agent is tool execution: running shell commands, processing files, or performing system actions. When an agent receives a task, tool execution should happen in an isolated environment, not in the server's main process.
A few sandboxing approaches:
Example of running a tool in a container with no network and strict limits:
docker run --rm \
--network none \
--read-only \
--memory 512m \
--cpus 1 \
--cap-drop ALL \
--pids-limit 64 \
sandbox-runner \
python run_tool.pyDanger
Never run tools that accept input from an external agent in the main process that has access to the database and secrets. A single successful prompt injection becomes code execution in the same environment as your production data.
The ideal combination: a sandbox for execution isolation, an egress policy to restrict the network, and rate limits as a cost safeguard. Together, the three create layered defense.
In this episode we built the infrastructure layer for a secure multi-agent network. We classified agents into private and public with the default-private principle, enforced traffic restrictions through ingress and egress policies, delegated mTLS and observability to a service mesh, then hardened each agent with rate limiting, input sanitization, and sandboxed tool execution.
Here's the core takeaway:
These steps already make the network harder to attack. But true security is born from thinking like an attacker — mapping threats before patching them one by one.
In the next episode, episode 14, we'll discuss Threat Modeling & Vulnerability Awareness: threats like agent card spoofing, prompt injection via messages, data leakage between tenants, and SSRF, along with mitigations like signed security cards, mTLS/TLS, partner allowlists, and audit trails. See you there!