Locally Test

Introduction

This guide provides instructions for creating a local testing sandbox for the Aiceberg Guardian. This setup emulates a production environment using Docker and the Squid proxy configured for SSL bumping and ICAP integration.

Prerequisites:

  • Docker and Docker Compose installed on the host machine.

  • An Aiceberg Account with a valid Profile ID and API Key.

1

Setup File Structure

For clarity, we recommend creating a dedicated testing directory, or you can use the repository root if you are comfortable with the paths.

mkdir llm-shield-test
cd llm-shield-test

Suggested file structure:

.
├── certs/
│   ├── ca.key
│   └── ca.crt
├── .env
├── dockerfile
├── docker-compose.yml
└── squid.conf
2

3

Create Configuration Files

Create the following files in your chosen directory.

.env File

Create a .env file with your credentials:

AICEBERG_PROFILE_ID=your_profile_id_here
AICEBERG_API_KEY=your_api_key_here

# Optional configuration

# AICEBERG_ENVIRONMENT=prod

# LLM_SHIELD_ICAP_PORT=1344

# SQUID_PORT=3128

# SQUID_CONFIG_PATH=./squid.conf

# CERTS_DIR=./certs

Dockerfile for Squid

This defines the Squid container image, setting up the SSL database permissions. We use this specific image because it supports SSL bumping and is multi-platform for ARM and AMD64.

FROM ghcr.io/b4tman/squid-ssl-bump:latest

USER root

# Initialize SSL DB
RUN rm -rf /var/lib/ssl_db \
    && /usr/lib/squid/security_file_certgen -c -s /var/lib/ssl_db -M 4MB \
    && chown -R squid:squid /var/lib/ssl_db

USER squid

docker-compose.yml

Defines the two services: the Aiceberg Shield (pulled from ECR) and your local Squid proxy.

services:
  llm_icap_shield:
    image: public.ecr.aws/n5w6j7z8/aiceberg/aiceberg_llm_shield:latest
    container_name: llm_icap_shield
    restart: always
    ports:
      - "${LLM_SHIELD_ICAP_PORT:-1344}:1344"
    environment:
      # Credentials loaded from .env file
      - AICEBERG_PROFILE_ID=${AICEBERG_PROFILE_ID:?err}
      - AICEBERG_API_KEY=${AICEBERG_API_KEY:?err}
      - ICAP_HTTP_TRANSFER_ENCODING=chunked
      - ICAP_ENCAPSULATED_MODE=identity

  squid:
    build: .
    container_name: squid-proxy
    ports:
      - "${SQUID_PORT:-3128}:3128"
    volumes:
      - ./squid.conf:/etc/squid/squid.conf:ro
      - ./certs:/etc/squid/certs:ro
    depends_on:
      - llm_icap_shield
    restart: always

squid.conf

This minimal configuration enables ICAP inspection for LLM traffic and SSL interception (required to see inside HTTPS requests).

########################################

# Basic proxy settings
########################################

# Listen on standard proxy port with SSL Bump enabled
http_port 3128 ssl-bump cert=/etc/squid/certs/ca.crt key=/etc/squid/certs/ca.key generate-host-certificates=on dynamic_cert_mem_cache_size=4MB

# SSL Cert generation program
sslcrtd_program /usr/lib/squid/security_file_certgen -s /var/lib/ssl_db -M 4MB
sslcrtd_children 5

# Recommended basic options
visible_hostname llm-icap-proxy
via on
forwarded_for off

# Disable ARP lookups to reduce noise in Docker
eui_lookup off

# Logging

