Kernloom IQ

Table of contents

Kernloom IQ (kliq) — Full reference

Kernloom IQ is the controller. It reads IPv4 and IPv6 telemetry from Kernloom Shield and applies progressive enforcement per source: OBSERVE → RATE_SOFT → RATE_HARD → BLOCK.

This page contains:

  • how IQ makes decisions (engine)
  • how to tune safely (recipes)
  • a complete flag-by-flag reference (every argument)

Decision engine

Inputs per tick

IQ polls /sys/fs/bpf/kernloom_src4_stats & /sys/fs/bpf/kernloom_src6_stats every --interval and computes deltas:

  • PPS, bytes/s
  • SYN/s
  • scan/s (destination-port change counter)
  • DropRL/s (rate-limit drops)

Severity score

Each feature is normalized by a trigger threshold and capped:

  • nPPS = min(PPS / trig-pps, sev-cap)
  • nSYN = min(SYN/s / trig-syn, sev-cap)
  • nSCAN = min(scan/s / trig-scan, sev-cap)

Severity is the weighted sum:

  • severity = w-pps*nPPS + w-syn*nSYN + w-scan*nSCAN

Strikes → levels

Severity increases “strikes” using step thresholds (sev-step* and sev-delta*). Strike thresholds map to levels:

  • soft-at → RATE_SOFT
  • hard-at → RATE_HARD
  • block-at → BLOCK (optionally gated)

Hysteresis (anti-flap)

IQ prevents oscillation using:

  • up-need / down-need streaks
  • min-hold-soft / min-hold-hard
  • cooldown

Non-compliance escalation

If an IP is in HARD and still produces DropRL/s (keeps hitting the limiter), IQ can move to BLOCK sooner (controlled by noncomp-*).

Blocking is risky behind NAT. Gate blocking using:

  • block-min-sev
  • block-min-dur

If the gate fails, IQ clamps to HARD instead of BLOCK.

Autotune and clean ticks

IQ can learn trigger thresholds using Median + MAD. Learning is limited to “clean ticks” via learn-* and optional totals drop-ratio gating.


Profiles

A profile is a starting behavior template for Kernloom IQ.

It defines the initial trigger thresholds, signal weights, rate-limit levels, hold times, cooldowns, and blocking gates that IQ uses when it sees suspicious traffic from a source IP.

In practice, a profile answers this question:

“What kind of service am I protecting, and what does normal traffic roughly look like at the beginning?”

Important: profiles are only starting points

Profiles are not static rules and they are not meant to perfectly match your environment forever.

They provide sensible seed values for:

  • when packet rate (PPS) should start to matter
  • how sensitive the profile is to SYN bursts
  • how much port-scan style behavior should matter
  • when IQ should move from observe to soft rate-limit, hard rate-limit, or block
  • how long those actions should be held

After startup, autotune adapts these values to your real traffic patterns.
This means the selected profile should be understood as:

best starting fit, not final behavior.

So even if two environments both use public-api, Kernloom will gradually adapt differently depending on actual traffic, burstiness, NAT effects, client behavior, and attack patterns.

How to choose a profile

Pick the profile that is closest to the service you want to protect:

  • protect a public website → public-web
  • protect a public API → public-api
  • protect login / auth / identity endpoints → idp
  • protect internal east-west workloads → internal-app
  • protect an SSH entrypoint / bastion → ssh-bastion
  • protect an OpenZiti Controller → ziti-controller or ziti-controller-bootstrap
  • protect an OpenZiti Router → ziti-router or ziti-router-bootstrap

If you are onboarding a new system and want to reduce false positives at the beginning, start with a *-bootstrap profile and usually also with --dry-run=true.

Profile details

ziti-router-bootstrap

Best starting profile for an OpenZiti Router during initial learning.

This profile assumes: - high throughput is normal - many packets may belong to legitimate tunnel/data-plane traffic - NAT aggregation is common - early hard blocking should be avoided

Behavior:

  • very tolerant trigger levels
  • prefers rate-limiting first
  • blocking is effectively disabled during bootstrap
  • longer holds and slower escalation reduce false positives during onboarding

Use this when:

  • you are attaching Kernloom to a router for the first time
  • you do not yet know the normal throughput envelope
  • you want Kernloom to learn safely before stronger enforcement

ziti-router

Recommended steady-state profile for an OpenZiti Router after bootstrap.

This profile assumes: - sustained throughput can be high - short spikes are often legitimate - blocking should only happen if abusive behavior persists over time

Behavior:

  • high PPS tolerance
  • moderate SYN sensitivity
  • scan detection matters, but less than throughput
  • block is allowed, but only after sustained severity and duration

