Skip to content

Detection CRDs

The detection family configures the operators that observe the cluster’s control- and data-plane and emit SecurityEvent CRs. All kinds live under security.ugallu.io/v1alpha1.

KindScopeCardinalityOwning operator
SigmaRuleClusterper ruleaudit-detection
AuditDetectionConfigClustersingletonaudit-detection
DNSDetectConfigClustersingletondns-detect
HoneypotConfigClusterper honeypot sethoneypot
WebhookAuditorConfigClustersingletonwebhook-auditor

Singleton, cluster-scoped. Toggles the optional gRPC event bus that honeypot and tenant-escape subscribe to, and declares per-consumer rate caps.

Spec

  • eventBus.enabled (bool) - default false. When true, the operator opens an in-cluster gRPC server on eventBus.listenAddr.
  • eventBus.tokenSecret (SecretKeySelector) - bearer token for stream auth.
  • consumers[].name (string) - subscriber identifier, must be unique.
  • consumers[].filter.objectRefHasNamespace (bool) - drop cluster-scoped events from this consumer’s stream.
  • consumers[].maxEventsPerSec (uint32) - per-consumer rate cap.

Status

  • lastConfigLoadAt - timestamp of the most recent successful spec load.
  • consumersConnected - live subscriber count.
  • conditions[].

Compatibility

Absence of the CR keeps the legacy “no gRPC bus” mode - downstream operators that rely on the bus will fail their pre-flight check loudly rather than silently miss events.

Example

apiVersion: security.ugallu.io/v1alpha1
kind: AuditDetectionConfig
metadata: { name: default }
spec:
eventBus:
enabled: true
listenAddr: "0.0.0.0:8444"
tokenSecret:
name: ugallu-audit-bus-token
key: token
consumers:
- name: honeypot
filter: { objectRefHasNamespace: false }
maxEventsPerSec: 1000
- name: tenant-escape
filter: { objectRefHasNamespace: false }
maxEventsPerSec: 500

One CR per detection rule, cluster-scoped, owned by audit-detection. The match grammar is a deliberately small subset of Sigma; see the operator page for the full surface.

Spec

  • enabled (bool) - default true.
  • description (string).
  • match.verb[] (enum) - create, update, patch, delete, get, list, watch, deletecollection, connect.
  • match.objectRef - {apiGroup, apiVersion, resource, subresource, namespace, name} predicates with glob support.
  • match.user - {userGlob, groups[]}.
  • match.requestObjectGlob[] - (jsonPath, glob[]) pairs evaluated against request.requestObject.
  • emit.securityEventType (string) - validated against the SE type catalog.
  • emit.severity (enum) - info, low, medium, high, critical.
  • rateLimit.burst / rateLimit.sustainedPerSec - per-rule token bucket override (defaults are global).

Status

  • matchCount - lifetime hit count (best-effort across replicas).
  • lastMatchedAt.
  • droppedRateLimit - matches dropped by the limiter.
  • parseError - non-empty string disables the rule.
  • conditions[].

Validation

The SE type is checked against the catalog at admission AND at runtime: a rule that survives admission can still flip to Disabled if the catalog narrows.

Example

apiVersion: security.ugallu.io/v1alpha1
kind: SigmaRule
metadata:
name: cluster-admin-granted
spec:
description: Detect creation of a wildcard ClusterRoleBinding to cluster-admin.
enabled: true
match:
verb: [create, update, patch]
objectRef:
resource: clusterrolebindings
requestObjectGlob:
- jsonPath: "$.roleRef.name"
glob: ["cluster-admin"]
emit:
securityEventType: ClusterAdminGranted
severity: critical
rateLimit:
burst: 5
sustainedPerSec: 1
references:
- "https://attack.mitre.org/techniques/T1078/"
tags: [iam, escalation]

Singleton, cluster-scoped. Configures the DNS source backend and each detector independently.

