DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Self-Hosted Inference Doesn’t Have to Be a Nightmare: How to Use GPUStack
  • AI Agents for DevOps on Kubernetes Need Real Engineering, Not Magic
  • Kubernetes Scheduler Plugins: Optimizing AI/ML Workloads
  • How Multimodal AI Is Reshaping Kubernetes Workflows: Future-Proofing Your Platform

Trending

  • REST-Assured Configuration and Specifications: Writing Maintainable API Tests
  • Machine Identity Debt: Why Human Identity Is No Longer Cloud Security's Primary Boundary
  • Why LLM Pipelines Fail in Production and How Temporal and Kafka Fix Them
  • Training a Neural Network Model With Java and TensorFlow
  1. DZone
  2. Data Engineering
  3. AI/ML
  4. Orchestrating Trusted Environments: Securing Untrusted Code Execution With Docker and GKE Agent Sandbox

Orchestrating Trusted Environments: Securing Untrusted Code Execution With Docker and GKE Agent Sandbox

A technical blueprint for building multi-tenant AI platforms by securely executing untrusted code with Docker and GKE Agent Sandbox.

By 
Anuj Ashok Potdar user avatar
Anuj Ashok Potdar
·
Aug. 06, 26 · Tutorial
Likes (0)
Comment
Save
Tweet
Share
63 Views

Join the DZone community and get the full member experience.

Join For Free

Building agentic AI systems fundamentally changes how we handle application security. We are no longer just securing our own code. We are securing our infrastructure against code written dynamically by an LLM and executed on the fly. When building a multi-tenant AI platform, allowing an agent to run arbitrary scripts is a massive escape vector waiting to happen.

Google recently made the GKE Agent Sandbox generally available on their custom Arm-based Axion N4A instances. This gives us a highly efficient, hardware-optimized path to run untrusted code safely. Under the hood, this relies on gVisor to intercept application kernel calls and run them in a heavily restricted user-space kernel.

In this blueprint, we will build a secure multi-tenant execution environment. We will containerize the agent runtime using Docker, provision a GKE cluster with Axion nodes, isolate the network, and orchestrate the execution layer using a robust Java backend.

Step 1: Containerizing the Agent Runtime

The first step is establishing a baseline execution environment. We want this Docker image to be as lightweight as possible to reduce the attack surface, while containing the necessary runtimes for the LLM to execute its logic.

Dockerfile
 
# Use a minimal Alpine base image to reduce attack surface
FROM python:3.11-alpine 
# Create a non-root user for execution
RUN addgroup -S agentgroup && adduser -S agentuser -G agentgroup

WORKDIR /sandbox

# Copy the execution wrapper script
COPY --chown=agentuser:agentgroup execute_payload.py /sandbox/

# Enforce non-root execution
USER agentuser 
# Prevent Python from writing pyc files and buffering stdout
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

CMD ["python", "execute_payload.py"]


To make this functional, we need an entrypoint script that safely reads the LLM-generated code from an injected environment variable or a mounted volume, executes it, and captures the output. Here is a simplified execute_payload.py implementation:

Python
 
import os
import sys
import traceback

def main():
    # In a production environment, this payload might be injected via 
    # a Kubernetes Secret or a secure sidecar proxy.
    encoded_payload = os.environ.get("AGENT_PAYLOAD", "")
    
    if not encoded_payload:
        print("Error: No payload provided.")
        sys.exit(1)
        
    try:
        # Execute the untrusted code within this isolated process
        # Security constraints are handled by the container and gVisor layers
        exec(encoded_payload, {"__builtins__": __builtins__}, {})
    except Exception as e:
        print(f"Execution Error: {str(e)}")
        traceback.print_exc()
        sys.exit(1)

if __name__ == "__main__":
    main()


Even if a malicious script breaks out of the Python runtime, it will find itself as an unprivileged user inside a minimal Alpine container.

Step 2: Provisioning GKE With Axion and Agent Sandbox

Google Axion (N4A) processors provide excellent performance per watt, making them ideal for running hundreds of concurrent, lightweight agent tasks. We will create a cluster and explicitly enable the sandbox feature.

Shell
 
# Create the GKE cluster with Sandbox enabled
gcloud container clusters create agent-sandbox-cluster \
  --region us-east4 \
  --enable-sandbox \
  --sandbox type=gvisor \
  --release-channel regular

# Create a dedicated node pool using Axion N4A instances
gcloud container node-pools create axion-agent-pool \
  --cluster agent-sandbox-cluster \
  --region us-east4 \
  --machine-type n4a-standard-4 \
  --num-nodes 3 \
  --node-labels dedicated=untrusted-agents \
  --tags untrusted-workload


Applying node labels ensures that trusted core microservices do not accidentally end up on the same physical infrastructure as untrusted agent execution environments.

Step 3: Enforcing Network Isolation

Compute isolation is useless if the untrusted code can scan your internal network or exfiltrate data to the public internet. We must deploy a strict NetworkPolicy to default-deny all egress traffic from our sandboxed namespace.

