Tools: Intercept, Inspect, Secure: Proxying Claude Code CLI Traffic

Tools: Intercept, Inspect, Secure: Proxying Claude Code CLI Traffic

Source: Dev.to

Gateway Configuration ## Test Connectivity ## Prompt Guards Architecture diagrams always look something like this: Agent -> Gateway -> LLM (or MCP Server). The Agents that organizations are typically referring to are Agents that perform an action via prompts by a user or autonomously, and those Agents are usually running in a system somewhere in production. That is, however, not where the majority of Agentic traffic originates. The larger chunk of traffic comes from Agentic clients (Claude Code CLI, Cursor, Copilot, etc.) and because of that, we now must think about Agents not only running in production systems, but on someone's laptop. In this blog post, you'll learn how to secure that traffic within an Agentic client with agentgateway. The first thing to ensure is that you have a proper AI Gateway configured so traffic from the Agentic CLI client can get from point A to point B securely. In this case, you can use agentgateway, which is an AI Gateway built from the ground up specifically for AI traffic. Two things to keep in mind with the AgentgatewayBackend config. The first is that notice the routes are going through /v1/messages and not /v1/chat/completions like you'd normally see in an OpenAI API format spec route. The reason is that Agentgateway can handle the translation (from Anthropic spec to OpenAI spec), but because you're routing traffic directly through Claude, no translation occurs, which is why the Anthropic spec is needed. The second thing is with the two configurations below, you'll see either a Model specified (Opus) or an open bracket to specify any Model you want. The reason why is because if you specify a Model in your AgentgatewayBackend and then use a different Model in Claude Code CLI, you will get a 400 error that says something along the lines of "thinking mode isn't enabled", which isn't the error that Claude Code should be showing you, but that's what you'll most likely see. If you specify Opus, you must use Opus in your Claude Code CLI configuration. If you specify no Model and just a Provider (anthropic: {}), you can use any Model you'd like. Or without a specified Model With the Gateway, Backend, and Route configured, let's ensure that the Claude Code CLI traffic can successfully go through agentgateway. You can also go into Claude Code CLI if you just run ANTHROPIC_BASE_URL="[http://127.0.0.1:8080](http://127.0.0.1:8080/)" claude or ANTHROPIC_BASE_URL="http://$INGRESS_GW_ADDRESS$:8080" and you'll be able to prompt it with whatever you'd like. With the traffic connectivity tested, let's implement Prompt Guards. Connectivity through agentgateway with Claude Code CLI has been tested and confirmed, so now, let's move into the security piece. The number 1 thing organizations want to be able to secure is what can actually get prompted via an Agent. For example, the last thing you want is to have someone prompt an Agent with Delete all of the Kubernetes clusters in production and it actually does it. To avoid this, you need to ensure that what a user can prompt is something that they should be able to prompt. You can also do the same thing without a Model specified: Run the check again by running either of the following: You'll get an output similar to the one below. With traffic routing through agentgateway from Claude Code CLI and the knowledge of how prompt guards can work in this scenario, you can now secure traffic from anyones laptop/desktop when they're using an Agentic CLI client. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK: export ANTHROPIC_API_KEY= Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: export ANTHROPIC_API_KEY= CODE_BLOCK: export ANTHROPIC_API_KEY= COMMAND_BLOCK: kubectl apply -f- <<EOF kind: Gateway apiVersion: gateway.networking.k8s.io/v1 metadata: name: agentgateway-route namespace: agentgateway-system labels: app: agentgateway spec: gatewayClassName: enterprise-agentgateway infrastructure: parametersRef: name: tracing group: enterpriseagentgateway.solo.io kind: EnterpriseAgentgatewayParameters listeners: - protocol: HTTP port: 8080 name: http allowedRoutes: namespaces: from: All EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: kubectl apply -f- <<EOF kind: Gateway apiVersion: gateway.networking.k8s.io/v1 metadata: name: agentgateway-route namespace: agentgateway-system labels: app: agentgateway spec: gatewayClassName: enterprise-agentgateway infrastructure: parametersRef: name: tracing group: enterpriseagentgateway.solo.io kind: EnterpriseAgentgatewayParameters listeners: - protocol: HTTP port: 8080 name: http allowedRoutes: namespaces: from: All EOF COMMAND_BLOCK: kubectl apply -f- <<EOF kind: Gateway apiVersion: gateway.networking.k8s.io/v1 metadata: name: agentgateway-route namespace: agentgateway-system labels: app: agentgateway spec: gatewayClassName: enterprise-agentgateway infrastructure: parametersRef: name: tracing group: enterpriseagentgateway.solo.io kind: EnterpriseAgentgatewayParameters listeners: - protocol: HTTP port: 8080 name: http allowedRoutes: namespaces: from: All EOF COMMAND_BLOCK: kubectl apply -f- <<EOF apiVersion: v1 kind: Secret metadata: name: anthropic-secret namespace: agentgateway-system labels: app: agentgateway-route type: Opaque stringData: Authorization: $ANTHROPIC_API_KEY EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: kubectl apply -f- <<EOF apiVersion: v1 kind: Secret metadata: name: anthropic-secret namespace: agentgateway-system labels: app: agentgateway-route type: Opaque stringData: Authorization: $ANTHROPIC_API_KEY EOF COMMAND_BLOCK: kubectl apply -f- <<EOF apiVersion: v1 kind: Secret metadata: name: anthropic-secret namespace: agentgateway-system labels: app: agentgateway-route type: Opaque stringData: Authorization: $ANTHROPIC_API_KEY EOF COMMAND_BLOCK: kubectl apply -f- <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayBackend metadata: labels: app: agentgateway-route name: anthropic namespace: agentgateway-system spec: ai: provider: anthropic: model: "claude-opus-4-6" policies: ai: routes: '/v1/messages': Messages '*': Passthrough auth: secretRef: name: anthropic-secret EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: kubectl apply -f- <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayBackend metadata: labels: app: agentgateway-route name: anthropic namespace: agentgateway-system spec: ai: provider: anthropic: model: "claude-opus-4-6" policies: ai: routes: '/v1/messages': Messages '*': Passthrough auth: secretRef: name: anthropic-secret EOF COMMAND_BLOCK: kubectl apply -f- <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayBackend metadata: labels: app: agentgateway-route name: anthropic namespace: agentgateway-system spec: ai: provider: anthropic: model: "claude-opus-4-6" policies: ai: routes: '/v1/messages': Messages '*': Passthrough auth: secretRef: name: anthropic-secret EOF COMMAND_BLOCK: kubectl apply -f - <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayBackend metadata: labels: app: agentgateway-route name: anthropic namespace: agentgateway-system spec: ai: provider: anthropic: {} policies: auth: secretRef: name: anthropic-secret ai: routes: '/v1/messages': Messages '*': Passthrough EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: kubectl apply -f - <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayBackend metadata: labels: app: agentgateway-route name: anthropic namespace: agentgateway-system spec: ai: provider: anthropic: {} policies: auth: secretRef: name: anthropic-secret ai: routes: '/v1/messages': Messages '*': Passthrough EOF COMMAND_BLOCK: kubectl apply -f - <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayBackend metadata: labels: app: agentgateway-route name: anthropic namespace: agentgateway-system spec: ai: provider: anthropic: {} policies: auth: secretRef: name: anthropic-secret ai: routes: '/v1/messages': Messages '*': Passthrough EOF COMMAND_BLOCK: kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: claude namespace: agentgateway-system labels: app: agentgateway-route spec: parentRefs: - name: agentgateway-route namespace: agentgateway-system rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: anthropic namespace: agentgateway-system group: agentgateway.dev kind: AgentgatewayBackend EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: claude namespace: agentgateway-system labels: app: agentgateway-route spec: parentRefs: - name: agentgateway-route namespace: agentgateway-system rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: anthropic namespace: agentgateway-system group: agentgateway.dev kind: AgentgatewayBackend EOF COMMAND_BLOCK: kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: claude namespace: agentgateway-system labels: app: agentgateway-route spec: parentRefs: - name: agentgateway-route namespace: agentgateway-system rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: anthropic namespace: agentgateway-system group: agentgateway.dev kind: AgentgatewayBackend EOF CODE_BLOCK: export INGRESS_GW_ADDRESS=$(kubectl get svc -n agentgateway-system agentgateway-route -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESS Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: export INGRESS_GW_ADDRESS=$(kubectl get svc -n agentgateway-system agentgateway-route -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESS CODE_BLOCK: export INGRESS_GW_ADDRESS=$(kubectl get svc -n agentgateway-system agentgateway-route -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESS CODE_BLOCK: ANTHROPIC_BASE_URL="http://$INGRESS_GW_ADDRESS$:8080" claude -p "What is a credit card" Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: ANTHROPIC_BASE_URL="http://$INGRESS_GW_ADDRESS$:8080" claude -p "What is a credit card" CODE_BLOCK: ANTHROPIC_BASE_URL="http://$INGRESS_GW_ADDRESS$:8080" claude -p "What is a credit card" CODE_BLOCK: ANTHROPIC_BASE_URL="http://127.0.0.1:8080" claude -p "What is a credit card" Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: ANTHROPIC_BASE_URL="http://127.0.0.1:8080" claude -p "What is a credit card" CODE_BLOCK: ANTHROPIC_BASE_URL="http://127.0.0.1:8080" claude -p "What is a credit card" COMMAND_BLOCK: kubectl apply -f- <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayBackend metadata: labels: app: agentgateway-route name: anthropic namespace: agentgateway-system spec: ai: provider: anthropic: model: "claude-opus-4-6" policies: ai: routes: '/v1/messages': Messages '*': Passthrough promptGuard: request: - response: message: "Rejected due to inappropriate content" regex: action: Reject matches: - "credit card" auth: secretRef: name: anthropic-secret EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: kubectl apply -f- <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayBackend metadata: labels: app: agentgateway-route name: anthropic namespace: agentgateway-system spec: ai: provider: anthropic: model: "claude-opus-4-6" policies: ai: routes: '/v1/messages': Messages '*': Passthrough promptGuard: request: - response: message: "Rejected due to inappropriate content" regex: action: Reject matches: - "credit card" auth: secretRef: name: anthropic-secret EOF COMMAND_BLOCK: kubectl apply -f- <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayBackend metadata: labels: app: agentgateway-route name: anthropic namespace: agentgateway-system spec: ai: provider: anthropic: model: "claude-opus-4-6" policies: ai: routes: '/v1/messages': Messages '*': Passthrough promptGuard: request: - response: message: "Rejected due to inappropriate content" regex: action: Reject matches: - "credit card" auth: secretRef: name: anthropic-secret EOF COMMAND_BLOCK: kubectl apply -f - <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayBackend metadata: labels: app: agentgateway-route name: anthropic namespace: agentgateway-system spec: ai: provider: anthropic: {} policies: auth: secretRef: name: anthropic-secret ai: routes: '/v1/messages': Messages '*': Passthrough promptGuard: request: - response: message: "Rejected due to inappropriate content" regex: action: Reject matches: - "credit card" EOF Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: kubectl apply -f - <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayBackend metadata: labels: app: agentgateway-route name: anthropic namespace: agentgateway-system spec: ai: provider: anthropic: {} policies: auth: secretRef: name: anthropic-secret ai: routes: '/v1/messages': Messages '*': Passthrough promptGuard: request: - response: message: "Rejected due to inappropriate content" regex: action: Reject matches: - "credit card" EOF COMMAND_BLOCK: kubectl apply -f - <<EOF apiVersion: agentgateway.dev/v1alpha1 kind: AgentgatewayBackend metadata: labels: app: agentgateway-route name: anthropic namespace: agentgateway-system spec: ai: provider: anthropic: {} policies: auth: secretRef: name: anthropic-secret ai: routes: '/v1/messages': Messages '*': Passthrough promptGuard: request: - response: message: "Rejected due to inappropriate content" regex: action: Reject matches: - "credit card" EOF - Generate an API key and put it into an environment variable so a k8s Secret can be created with it later. - Create the Gateway object. - Create the secret for Anthropic. This way, you have proper access to Anthropic via your Gateway for LLM calls. - Create an Agentgateway Backend. - Create the routing configurations that point to your Gateway and use the Agentgateway Backend you created in the previous step as the reference. - Grab your ALB IP from the Gateway within an environment variable. If you're running this locally and don't have access to an ALB IP, you can skip this test and just use localhost after port-forwarding the Gateway service. - Test the LLM connectivity through your Gateway with a single prompt. - Modify the AgentgatewayBackend with a prompt guard. Notice how this is a regex and for the test, we want to block any traffic that has the words credit card in it. - ANTHROPIC_BASE_URL="http://$INGRESS_GW_ADDRESS:8080" claude -p "What is a credit card" - ANTHROPIC_BASE_URL="http://$INGRESS_GW_ADDRESS:8080" claude -p and then prompting within Claude Code What is a credit card