Secure ZooKeeper in Confluent Platform¶
ZooKeeper authentication overview¶
As of version 3.5.x, ZooKeeper supports mutual TLS (mTLS) authentication. As of version 2.5, Kafka supports authenticating to ZooKeeper with SASL and mTLS–either individually or together. See KIP-515 for details.
When using mTLS alone, every broker and CLI tool (such as the ZooKeeper security migration tool, ZkSecurityMigrator) must identify itself using the same Distinguished Name (DN). The DN is included in the ZooKeeper ACL, so ZooKeeper will only authorize that DN; therefore all connections to ZooKeeper must provide the DN. You can modify what gets put into the ACL from the DN, but it involves writing and deploying a custom ZooKeeper authentication provider. For details, see mTLS authentication. Each CA certificate should use the same DN, and also specify a different Subject Alternative Name (SAN) to ensure that ZooKeeper hostname verification of brokers and any CLI tools will succeed.
When using SASL and mTLS authentication simultaneously with ZooKeeper, the SASL identity and either the DN that created the znode (the creating broker’s CA certificate) or the DN of the security migration tool (if migration was performed after the znode was created) are included in ACLs. Hence, all brokers and CLI tools will be authorized even if they all use different DNs because they all use the same SASL identity. When using mTLS authentication only, all DNs must match, and SANs become critical in the absence of writing and deploying a custom ZooKeeper authentication provider.
Important
As of Confluent Platform 7.5, ZooKeeper is deprecated for new deployments. Confluent recommends KRaft mode for new deployments. For more information, see KRaft Overview for Confluent Platform.
You can enable security in ZooKeeper by using the examples below.
Note
When authenticating brokers with ZooKeeper, set zookeeper.set.acl=true
for all
brokers. If you accept the default (zookeeper.set.acl=false
), then no ACLs
are created and the information in ZooKeeper will be writeable by everyone.
SASL authentication¶
This section describes how to enable and use SASL authentication with ZooKeeper. For details about adding TLS encryption so that SASL credentials are not transmitted in the clear, refer to Encrypting communication to ZooKeeper with TLS.
Enable ZooKeeper authentication with SASL¶
To enable ZooKeeper authentication with SASL add the following to
zookeeper.properties
:
authProvider.sasl=org.apache.zookeeper.server.auth.SASLAuthenticationProvider
SASL with Digest-MD5¶
Here is an example of a ZooKeeper node JAAS file:
Server {
org.apache.zookeeper.server.auth.DigestLoginModule required
user_super="adminsecret"
user_bob="bobsecret";
};
Here is an example of a ZooKeeper client JAAS file, including brokers and admin scripts like kafka-topics:
Client {
org.apache.zookeeper.server.auth.DigestLoginModule required
username="bob"
password="bobsecret";
};
If your Confluent Server broker already has a JAAS file, this section must be added to it.
SASL with Kerberos¶
Here is an example of ZooKeeper node JAAS file:
Server {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
keyTab="<path-to-server-keytab>"
storeKey=true
useTicketCache=false
principal="zookeeper/yourzkhostname@EXAMPLE.COM";
};
Here is an example of a ZooKeeper client JAAS file, including brokers and admin
scripts like kafka-topics
:
Client {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
storeKey=true
keyTab="<path-to-client-keytab>"
principal="kafka/kafka1.hostname.com@EXAMPLE.COM";
};
Note
Before starting ZooKeeper, check the JAAS syntax and keytab permissions. The most common errors that prevent the server from starting are JAAS syntax errors or permissions set incorrectly on the keytab file. Refer to Encrypting communication to ZooKeeper with TLS for details.
mTLS authentication¶
You can enable ZooKeeper mTLS authentication with or without SASL authentication.
All clients that connect to ZooKeeper must share an identity; every connection has zero or more identities associated with it. Two identities are possible, for example, if both mTLS and SASL are enabled and the client successfully authenticates with both. If you use mTLS with SASL, then mTLS doesn’t have to be the source of the common identity–the SASL identity can be the same for everyone.
When enabling mTLS without SASL authentication, every broker and/or CLI tool (such as the ZooKeeper security migration tool, ZkSecurityMigrator) must identify itself using the same Distinguished Name (DN). By default, the full DN is included in the ZooKeeper ACL, and ZooKeeper only authorizes what is in the ACL, so all connections to ZooKeeper should provide that DN. However, ZooKeeper cannot use this single DN to map to multiple hosts from which requests originate. Therefore, each CA certificate should include a subject alternative name (SAN). If there is no SAN, hostname verification of brokers and/or any CLI tools will fail and ZooKeeper will reject the connection.
To illustrate the DN and SAN requirement, consider the scenario where
you browse the web site https://meeting.bigdata.us
. The CA certificate for
the site must include the DN CN=meeting.bigdata.us
(or a wildcard certificate
with CN=*.bigdata.us
); if it does not, then your browser will reject the
connection due to a failed hostname verification. Similarly, if a client makes
an mTLS connection to ZooKeeper, and your client hostname is foo.example.org
, then
you must present a certificate with DN CN=foo.example.org
(or a
wildcard certificate using CN=*.example.org
). If you don’t present a
certificate with this DN, then ZooKeeper will reject the connection unless you also
define a Subject Alternative Name (SAN) containing foo.example.org
. In the
absence of SASL authentication, you can have different brokers and CLI tools
connect to ZooKeeper with mTLS from different hosts only if they all use the same DN,
in which case they must also specify a SAN that matches their respective hostnames.
Important
If using mTLS only (without SASL) and specifying zookeeper.set.acl=true
,
do not use certificates with CN=hostname
where hostname differs based
on the location from which the request originates as a means to satisfy
hostname verification. If you do, you may find that brokers cannot access
ZooKeeper nodes. Note that the full DN is included in the ZooKeeper ACL, and ZooKeeper only
authorizes what is in the ACL.
As an alternative to using the DN, you can specify the identity of mTLS clients
by writing a class that extends org.apache.zookeeper.server.auth.X509AuthenticationProvider
and overrides the method protected String getClientId(X509Certificate clientCert)
.
Choose a scheme name and set authProvider.[scheme]
in ZooKeeper to be the
fully-qualified class name of the custom implementation. Then configure
ssl.authProvider=[scheme]
to use it.
To enable mTLS authentication for ZooKeeper you must configure both ZooKeeper and Kafka. The following example shows a ZooKeeper configuration that enables mTLS authentication:
secureClientPort=2182
serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory
authProvider.x509=org.apache.zookeeper.server.auth.X509AuthenticationProvider
ssl.keyStore.location=<path-to-zookeeper-keystore>
ssl.keyStore.password=<zookeeper-keystore-password>
ssl.trustStore.location=<path-to-zookeeper-truststore>
ssl.trustStore.password=<zookeeper-truststore-password>
Important
ZooKeeper does not support setting the key password in the ZooKeeper server keystore to a value different from the keystore password itself. Be sure to set the key password to be the same as the keystore password.
The following example shows a Kafka configuration that connects to ZooKeeper using mTLS authentication:
# Connect to the ZooKeeper port configured for TLS
zookeeper.connect=zk1:2182,zk2:2182,zk3:2182
# Required to use TLS-to-ZooKeeper (default is false)
zookeeper.ssl.client.enable=true
# Required to use TLS-to-ZooKeeper
zookeeper.clientCnxnSocket=org.apache.zookeeper.ClientCnxnSocketNetty
# Define key/trust stores to use TLS-to-ZooKeeper; ignored unless zookeeper.ssl.client.enable=true
zookeeper.ssl.keystore.location=<path-to-kafka-keystore>
zookeeper.ssl.keystore.password=<kafka-keystore-password>
zookeeper.ssl.truststore.location=<path-to-kafka-truststore>
zookeeper.ssl.truststore.password=<kafka-truststore-password>
# Tells broker to create ACLs on znodes
zookeeper.set.acl=true
Important
ZooKeeper does not support setting the key password in the ZooKeeper client keystore
(broker) to a value different from the keystore password itself. Be sure to
set the key password to be the same as the keystore password. Also note that
unlike ZooKeeper, Kafka does not use camel case names for TLS-related configurations
(for example, Kafka uses zookeeper.ssl.keystore.location
, while ZooKeeper uses
ssl.keyStore.location
).
Encrypting communication to ZooKeeper with TLS¶
ZooKeeper connections that use mTLS are encrypted. Beginning with ZooKeeper 3.5.7
(the version shipped with Kafka 2.5), ZooKeeper supports the server-side
configuration ssl.clientAuth=none
, which is case-insensitive; valid
options are: want
, need
(the default), and none
.
When set to none
, ZooKeeper allows clients to connect using a TLS-encrypted
connection without presenting their own CA certificate.
The following (partial) Confluent Server broker configuration shows how to connect to ZooKeeper with TLS encryption only:
Note
ZooKeeper configurations in zookeeper.properties
with explicit enumerated
values–such as ssl.clientAuth
–do not allow trailing whitespaces.
If you include trailing spaces then you will get a value like " need "
,
(note the extra whitespaces), which is not among the set of valid values {want,
need, none}
.
# Connect to the ZooKeeper port configured for TLS
zookeeper.connect=zk1:2182,zk2:2182,zk3:2182
# Required to use TLS-to-ZooKeeper (default is false)
zookeeper.ssl.client.enable=true
# Required to use TLS-to-ZooKeeper
zookeeper.clientCnxnSocket=org.apache.zookeeper.ClientCnxnSocketNetty
# Define trust store to use encryption-only TLS-to-ZooKeeper; ignored unless zookeeper.ssl.client.enable=true
# There is no need to set keystore information assuming ssl.clientAuth=none on ZooKeeper
zookeeper.ssl.truststore.location=<path-to-kafka-truststore>
zookeeper.ssl.truststore.password=<kafka-truststore-password>
# Tells broker to create ACLs on znodes (if using SASL authentication, otherwise do not set)
zookeeper.set.acl=true
Connecting to TLS-enabled ZooKeeper using CLI tools¶
You can connect to TLS-enabled ZooKeeper quorums using the CLI tools
zookeeper-security-migration.sh
, kafka-acls.sh
, and kafka-configs.sh
.
When using one of these tools, place the tool’s TLS configuration in a
file and refer to that file using --zk-tls-config-file <filename>
.
The file contains all of the ZooKeeper-related configuration options that a broker
would use (except it uses a different keystore when using mTLS instead of TLS
encryption only). Refer to mTLS authentication for an mTLS configuration example or
Encrypting communication to ZooKeeper with TLS for an encryption-only example. The following tool invocation
shows how to use --zk-tls-config-file <filename>
:
kafka-acls.sh --zk-tls-config-file <filename> --authorizer-properties zookeeper.connect=zk1:2182 --authorizer-properties zookeeper.set.acl=true --add --allow-principal User:Alice --operation Read --operation Write --topic test-topic
You can connect to TLS-enabled ZooKeeper quorums using the ZooKeeper shell as follows:
zookeeper-shell.sh zk1:2182 -zk-tls-config-file <filename>
Be sure to use a single dash (-
) rather than double-dash (--
) when
identifying the file containing the TLS configuration for zookeeper-shell.sh
.
ZooKeeper quorum mTLS authentication¶
You can enable mTLS authentication between the ZooKeeper servers. For details, refer to Quorum TLS.
Demo¶
For a working example of these security features in ZooKeeper, including mTLS and SASL with Digest-MD5, see the Confluent Platform demo.