Secure the ZooKeeper quorum communication using SSL certificates.

Certification authority

Generate certification authority for testing purposes.

Create simplified CA configuration.

$ cat <<EOF | tee ca.config
# OpenSSL configuration file for certification authority.

[ req ]
prompt = no
distinguished_name = req_distinguished_name
x509_extensions    = v3_ca

[ req_distinguished_name ]
O               = ZooKeeper
OU              = Backend
emailAddress    = admin@example.org

[ v3_ca ]
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints       = critical,CA:true
EOF

Generate CA certificate and private key.

$ openssl req -new -x509 -days 3650 -keyout ca.key -out ca.pem -passout "pass:capassword" -config ca.config
Generating a RSA private key
..........................................................................+++++
.+++++
writing new private key to 'ca.key'
-----

Inspect created CA certificate.

$ openssl x509 -in ca.pem -text --noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            75:af:15:b2:5f:1c:6d:67:08:59:38:11:f4:1d:54:7b:0b:3d:f6:e5
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: O = ZooKeeper, OU = Backend, emailAddress = admin@example.org
        Validity
            Not Before: Aug  8 19:14:09 2021 GMT
            Not After : Aug  6 19:14:09 2031 GMT
        Subject: O = ZooKeeper, OU = Backend, emailAddress = admin@example.org
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:b7:08:4b:ba:bf:bb:55:96:b9:cf:02:f5:11:38:
                    7c:70:f9:58:a4:c3:fc:88:70:23:6e:ce:35:03:10:
                    83:ff:ba:5c:ad:1e:e1:55:ce:8e:53:c2:e5:6b:12:
                    5b:93:39:b3:ad:7a:d8:4c:73:d1:68:5e:67:98:6b:
                    ba:2a:0e:64:af:49:11:77:23:9d:74:fe:5a:5c:a8:
                    59:97:80:ff:80:73:49:67:b1:60:b7:ae:57:30:42:
                    e3:71:aa:48:84:e8:30:4d:8c:d4:18:52:63:75:37:
                    21:d6:e9:61:35:b6:31:d7:60:a9:9c:56:ad:9c:b7:
                    60:f6:7c:9b:bb:ee:a6:f4:14:8c:87:81:dc:5a:69:
                    7f:f5:99:aa:57:c3:fb:29:2a:bb:dd:b8:e8:2b:8e:
                    ff:f1:5e:2b:6e:4f:a9:11:a3:4b:e5:d7:0c:3a:ef:
                    0d:ff:4a:4d:e8:c1:e8:e1:0d:2f:4a:da:3c:32:9f:
                    55:d3:9f:6f:40:5b:14:dc:00:84:30:7a:b3:55:3e:
                    70:a1:f8:6f:92:7f:41:51:93:62:00:e4:72:19:dc:
                    a2:31:99:30:ac:72:a2:d9:50:4c:11:17:fc:10:44:
                    48:99:41:3c:22:81:6d:42:1c:2b:fc:6e:49:52:01:
                    db:99:94:e4:16:a9:51:bd:40:55:54:71:94:dc:a3:
                    83:17
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                E8:58:8D:F9:A3:9A:67:5A:09:3D:0E:72:3A:EE:1E:FF:51:76:1E:63
            X509v3 Authority Key Identifier: 
                keyid:E8:58:8D:F9:A3:9A:67:5A:09:3D:0E:72:3A:EE:1E:FF:51:76:1E:63

            X509v3 Basic Constraints: critical
                CA:TRUE
    Signature Algorithm: sha256WithRSAEncryption
         1e:51:83:61:51:63:6c:47:8f:ac:21:ce:84:a7:ee:dd:77:68:
         28:63:a5:16:c4:ad:b2:30:5a:94:5b:52:63:82:6b:9d:29:2e:
         f9:91:79:6c:92:bc:63:df:e9:a0:21:5d:ad:81:37:5e:ff:d2:
         06:a1:5d:6c:2e:37:e9:f9:38:8e:13:ce:c7:a0:45:ff:fc:7c:
         e5:7a:9c:33:69:44:4b:01:88:83:03:ed:d3:49:26:bc:4b:43:
         73:5f:67:1e:3f:eb:24:b1:98:41:3e:66:fb:2a:78:83:35:c9:
         68:9b:80:67:89:d5:1a:ee:50:9e:b0:78:36:45:31:02:c7:3e:
         56:09:dd:2c:15:47:0a:e1:e4:45:ce:67:f7:c0:bc:87:6e:f5:
         4f:43:1e:8e:d7:c1:f5:d9:5f:6d:b4:70:a4:31:16:96:e4:9a:
         04:a5:b0:ac:a0:a6:44:6e:91:48:ae:59:0a:51:a0:77:9d:dc:
         66:64:47:5a:38:e1:e6:e0:51:7a:88:b9:ce:ca:e4:71:26:f7:
         e1:ea:46:19:7e:ae:b6:bb:50:a2:6f:6a:1a:2d:73:c7:44:7e:
         3f:64:8f:1e:a2:5c:5b:80:2f:5d:7d:30:ca:8a:6f:2c:8e:1a:
         46:8a:85:48:b7:db:cc:49:f2:3a:fd:f0:fd:7b:d1:7b:22:6e:
         31:c7:e4:a3

