Wolfee-Watcher watches what runs in your clusters, who changes them and what they are built from, and lets you reconstruct an incident without shelling into a node. One hub UI, any number of clusters, mandatory mTLS between every component.
- Runtime — Tracee (eBPF) on every node reports syscalls, tracepoints and LSM hooks;
tracee-bridgestreams them through Kafka to the rule engine and the anomaly detector. - Kubernetes API —
sentry-auditis a validating admission webhook for creates, updates, deletes,exec,attachandport-forward; it can also tail the kube-apiserver audit log for reads, users and source IPs. - Supply chain —
scanner-agentscans every image running in the cluster with Trivy, adds EPSS, CISA KEV, public PoCs and FSTEC BDU, and keeps a package inventory. - Cluster state —
sensorkeeps a live snapshot of workloads, RBAC, network policies, secrets metadata and webhooks for posture checks and configuration views. - Deception and forensics —
honey-operatordeploys honeypots, andforensic-watcherkeeps per-pod file changes and binary exec history that survive the pod.
- 🛡️ Runtime policies: syscall, binary, tracepoint and LSM-hook rules evaluated on the live event stream.
- 🧠 Anomaly detection: per-deployment baselines flag unexpected binaries, fileless execution, raw sockets, process injection, namespace escapes and binary tampering.
- 📜 Kubernetes audit logs: monitoring and investigation views, audit checks grouped by risk, silences, per-user rollups and a configurable retention.
- 🔍 Vulnerability management: CVEs by image, workload and node, risk score from CVSS and EPSS, KEV and PoC flags, fixable counts, SBOM and FSTEC BDU mapping.
- 📋 Compliance and posture: CIS, FSTEC, NIST, PCI DSS and HIPAA controls, plus Build and Deploy policies evaluated server-side.
- 🍯 Honeypots: decoy services inside the cluster that record connections and login attempts.
- 🧪 Forensics: per-pod syscall watch, file change history, container logs and TAR export.
- 🌐 Network: network policy graph, policy viewer and network anomaly events.
- 🏢 Multi-cluster: one hub UI for every cluster, data shared in one PostgreSQL or kept in a dedicated database per cluster.
- 🔔 Alerting: Discord, Mattermost and Jira integrations with a durable delivery queue and retries; Harbor as an image source.
- 🔐 Secure by default: mTLS everywhere with certificates rotated by
cert-server, least-privilege database roles, network policies and admin / read-only accounts.
The diagram shows a cluster connected as an edge to an external kvisior hub and the shared
PostgreSQL. Without a hub, kvisior-ui is the only UI and PostgreSQL can run in the cluster.
- Get Started
- Detection
- Alerts and Integrations
- Multi-Cluster
- Configuration
- FSTEC Database
- Production Notes
- Documentation
- Contributing
- License
Wolfee-Watcher runs in two shapes. A standalone cluster has its own UI and database inside the cluster. A hub serves one UI for many clusters: each cluster runs the chart as an edge that collects and writes its data, and the hub shows everything.
Requirements: a Kubernetes cluster on containerd (k3s works best; Tracee is a privileged eBPF
DaemonSet with hostPID), plus podman or docker, ctr, helm, kubectl, go and openssl
on the build host.
# 1. Build every image and import it into the node's containerd
deploy/build-images.sh
# 2. Generate a CA and the per-service certificates
cd pkg/certgen && go run . -out ../../deploy/certs -namespace wolfee-watcher && cd ../..
# 3. Namespace and CA secret
kubectl create ns wolfee-watcher
kubectl apply -f deploy/certs/00-ca-key-secret.yaml -n wolfee-watcher
# 4. Install
helm install wolfee-watcher ./helm -n wolfee-watcher \
--set namespace.create=false \
--set global.internalPushSecret="$(openssl rand -hex 32)" \
--set ui.ingress.enabled=true \
--set ui.ingress.host=kvisior8.127.0.0.1.nip.io \
--set ui.ingress.denyInternalPaths=true \
--set networkPolicy.nodeCIDRs="{10.0.0.0/8}"The initial admin password is printed once by the migration job:
kubectl logs -n wolfee-watcher job/central-migrateOpen the UI at the ui.ingress.host you set.
Tip: on a multi-node cluster push the images to a registry with
deploy/build-images.sh --push <registry> --tag <tag>and setimage.repository,image.tagandimage.pullPolicy: IfNotPresentfor each component. The single-node default (localhost/wolfee-watcher/*,pullPolicy: Never) needs the image on every node.
The hub is kvisior and PostgreSQL in containers on a plain host. Clusters connect to it as edges.
# On the hub host
cd deploy/hub
cp hub.env.example hub.env # fill every secret: openssl rand -hex 24
./pki.sh /srv/wolfee <hub-ip> <edge-ip> ... # private CA, PostgreSQL and hub certificates
./pg-firewall.sh <edge-ip> <hub-ip> <pod-cidr> # allow-list PostgreSQL
podman-compose --env-file hub.env up -d # or docker compose
# From any machine with kubectl, helm and SSH to the hub
deploy/edge/install.sh # one edge per kubeconfig, asks as it goes- The hub UI listens on
https://<hub-ip>:8443; edges serve no UI of their own. - Every edge writes its data straight into its database; the hub reads it and reaches the edge
over federation (
:30443) only for live views. - A cluster can keep its data in a dedicated PostgreSQL on another host:
deploy/hub/add-cluster-db.shprepares the database, its certificate and the hub route.
See deploy/hub/README.md for the full walkthrough.
Policies are created in Policy Management and apply to every cluster connected to the hub.
| Type | Evaluated on | Example |
|---|---|---|
| Runtime — syscall | Tracee syscall events | setns from a workload pod |
| Runtime — binary | process and path | nc started anywhere in production |
| Tracepoint | kernel tracepoints | kernel module load |
| LSM hook | LSM decisions | security_inode_unlink on a system binary |
| Build | image layers and registry | image not from a trusted registry, curl | sh in a layer |
| Deploy | workload specs | privileged container, hostPID, missing limits |
| Audit | Kubernetes API activity | exec into a pod, ClusterRoleBinding created |
Matches become violations with severity, workload and evidence. A policy marked Alert also produces an alert. False positives can be marked per instance or silenced per category.
Note: the anomaly detector works without policies: it learns a baseline per deployment during the observation period and reports deviations on its own.
Alerts from every source land in the Alert Log and fan out to the integrations configured in Settings → Integrations:
| Integration | Purpose |
|---|---|
| Discord | alert messages to a channel webhook |
| Mattermost | alert messages to an incoming webhook |
| Jira | an issue for every anomaly |
| Harbor | registry credentials the scanner uses to pull images for Trivy |
Delivery is durable: each alert is queued in PostgreSQL, retried with backoff and marked delivered once accepted, so a webhook outage does not lose alerts.
Every row carries a cluster_id taken from the agent's mTLS certificate, and the hub UI has a
cluster switcher. Two storage layouts can be mixed:
- Shared database — every edge writes into the hub's PostgreSQL. Simple, suits a few clusters.
- Dedicated database per cluster — a cluster writes into its own PostgreSQL on any host. The hub
opens a connection pool per cluster from
clusters/databases.conf(reloaded every 15 s) and copies policies, audit rules and retention settings into it.
Accounts, sessions, policies and the cluster list always live in the hub database.
Note: clusters move to a dedicated database one at a time; data already written to the shared database stays there.
See docs/multicluster.md and deploy/hub/README.md.
The Helm chart is the single source of truth. The values most edge installs set:
global:
clusterId: prod-eu-1 # stable, unique, DNS-label; stamped into every row and certificate
internalPushSecret: ${PUSH_SECRET} # required; an empty value fails the install
federationToken: ${FED_TOKEN} # shared with the hub (hub.env FEDERATION_TOKEN)
postgres:
enabled: false # true runs PostgreSQL inside the cluster (standalone)
external:
enabled: true
host: 10.20.0.5 # hub database or the cluster's dedicated database
port: 5432
sslMode: verify-ca
caBundle: | # hub CA (/srv/wolfee/pki/ca.crt)
-----BEGIN CERTIFICATE-----
serviceCredentials: # one least-privilege role per service
ui: { user: ww_ui, password: ${PG_UI_PASSWORD} }
traceeBridge: { user: ww_tracee_bridge, password: ${PG_TRACEE_BRIDGE_PASSWORD} }
anomaly: { user: ww_anomaly, password: ${PG_ANOMALY_PASSWORD} }
ui:
ingress:
enabled: false # standalone only; edges are viewed from the hub
advertiseURL: https://10.20.0.10:30443 # where the hub reaches this cluster
federation:
nodePort: 30443
allowedSources: [10.20.0.1/32] # the hub address
kafka:
replicaCount: 3 # 1 on a single node, with replicationFactor and minIsr 1
replicationFactor: 3
minIsr: 2
networkPolicy:
nodeCIDRs: [10.0.0.0/8] # where Tracee and the API server reach the cluster fromExplanation:
global.clusterIdnames the cluster everywhere: rows, client certificates and the UI switcher.global.internalPushSecretauthenticates/internal/push/*; the chart refuses to render without it.postgres.externalpoints the edge at its database; withsslMode: verify-cathecaBundleis mounted into every pod.serviceCredentialsgive each writer its own role; the passwords come fromhub.envor fromadd-cluster-db.sh.ui.ingressis off unless set, so a cluster connected to a hub exposes no UI.networkPolicy.nodeCIDRsis required outside Cilium; on Cilium the chart allows node entities and also restricts egress.
The full reference with every value is in DEPLOY.md and helm/values.yaml.
The FSTEC BDU database is not included in this repository. To enable BDU enrichment, download the
latest database file from the official FSTEC website and make it available to scanner-agent.
- Own your CA. The test CA in
deploy/certs/has its key committed: development only. - mTLS is mandatory. There is no plaintext fallback; pods refuse to start without CA material.
- Install with Helm only, without
--wait. Thecentral-migratehook and the pods'wait-for-schemainit containers deadlock under--wait. - The schema belongs to
central-migrate. It applies the DDL, seedsadminand creates one role per service; onlykvisiorandanomaly-detectorhold broad database access, andtracee-bridgewrites directly only when Central is down. - Network policies are on by default. Every component accepts traffic only from its callers. On
Cilium egress is limited to DNS, the namespace, the API server and PostgreSQL;
kvisior-uiandanomaly-detectormay reach integrations onnetworkPolicy.cilium.integrations.portsandglobal.integrations.allowedCIDRs. Add anything else withnetworkPolicy.cilium.extraEgress.<component>. - Size Kafka and PostgreSQL for the event rate: three Kafka brokers with
minIsr: 2survive a broker restart, and busy clusters belong in a dedicated database.
Warning: on a hub set
ADMIN_BOOTSTRAP_PASSWORDinhub.envbefore the firstup, or read the generated one-time password from the first migration log.
DEPLOY.md— deployment reference, step by stepdeploy/hub/README.md— hub host, edges and dedicated cluster databasesdocs/multicluster.md— cluster identity and database scalingdocs/audit-logs.md— Kubernetes audit sources, rules and retentiondocs/audit-delivery.md— durable audit event deliverydocs/logging.md— structured logs and alert contextdocs/api/tracee-events-query.md— runtime event query APIhelm/UPGRADE.md— upgrade notes
Each service is its own Go module and the React UI ships inside kvisior. Build, layout and
conventions are in CONTRIBUTING.md. Please report vulnerabilities privately as
described in SECURITY.md.
Wolfee-Watcher is licensed under the Apache License 2.0. See LICENSE.