TL;DR: AI tools during pentests send client data (IPs, domains, emails) to third-party LLM providers. LiteLLM Proxy intercepts and masks that data before it leaves the machine. This post walks through setting it up with Claude Code.

The Problem - Client Data Is Leaking

During a pentest under NDA, while using AI tools to analyze findings, a question came up: what happens to client data in those prompts? Server IPs, internal domains, credentials - all flowing directly to external LLM providers. Under NDA terms, that is a problem.

The fix: a local proxy that masks sensitive patterns before they reach the provider. LiteLLM does this with built-in pattern matching.

What Is an LLM Gateway?

An LLM gateway is a proxy between AI tools and LLM providers.

LLM Gateway flow

Every request flows through the gateway. Two capabilities relevant to pentest:

  • Inspect and control all data flowing to and from LLM providers

  • Apply content rules: mask or block specific patterns before data leaves the network

What Is Content Filtering?

Content filtering inspects and modifies data as it passes through the gateway.

What this means for pentests:

  1. Mask client infrastructure - IPs, domains, hostnames get replaced with [IP_REDACTED] before reaching the provider

  2. Block sensitive patterns - credentials, API keys, internal URLs never leave the machine

Why LiteLLM?

LiteLLM is an open-source LLM gateway. It does the pattern matching locally, no extra services needed.

Two reasons it fits this use case:

  1. Built-in litellm_content_filter - regex + keyword matching, no external dependencies, runs locally

  2. 40+ integrations available - Presidio for PII, Lakera for prompt injection detection, and others when requirements grow

Works with 100+ LLM providers. One API format (OpenAI-compatible).

Setup

This tutorial assumes familiarity with Docker and basic command line.

Step 1: Install LiteLLM via Docker

  • Create a docker-compose.yml:

services:
  litellm:
    build:
      context: .
      args:
        target: runtime
    image: docker.litellm.ai/berriai/litellm:main-stable
    volumes:
      - ./config.yaml:/app/config.yaml
    command:
      - "--config=/app/config.yaml"
    ports:
      - "4000:4000"
    environment:
      DATABASE_URL: "postgresql://llmproxy:dbpassword9090@db:5432/litellm"
      STORE_MODEL_IN_DB: "True"
    env_file:
      - .env
    depends_on:
      - db
    healthcheck:
      test:
        - CMD-SHELL
        - python3 -c "import urllib.request; urllib.request.urlopen('http://localhost:4000/health/liveliness')"
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  db:
    image: postgres:16
    restart: always
    container_name: litellm_db
    environment:
      POSTGRES_DB: litellm
      POSTGRES_USER: llmproxy
      POSTGRES_PASSWORD: dbpassword9090
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d litellm -U llmproxy"]
      interval: 1s
      timeout: 5s
      retries: 10

volumes:
  postgres_data:
    name: litellm_postgres_data

Step 2: Configure the LLM Provider

  • Create config.yaml. This example uses models from z.ai Coding plan:

model_list:
  - model_name: zai-org/glm-4.5-flash
    litellm_params:
      model: anthropic/glm-4.5-flash
      api_base: "https://api.z.ai/api/anthropic"
      api_key: os.environ/ZAI_API_KEY
  - model_name: zai-org/glm-4.7
    litellm_params:
      model: anthropic/glm-4.7
      api_base: "https://api.z.ai/api/anthropic"
      api_key: os.environ/ZAI_API_KEY
  - model_name: zai-org/glm-5
    litellm_params:
      model: anthropic/glm-5
      api_base: "https://api.z.ai/api/anthropic"
      api_key: os.environ/ZAI_API_KEY
  - model_name: zai-org/glm-5.1
    litellm_params:
      model: anthropic/glm-5.1
      api_base: "https://api.z.ai/api/anthropic"
      api_key: os.environ/ZAI_API_KEY

litellm_settings:
  drop_params: true

general_settings:
  master_key: sk-1234  # Replace with a long random key in production, must start with `sk-`
  database_url: "postgresql://llmproxy:dbpassword9090@db:5432/litellm"
  store_model_in_db: true
  store_prompts_in_spend_logs: true