Use this when:

  • the router has already been observed for a while
  • you want stronger protection than bootstrap
  • you need a NAT-friendlier profile for busy tunnel/data traffic

ziti-controller-bootstrap

Best starting profile for an OpenZiti Controller during onboarding.

This profile assumes: - controller traffic is much lower than router traffic - public API / enrolment endpoints can be sensitive - false positives during first deployment should be minimized

Behavior:

  • more tolerant than the normal controller profile
  • rate-limits earlier than it blocks
  • blocking is effectively disabled
  • good for building a baseline safely

Use this when:

  • Kernloom is newly deployed in front of a controller
  • you want to observe and autotune before enabling real enforcement
  • you want a safer first step for public controller/API exposure

ziti-controller

Recommended steady-state profile for an OpenZiti Controller.

This profile assumes: - the service is public-facing - new connection bursts and SYN-heavy abuse matter a lot - traffic volume is much lower than on routers - cautious blocking is acceptable once a source is clearly abusive

Behavior:

  • relatively low trigger thresholds
  • stronger weight on SYN activity
  • faster escalation than router profiles
  • blocking is allowed, but still gated by severity and duration

Use this when:

  • the controller is exposed to the internet
  • you want to protect enrolment, management API, or edge-facing controller endpoints
  • you already completed bootstrap / dry-run learning

public-web

Good default for a public website or web frontend.

This profile assumes: - normal traffic is mostly HTTP/HTTPS - moderate bursts are possible - PPS and SYN matter more than scan behavior

Behavior:

  • balanced for public web traffic
  • tolerates normal browsing bursts
  • scan detection contributes, but less than packet rate and connection pressure

Use this when:

  • you protect a typical public website
  • the application is mostly browser-driven
  • traffic is bursty, but not as API-heavy as machine-to-machine workloads

public-api

Good default for a public JSON/API endpoint.

This profile assumes: - legitimate clients may be more bursty than browsers - request rates can be higher than on a normal website - abuse may look like sustained machine-generated traffic

Behavior:

  • higher PPS/SYN tolerance than public-web
  • still reacts strongly to sustained pressure
  • rate-limits and block thresholds are tuned for burstier API patterns

Use this when:

  • the protected service is API-first
  • traffic comes from apps, scripts, agents, or integrations
  • a normal web profile would likely be too conservative

idp

Recommended for identity, authentication, login, token, or enrolment endpoints.

This profile assumes: - normal throughput is not extremely high - connection bursts and abusive login behavior matter a lot - protecting auth infrastructure is more important than maximizing throughput tolerance

Behavior:

  • high SYN sensitivity
  • lower soft/hard thresholds
  • stricter rate limits than generic web/api profiles
  • blocking is possible, but still gated to avoid overreacting too fast

Use this when:

  • you protect an IdP
  • you protect login pages, token endpoints, OIDC/SAML frontends, enrolment APIs, or authentication gateways
  • you want stronger sensitivity to early connection abuse

internal-app

Recommended for internal east-west services.

This profile assumes: - lateral movement and reconnaissance matter more than raw volume - scanning is often a more meaningful signal than PPS alone - aggressive blocking may be risky inside internal environments

Behavior:

  • high weight on scan behavior
  • blocking is effectively disabled by default
  • prefers soft and hard rate-limits over outright deny
  • longer holds reduce state flapping

Use this when:

  • you protect internal workloads
  • you want to detect scanning / lateral movement pressure
  • you prefer a conservative internal posture before allowing blocks

ssh-bastion

Recommended for SSH entrypoints and bastion hosts.

This profile assumes: - legitimate traffic volume is low - SYN bursts, repeated attempts, and scan behavior are suspicious quickly - stronger protection is acceptable because SSH should be tightly controlled

Behavior:

  • low trigger thresholds
  • strong sensitivity to SYN-heavy behavior
  • tight soft/hard rate limits
  • blocking is allowed, but still gated to avoid blocking on tiny accidental bursts

Use this when:

  • you protect an SSH bastion
  • you expose administrative access to the internet
  • you want fast reaction on brute-force / scan-like behavior

Practical recommendation

A safe rollout usually looks like this:

  1. Start with the closest matching *-bootstrap profile, if available
  2. Run with --dry-run=true
  3. Let Kernloom observe and autotune on real traffic
  4. Review state transitions and top talkers
  5. Move to the non-bootstrap profile
  6. Enable enforcement