Server certificates

The best solution would be to use Subject Alternative Names extension to generate a single certificate and use it on each server.

$ cat <<EOF | tee san_certificate.config
# OpenSSL configuration file for SAN certificate.

[ req ]
req_extensions     = req_ext
distinguished_name = req_distinguished_name
prompt             = no

[ req_distinguished_name ]
commonName=zookeeper.backend.example.org

[ req_ext ]
subjectAltName   = @alt_names

[ alt_names ]
DNS.1  = zookeeper1.example.org
DNS.2  = zookeeper2.example.org
DNS.3  = zookeeper3.example.org
EOF

Generate certification request.

$ openssl req -new -config san_certificate.config -extensions req_ext -nodes -keyout san_certificate.key -out san_certificate.csr
Generating a RSA private key
.....+++++
...................................................................................+++++
writing new private key to 'san_certificate.key'
-----

Create SAN certificate.

$ openssl x509 -req -CA ca.pem -CAkey ca.key -in san_certificate.csr -out san_certificate.pem -days 365 -CAcreateserial -extfile san_certificate.config -extensions req_ext -passin pass:capassword
Signature ok
subject=CN = zookeeper.backend.example.org
Getting CA Private Key

Inspect created certificate.

$ openssl x509 -in san_certificate.pem -text -noout
Certificate:
    Data:                                                          
        Version: 3 (0x2)
        Serial Number:
            05:be:44:95:b8:4d:c3:1f:ac:90:d4:ef:d4:d9:43:ef:2c:49:1a:42
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: O = ZooKeeper, OU = Backend, emailAddress = admin@example.org
        Validity
            Not Before: Aug  8 19:16:12 2021 GMT
            Not After : Aug  8 19:16:12 2022 GMT
        Subject: CN = zookeeper.backend.example.org
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:c3:99:c5:40:0d:49:09:9d:73:95:5e:a2:04:ba:
                    97:c1:15:8d:54:d4:a2:72:8d:aa:b5:4b:5c:ab:1c:
                    c1:81:fb:57:d6:2a:9f:df:d0:01:15:aa:40:c7:36:
                    59:8c:93:3a:70:28:de:ad:c9:a0:29:1c:05:3d:c3:
                    95:2d:44:79:8b:7a:7a:aa:fb:20:78:2c:73:ba:c9:
                    a8:d9:5c:48:ee:b6:39:a9:f3:5e:09:b5:32:6a:81:
                    a8:c3:82:57:8c:96:8f:b1:80:e8:9b:b7:4c:6a:a4:
                    9a:f7:a1:dc:4b:9d:60:6a:d7:fa:5b:97:84:f1:78:
                    60:f2:e9:ea:30:28:be:9b:fa:d9:b2:87:ae:05:22:
                    39:84:c7:4d:67:b1:23:f3:6c:35:74:3a:03:a1:a6:
                    06:e0:8a:16:58:79:7b:df:12:4a:1e:d1:35:e3:7f:
                    11:03:94:8a:5c:ac:07:4b:d9:b4:e9:0d:b5:f4:e5:
                    36:eb:f7:f3:55:aa:0e:8f:64:61:23:f8:dd:c3:77:
                    06:96:6f:2d:c5:81:56:e1:06:02:cf:fa:82:e3:fc:
                    d4:a6:80:7c:3d:44:77:25:85:45:b3:fd:b9:49:5a:
                    7d:d8:87:75:b1:f5:f0:5a:6e:56:f0:80:b5:30:74:
                    0b:d6:9f:c3:ca:b1:a7:e5:6b:87:2b:81:7d:91:d0:
                    3d:3b
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:zookeeper1.example.org, DNS:zookeeper2.example.org, DNS:zookeeper3.example.org
    Signature Algorithm: sha256WithRSAEncryption
         3d:05:a5:90:a7:a5:ff:d5:c2:61:08:c0:42:15:d6:a1:20:ce:
         18:b5:c7:8b:c2:0c:fb:a6:a5:35:a1:7d:07:96:32:8f:6e:84:
         81:01:82:9a:94:86:63:b9:20:00:90:52:a0:ff:52:c4:9a:e8:
         b2:57:63:cd:5a:a3:26:67:39:c1:f8:54:0c:b4:4d:d6:9c:e5:
         4c:37:1d:99:70:06:52:65:f1:5f:f1:20:6d:59:a2:cf:db:09:
         8d:2a:98:a7:b4:b2:f2:cc:82:80:fd:b7:5a:99:c4:18:02:6e:
         22:16:94:e4:1d:d3:49:e6:ad:5f:c2:c0:fc:5e:18:48:7b:f1:
         0d:e7:33:fd:a2:08:0b:6f:83:2d:64:67:f3:d2:ce:1b:b4:72:
         f0:23:07:70:7b:d2:0a:b3:56:10:b9:65:02:b8:b6:0a:20:50:
         76:96:78:e7:e2:e1:f9:18:45:17:2f:93:7e:79:12:ae:ad:89:
         7d:41:89:5f:33:f1:fe:4c:76:01:10:fb:f4:07:b8:c7:49:a7:
         07:f9:18:06:06:62:f2:29:bf:cf:79:02:0c:9c:10:e8:07:7a:
         a8:d2:25:94:d2:9a:b1:46:d1:62:b4:60:2e:84:08:99:e8:59:
         40:e1:49:71:24:21:06:7c:ea:d2:d4:26:71:b1:1e:33:4f:1d:
         9b:2f:f2:d0

