How do you configure SASL and TLS between Kafka brokers and ZooKeeper, and what do zookeeper.set.acl and zookeeper.ssl.client.enable control?
answer
- JAAS Client section = broker authN to ZK
- zookeeper.set.acl=true → restrictive znode ACLs (new nodes only)
- zookeeper.ssl.client.enable + keystore/truststore = TLS
- ZK 3.5+ Netty factory, secure port 2182
- zookeeper-security-migration retrofits existing znodes
basics
~10 sSASL is set up via a JAAS Client section so brokers authenticate to ZooKeeper. zookeeper.set.acl=true makes Kafka write znodes with restrictive ACLs. zookeeper.ssl.client.enable=true plus keystore/truststore turns on TLS encryption to ZooKeeper.
solid answer
~40 sAuthentication: give each broker a JAAS file with a `Client { ... }` section (e.g. Kerberos/GSSAPI or DIGEST-MD5) pointed at via -Djava.security.auth.login.config; ZooKeeper itself needs a matching `Server` section and the SASL authentication provider enabled. With brokers authenticated, set zookeeper.set.acl=true so Kafka writes its znodes with ACLs limiting write access to the broker principal — protecting metadata from other ZK clients. Encryption: on ZK 3.5+ with the Netty connection factory, set zookeeper.ssl.client.enable=true and provide zookeeper.ssl.keystore.location/password and zookeeper.ssl.truststore.location/password (these map to ZK client TLS properties); ZK listens on the secure client port (2182). For an already-running unsecured cluster, applying set.acl alone doesn't fix existing znodes — run the zookeeper-security-migration tool to retrofit ACLs.
go deeper
Recognize the three knobs by name: SASL JAAS, zookeeper.set.acl, zookeeper.ssl.client.enable.
Be able to actually wire up a JAAS Client section, set the ACL flag, and supply ZK TLS keystore/truststore properties.
Explain the new-nodes-only caveat, the migration tool, the ZK-server-side prerequisites (Netty, authProvider), and mTLS-as-authN.
Design a phased rollout that hardens both broker and ZK sides without lockout, and choose between DIGEST/Kerberos/mTLS for the org.
## The two independent concerns Securing the broker↔ZooKeeper link has two orthogonal layers: - **Authentication (SASL)** — proving *who* the broker is, and using that identity to restrict znode access. - **Encryption (TLS)** — protecting the *channel* so metadata and credentials can't be sniffed. You can enable either independently, but production hardening uses both. ## SASL authentication ZooKeeper supports SASL via Java's JAAS framework. You supply a JAAS login config (commonly `-Djava.security.auth.login.config=/path/jaas.conf`). On the **broker** side the file has a `Client` section — for DIGEST-MD5: ``` Client { org.apache.zookeeper.server.auth.DigestLoginModule required username="kafka" password="secret"; }; ``` or a Kerberos `Client` using `com.sun.security.auth.module.Krb5LoginModule`. On the **ZooKeeper server** side you set a `Server` JAAS section with matching credentials and enable the SASL authentication provider (`authProvider.sasl=org.apache.zookeeper.server.auth.SASLAuthenticationProvider`) and, for DIGEST, the digest provider. Now ZooKeeper knows the authenticated identity of each connecting broker. ## zookeeper.set.acl Set in the **Kafka broker** config: ``` zookeeper.set.acl=true ``` When true, Kafka creates its znodes with ZooKeeper ACLs that grant **write** access only to the authenticated Kafka principal (and typically world read for non-sensitive nodes, with sensitive nodes locked tighter). Without it, znodes are created world-writable. **Crucially**, this only affects znodes Kafka creates *after* the flag is enabled — it does not retroactively secure existing nodes. ## TLS to ZooKeeper TLS requires **ZooKeeper 3.5+** and the **Netty** connection factory on the server (`serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory`) plus a secure client port (default 2182). On the **Kafka broker**, Kafka exposes ZK TLS settings prefixed `zookeeper.ssl.`: ``` zookeeper.ssl.client.enable=true zookeeper.ssl.keystore.location=/path/zk.keystore.jks zookeeper.ssl.keystore.password=... zookeeper.ssl.truststore.location=/path/zk.truststore.jks zookeeper.ssl.truststore.password=... ``` Kafka translates these into the underlying `zookeeper.client.secure`, `zookeeper.ssl.keyStore.*`, and `zookeeper.ssl.trustStore.*` system properties the ZK client reads. Mutual TLS (broker cert verified by ZK) can also serve as the authentication mechanism via the X509AuthenticationProvider, so TLS can do double duty as authN. ## Retrofitting an existing cluster Because `zookeeper.set.acl=true` doesn't touch existing znodes, an unsecured cluster being hardened must run: ``` bin/zookeeper-security-migration.sh --zookeeper.acl=secure --zookeeper.connect=... ``` This walks the existing znode tree and applies the secure ACLs. To reverse (e.g., decommissioning), use `--zookeeper.acl=unsecure`. The migration tool itself must run with broker credentials. ## Edge cases - Enabling TLS but forgetting the Netty factory on the ZK server side causes connection failures. - DIGEST-MD5 passwords sit in the JAAS file in plaintext — protect file permissions; many shops prefer mTLS or Kerberos. - If you enable set.acl but later lose the broker principal, you can lock yourself out of the znodes (recover via `skipACL=yes` superuser on the ZK server).
- You set zookeeper.set.acl=true on a cluster that ran unsecured for a year. Are the old znodes now protected?No. The flag only applies ACLs to znodes Kafka creates after the change. Existing znodes remain with their old (open) ACLs until you run bin/zookeeper-security-migration.sh with --zookeeper.acl=secure to walk and re-ACL the tree.
- Can TLS to ZooKeeper also serve as authentication?Yes — with mutual TLS the ZooKeeper X509AuthenticationProvider can derive the broker's identity from its client certificate's subject DN, so mTLS handles both encryption and authentication, avoiding plaintext DIGEST passwords.
saying these in an interview costs you the question
- Thinking zookeeper.set.acl=true retroactively secures existing znodes.
- Forgetting that ZK TLS needs 3.5+ and the Netty connection factory on the server side.
- Confusing the broker's ssl.* (client listener) settings with the zookeeper.ssl.* (broker↔ZK) settings.
- Assuming the broker-side flag alone configures ZooKeeper — the ZK server needs matching JAAS Server / authProvider config.