Centinel AnalyticaCentinel Analytica
PlatformsWeb Server

Varnish

Bot protection for Varnish Cache 6.0 LTS using a VCL config and a small helper VMOD.

Overview

Centinel integrates with Varnish through VCL. Protected requests are routed to the Centinel validator as a normal Varnish backend fetch, so Varnish's backend connection pool keeps the connection warm. The decision comes back in x-centinel-* response headers, so VCL never parses JSON. A small helper module (libvmod_centinel) base64-decodes the block or challenge page for synth delivery and appends the validator's Set-Cookie without overwriting cookies from your origin. Requests are blocked, challenged, or restarted to your origin based on the decision.

If the validator is unreachable, times out, or returns an error, requests fail open to your origin.

Backend TLS required

Open-source Varnish has no native backend TLS, and the validator is HTTPS. You must run a small local TLS proxy in front of validator.centinelanalytica.com and point the validator backend at it over plain HTTP on localhost. Reuse an nginx/HAProxy you already run, or add a ghostunnel sidecar — the install steps show both. Varnish Enterprise users can use a native TLS backend and skip the proxy. (hitch can't be used here — it only terminates inbound TLS, not the outbound TLS this leg needs.)

Prerequisites

  • Secret key from your dashboard
  • Varnish Cache 6.0 LTS (open source) or Varnish Enterprise 6, plus the matching varnish-dev headers
  • Build tools: autoconf, automake, libtool, pkg-config, python3-docutils
  • A local TLS proxy for the HTTPS validator (an existing nginx/HAProxy, or a ghostunnel sidecar)
  • A configured origin backend

Install

Download and extract the package

Download centinel-varnish.zip and extract it:

curl -O https://docs.centinelanalytica.com/downloads/centinel-varnish.zip
unzip centinel-varnish.zip -d centinel-varnish/
cd centinel-varnish/

The zip contains:

  • centinel.vcl — the VCL config template
  • src/ — the libvmod_centinel VMOD source (base64 decoding for block pages)
  • autogen.sh, configure.ac, Makefile.am — autotools build files
Build and install the VMOD

Install the build dependencies (Debian/Ubuntu example), then build:

sudo apt-get install -y varnish varnish-dev \
  build-essential automake autoconf libtool pkg-config python3-docutils

sh autogen.sh
./configure --disable-dependency-tracking
make
sudo make install

make install puts libvmod_centinel.so into Varnish's vmod directory.

Why --disable-dependency-tracking

The module includes the vmodtool-generated header, which doesn't exist when configure's dependency bootstrap runs. The flag defers that to the build step.

Run a TLS proxy to the validator

Open-source Varnish can't speak TLS to a backend, so a local proxy accepts plain HTTP on 127.0.0.1:8443 and originates TLS (with SNI) to the validator. All three options keep the validator connection warm under load; pick by what's already on the host.

If the host already runs nginx (or HAProxy), add a TLS-originating proxy — no extra daemon, and the most resource-efficient option in our benchmark. The keepalive + proxy_ssl_session_reuse + Connection "" lines keep the upstream TLS connection warm. The proxy_ssl_verify + proxy_ssl_name lines are not optional: without them nginx sends the upstream group name as SNI and accepts any certificate, so an on-path attacker could impersonate the validator and capture your API key:

# nginx.conf
upstream centinel_validator { server validator.centinelanalytica.com:443; keepalive 16; }
server {
  listen 127.0.0.1:8443;
  location / {
    proxy_pass https://centinel_validator;
    proxy_ssl_server_name on;                              # SNI
    proxy_ssl_name validator.centinelanalytica.com;        # SNI defaults to the upstream name, not the host
    proxy_ssl_verify on;                                   # verify the validator's cert
    proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
    proxy_ssl_verify_depth 3;
    proxy_ssl_session_reuse on;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_set_header Host validator.centinelanalytica.com;
  }
}

HAProxy equivalent: a backend with server centinel validator.centinelanalytica.com:443 ssl sni str(validator.centinelanalytica.com) verify required ca-file /etc/ssl/certs/ca-certificates.crt plus http-reuse always.

ghostunnel is a maintained single-binary TLS proxy (Prometheus metrics, cert hot-reload, graceful restart):

ghostunnel client \
  --listen 127.0.0.1:8443 \
  --target validator.centinelanalytica.com:443 \
  --disable-authentication   # present no client cert; the server cert is still verified

The connection stays warm as long as Varnish keeps its (pooled) front-side connection open.

stunnel still works (it just uses the most CPU of the options we measured):

# /etc/stunnel/centinel.conf
foreground = no

[centinel]
client      = yes
accept      = 127.0.0.1:8443
connect     = validator.centinelanalytica.com:443
sni         = validator.centinelanalytica.com
verifyChain = yes                                   # sni alone does not verify the cert
checkHost   = validator.centinelanalytica.com
CAfile      = /etc/ssl/certs/ca-certificates.crt
stunnel /etc/stunnel/centinel.conf
Configure centinel.vcl

Edit the bundled centinel.vcl:

  1. Point the origin backend at your app.
  2. Point the validator backend at the TLS proxy (127.0.0.1:8443).
  3. Set your secret key in vcl_backend_fetch (set bereq.http.X-API-Key = "...";).
backend origin    { .host = "127.0.0.1"; .port = "8080"; }
backend validator { .host = "127.0.0.1"; .port = "8443"; .max_connections = 200; }
Load it
varnishd -f /etc/varnish/centinel.vcl -a :80 -s malloc,256m -p workspace_client=256k

Or on a running instance:

varnishadm vcl.load centinel /etc/varnish/centinel.vcl && varnishadm vcl.use centinel

Configure

How it works

On every request, the VCL:

  1. Bypasses static assets (configurable regex in vcl_recv) and lets Varnish cache them normally.
  2. Reads the decision from x-centinel-* response headers. centinel.b64decode() decodes the block or challenge page.
  3. Applies the decision:
    • allow / not_matched — restart to your origin
    • block — 403 plus the block page
    • redirect — 200 plus the verification page
  4. Forwards the validator's Set-Cookie and a whitelisted set of response headers (Content-Type, Cache-Control, X-Content-Type-Options, X-Frame-Options, Content-Security-Policy, Referrer-Policy).

Configuration reference

SettingWhereDefault
Secret keyvcl_backend_fetchX-API-Key— (required)
Origin backendbackend originyour app
Validator backendbackend validatorTLS proxy → validator
Protected pathsvcl_recv static-asset regexstatic assets bypass
Block/challenge pagefrom the validator (x-centinel-response-html-b64)served via synth

Keep-alive & tuning

  • Keep the validator connection warm. Varnish pools the localhost hop to the proxy automatically (.max_connections on the validator backend). The proxy must keep its upstream TLS connection warm so the handshake is paid once, not per request — nginx does this with keepalive + proxy_ssl_session_reuse, HAProxy with http-reuse always, and the stunnel/ghostunnel tunnels stay warm as long as Varnish keeps its front-side connection pooled.
  • Low-traffic sites. Varnish closes idle pooled backends after backend_idle_timeout (default 60s). Raise it (e.g. -p backend_idle_timeout=300) so a quiet period doesn't force a fresh TLS handshake on the next request.
  • Large block/challenge pages. The page is delivered in a response header (x-centinel-response-html-b64), so a very large page can exceed Varnish's header limit. If your block page is big, raise -p http_resp_hdr_len (and -p workspace_client for the decoded page).

Changelog

  • v1.0.1 — TLS-proxy and keep-alive guidance
  • v1.0.0 — Initial release

On this page