Create PKCS12 keystore.

$ openssl pkcs12 -export -in san_certificate.pem -inkey san_certificate.key -out server.keystore.p12 -name "zookeeper.backend" -password pass:certpassword

To add multiple certificates simply concatenate these including keys.

$ cat zookeeper_servers/zookeeper1.{pem,enckey} zookeeper_servers/zookeeper2.{pem,enckey} zookeeper_servers/zookeeper3.{pem,enckey} | tee multiple_certificates.pem
$ openssl pkcs12 -export -in multiple_certificates.pem -out server.keystore.p12 -name "zookeeper.backend" -password pass:certpassword

Inspect PKCS12 files.

$ openssl pkcs12 -in server.keystore.p12 -info -password pass:certpassword -nokeys
MAC: sha1, Iteration 2048
MAC length: 20, salt length: 8
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2048
Certificate bag
Bag Attributes
    localKeyID: 34 E5 5B 82 D8 07 C6 3A 42 76 29 A8 54 3C 29 65 27 CB 33 D2 
    friendlyName: zookeeper.backend
subject=CN = zookeeper.backend.example.org

issuer=O = ZooKeeper, OU = Backend, emailAddress = admin@example.org

-----BEGIN CERTIFICATE-----
MIIDUzCCAjugAwIBAgIUBb5ElbhNwx+skNTv1NlD7yxJGkIwDQYJKoZIhvcNAQEL
BQAwSDESMBAGA1UECgwJWm9vS2VlcGVyMRAwDgYDVQQLDAdCYWNrZW5kMSAwHgYJ
KoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLm9yZzAeFw0yMTA4MDgxOTE2MTJaFw0y
MjA4MDgxOTE2MTJaMCgxJjAkBgNVBAMMHXpvb2tlZXBlci5iYWNrZW5kLmV4YW1w
bGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw5nFQA1JCZ1z
lV6iBLqXwRWNVNSico2qtUtcqxzBgftX1iqf39ABFapAxzZZjJM6cCjercmgKRwF
PcOVLUR5i3p6qvsgeCxzusmo2VxI7rY5qfNeCbUyaoGow4JXjJaPsYDom7dMaqSa
96HcS51gatf6W5eE8Xhg8unqMCi+m/rZsoeuBSI5hMdNZ7Ej82w1dDoDoaYG4IoW
WHl73xJKHtE1438RA5SKXKwHS9m06Q219OU26/fzVaoOj2RhI/jdw3cGlm8txYFW
4QYCz/qC4/zUpoB8PUR3JYVFs/25SVp92Id1sfXwWm5W8IC1MHQL1p/DyrGn5WuH
K4F9kdA9OwIDAQABo1UwUzBRBgNVHREESjBIghZ6b29rZWVwZXIxLmV4YW1wbGUu
b3JnghZ6b29rZWVwZXIyLmV4YW1wbGUub3JnghZ6b29rZWVwZXIzLmV4YW1wbGUu
b3JnMA0GCSqGSIb3DQEBCwUAA4IBAQA9BaWQp6X/1cJhCMBCFdahIM4YtceLwgz7
pqU1oX0HljKPboSBAYKalIZjuSAAkFKg/1LEmuiyV2PNWqMmZznB+FQMtE3WnOVM
Nx2ZcAZSZfFf8SBtWaLP2wmNKpintLLyzIKA/bdamcQYAm4iFpTkHdNJ5q1fwsD8
XhhIe/EN5zP9oggLb4MtZGfz0s4btHLwIwdwe9IKs1YQuWUCuLYKIFB2lnjn4uH5
GEUXL5N+eRKurYl9QYlfM/H+THYBEPv0B7jHSacH+RgGBmLyKb/PeQIMnBDoB3qo
0iWU0pqxRtFitGAuhAiZ6FlA4UlxJCEGfOrS1CZxsR4zTx2bL/LQ
-----END CERTIFICATE-----
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048