# Custom log format for debugging errors
logformat icap_debug %ts.%03tu %6tr %>a %Ss/%03>Hs %<st %rm %ru %[un %Sh/%<a %mt Err=%err_code Detail=%err_detail Adapt=%<A
access_log stdio:/dev/stdout icap_debug
cache_log stdio:/dev/stderr
icap_log stdio:/dev/stdout
cache_store_log none

# Debug options for ICAP (93), uncomment for more verbose logging

# debug_options 93,9 28,9 11,5 33,3

# Disable on-disk caching (LLM calls are usually small + sensitive)
cache deny all
maximum_object_size 0 KB
request_header_max_size 128 KB
reply_header_max_size 128 KB

########################################

# ACLs: networks and ports
########################################

# Adjust this to your internal subnet(s)
acl localnet src 10.0.0.0/8        # RFC1918 example
acl localnet src 172.16.0.0/12
acl localnet src 192.168.0.0/16

# Squid internal assets (error pages/icons)
acl squid_internal urlpath_regex -i ^/squid-internal-static/

# Safe ports
acl SSL_ports port 443
acl Safe_ports port 80      # http
acl Safe_ports port 443     # https
acl Safe_ports port 8080    # alt http

# Only allow CONNECT to HTTPS ports
acl CONNECT method CONNECT

########################################

# LLM destination ACLs (ChatGPT, Gemini, Claude)
########################################

# OpenAI / ChatGPT
acl llm_openai_dstdomain dstdomain .openai.com .chatgpt.com
acl llm_openai_sni ssl::server_name .openai.com .chatgpt.com

# Gemini (Google)

# Gemini APIs typically use *.googleapis.com / generativelanguage.googleapis.com / ai.google.dev
acl llm_gemini_dstdomain dstdomain .gemini.google.com .googleapis.com .ai.google.dev
acl llm_gemini_sni ssl::server_name .gemini.google.com .googleapis.com .ai.google.dev

# Claude (Anthropic + possible Bedrock endpoint)
acl llm_claude_dstdomain dstdomain .anthropic.com .bedrock.amazonaws.com
acl llm_claude_sni ssl::server_name .anthropic.com .bedrock.amazonaws.com

# Combined ACL for all LLM traffic

# TODO: Not used at the moment
acl llm_traffic any-of llm_openai_dstdomain llm_openai_sni \
                      llm_gemini_dstdomain llm_gemini_sni \
                      llm_claude_dstdomain llm_claude_sni

########################################

# Auth/security domains to NOT bump
########################################

# High-risk auth/security domains: NEVER bump these

# NOTE: do not include subdomains already covered by a parent.
acl auth_security_sni ssl::server_name \
  .google.com \
  .gstatic.com \
  .googleapis.com \
  .recaptcha.net \
  .auth.openai.com \
  .oaistatic.com \
  .sentinel.openai.com \
  .cloudflare.com \
  .cf-binary.cloudflare.com

# Websocket endpoint(s) — treat as high-risk: DO NOT bump, DO NOT ICAP
acl chatgpt_ws_sni ssl::server_name ws.chatgpt.com

########################################

# Only care about these *paths* for monitoring
########################################

acl openai_conversation_path urlpath_regex -i ^/backend.*conversation$

########################################

# SSL Bump policy (order matters!)
########################################

# SSL Bump Steps
acl step1 at_step SslBump1

# Step 1: peek to learn SNI
ssl_bump peek step1

# Splice auth/security domains always
ssl_bump splice auth_security_sni
ssl_bump splice chatgpt_ws_sni

# Bump chatgpt/openai so we can see HTTP paths
ssl_bump bump llm_openai_sni

# Default: splice everything else
ssl_bump splice all

########################################

# Access control
########################################

# Allow local network to use the proxy
http_access allow localnet

# Let Squid serve its internal assets (error pages/icons)
http_access allow squid_internal

# Keep basic safety checks
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports

########################################

# ICAP configuration: llm_icap_shield
########################################

icap_enable on
icap_connect_timeout 5 seconds
icap_io_timeout 30 seconds
icap_service_failure_limit -1
icap_send_client_ip on
icap_send_client_username on

# Optional: include client username header if you use auth
icap_client_username_header X-Client-Username

# Define the ICAP service

# Replace 'icap-server' with your ICAP host or service DNS name
icap_service llm_icap_shield_req reqmod_precache icap://llm_icap_shield:1344/llm_icap_shield bypass=0 on-overload=bypass
icap_service llm_icap_shield_resp respmod_precache icap://llm_icap_shield:1344/llm_icap_shield bypass=0 on-overload=bypass

# Disable ICAP service suspension on failure
icap_service_failure_limit -1

icap_preview_enable off
#icap_preview_size 1024

########################################

# Apply ICAP only to LLM destinations with conversation endpoints
########################################

# Define Websockets
acl is_websocket url_regex -i ^wss:// ^ws://
acl chatgpt_ws_dstdomain dstdomain ws.chatgpt.com

# Request modification: only for LLM traffic AND the path matches, exclude websocket
adaptation_access llm_icap_shield_req deny is_websocket
adaptation_access llm_icap_shield_req deny chatgpt_ws_dstdomain
adaptation_access llm_icap_shield_req allow llm_openai_dstdomain openai_conversation_path
adaptation_access llm_icap_shield_req deny all

# Response modification: only for LLM traffic AND the path matches, exclude websocket
adaptation_access llm_icap_shield_resp deny is_websocket
adaptation_access llm_icap_shield_resp deny chatgpt_ws_dstdomain
adaptation_access llm_icap_shield_resp allow llm_openai_dstdomain openai_conversation_path
adaptation_access llm_icap_shield_resp deny all

########################################

# Misc recommended options
########################################

shutdown_lifetime 5 seconds

# Keep connections alive reasonably to reduce connection churn
icap_persistent_connections on
client_persistent_connections on
server_persistent_connections on
4

Generate Certificates

Squid acts as a "Man-in-the-Middle" to inspect HTTPS traffic, so you need to generate a local Certificate Authority (CA) and trust it.

# Create certs directory
mkdir certs

# 1. Generate CA Key
openssl genrsa -out certs/ca.key 4096

# 2. Generate CA Certificate (Self-Signed)
openssl req -x509 -new -nodes -key certs/ca.key -sha256 -days 3650 \
  -subj "/CN=Local LLM Shield CA" -out certs/ca.crt

# Note: The squid.conf expects these exact filenames (ca.key, ca.crt)
5

Trust the CA Certificate

For browsers to accept the intercepted traffic, you must trust certs/ca.crt.

Chrome (macOS CLI)

You can launch Chrome pointing to the proxy and ignoring certificate errors for quick testing (NOT SECURE for production):

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
  --user-data-dir=/tmp/chrome-squid-test \
  --proxy-server="http://127.0.0.1:3128" \
  --ignore-certificate-errors

System-wide Trust (macOS)

  1. Open Keychain Access.

  2. Drag certs/ca.crt into the System config.

  3. Double click, expand Trust, and set "When using this certificate" to Always Trust.

6

Run the Stack

Start the environment:

docker compose up -d --build

Check logs to verify everything is running:

docker compose logs -f
7

Test with Browser

You need to trust the CA cert you generated (certs/ca.crt) and configure your browser to use the proxy.

For detailed instructions on configuring Firefox, Chrome, or your OS settings, please refer to the Proxy Configuration Guide.

Verification

  1. Navigate to https://chatgpt.com.

  2. Check the logs:

docker compose logs -f llm_icap_shield
  1. You should see REQMOD entries, indicating the traffic was intercepted and scanned.

Last updated