YAML
 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-agent-egress
  namespace: isolated-agents
spec:
  podSelector:
    matchLabels:
      app: agent-executor
  policyTypes:
  - Egress
  egress:
  # Only allow DNS resolution
  - ports:
    - port: 53
      protocol: UDP
    - port: 53
      protocol: TCP
  # Allow outbound only to a specific internal API gateway if needed
  # - to:
  #   - ipBlock:
  #       cidr: 10.0.0.50/32


Step 4: Deploying the Sandboxed Workload

With the network secured, we define the Kubernetes deployment. By setting the runtimeClassName to gvisor, Kubernetes routes the container lifecycle through the GKE Agent Sandbox rather than the standard container runtime.

YAML
 
apiVersion: apps/v1
kind: Pod
metadata:
  generateName: dynamic-agent-task-
  namespace: isolated-agents
  labels:
    app: agent-executor
spec:
  # Instruct GKE to use the Agent Sandbox (gVisor)
  runtimeClassName: gvisor
  
  # Ensure these pods only land on our Axion node pool
  nodeSelector:
    dedicated: untrusted-agents
    
  restartPolicy: Never
    
  containers:
  - name: execution-environment
    image: your-registry/agent-runtime:v1.0.0
    env:
      - name: AGENT_PAYLOAD
        valueFrom:
          secretKeyRef:
            name: task-payload-secret
            key: payload
            
    # Drop all unnecessary Linux capabilities
    securityContext:
      runAsUser: 1000
      runAsNonRoot: true
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
          - ALL
          
    resources:
      requests:
        memory: "256Mi"
        cpu: "250m"
      limits:
        memory: "512Mi"
        cpu: "500m"
        
    volumeMounts:
    - name: temp-storage
      mountPath: /tmp
      
  volumes:
  - name: temp-storage
    emptyDir: {}


Step 5: Orchestrating the Execution via Java Spring Boot

To bring this architecture together, the control plane must dynamically spin up these sandboxed pods whenever an AI agent decides it needs to run code. In a modern distributed system, this is typically handled by a core backend microservice.

Using the Fabric8 Kubernetes Client in a Java Spring Boot application provides a highly resilient way to orchestrate these ephemeral workloads programmatically.

Java
 
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.springframework.stereotype.Service;

@Service
public class AgentOrchestratorService {

    private final KubernetesClient kubernetesClient;

    public AgentOrchestratorService(KubernetesClient kubernetesClient) {
        this.kubernetesClient = kubernetesClient;
    }

    public String executeUntrustedCode(String tenantId, String pythonCode) {
        // 1. Create a Kubernetes Secret containing the code payload
        String secretName = createPayloadSecret(tenantId, pythonCode);

        // 2. Load the sandbox Pod template and inject the specific payload secret
        Pod sandboxedPod = kubernetesClient.pods()
            .inNamespace("isolated-agents")
            .load(getClass().getResourceAsStream("/k8s/agent-pod-template.yaml"))
            .item();
            
        // 3. Launch the pod dynamically via the API server
        Pod runningPod = kubernetesClient.pods()
            .inNamespace("isolated-agents")
            .create(sandboxedPod);

        // 4. Await completion and extract the logs safely
        kubernetesClient.pods()
            .inNamespace("isolated-agents")
            .withName(runningPod.getMetadata().getName())
            .waitUntilCondition(pod -> 
                pod.getStatus().getPhase().equals("Succeeded") || 
                pod.getStatus().getPhase().equals("Failed"), 
                30, java.util.concurrent.TimeUnit.SECONDS);

        String executionLogs = kubernetesClient.pods()
            .inNamespace("isolated-agents")
            .withName(runningPod.getMetadata().getName())
            .getLog();

        // 5. Clean up the ephemeral resources
        kubernetesClient.pods().delete(runningPod);
        kubernetesClient.secrets().withName(secretName).delete();

        return executionLogs;
    }
}


The Defense in Depth Strategy

This architecture relies on a strict defense in depth model. If an LLM hallucinates a malicious payload or a user deliberately attempts prompt injection to compromise the platform, the attacker faces multiple independent barriers. The code executes as a non-root user in a minimal Alpine environment with a read-only filesystem. Network access is completely blocked by native Kubernetes policies. Finally, any attempt to exploit kernel vulnerabilities is intercepted by the gvisor runtime boundary running on dedicated Axion hardware.

By combining these layers, engineering teams can build and scale trustworthy Agentic AI platforms without risking the integrity of their core cloud infrastructure.

AI Kubernetes

Opinions expressed by DZone contributors are their own.

Related

  • Self-Hosted Inference Doesn’t Have to Be a Nightmare: How to Use GPUStack
  • AI Agents for DevOps on Kubernetes Need Real Engineering, Not Magic
  • Kubernetes Scheduler Plugins: Optimizing AI/ML Workloads
  • How Multimodal AI Is Reshaping Kubernetes Workflows: Future-Proofing Your Platform

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook