DEV 20 min

Apache Kafka in Spring Boot: Architecture, mTLS & ACLs

Master Apache Kafka: Brokers, Topics, Partitions, Offsets, and the transition to KRaft (no Zookeeper). Includes a full implementation of Producers and Consumers using Spring Boot and Docker Compose.

Written by Samir El Azem Crovetto • Senior Backend Engineer & DevSecOps

Event-Driven Architecture (EDA) and the Role of Apache Kafka

In modern enterprise distributed systems and microservices engineering, traditional synchronous point-to-point communication via HTTP/REST introduces tight temporal coupling and systemic fragility. If a downstream service encounters latency degradation or a network outage, incoming requests back up rapidly, exhausting thread pools and cascading failures across the entire ecosystem, as examined in our guide on enterprise microservices architecture.

Event-Driven Architecture (EDA) resolves this friction by decoupling event publishers (Producers) from event subscribers (Consumers) through a durable, distributed message bus. Within this domain, Apache Kafka represents the industry's dominant de facto standard.

Unlike traditional message queues governed by JMS or AMQP standards (such as RabbitMQ or ActiveMQ), Kafka is not a broker that destroys messages upon consumption. Kafka operates as a partitioned, horizontally replicated, append-only Distributed Commit Log:

  • High-Throughput Sequential Disk I/O: Events are appended sequentially to disk blocks, taking direct advantage of Linux sendfile system calls (Zero-Copy memory transfer) to transfer bytes straight from page cache to the network socket without passing through JVM user space.
  • Event Replayability: Consumers manage their own read cursors (offsets). This allows new microservices to reprocess historical logs from the beginning of time or replay transactions following business logic hotfixes.

However, operating Apache Kafka in enterprise production introduces severe attack vectors if deployed with factory-default configurations: any client possessing network reachability to port 9092 can read unencrypted financial transactions or PII, publish fraudulent events, or manipulate consumer group offsets.


Apache Kafka Security Architecture: Authentication, ACLs, and Encryption

Achieving confidentiality, integrity, and non-repudiation across a Kafka cluster demands a defense-in-depth model spanning transport-layer cryptography through data contract governance:

graph TD
    subgraph Client_Tier ["🖥️ Spring Boot 3 Microservices"]
        ProducerApp["Order Producer Service<br/>(Client Cert: order-producer.p12)"]
        ConsumerApp["Billing Consumer Service<br/>(Client Cert: billing-consumer.p12)"]
    end

    subgraph Kafka_Security_Perimeter ["🛡️ Kafka Security Perimeter (Port 9093 SSL)"]
        mTLS_Gateway["mTLS Termination (X.509 Certificate Verification)"]
        ACL_Engine["Kafka ACL Authorization Engine (Least Privilege)"]
    end

    subgraph Kafka_Broker_Cluster ["⚙️ Kafka Broker Cluster (KRaft Metadata)"]
        TopicOrders[("Topic: orders-v1<br/>3 Partitions / RF=3")]
        TopicDLT[("Topic: orders-v1.DLT<br/>Dead Letter Topic")]
    end

    subgraph Governance ["📋 Data Governance Tier"]
        SchemaRegistry["Confluent Schema Registry (Avro Validation)"]
    end

    ProducerApp -->|1. Validate Schema Contract| SchemaRegistry
    ProducerApp -->|2. TCP 9093: mTLS Handshake| mTLS_Gateway
    mTLS_Gateway -->|3. Evaluate WRITE on orders-v1| ACL_Engine
    ACL_Engine -->|Authorized: Append Commit Log| TopicOrders

    ConsumerApp -->|mTLS Handshake + SASL/SCRAM| mTLS_Gateway
    mTLS_Gateway -->|Evaluate READ on orders-v1| ACL_Engine
    ACL_Engine -->|Authorized: Poll Records| TopicOrders
    TopicOrders --> ConsumerApp

    ConsumerApp -.->|Processing Failure / Retries Exhausted| TopicDLT

    classDef client fill:#e1f5fe,stroke:#0288d1,stroke-width:2px;
    classDef perimeter fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px;
    classDef cluster fill:#e8f5e9,stroke:#388e3c,stroke-width:2px;
    classDef gov fill:#fff3e0,stroke:#f57c00,stroke-width:2px;

    class ProducerApp,ConsumerApp client;
    class mTLS_Gateway,ACL_Engine perimeter;
    class TopicOrders,TopicDLT cluster;
    class SchemaRegistry gov;

1. Distributed Messaging Threat Modeling

In unhardened message broker deployments, critical threat vectors emerge:

  1. Eavesdropping and Traffic Interception: Cleartext event transmission across internal VPC subnets. If an adversary gains lateral execution on any co-located container, network capture tools can reconstruct entire payloads containing credit card numbers or credentials, as analyzed in Wireshark packet capture in containers.
  2. Message Injection & Spoofing (Poison Pills): Unauthorized rogue clients publishing fraudulent state transitions (such as forged bank credits or malformed binary payloads engineered to trigger deserialization exceptions in consumer services).
  3. Consumer Group Hijacking: Unauthenticated consumers joining an existing group.id, inducing partition rebalance storms and intercepting messages before legitimate consumers process them.

2. Cryptographic Authentication: mTLS & SASL/SCRAM-SHA-512

Kafka supports enterprise-grade authentication protocols at the transport layer:

Option A: Mutual TLS (mTLS)

Brokers and clients exchange X.509 digital certificates validated against a trusted internal Certificate Authority (CA), aligning with TLS and mTLS in Spring Boot 3.

  • Key Advantages: End-to-end transport encryption with non-repudiable machine-to-machine identity without static passwords.
  • Broker Configuration (server.properties):
listeners=SSL://0.0.0.0:9093
advertised.listeners=SSL://kafka.enterprise.local:9093
ssl.keystore.location=/var/private/ssl/server.keystore.p12
ssl.keystore.password=KeystorePassword2026!
ssl.keystore.type=PKCS12
ssl.truststore.location=/var/private/ssl/server.truststore.p12
ssl.truststore.password=TruststorePassword2026!
ssl.truststore.type=PKCS12
# Mandate mutual client certificate verification
ssl.client.auth=required
ssl.enabled.protocols=TLSv1.3

Option B: SASL/SCRAM-SHA-512 over SSL (SASL_SSL)

In architectures where managing individual client certificates per microservice introduces excessive PKI operational overhead, utilize SASL/SCRAM-SHA-512. This implements a cryptographic challenge-response protocol where salted hashes prevent password exposure during network traversal or within KRaft metadata stores.


3. Granular Authorization with Kafka ACLs (Access Control Lists)

Authentication verifies caller identity (e.g., User:CN=order-producer-service), but does not restrict operational scope. Authorization is governed by Kafka Access Control Lists (ACLs).

Brokers must enforce default-deny authorization:

authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer
allow.everyone.if.no.acl.found=false
super.users=User:CN=admin-kafka-service

Declarative Least-Privilege Provisioning via CLI:

For the order producer service (order-producer-service):

# Authorize WRITE strictly to orders-v1 topic
kafka-acls --bootstrap-server kafka.enterprise.local:9093 \
    --command-config admin-client.properties \
    --add \
    --allow-principal User:CN=order-producer-service \
    --operation WRITE \
    --operation DESCRIBE \
    --topic orders-v1

For the billing consumer service (billing-consumer-service):

# Authorize READ strictly from orders-v1 topic under assigned consumer group
kafka-acls --bootstrap-server kafka.enterprise.local:9093 \
    --command-config admin-client.properties \
    --add \
    --allow-principal User:CN=billing-consumer-service \
    --operation READ \
    --operation DESCRIBE \
    --topic orders-v1 \
    --group billing-processing-group

If the billing microservice suffers a container escape or remote code vulnerability, the compromised credentials cannot write unauthorized messages to orders-v1 nor join unrelated consumer groups.


4. Spring Boot 3 Integration with spring-kafka

In Spring Boot 3 (Java 21), spring-kafka provides declarative configuration for cryptographic connections and resilience policies.

Production Security Configuration in application.yml:

spring:
  kafka:
    bootstrap-servers: kafka.enterprise.local:9093
    security:
      protocol: SSL
    ssl:
      protocol: TLSv1.3
      key-store-location: classpath:certs/order-service-keystore.p12
      key-store-password: "${KAFKA_KEYSTORE_PASSWORD}"
      key-store-type: PKCS12
      trust-store-location: classpath:certs/ca-truststore.p12
      trust-store-password: "${KAFKA_TRUSTSTORE_PASSWORD}"
      trust-store-type: PKCS12

    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
      acks: all # Enforce replication across all In-Sync Replicas (ISR)
      properties:
        enable.idempotence: true # Idempotent producer prevents duplicate delivery
        retries: 5
        max.in.flight.requests.per.connection: 1

    consumer:
      group-id: billing-processing-group
      auto-offset-reset: earliest
      enable-auto-commit: false # Manual acknowledgments guarantee At-Least-Once delivery
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
      properties:
        spring.deserializer.value.delegate.class: org.springframework.kafka.support.serializer.JsonDeserializer
        spring.json.trusted.packages: "es.secdevoops.dto"

5. Resilience Engineering: Dead Letter Topics (DLT)

A common point of failure in Kafka consumers is the Poison Pill: a record with malformed JSON or corrupted data that triggers an unhandled deserialization exception. Without a recoverer, the consumer thread retries the record endlessly in an infinite loop, starving the entire partition.

Spring Boot 3 resolves this via DefaultErrorHandler combined with exponential retry backoff and automatic routing to a Dead Letter Topic (DLT):

package es.secdevoops.config;

import org.apache.kafka.common.TopicPartition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.KafkaOperations;
import org.springframework.kafka.listener.CommonErrorHandler;
import org.springframework.kafka.listener.DeadLetterPublishingRecoverer;
import org.springframework.kafka.listener.DefaultErrorHandler;
import org.springframework.util.backoff.ExponentialBackOff;

@Configuration
public class KafkaConsumerConfig {

    @Bean
    public CommonErrorHandler errorHandler(KafkaOperations<Object, Object> kafkaTemplate) {
        // Forward failed records to <topic>.DLT after retry exhaustion
        DeadLetterPublishingRecoverer recoverer = new DeadLetterPublishingRecoverer(
            kafkaTemplate,
            (record, ex) -> new TopicPartition(record.topic() + ".DLT", record.partition())
        );

        // Exponential backoff: 1s initial delay, 2.0 multiplier, 3 attempts max
        ExponentialBackOff backOff = new ExponentialBackOff(1000L, 2.0);
        backOff.setMaxAttempts(3);

        DefaultErrorHandler handler = new DefaultErrorHandler(recoverer, backOff);
        // Fatal non-retryable exceptions route immediately to DLT
        handler.addNotRetryableExceptions(IllegalArgumentException.class);
        return handler;
    }
}

6. Schema Governance and Partition Retention Policies (Schema Registry & Log Compaction)

In distributed enterprise architectures, data reliability and security depend heavily on schema contract integrity across software release lifecycles.

Strict Validation with Schema Registry

When multiple independent microservices publish and consume events on Kafka, uncoordinated payload schema mutations (such as dropping required attributes or mutating field types) trigger catastrophic consumer desynchronization and downstream application crashes.

Integrating Confluent Schema Registry or Apicurio Registry with Apache Avro or JSON Schema enforces strict compatibility modes (BACKWARD, FORWARD, or FULL):

# Schema Registry client configuration in Spring Boot application.yml
spring:
  kafka:
    properties:
      schema.registry.url: "https://schema-registry.enterprise.local:8081"
      auto.register.schemas: false # Production rule: forbid runtime schema registration without CI/CD approval
      use.latest.version: true

Enforcing auto.register.schemas: false in production environments is a foundational security control: it prevents producers from registering arbitrary schemas at runtime, ensuring schema evolutions are verified and approved via static linting in DevSecOps pipelines.

Partition Retention: Log Compaction vs. Deletion

At the broker infrastructure level, partition cleanup strategies (cleanup.policy) must align with business data semantics:

  • cleanup.policy=delete: Purges segments based on age thresholds (retention.ms, e.g., 7 days) or aggregate storage volume (retention.bytes). Best suited for transient audit logs, tracing spans, and operational telemetry.
  • cleanup.policy=compact: Perpetually retains the most recent message payload for each record key. Publishing a record containing a null payload (a tombstone) marks the key for complete deletion in subsequent compaction cycles, enabling deterministic GDPR Right to Be Forgotten workflows across immutable log backbones.

Enterprise Apache Kafka Hardening Checklist

  • [x] Unencrypted plaintext port 9092 disabled; SSL port 9093 with TLS 1.3 enforced.
  • [x] Mutual TLS (mTLS) or SASL/SCRAM-SHA-512 authentication mandated on all brokers and clients.
  • [x] Default-deny authorization policy (allow.everyone.if.no.acl.found=false) enabled.
  • [x] Granular Topic and Consumer Group ACL permissions scoped strictly to authorized principals.
  • [x] Producers configured with enable.idempotence=true and acks=all to prevent data loss or duplicates.
  • [x] Consumers protected via ErrorHandlingDeserializer and automatic Dead Letter Topics (DLT).

If you need to audit your organization's distributed event-driven architectures, implement mTLS and ACL governance across Apache Kafka clusters, or build resilient Spring Boot streaming pipelines, explore my Cybersecurity Consulting Services or reach out directly via About Me.