Documentation

Karna for developers

Everything you need to run Karna in production, configure it for your service, and write your own detection and mitigation rules. Karna is a self-contained WAF engine for Kong Gateway, compatible with the OWASP CoreRuleSet.

Overview

Karna is a WAF (Web Application Firewall) engine that runs as a native Kong Gateway plugin. It loads the OWASP CoreRuleSet at worker start, evaluates incoming requests against those rules plus any rules you write, and either logs or blocks attacks. It needs no other plugin to work.

There are three things you will typically do, each documented here:

  • Install and deploy Karna in front of your application (this page).
  • Configure the plugin for your service: blocking mode, paranoia level, request limits, audit logging, and more. See Configuration options.
  • Write rules: custom detection, sanitization, rate limiting, and CRS exceptions. See Writing rules.

Installation

Two supported paths. Use the Docker image for a self-contained stack, or install the plugin into a Kong you already run.

Docker (production)

The production Dockerfile builds one self-contained image: Kong, the OWASP CoreRuleSet, libinjection, the native RE2 / Aho-Corasick scanners, and Karna. Kong runs DB-less; the declarative config in docker/kong.yml puts Karna in front of your backend. Redis is optional — it backs rate limiting, Redis inspection rules, and the auto-ban write actions.

shelldocker
# 1. get the source (the production Dockerfile ships with it)
git clone https://github.com/sicuranext/karna.git
cd karna

# 2. point the upstream at your app:
#    edit docker/kong.yml -> services[0].url: http://your-app:8080

# 3. build and run (Kong DB-less + Redis)
docker compose -f docker/docker-compose.prod.yml up -d --build

# traffic now flows: client -> :8000 (Karna / Kong) -> your app

Ports 8000 (proxy) and 8443 (proxy TLS) are exposed. The image bakes in KONG_PLUGINS=bundled,karna and the PCRE backtracking cap KONG_NGINX_HTTP_LUA_REGEX_MATCH_LIMIT=100000, so there is nothing to compile by hand.

Install into an existing Kong

If you already run Kong, install the plugin and its native dependencies, then enable it. Karna needs lua-zlib (gzip bodies), libinjection.so (SQLi/XSS), the OWASP CoreRuleSet on disk, and optionally the native scanners (they fall back to pure Lua if absent).

shellbash
# lua-zlib must come from the direct rockspec URL — the full
# luarocks.org manifest is too large for LuaJIT to parse.
luarocks install https://luarocks.org/manifests/brimworks/lua-zlib-1.2-2.rockspec

# the plugin itself (from the cloned repo)
cd /path/to/karna && luarocks make

# libinjection.so
git clone --branch v3.10.0 https://github.com/client9/libinjection.git
cd libinjection/src && gcc -shared -fPIC -O2 -o /usr/local/lib/libinjection.so \
    libinjection_sqli.c libinjection_xss.c libinjection_html5.c && ldconfig

# OWASP CoreRuleSet
mkdir -p /opt/coreruleset
curl -fsSL https://github.com/coreruleset/coreruleset/archive/refs/tags/v4.26.0.tar.gz \
    | tar -xz --strip-components=1 -C /opt/coreruleset

# optional native scanners (graceful fallback to Lua if missing)
g++ -shared -fPIC -O2 -std=c++17 -o /usr/local/lib/libka_re2.so src/libka_re2/ka_re2.cc -lre2
gcc -shared -fPIC -O2 -o /usr/local/lib/libka_ac.so src/libka_ac/ka_ac.c && ldconfig

Then wire Kong:

  • Add karna to plugins in kong.conf (plugins = bundled,karna).
  • Set the PCRE cap: KONG_NGINX_HTTP_LUA_REGEX_MATCH_LIMIT=100000.
  • Expose the KARNA_* env vars to nginx workers via KONG_NGINX_MAIN_INCLUDE pointing at a snippet like docker/main-env.conf (nginx wipes the environment by default — see Environment variables).
  • Make the audit-log directory writable by the Kong worker user: chown -R kong:kong /usr/local/openresty/nginx/logs.
  • kong reload.
TipThe native scanners and CRS path are overridable with env vars (KARNA_LIBKA_RE2_SO, KARNA_LIBKA_AC_SO, KARNA_LIBINJECTION_SO, KARNA_CRS_PATH). Missing native libs never cause a silent drop — the engine falls back to the pure-Lua path.

Attach Karna to a service

Declaratively in kong.yml (DB-less):

kong.ymlyaml
_format_version: "3.0"
services:
  - name: my-app
    url: http://my-backend:8080      # your existing app
    routes:
      - name: my-app
        paths: ["/"]
    plugins:
      - name: karna
        config:
          engine_blocking_mode: false   # detection-only to start
          paranoia_level: 1
          auditlog_enabled: true

Or via the Admin API on a running Kong:

shellcurl
curl -X POST http://localhost:8001/services/<service_id>/plugins \
  -H "Content-Type: application/json" \
  -d '{
    "name": "karna",
    "config": {
      "engine_blocking_mode": false,
      "paranoia_level": 1,
      "auditlog_enabled": true
    }
  }'

Run detection-only first

Always start with engine_blocking_mode: false. In this mode Karna evaluates every rule and writes the matches to the audit log, but never blocks. Watch the log against real traffic, tune away false positives with rule overrides and exceptions (see Writing rules), then flip engine_blocking_mode to true once you trust the result.

NoteA handful of request-validation gates (allowed methods, path character limits, denied headers, content-type/charset) always run regardless of blocking mode and the CRS toggles. They are hard limits — to loosen them, raise the relevant schema values. See Always-on validation gates.