Skip to main content

KafkaBackupValidation CRD

The KafkaBackupValidation custom resource validates backups and can produce evidence reports for compliance workflows.

Example​

apiVersion: kafka.oso.sh/v1alpha1
kind: KafkaBackupValidation
metadata:
name: daily-backup-validation
namespace: kafka-backup
spec:
backupRef:
name: production-backup
backupId: "production-backup-20260413-120000"

kafkaCluster:
bootstrapServers:
- kafka:9092
connection:
connectionsPerBroker: 4

checks:
messageCount:
enabled: true
failThreshold: 0
topics:
- orders
offsetRange:
enabled: true
consumerGroupOffsets:
enabled: true
consumerGroups:
- order-processor

evidence:
formats:
- json
- pdf
retentionDays: 90
signing:
enabled: true
keySecret:
name: evidence-signing-key
privateKeyKey: signing-key.pem
publicKeyKey: signing-key-pub.pem

schedule: "0 0 2 * * * *"
suspend: false

Spec Fields​

backupRef​

FieldTypeRequiredDescription
namestringYesKafkaBackup resource to validate; use "" when using direct storage
namespacestringNoBackup namespace; defaults to the validation namespace
backupIdstringNoSpecific backup ID to validate
storageobjectNoDirect storage reference for external backups

checks​

FieldTypeDescription
messageCountobjectCompare record counts against the backup manifest
offsetRangeobjectVerify backup offset ranges
consumerGroupOffsetsobjectVerify consumer group positions are backed up
customWebhooksarrayPOST validation payloads to external endpoints

kafkaCluster is required: the checks compare the backup against this cluster, and a validation without it fails. The shared kafkaCluster.connection tuning fields are supported here, along with tlsSecret, caSecret, and saslSecret for secure clusters.

evidence​

Available from operator v1.6.2

Operator releases before 1.6.2 accepted evidence but never wrote a report: a run reported Completed with no evidence in storage and no path in status (#93).

FieldTypeDefaultDescription
formats[]string["json"]json and/or pdf (case-insensitive). Any other value, or an empty list, fails validation
signingobjectunsetECDSA-P256-SHA256 signing configuration
storageobjectthe backup's storageWhere reports are written, in the same format as KafkaBackup.spec.storage
retentionDaysint90Retention recorded in the report's compliance mappings

Reports are written to evidence-reports/<reportId>/<yyyy>/<mm>/ under evidence.storage (and its prefix), or under the backup's own storage when evidence.storage is unset, the same layout as kafka-backup validation run, so kafka-backup validation evidence-list and evidence-get work against it. Each run writes:

  • <reportId>.json: the report (always written when signing is enabled, because the signature covers its bytes)
  • <reportId>.pdf: when pdf is in formats
  • <reportId>.sig: the detached signature envelope, when signing is enabled

<reportId> is <name>-<yyyymmdd>-<hhmmss>-<first 8 characters of the resource uid>. The report records the storage location without credentials (for example azure://account/container/prefix) and kafka-backup-operator with its version as the tool.

If the reports cannot be stored, the run fails with condition reason EvidenceEmissionFailed; the check results are not reported as a pass without their evidence.

Signing key. signing.keySecret names a Secret holding a PEM-encoded PKCS#8 ECDSA P-256 private key (-----BEGIN PRIVATE KEY-----) under privateKeyKey (default signing-key.pem). A SEC1 key (-----BEGIN EC PRIVATE KEY-----, as openssl ecparam -genkey writes) fails validation with the conversion command:

openssl pkcs8 -topk8 -nocrypt -in key.pem -out key.p8.pem

See Evidence Signing to create a key pair and verify reports.

notifications​

Available from operator v1.6.2

Send each run's result and evidence location to Slack and/or PagerDuty. A delivery failure is reported in the status message and does not fail the run.

notifications:
slack:
webhookSecret:
name: validation-slack
key: webhook-url
pagerduty:
routingKeySecret:
name: validation-pagerduty
key: routing-key
severity: critical # critical, error, warning or info

schedule​

schedule uses the same cron parser as KafkaBackup and includes seconds. For example, 0 0 2 * * * * runs daily at 02:00 UTC. A validation runs once when it is created or its spec changes, then at every tick of its schedule, after a passed or failed run alike; status.nextScheduledValidation shows the next tick.

Available from operator v1.6.3

Operator releases before 1.6.3 ran a scheduled validation only once, at creation or spec change, and never again (#105).

suspend: true stops scheduled runs. Setting it does not start a run (before 1.6.3 it did, once).

Status​

status:
phase: Completed
validationResult: Pass
checksTotal: 3
checksCompleted: 3
checksPassed: 3
evidenceReportId: daily-backup-validation-20260414-020000-3f2a9c1e
evidenceReportPath: evidence-reports/daily-backup-validation-20260414-020000-3f2a9c1e/2026/04/daily-backup-validation-20260414-020000-3f2a9c1e.json
evidencePdfPath: evidence-reports/daily-backup-validation-20260414-020000-3f2a9c1e/2026/04/daily-backup-validation-20260414-020000-3f2a9c1e.pdf
evidenceSignaturePath: evidence-reports/daily-backup-validation-20260414-020000-3f2a9c1e/2026/04/daily-backup-validation-20260414-020000-3f2a9c1e.sig
evidenceReportSha256: 9f2b6c0d4e1a8b7f3c5d2e9a0b4c6d8e1f3a5b7c9d0e2f4a6b8c0d1e3f5a7b9c
evidenceReportSigned: true
lastValidationTime: "2026-04-14T02:00:00Z"
nextScheduledValidation: "2026-04-15T02:00:00Z"
FieldDescription
evidenceReportIdReport ID of the last completed run
evidenceReportPathKey of the JSON report, relative to the evidence storage and its prefix
evidencePdfPathKey of the PDF report
evidenceSignaturePathKey of the signature envelope
evidenceReportSha256Lowercase hex SHA-256 of the exact stored JSON bytes
evidenceReportSignedWhether the report is signed
nextScheduledValidationNext scheduled run (scheduled validations, from 1.6.3)

The evidence fields describe the last completed run; a field the run did not produce is removed. The evidence* fields other than evidenceReportPath and evidenceReportSigned need the CRDs from 1.6.2 or later (kubectl apply --server-side -f deploy/crds/all.yaml): on an older CRD the API server drops them.

A run that fails sets phase: Failed and a Ready condition with one of these reasons:

ReasonMeaning
ValidationFailedThe spec is invalid or the checks could not run
EvidenceEmissionFailedThe checks ran but the evidence could not be stored (1.6.2+)
InterruptedThe operator restarted while the validation was running; it is not resumed (1.6.5+). A scheduled validation runs again at its next tick; change the spec to run a one-off validation again