# Replace sk-1234 with a long random key in production. For other providers, check the LiteLLM providers docs.

Step 3: Run LiteLLM

Command: Send below command to terminal

docker compose up -d

Expected output:

[+] Running 3/3
 ✔ Network litellm_default  Created
 ✔ Container litellm_db     Started
 ✔ Container litellm-1      Started

Step 4: Test the Connection

Command: Send below command to terminal

curl -X POST 'http://127.0.0.1:4000/chat/completions' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer sk-1234' \
  -d '{
    "model": "zai-org/glm-5",
    "messages": [
      {
        "role": "user",
        "content": "what is your name?"
      }
    ]
  }'

Expected output (truncated):

{
  "id": "chatcmpl-abc123",
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "I'm Claude, made by Anthropic."
      }
    }
  ],
  "model": "zai-org/glm-5"
}

Step 5: Access the Admin UI

Open http://127.0.0.1:4000/ui in a browser.

How Data Masking Works

Supported Modes

Mode

When it runs

Use case

pre_call

Before LLM call

Mask input before it leaves the machine

post_call

After LLM response

Mask output before returning to client

during_call

Each streaming chunk

Real-time filtering of streamed responses

For this setup: pre_call - mask sensitive data before it reaches the provider.

Actions

Action

What happens

BLOCK

Reject the request (HTTP 400)

MASK

Replace matched text with a redaction tag like [EMAIL_REDACTED]

Configure Data Masking via LiteLLM UI

Set up pattern matching for pentest data (IP addresses, domains, emails).

  1. Open the admin UI at http://127.0.0.1:4000/ui/

  2. Click Guardrails in the sidebar

  3. Search for "Pattern Matching", click it

  4. Click Create guardrail

    1. Set mode → pre_call

    2. Set "Always On" → yes

Creating pattern matching, pict src: official docs of litellm

  1. Click through until Patterns and Keywords - select the patterns to mask:

    • IP Address

    • Email

    • Domain

    • etc..

Creating pattern matching, pict src: official docs of litellm

  1. Set the action to MASK for each pattern

  2. Click through to Create Guardrail

  3. Verify the rule is active

Creating pattern matching, pict src: official docs of litellm

→ Full configuration details: LiteLLM Content Filter docs

Demo: LiteLLM + Claude Code

Integrate Claude Code to LiteLLM

Command: Send below command to terminal

# set the LLM provider pointing to LiteLLM
export ANTHROPIC_AUTH_TOKEN=sk-1234
export ANTHROPIC_BASE_URL=http://127.0.0.1:4000

# start claude code
claude

Now Claude Code routes all requests through LiteLLM.

Send a Prompt with PII

Prompt: Send below prompt to Claude Code

My information

Server IP: 172.12.10.1
Server Domain: mycompany.com
My Email: [email protected]
Location: Indonesia

Inspecting the pre LLM Request

Use mitmproxy to inspect what actually reaches the LLM provider:

Sensitive data are masked before request is make to LLM provider

The sensitive data was masked before reaching the LLM provider. The provider never saw the real IP, domain, or email.

Result: 1 proxy setup replaces manual data scrubbing - client IPs, domains, and emails never reach the LLM provider.

Sensitive data are redacted

Future Improvements

  • Custom regex patterns - build your own patterns for specific client data formats (internal ticket IDs, custom PII formats, etc)

  • Keyword filtering - block specific client names, project codenames, or sensitive terms, etc

  • Custom code filters - write Python functions for complex masking logic. See Custom Code Content Filter docs

Further Reading

MCP Servers:

Skills & Agents:

Config Management:

Disclaimer

This content reflects personal views, experiments, and use cases in AI and security engineering. It does not represent any employer's positions, policies, or practices.

Ready to apply AI to your Security Engineering ?

Subscribe to Secengai Newsletter for weekly actionable content on AI for security engineers.

Keep Reading