← Back to Labs / Qdrant

How to Secure Qdrant Vector Database Behind OPNsense Firewall

OpsNexusAI Engineering •
12 min read

Overview

Vector databases like Qdrant store high-dimensional embeddings and contextual indices for Retrieval-Augmented Generation (RAG) pipelines. Exposing Qdrant endpoints (6333 HTTP and 6334 gRPC) directly to local networks—or worse, public subnets—exposes sensitive embeddings and knowledge bases to unauthorized querying and data exfiltration.

This guide details a production-grade architecture for isolating Qdrant within a dedicated OPNsense VLAN, enforcing mutual TLS (mTLS) via a proxy layer, and configuring stateful firewall rules to prevent unauthorized vector extraction.


Network Architecture & Data Flow

[ Untrusted / Client Subnet ]
          │
          │ HTTPS / mTLS (Port 443)
          ▼
┌────────────────────────────────────────────────────────┐
│               OPNsense Firewall (VLAN 30)             │
│  - Stateful Packet Inspection                           │
│  - Strict Ingress/Egress Rule Enforcement               │
└─────────────────────────┬──────────────────────────────┘
                          │
                          │ Filtered Vector API Traffic
                          ▼
┌────────────────────────────────────────────────────────┐
│             Isolated AI Compute Host                   │
│  ┌──────────────────────────────────────────────────┐  │
│  │ Envoy / Nginx mTLS Proxy Sidecar (Port 6333/6334) │  │
│  └──────────────────────┬───────────────────────────┘  │
│                         │ Loopback Only (127.0.0.1)     │
│  ┌──────────────────────▼───────────────────────────┐  │
│  │ Qdrant Vector Engine (v1.9+)                     │  │
│  │ - Storage: Encrypted NVMe Volume                 │  │
│  │ - Auth: API Key Header Validation                │  │
│  └──────────────────────────────────────────────────┘  │
└────────────────────────────────────────────────────────┘

Production Prerequisites & System Specs

Before implementing this security blueprint, ensure your firewall and storage host satisfy the following minimum requirements:

  • OPNsense Firewall: v24.1 or higher running on dedicated hardware or hardened hypervisor.
  • Vector Storage Node: Linux kernel 6.x (Ubuntu 24.04 LTS or Debian 12) with fast PCIe Gen4/Gen5 NVMe storage for storage payload indexing.
  • Memory Requirements: Minimum 16GB RAM for small-to-medium collections; 64GB+ for high-dimensional payload HNSW indexing.
  • Docker Stack: Docker Engine 26.1+ with Compose v2.27+.

Implementation Guide

Step 1: OPNsense VLAN & Firewall Rule Configuration

Create a dedicated isolated VLAN for Vector DB workloads (e.g., VLAN ID 30, Subnet 10.30.0.0/24).

1. Define Firewall Rules in OPNsense

Navigate to Firewall -> Rules -> VLAN30 and configure the following inbound/outbound matrix:

ActionProtocolSourcePortDestinationPortDescription
PassTCP10.10.0.0/24 (AI Apps)Any10.30.0.50 (Qdrant Proxy)443Allow mTLS App Traffic
PassTCP10.30.0.50Any10.30.0.1 (Gateway)53Encrypted DNS Lookups
BlockAny10.30.0.0/24Any10.0.0.0/8 (Internal Nets)AnyPrevent Lateral Movement
BlockAny10.30.0.0/24AnyWAN / InternetAnyDisable Direct Egress

Step 2: Production Docker Compose Specification

Deploy Qdrant bound strictly to localhost (127.0.0.1) alongside an Nginx mTLS SSL termination sidecar.

Create /opt/qdrant-stack/docker-compose.yml:

version: '3.8'

services:
  qdrant:
    image: qdrant/qdrant:v1.9.2
    container_name: qdrant_engine
    restart: always
    environment:
      - QDRANT__SERVICE__HTTP_PORT=6333
      - QDRANT__SERVICE__GRPC_PORT=6334
      - QDRANT__SERVICE__API_KEY=${QDRANT_API_KEY}
      - QDRANT__STORAGE__PERFORMANCE__MAX_THREADS=8
    volumes:
      - ./data:/qdrant/storage:z
      - ./config/qdrant.yaml:/qdrant/config/production.yaml:ro
    ports:
      - "127.0.0.1:6333:6333"
      - "127.0.0.1:6334:6334"
    networks:
      - qdrant_internal

  mtls_proxy:
    image: nginx:1.26-alpine
    container_name: qdrant_proxy
    restart: always
    ports:
      - "443:443"
    volumes:
      - ./certs:/etc/nginx/certs:ro
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - qdrant
    networks:
      - qdrant_internal

networks:
  qdrant_internal:
    driver: bridge

Step 3: Nginx mTLS & Reverse Proxy Configuration

Create ./nginx.conf to enforce client certificate authentication (mTLS) and proxy requests to Qdrant:

user nginx;
worker_processes auto;

events {
    worker_connections 2048;
}

