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.
| Kind | Scope | Cardinality | Owning operator |
|---|---|---|---|
SigmaRule | Cluster | per rule | audit-detection |
AuditDetectionConfig | Cluster | singleton | audit-detection |
DNSDetectConfig | Cluster | singleton | dns-detect |
HoneypotConfig | Cluster | per honeypot set | honeypot |
WebhookAuditorConfig | Cluster | singleton | webhook-auditor |
AuditDetectionConfig
Section titled “AuditDetectionConfig”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) - defaultfalse. When true, the operator opens an in-cluster gRPC server oneventBus.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/v1alpha1kind: AuditDetectionConfigmetadata: { 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: 500SigmaRule
Section titled “SigmaRule”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) - defaulttrue.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 againstrequest.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/v1alpha1kind: SigmaRulemetadata: name: cluster-admin-grantedspec: 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]DNSDetectConfig
Section titled “DNSDetectConfig”Singleton, cluster-scoped. Configures the DNS source backend and each detector independently.
Spec
source.primary(enum) -coredns_plugin(default) ortetragon_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-namespaceConfigMapreferences (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 fromspec.source.primaryduring a fallback).lastConfigLoadAt.inflightLookups- live pending RDAP lookups (cap is enforced by the YoungDomain detector).conditions[].
Example
apiVersion: security.ugallu.io/v1alpha1kind: DNSDetectConfigmetadata: { 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 }HoneypotConfig
Section titled “HoneypotConfig”Cluster-scoped, supports multiple CRs (one per honeypot set). Owned by honeypot.
Spec
decoys[].kind(enum) -SecretorServiceAccount.decoys[].name,decoys[].namespace(string).decoys[].data(map) - key-value payload forSecretdecoys. Treat values as fake credentials - never put a real token here.emitOnRead(bool) - defaulttrue. Fires onget/list/watchas 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/v1alpha1kind: HoneypotConfigmetadata: { 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" }WebhookAuditorConfig
Section titled “WebhookAuditorConfig”Singleton, cluster-scoped. Tunes the webhook risk model and the caBundle resolution allowlist.
Spec
riskThreshold(int 0-100) - default60.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/v1alpha1kind: WebhookAuditorConfigmetadata: { 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-" }