Configure ZooKeeper

Inspect default configuration.

$ cat conf/zoo.cfg 
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/opt/zookeeper/zookeeper/data
clientPort=2181
server.1=zookeeper1.example.org:2888:3888
server.2=zookeeper2.example.org:2888:3888
server.3=zookeeper3.example.org:2888:3888
4lw.commands.whitelist=*

Secure the ZooKeeper quorum communication.

$ cat conf/zoo.cfg 
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/opt/zookeeper/zookeeper/data
clientPort=2181
server.1=zookeeper1.example.org:2888:3888
server.2=zookeeper2.example.org:2888:3888
server.3=zookeeper3.example.org:2888:3888
4lw.commands.whitelist=*

sslQuorum=true
ssl.quorum.trustStore.location=/opt/zookeeper/ca.pem
ssl.quorum.trustStore.password=capassword
ssl.quorum.keyStore.location=/opt/zookeeper/server.keystore.p12
ssl.quorum.keyStore.password=certpassword
ssl.quorum.hostnameVerification=true

Additional notes

Using PKCS12 format for truststore requires additional steps, as PKCS12 needs to contain a special attribute which is not easily added using OpenSSL utilities, at least for now.

Export CA to the JKS using keytool utility.

$ keytool -noprompt -storepass "capassword" -keystore server.truststore.jks -alias CARoot -import -file ca.pem
Certificate was added to keystore

