conduktor.io ↗

Financial Services

Defaults aligned with SOC2, PCI, GDPR, and HIPAA controls.

Why this bundle

Defaults aligned with SOC2, PCI, GDPR, and HIPAA controls.

Sixteen policies for data-criticality and residency labels, audit-retention floors, regulated-tier replication, FULL schema compatibility, secret-provider enforcement on connectors, and SSO-backed ApplicationGroups. Aimed at banks, insurers, healthcare, and payment processors where every topic, schema, and connector has to survive an audit.

Apply the whole bundle

One concatenated YAML stream with every ResourcePolicy in this bundle. Copy, save, apply.

# All policies in the Financial Services bundle (17 resources)
# Save as bundle-financial-services.yaml then: conduktor apply -f bundle-financial-services.yaml
# Each ResourcePolicy must still be linked via Application(Instance).spec.policyRef
# or KafkaCluster.spec.policiesRef to take effect.
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: min-replication-factor
spec:
  targetKind: Topic
  description: Production topics must have replication factor >= 3
  rules:
    - condition: '!metadata.name.startsWith("prod.") || spec.replicationFactor >= 3'
      errorMessage: "Production topics must have replication factor >= 3"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: isr-alignment
spec:
  targetKind: Topic
  description: min.insync.replicas must equal replicationFactor - 1
  rules:
    - condition: '"min.insync.replicas" in spec.configs && int(string(spec.configs["min.insync.replicas"])) == int(spec.replicationFactor) - 1'
      errorMessage: "min.insync.replicas must equal replicationFactor - 1 (e.g. RF=3 -> min.insync.replicas=2)"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: max-retention-bound
spec:
  targetKind: Topic
  description: retention.ms must be <= 30d and not infinite, unless labels.retention-justified == "true"
  rules:
    - condition: '("retention-justified" in metadata.labels && metadata.labels["retention-justified"] == "true") || ("retention.ms" in spec.configs && int(string(spec.configs["retention.ms"])) != -1 && int(string(spec.configs["retention.ms"])) <= 2592000000)'
      errorMessage: "retention.ms must be <= 30 days and not -1 (set label retention-justified=true to override)"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: schema-required-non-internal
spec:
  targetKind: Topic
  description: Non-internal topics must declare a schema subject via labels.schema-subject
  rules:
    - condition: 'metadata.name.startsWith("__") || metadata.name.startsWith("_") || ("schema-subject" in metadata.labels && size(metadata.labels["schema-subject"]) > 0)'
      errorMessage: "Non-internal topics must set label schema-subject=<subject-name> (or be Schema-Registry-governed)"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: no-wildcard-acl-prod
spec:
  targetKind: ApplicationGroup
  description: ApplicationGroups touching prod.* resources cannot use wildcard LITERAL resource patterns
  rules:
    - condition: 'spec.permissions.all(p, !(p.name.startsWith("prod.") || p.name == "*") || (p.patternType != "LITERAL" || p.name != "*"))'
      errorMessage: "Wildcard LITERAL resource is not allowed on production resources"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: topic-owner-label-required
spec:
  targetKind: Topic
  description: every topic must carry owner (email) and data-criticality (C0..C3) labels
  rules:
    - condition: 'has(metadata.labels.owner) && metadata.labels.owner.matches("^[a-z0-9._-]+@[a-z0-9.-]+\\.[a-z]{2,}$") && "data-criticality" in metadata.labels && metadata.labels["data-criticality"] in ["C0", "C1", "C2", "C3"]'
      errorMessage: "metadata.labels.owner (email) and metadata.labels[\"data-criticality\"] (C0..C3) are required — unowned topics become stale and unpageable"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: subject-compatibility-not-none
spec:
  targetKind: Subject
  description: spec.compatibility must be set and not NONE
  rules:
    - condition: 'has(spec.compatibility) && spec.compatibility in ["BACKWARD", "BACKWARD_TRANSITIVE", "FORWARD", "FORWARD_TRANSITIVE", "FULL", "FULL_TRANSITIVE"]'
      errorMessage: "spec.compatibility must be explicitly set and not NONE — NONE allows arbitrary schema changes that break downstream consumers"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: applicationgroup-no-wildcard-write
spec:
  targetKind: ApplicationGroup
  description: wildcard (name="*") permissions cannot include write/create/delete on topics or connectors
  rules:
    - condition: 'spec.permissions.all(p, p.name != "*" || !p.permissions.exists(perm, perm in ["topicProduce", "topicCreate", "topicDelete", "kafkaConnectCreate", "kafkaConnectDelete"]))'
      errorMessage: "wildcard (name=\"*\") permissions cannot include write/create/delete on topics or connectors — scope by prefix instead"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: applicationgroup-prod-requires-external-group
spec:
  targetKind: ApplicationGroup
  description: production ApplicationGroups must use externalGroups/externalGroupRegex (SSO sync)
  rules:
    - condition: '!spec.permissions.exists(p, p.appInstance.matches(".*-(prod|prd)$")) || (size(spec.members) == 0 && ((has(spec.externalGroups) && size(spec.externalGroups) > 0) || (has(spec.externalGroupRegex) && size(spec.externalGroupRegex) > 0)))'
      errorMessage: "production ApplicationGroups must use externalGroups/externalGroupRegex (SSO sync) — manual spec.members lists rot when staff leave"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: data-criticality-label-required
spec:
  targetKind: Topic
  description: Every topic must declare metadata.labels.data-criticality in [C0,C1,C2,C3]
  rules:
    - condition: 'has(metadata.labels) && "data-criticality" in metadata.labels && metadata.labels["data-criticality"] in ["C0","C1","C2","C3"]'
      errorMessage: "Topic must set metadata.labels.data-criticality to one of C0, C1, C2, C3 (C0=public, C3=PHI/PCI)"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: data-residency-label-required
