Learn A2A - Secure Multi-Agent Deployment
Series/Learn A2A/Episode 13
Episode 13 of 23

Learn A2A - Secure Multi-Agent Deployment

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.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Introduction

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.

Network Topology: Private vs Public Agents

The first decision in a multi-agent architecture is classifying each agent:

  • A private agent can only be accessed from inside the internal network. Examples include an internal data research agent, a database agent, or a report writer agent. These agents must not accept connections from the internet.
  • A public agent is exposed outside the organization, exposes its AgentCard on a public domain, and serves trusted partners. Example: a customer service agent called by business partners.

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:

Network zone separation
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.

Ingress and Egress Policies

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.

network-policy.yaml — restrict ingress and egress
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: 8000

Warning

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.

Service Mesh for Inter-Agent Communication

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:

  • Automatic mTLS between pods, without changing application code.
  • Traffic routing and retries at the network layer.
  • Consistent telemetry across all agents.
  • Identity-based authorization at the mesh level.

Illustration of the mTLS flow in a service mesh:

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.

Hardening 1: Rate Limiting

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:

Linuxnginx.conf — rate limit the A2A endpoint
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.

Hardening 2: Input Sanitization

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:

  1. Validate payload size and type — limit the text length per part, the number of parts, and the total message size to avoid denial-of-service via giant messages.
  2. Validate part types — make sure file parts only contain allowed mimeTypes and allowlisted URLs, not file:// schemes or internal IPs.
  3. Sanitize instructions — when reassembling a prompt for the LLM, clearly separate system instructions from message content, and escape delimiters so a malicious agent can't inject instructions.

Example of simple validation in Python:

Pythonsanitize.py — validate message input
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 True

Remember: 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.

Hardening 3: Sandboxing Tool Execution

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:

  • Disposable containers — tools run in a container that is destroyed after completion, with no network, and with resource limits.
  • gVisor / microVM — sandboxes with kernel isolation for more dangerous workloads.
  • Seccomp / AppArmor — restricting the syscalls the agent process may use.

Example of running a tool in a container with no network and strict limits:

Run tool execution in a sandbox
docker run --rm \
  --network none \
  --read-only \
  --memory 512m \
  --cpus 1 \
  --cap-drop ALL \
  --pids-limit 64 \
  sandbox-runner \
  python run_tool.py

Danger

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.

Conclusion

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:

  • Default-private: as few agents as possible exposed publicly, and public agents are only gates to internal agents.
  • Egress policy is the first defense against SSRF and prompt injection attempting to reach the internal network.
  • A service mesh provides mTLS, routing, and telemetry without changing agent code.
  • Rate limiting must be based on agent identity, not just IP, to be fair to partners behind NAT.
  • Tool execution must run in a sandbox isolated from production data.

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!

Learn A2A - Secure Multi-Agent Deployment | Learn A2A