In short:

  • bootstrap profiles = safer onboarding, fewer false positives, no real blocking
  • regular profiles = stronger steady-state enforcement
  • autotune = adapts both to your real environment over time

Complete CLI flag reference (every argument)

Conventions

  • Durations use Go syntax (1s, 10m, 24h, 336h).
  • Many flags accept “use profile” by setting 0 / NaN / -1:
    • 0 for most numeric knobs → use profile defaults
    • block-min-sev=NaN → use profile defaults
    • block-min-dur=-1 → use profile defaults

Core runtime

FlagTypeDefaultMeaning / notes
--intervalduration1sPoll interval and decision tick.
--topint200Evaluate top-N sources by severity per tick. Larger values = more CPU in userspace.
--min-ppsfloat10Ignore sources below this PPS unless severity/DropRL/s keeps them relevant.
--min-sevfloat0.0Include candidates with severity >= this.
--dry-runbooltrueIf true: never write /sys/fs/bpf/kernloom_rl_policy4 or /sys/fs/bpf/kernloom_deny4_hash and /sys/fs/bpf/kernloom_rl_policy6 or /sys/fs/bpf/kernloom_deny6_hash.

Profile + persistence

FlagTypeDefaultMeaning / notes
--profilestringcontrollerInitial profile name. Alias mapping may apply.
--state-filestring/var/lib/kernloom/iq/state.jsonPersist tuned triggers, bootstrap metadata, and history. Empty disables persistence.
--max-state-ageduration336hIgnore persisted state older than this (0 disables).
--state-historyint30Keep last N history entries.

Whitelist (permanent exemptions)

FlagTypeDefaultMeaning / notes
--whiteliststring/etc/kernloom/iq/whitelist.txtIPv4/IPv6 or CIDR, one per line; empty disables.
--whitelist-reloadduration10sReload if file changed (0 disables).
--whitelist-learnboolfalseIf true, whitelisted sources may contribute to learning (riskier).

Feedback (temporary exemptions)

FlagTypeDefaultMeaning / notes
--feedback-filestring/var/lib/kernloom/iq/feedback.jsonJSON array with temporary exemptions; empty disables.
--feedback-reloadduration10sReload if file changed (0 disables).
--feedback-learnboolfalseIf true, feedback-exempt sources may contribute to learning (riskier).
--feedback-deenforce-cidrbooltrueCIDR feedback may trigger periodic map scans to remove RL/deny entries.
--feedback-cidr-everyduration30sInterval for CIDR de-enforcement scans (0 disables).
--feedback-cidr-maxint5000Deletion budget per scan (bounds CPU).

Bootstrap schedule (self-tuning ramp)

FlagTypeDefaultMeaning / notes
--bootstrapbooltrueEnable bootstrap tuning schedule.
--bootstrap-windowduration336hTotal bootstrap duration (recommend ~14d).
--bootstrap-phase1-endduration48hEnd of phase 1 since bootstrap start.
--bootstrap-phase2-endduration120hEnd of phase 2 since bootstrap start.
--bootstrap-every1duration1hAutotune interval during phase 1.
--bootstrap-every2duration6hAutotune interval during phase 2.
--bootstrap-every3duration24hAutotune interval during phase 3.
--steady-everyduration84hAutotune interval after bootstrap.
--bootstrap-k-startfloat4.0Higher k early = more conservative thresholds.
--bootstrap-k-finalfloat3.5Target k at end of bootstrap.
--bootstrap-max-up1float0.10Phase 1 max relative increase per update.
--bootstrap-max-down1float0.02Phase 1 max relative decrease per update.
--bootstrap-max-up2float0.08Phase 2 max relative increase per update.
--bootstrap-max-down2float0.03Phase 2 max relative decrease per update.
--bootstrap-max-up3float0.05Phase 3 max relative increase per update.
--bootstrap-max-down3float0.05Phase 3 max relative decrease per update.
--bootstrap-alpha1float0.10Smoothing alpha for phase 1.
--bootstrap-alpha2float0.15Smoothing alpha for phase 2.
--bootstrap-alpha3float0.20Smoothing alpha for phase 3.

Autotune (Median + MAD)

FlagTypeDefaultMeaning / notes
--autotunebooltrueEnable learning of trig-* thresholds.
--autotune-everyduration24hTune interval when bootstrap is off.
--autotune-min-samplesint5000Minimum samples per feature before applying.
--autotune-kfloat3.5k for median + k*MAD (higher = more conservative).
--autotune-max-changefloat0.05Max relative change per update (±).
--autotune-max-change-upfloat0Max relative increase (0 → use max-change).
--autotune-max-change-downfloat0Max relative decrease (0 → use max-change).
--autotune-alphafloat0.2Smoothing alpha (0 disables smoothing).
--autotune-floor-ppsfloat100Minimum trig-pps floor.
--autotune-floor-synfloat50Minimum trig-syn floor.
--autotune-floor-scanfloat20Minimum trig-scan floor.

Clean tick gates (anti-poison)

FlagTypeDefaultMeaning / notes
--learn-sev-gtfloat1.0A source counts as “high severity” if sev >= this.
--learn-frac-gtfloat0.005Max fraction of high-sev sources for a tick to be “clean”.
--learn-max-sevfloat0.8Only learn from sources with sev <= this.
--learn-skip-if-blocksbooltrueSkip learning if any IP is currently BLOCK.
--learn-max-drop-ratiofloat0.02Skip learning if global drop ratio is above this (0 disables).

Severity model (triggers + weights)

FlagTypeDefaultMeaning / notes
--trig-ppsfloat0PPS trigger (0 → profile/state).
--trig-synfloat0SYN/s trigger (0 → profile/state).
--trig-scanfloat0scan/s trigger (0 → profile/state).
--w-ppsfloat0PPS weight (0 → profile).
--w-synfloat0SYN weight (0 → profile).
--w-scanfloat0scan weight (0 → profile).
--sev-capfloat0normalization cap (0 → profile).

Strike mapping (severity → strikes)

FlagTypeDefaultMeaning / notes
--sev-step1float1.0sev >= step1 adds delta1 strikes.
--sev-step2float2.0sev >= step2 adds delta2 strikes.
--sev-step3float3.0sev >= step3 adds delta3 strikes.
--sev-delta1int1strikes added at step1.
--sev-delta2int2strikes added at step2.
--sev-delta3int3strikes added at step3.
--sev-decay-belowfloat0.25if sev < this (and quiet), strikes may decay.

Level thresholds (strikes → action)

FlagTypeDefaultMeaning / notes
--soft-atint0strikes >= soft-at → RATE_SOFT (0 → profile).
--hard-atint0strikes >= hard-at → RATE_HARD (0 → profile).
--block-atint0strikes >= block-at → BLOCK (0 → profile).

Enforcement action parameters

FlagTypeDefaultMeaning / notes
--soft-rateuint640SOFT rate in PPS (0 → profile).
--soft-burstuint640SOFT burst tokens (0 → profile).
--soft-ttlduration0SOFT TTL (0 → profile).
--hard-rateuint640HARD rate in PPS (0 → profile).
--hard-burstuint640HARD burst tokens (0 → profile).
--hard-ttlduration0HARD TTL (0 → profile).
--block-ttlduration0BLOCK TTL (0 → profile).
--cooldownduration0Minimum time between level changes (0 → profile).

Block safety gate

FlagTypeDefaultMeaning / notes
--block-min-sevfloatNaNOnly allow BLOCK if severity >= this (NaN → profile; 0 disables).
--block-min-durduration-1Require sev>=block-min-sev for this long (-1 → profile; 0 disables).

Hysteresis (anti-flap)

FlagTypeDefaultMeaning / notes
--up-needint0Require N consecutive “high ticks” before escalating (0 → profile).
--down-needint0Require N consecutive “low ticks” before stepping down (0 → profile).
--min-hold-softduration0Minimum time in SOFT before stepping down (0 → profile).
--min-hold-hardduration0Minimum time in HARD before stepping down (0 → profile).

Non-compliance escalation

FlagTypeDefaultMeaning / notes
--noncomp-atint0In HARD: after N non-compliance ticks → BLOCK sooner (0 → profile).
--noncomp-dropfloat0Count non-compliance if DropRL/s >= this (0 → profile).
--noncomp-sevfloat0Count non-compliance if severity >= this (0 → profile).
--noncomp-reset-belowfloat0Reset counter if sev < this and DropRL/s==0 (0 → profile).

Housekeeping (memory bounds)

FlagTypeDefaultMeaning / notes
--prev-ttlduration10mForget prev delta snapshot if not seen (bounds memory).
--state-ttlduration60mForget OBSERVE-only state if not seen for this long.

Example commands

Conservative onboarding

sudo ./kliq \
  --profile ziti-controller-bootstrap \
  --interval 1s \
  --top 100 \
  --dry-run=true \
  --bootstrap=true

Enable enforcement

sudo ./kliq --dry-run=false

NAT-friendly blocking

sudo ./kliq \
  --block-min-sev 3.0 \
  --block-min-dur 60s \
  --noncomp-at 30 \
  --noncomp-drop 50