spec:
  targetKind: Topic
  description: Every topic must declare metadata.labels.region for cross-border replication checks
  rules:
    - condition: 'has(metadata.labels) && "region" in metadata.labels && metadata.labels["region"] in ["eu","us","uk","apac","global"]'
      errorMessage: "Topic must set metadata.labels.region to one of eu, us, uk, apac, global — required for GDPR cross-border controls"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: audit-retention-floor
spec:
  targetKind: Topic
  description: No infinite retention; audit-tagged topics must retain >= 7 years
  rules:
    - condition: '"retention.ms" in spec.configs && int(string(spec.configs["retention.ms"])) > 0'
      errorMessage: "retention.ms must be a finite positive value — retention.ms=-1 (infinite) is not allowed in a regulated cluster"
    - condition: '!(has(metadata.labels) && "audit" in metadata.labels && metadata.labels["audit"] == "true") || ("retention.ms" in spec.configs && int(string(spec.configs["retention.ms"])) >= 220752000000)'
      errorMessage: "Audit-tagged topics (metadata.labels.audit=true) must set retention.ms >= 220752000000 (7 years) for SOX/FINRA/HIPAA"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: min-isr-regulated-floor
spec:
  targetKind: Topic
  description: C2/C3 topics need replicationFactor>=5 and min.insync.replicas>=3
  rules:
    - condition: '!(has(metadata.labels) && "data-criticality" in metadata.labels && metadata.labels["data-criticality"] in ["C2","C3"]) || spec.replicationFactor >= 5'
      errorMessage: "Topics with data-criticality C2/C3 require replicationFactor >= 5"
    - condition: '!(has(metadata.labels) && "data-criticality" in metadata.labels && metadata.labels["data-criticality"] in ["C2","C3"]) || ("min.insync.replicas" in spec.configs && int(string(spec.configs["min.insync.replicas"])) >= 3)'
      errorMessage: "Topics with data-criticality C2/C3 require min.insync.replicas >= 3"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: subject-full-compatibility-regulated
spec:
  targetKind: Subject
  description: Subjects flagged regulated=true must use FULL or FULL_TRANSITIVE compatibility
  rules:
    - condition: '!(has(metadata.labels) && "regulated" in metadata.labels && metadata.labels["regulated"] == "true") || spec.compatibility in ["FULL","FULL_TRANSITIVE"]'
      errorMessage: "Subjects tagged regulated=true must declare compatibility FULL or FULL_TRANSITIVE — no breaking changes for audit/fraud/claims consumers"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: connector-no-inline-credentials
spec:
  targetKind: Connector
  description: Connector config must reference secrets via ${secret:...} / ${vault:...}
  rules:
    - condition: '!("connection.password" in spec.config) || spec.config["connection.password"].startsWith("${secret:") || spec.config["connection.password"].startsWith("${vault:") || spec.config["connection.password"].startsWith("${file:") || spec.config["connection.password"].startsWith("${env:")'
      errorMessage: "connection.password must be a secret reference (${secret:...}, ${vault:...}, ${file:...}, ${env:...}) — not an inline value"
    - condition: '!("consumer.override.sasl.jaas.config" in spec.config) || !spec.config["consumer.override.sasl.jaas.config"].contains("password=")'
      errorMessage: "consumer.override.sasl.jaas.config contains an inline password — use a secret-provider reference"
    - condition: '!("producer.override.sasl.jaas.config" in spec.config) || !spec.config["producer.override.sasl.jaas.config"].contains("password=")'
      errorMessage: "producer.override.sasl.jaas.config contains an inline password — use a secret-provider reference"
    - condition: '!("aws.secret.access.key" in spec.config) || spec.config["aws.secret.access.key"].startsWith("${")'
      errorMessage: "aws.secret.access.key must be a secret-provider reference, not an inline AWS key"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: connector-jdbc-host-allowlist
spec:
  targetKind: Connector
  description: JDBC connectors may only target hosts under the approved internal DNS
  rules:
    - condition: '!("connector.class" in spec.config && spec.config["connector.class"].contains("Jdbc")) || ("connection.url" in spec.config && spec.config["connection.url"].matches("^jdbc:[a-z]+://[a-z0-9-]+\\.db\\.internal\\.[a-z]+(:[0-9]+)?/.*$"))'
      errorMessage: "JDBC connector connection.url must target *.db.internal.<corp> — public DBs and shadow-IT endpoints are not allowed"
---
apiVersion: self-serve/v1
kind: ResourcePolicy
metadata:
  name: topic-owner-slug-required
spec:
  targetKind: Topic
  description: Every topic must declare metadata.labels.owner as a team slug
  rules:
    - condition: 'has(metadata.labels) && "owner" in metadata.labels && metadata.labels["owner"].matches("^[a-z][a-z0-9-]{1,38}$")'
      errorMessage: "Topic must set metadata.labels.owner to a team slug (e.g. payments-platform), not a person or TBD"

Each policy must still be linked via Application(Instance).spec.policyRef or KafkaCluster.spec.policiesRef to take effect.

Policies in this bundle

Grouped by category. Click any policy for the rationale, examples, and YAML.

Replication

3

Retention

2

Schema Enforcement

3

Security & ACLs

3

Operational Hygiene

4

Connectors

2

Enforce this bundle automatically?

Drop these YAMLs into Conduktor Console to get central enforcement, audit history, and pre-commit feedback for every change.

See Conduktor Console →