Spec

  • source.primary (enum) - coredns_plugin (default) or tetragon_kprobe.
  • source.fallback (enum) - source the operator falls back to when the primary is unreachable for >60s.
  • source.plugin.grpcEndpoint (string) - host:port for the CoreDNS plugin stream.
  • detectors.exfiltration.enabled (bool) - entropy + length anomaly on TXT/A records.
  • detectors.tunneling.enabled (bool) - base64-in-subdomain heuristic.
  • detectors.blocklist.enabled (bool) + per-namespace ConfigMap references (hot-reloaded).
  • detectors.youngDomain.enabled (bool) - opens an RDAP/WHOIS lookup on domains registered fewer than N days ago.
  • detectors.anomalousPort.enabled (bool) - flags DNS to non-53 ports.

Status

  • source - active backend (may differ from spec.source.primary during a fallback).
  • lastConfigLoadAt.
  • inflightLookups - live pending RDAP lookups (cap is enforced by the YoungDomain detector).
  • conditions[].

Example

apiVersion: security.ugallu.io/v1alpha1
kind: DNSDetectConfig
metadata: { name: default }
spec:
source:
primary: coredns_plugin
fallback: tetragon_kprobe
plugin:
grpcEndpoint: "ugallu-coredns-grpc.kube-system.svc:9443"
detectors:
exfiltration: { enabled: true, minLabelLength: 24, minEntropy: 4.0 }
tunneling: { enabled: true }
blocklist:
enabled: true
namespaceConfigMaps:
- { namespace: ops, name: dns-blocklist }
youngDomain: { enabled: true, registeredWithinDays: 30 }
anomalousPort: { enabled: true }

Cluster-scoped, supports multiple CRs (one per honeypot set). Owned by honeypot.

Spec

  • decoys[].kind (enum) - Secret or ServiceAccount.
  • decoys[].name, decoys[].namespace (string).
  • decoys[].data (map) - key-value payload for Secret decoys. Treat values as fake credentials - never put a real token here.
  • emitOnRead (bool) - default true. Fires on get / list / watch as well as write verbs.
  • allowlistedActors[] (string) - SA usernames that touch decoys without triggering an SE (cluster admins running disaster drills).

Status

  • deployedDecoys[] - {kind, namespace, name, uid} for each materialised decoy.
  • lastReconcileAt.
  • conditions[].

Example

apiVersion: security.ugallu.io/v1alpha1
kind: HoneypotConfig
metadata: { name: prod-decoys }
spec:
emitOnRead: true
allowlistedActors:
- "system:serviceaccount:ops:dr-runbook"
decoys:
- kind: Secret
namespace: prod
name: aws-prod-creds
labels: { ugallu.io/honeypot: "true" }
data:
access_key_id: "AKIAFAKE000000000000"
secret_access_key: "fake/decoy/never-real"
- kind: ServiceAccount
namespace: prod
name: payments-deployer
labels: { ugallu.io/honeypot: "true" }

Singleton, cluster-scoped. Tunes the webhook risk model and the caBundle resolution allowlist.

Spec

  • riskThreshold (int 0-100) - default 60.
  • ignore[].nameGlobs[] (string) - glob patterns that skip evaluation entirely (ugallu’s own admission policies, cert-manager, distribution-bundled webhooks).
  • trustedSubjectDNs[] (string) - RFC 4514 DN allowlist for caBundle CAs.
  • trustedCASources[].namespace (string) - namespaces whose Secrets the auditor will dereference for indirect caBundle refs.

Status

  • lastConfigLoadAt.
  • observedWebhooks - live MWC + VWC count at the most recent reconcile.
  • conditions[].

Example

apiVersion: security.ugallu.io/v1alpha1
kind: WebhookAuditorConfig
metadata: { name: default }
spec:
riskThreshold: 60
ignore:
- { nameGlobs: ["ugallu-*", "cert-manager-*", "rancher.*"] }
trustedSubjectDNs:
- "CN=cert-manager-webhook-ca,O=cert-manager"
trustedCASources:
- { namespace: cert-manager, namePrefix: "webhook-cert-" }
- { namespace: kube-system, namePrefix: "ugallu-" }