http {
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log warn;

    upstream qdrant_backend {
        server qdrant:6333;
        keepalive 32;
    }

    server {
        listen 443 ssl http2;
        server_name vector.internal.local;

        # Server TLS Certificates
        ssl_certificate /etc/nginx/certs/server.crt;
        ssl_certificate_key /etc/nginx/certs/server.key;

        # mTLS Client Certificate Validation
        ssl_client_certificate /etc/nginx/certs/ca.crt;
        ssl_verify_client on;
        ssl_verify_depth 2;

        # Hardened Cipher Suite
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
        ssl_prefer_server_ciphers on;

        location / {
            proxy_pass http://qdrant_backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto https;
            proxy_set_header api-key $http_api_key;
            
            # Timeout tuning for vector payloads
            proxy_connect_timeout 10s;
            proxy_read_timeout 60s;
            proxy_send_timeout 60s;
        }
    }
}

Verification & Hardening Benchmark

1. Test Unauthorized Access (Should Fail)

Attempt to reach Qdrant directly without client certificates:

curl -k -v https://10.30.0.50/collections

Expected Output: 440 Client Error: No Required Certificate Provided or HTTP 400 Bad Request.

2. Test Authorized mTLS Access (Should Succeed)

Supply the client certificate and private key:

curl --cert ./certs/client.crt      --key ./certs/client.key      --cacert ./certs/ca.crt      -H "api-key: ${QDRANT_API_KEY}"      https://10.30.0.50/collections

Expected Output:

{
  "result": {
    "collections": []
  },
  "status": "ok",
  "time": 0.001
}

Troubleshooting Matrix

Error SymptomRoot CauseRemediation
400 Bad Request: No required SSL certificateClient omitted mTLS cert or CA mismatchVerify client certificate was signed by ca.crt.
Connection Refused on 6333Host IP binding exposed directlyEnsure Qdrant port binding is 127.0.0.1:6333.
401 UnauthorizedInvalid api-key header passedVerify QDRANT__SERVICE__API_KEY matches environment variable.
OPNsense Rule DropVLAN routing or DNS blockedCheck OPNsense Status -> Log Files -> Firewall for dropped TCP packets on port 443.

Key Security Takeaways

  1. Defense in Depth: Never rely solely on application API keys; enforce network-level VLAN segmentation and mTLS proxying.
  2. Zero Ingress/Egress: Qdrant vector nodes should not have unconstrained outbound access to the internet.
  3. Encrypted Persistence: Mount Qdrant storage volumes on LUKS-encrypted partitions to safeguard raw embeddings on disk.

Root Cause Analysis & Deep Diagnostics

┌────────────────────────────────────────────────────────────┐
│                DIAGNOSTIC WORKFLOW MATRIX                  │
│                                                            │
│  1. Check Kernel & Driver State (dmesg / nvidia-smi)       │
│  2. Inspect Container Runtime Logs (docker logs -f)        │
│  3. Verify Host Network Bindings (netstat / ss -tulpn)    │
│  4. Validate Environment Variables & Secret Tokens         │
└────────────────────────────────────────────────────────────┘

Diagnostic Command Sequence

Run the following diagnostic script to isolate kernel, container, and memory bottlenecks:

#!/usr/bin/env bash
set -euo pipefail

echo "=== System Kernel & Driver Status ==="
uname -r
nvidia-smi || echo "NVIDIA Driver missing or unloaded"

echo "=== Container Health & Port Allocation ==="
docker ps --format "table {{.Names}}	{{.Status}}	{{.Ports}}"

echo "=== Memory & Swap Diagnostics ==="
free -h
swapon --show

echo "=== Systemd Service Logs ==="
journalctl -u docker -n 50 --no-pager

Comprehensive Failure Modes & Solution Matrix

Error Code / Log EntryUnderlying Root CauseVerification CommandResolution Procedure
CUDA_ERROR_OUT_OF_MEMORYContext window exceeds allocated VRAMnvidia-smiEnable 4-bit quantization (AWQ/GPTQ) or reduce --max-model-len.
docker: unknown option --gpusMissing NVIDIA Container Toolkit hookdocker info | grep RuntimeRun sudo nvidia-ctk runtime configure and restart Docker.
502 Bad GatewayReverse proxy routed before backend readycurl -Iv http://localhost:8000Add container dependency healthchecks in docker-compose.yml.
Permission denied (publickey)Incorrect SSH or token permissionls -la ~/.ssh/Fix file permissions: chmod 600 ~/.ssh/id_rsa.

Step-by-Step Verification Procedure

  1. Verify Backend Availability: Ensure the backend container process is listening on the expected loopback interface using ss -tulpn | grep <port>.
  2. Validate Payload Responses: Send a synthetic request payload using curl with verbose output -v to confirm HTTP 200 OK status.
  3. Monitor System Telemetry: Watch memory and CPU counters in real time with htop or docker stats during peak request load.

OpsNexusAI Engineering

Verified Lab Publication

OpsNexusAI is a technical laboratory dedicated to sovereign AI infrastructure. Every implementation guide and architectural blueprint published here is tested on physical hardware and isolated networks. Our team specializes in the deployment of private LLMs, network hardening with OPNsense, and enterprise-grade automation patterns.


Join the OpsNexus Brief

Get technical teardowns on sovereign AI architectures delivered to your inbox.