Export it to PKCS12 format using keytool utility.

$ keytool -importkeystore -srckeystore server.truststore.jks -destkeystore cacertificate.p12 -srcstoretype jks -deststoretype pkcs12 -srcstorepass capassword -deststorepass capassword
Importing keystore server.truststore.jks to cacertificate.p12...
Entry for alias caroot successfully imported.
Import command completed:  1 entries successfully imported, 0 entries failed or cancelled

Inspect PKCS12 file, notice additional 2.16.840.1.113894.746875.1.1 OID.

$ openssl pkcs12 -in cacertificate.p12  -info -password pass:capassword
MAC: sha1, Iteration 100000
MAC length: 20, salt length: 20
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 50000
Certificate bag
Bag Attributes
    friendlyName: caroot
    2.16.840.1.113894.746875.1.1: <Unsupported tag 6>
subject=O = ZooKeeper, OU = Backend, emailAddress = admin@example.org

issuer=O = ZooKeeper, OU = Backend, emailAddress = admin@example.org

-----BEGIN CERTIFICATE-----
MIIDcTCCAlmgAwIBAgIUda8Vsl8cbWcIWTgR9B1Uews99uUwDQYJKoZIhvcNAQEL
BQAwSDESMBAGA1UECgwJWm9vS2VlcGVyMRAwDgYDVQQLDAdCYWNrZW5kMSAwHgYJ
KoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLm9yZzAeFw0yMTA4MDgxOTE0MDlaFw0z
MTA4MDYxOTE0MDlaMEgxEjAQBgNVBAoMCVpvb0tlZXBlcjEQMA4GA1UECwwHQmFj
a2VuZDEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBsZS5vcmcwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3CEu6v7tVlrnPAvUROHxw+Vikw/yIcCNu
zjUDEIP/ulytHuFVzo5TwuVrEluTObOtethMc9FoXmeYa7oqDmSvSRF3I510/lpc
qFmXgP+Ac0lnsWC3rlcwQuNxqkiE6DBNjNQYUmN1NyHW6WE1tjHXYKmcVq2ct2D2
fJu77qb0FIyHgdxaaX/1mapXw/spKrvduOgrjv/xXituT6kRo0vl1ww67w3/Sk3o
wejhDS9K2jwyn1XTn29AWxTcAIQwerNVPnCh+G+Sf0FRk2IA5HIZ3KIxmTCscqLZ
UEwRF/wQREiZQTwigW1CHCv8bklSAduZlOQWqVG9QFVUcZTco4MXAgMBAAGjUzBR
MB0GA1UdDgQWBBToWI35o5pnWgk9DnI67h7/UXYeYzAfBgNVHSMEGDAWgBToWI35
o5pnWgk9DnI67h7/UXYeYzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
A4IBAQAeUYNhUWNsR4+sIc6Ep+7dd2goY6UWxK2yMFqUW1JjgmudKS75kXlskrxj
3+mgIV2tgTde/9IGoV1sLjfp+TiOE87HoEX//HzlepwzaURLAYiDA+3TSSa8S0Nz
X2ceP+sksZhBPmb7KniDNclom4BnidUa7lCesHg2RTECxz5WCd0sFUcK4eRFzmf3
wLyHbvVPQx6O18H12V9ttHCkMRaW5JoEpbCsoKZEbpFIrlkKUaB3ndxmZEdaOOHm
4FF6iLnOyuRxJvfh6kYZfq62u1Cib2oaLXPHRH4/ZI8eolxbgC9dfTDKim8sjhpG
ioVIt9vMSfI6/fD9e9F7Im4xx+Sj
-----END CERTIFICATE-----

Remember, you can also use a PEM file as keystore, just remember to encrypt private keys.