Repository: provectus/kafka-ui
Issue: Kafka with SSL doesn't work
Labels: type/documentation
status/duplicate

Hello.
Try connect to three kafka brokers.
 KAFKA_CLUSTERS_0 have not security and all ok
KAFKA_CLUSTERS_1 and KAFKA_CLUSTERS_2 have SSL.
 in docker compose add certs and all configs.
as a result, kafka without ssl is only connected, two othert crash with exception
**Caused by: org.apache.kafka.common.errors.ClusterAuthorizationException: Cluster authorization failed.**
ofset explorer and conductor with this certs and settings connect successfully





**docker-compose**
version: '3'

services:
  kafka-ui:
    image: provectuslabs/kafka-ui:master
    container_name: kafka-ui
    ports:
      - "8080:8080"
    restart: always
    volumes:
      - ./cert:/opt/java/openjdk/lib/security
    environment:
      - KAFKA_CLUSTERS_0_NAME=TB
      - KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=d5tbcr-kfc001lk.corp.dev:9092
      - KAFKA_CLUSTERS_1_NAME=AF
      - KAFKA_CLUSTERS_1_BOOTSTRAPSERVERS=d5aflc-kfc001lk.corp.dev:9092
      - KAFKA_CLUSTERS_1_PROPERTIES_SECURITY_PROTOCOL=SSL
      - KAFKA_CLUSTERS_1_PROPERTIES_SSL_KEYSTORE_LOCATION=/opt/java/openjdk/lib/security/keystore.jks
      - KAFKA_CLUSTERS_1_PROPERTIES_SSL_KEYSTORE_PASSWORD=[pass]
      - KAFKA_CLUSTERS_1_PROPERTIES_SSL_KEYSTORE_TYPE=JKS
      - KAFKA_CLUSTERS_1_PROPERTIES_SSL_TRUSTSTORE_LOCATION=/opt/java/openjdk/lib/security/kafka-trust.pfx
      - KAFKA_CLUSTERS_1_PROPERTIES_SSL_TRUSTSTORE_PASSWORD=[pass]
      - KAFKA_CLUSTERS_1_PROPERTIES_SSL_TRUSTSTORE_TYPE=PKCS12
      - KAFKA_CLUSTERS_2_NAME=FB
      - KAFKA_CLUSTERS_2_BOOTSTRAPSERVERS=d5pbrc-kfc002lk.corp.dev:9092
      - KAFKA_CLUSTERS_2_PROPERTIES_SECURITY_PROTOCOL=SSL
      - KAFKA_CLUSTERS_2_PROPERTIES_SSL_KEYSTORE_LOCATION=/opt/java/openjdk/lib/security/keystore.jks
      - KAFKA_CLUSTERS_2_PROPERTIES_SSL_KEYSTORE_PASSWORD=[pass]
      - KAFKA_CLUSTERS_2_PROPERTIES_SSL_KEYSTORE_TYPE=JKS
      - KAFKA_CLUSTERS_2_PROPERTIES_SSL_TRUSTSTORE_LOCATION=/opt/java/openjdk/lib/security/omni-tech-client.jks
      - KAFKA_CLUSTERS_2_PROPERTIES_SSL_TRUSTSTORE_PASSWORD=[pass]
      - KAFKA_CLUSTERS_2_PROPERTIES_SSL_TRUSTSTORE_TYPE=JKS





**LOGS**
kafka-ui  | 2022-02-05 21:51:44,118 INFO  [background-preinit] o.h.v.i.u.Version: HV000001: Hibernate Validator 6.2.0.Final
kafka-ui  | 2022-02-05 21:51:44,152 INFO  [main] c.p.k.u.KafkaUiApplication: Starting KafkaUiApplication using Java 13.0.9 on 17f613c3648a with PID 1 (/kafka-ui-api.jar started by root in /)
kafka-ui  | 2022-02-05 21:51:44,155 DEBUG [main] c.p.k.u.KafkaUiApplication: Running with Spring Boot v2.5.6, Spring v5.3.12
kafka-ui  | 2022-02-05 21:51:44,156 INFO  [main] c.p.k.u.KafkaUiApplication: No active profile set, falling back to default profiles: default
kafka-ui  | 2022-02-05 21:51:45,981 INFO  [main] o.s.d.r.c.RepositoryConfigurationDelegate: Bootstrapping Spring Data LDAP repositories in DEFAULT mode.
kafka-ui  | 2022-02-05 21:51:46,046 INFO  [main] o.s.d.r.c.RepositoryConfigurationDelegate: Finished Spring Data repository scanning in 54 ms. Found 0 LDAP repository interfaces.
kafka-ui  | 2022-02-05 21:51:47,097 INFO  [main] o.s.b.a.s.r.ReactiveUserDetailsServiceAutoConfiguration:
kafka-ui  |
kafka-ui  | Using generated security password: 01036954-fda0-42eb-9ff6-2f0fd4a4f7e3
kafka-ui  |
kafka-ui  | 2022-02-05 21:51:47,399 INFO  [main] c.p.k.u.s.DeserializationService: Using SchemaRegistryAwareRecordSerDe for cluster 'TB'
kafka-ui  | 2022-02-05 21:51:47,407 INFO  [main] c.p.k.u.s.DeserializationService: Using SchemaRegistryAwareRecordSerDe for cluster 'AF'
kafka-ui  | 2022-02-05 21:51:47,408 INFO  [main] c.p.k.u.s.DeserializationService: Using SchemaRegistryAwareRecordSerDe for cluster 'FB'
kafka-ui  | 2022-02-05 21:51:48,155 INFO  [main] o.s.b.a.e.w.EndpointLinksResolver: Exposing 2 endpoint(s) beneath base path '/actuator'
kafka-ui  | 2022-02-05 21:51:48,360 WARN  [main] c.p.k.u.c.a.DisabledAuthSecurityConfig: Authentication is disabled. Access will be unrestricted.
kafka-ui  | 2022-02-05 21:51:48,602 INFO  [main] o.s.l.c.s.AbstractContextSource: Property 'userDn' not set - anonymous context will be used for read-write operations
kafka-ui  | 2022-02-05 21:51:48,985 INFO  [main] o.s.b.w.e.n.NettyWebServer: Netty started on port 8080
kafka-ui  | 2022-02-05 21:51:49,012 INFO  [main] c.p.k.u.KafkaUiApplication: Started KafkaUiApplication in 5.787 seconds (JVM running for 6.384)
kafka-ui  | 2022-02-05 21:51:49,054 DEBUG [parallel-1] c.p.k.u.s.ClustersMetricsScheduler: Start getting metrics for kafkaCluster: TB
kafka-ui  | 2022-02-05 21:51:49,055 DEBUG [parallel-2] c.p.k.u.s.ClustersMetricsScheduler: Start getting metrics for kafkaCluster: AF
kafka-ui  | 2022-02-05 21:51:49,057 DEBUG [parallel-3] c.p.k.u.s.ClustersMetricsScheduler: Start getting metrics for kafkaCluster: FB
kafka-ui  | 2022-02-05 21:51:49,084 INFO  [parallel-2] o.a.k.c.a.AdminClientConfig: AdminClientConfig values:
kafka-ui  |     bootstrap.servers = [d5aflc-kfc001lk.corp.dev:9092]
kafka-ui  |     client.dns.lookup = use_all_dns_ips
kafka-ui  |     client.id =
kafka-ui  |     connections.max.idle.ms = 300000
kafka-ui  |     default.api.timeout.ms = 60000
kafka-ui  |     metadata.max.age.ms = 300000
kafka-ui  |     metric.reporters = []
kafka-ui  |     metrics.num.samples = 2
kafka-ui  |     metrics.recording.level = INFO
kafka-ui  |     metrics.sample.window.ms = 30000
kafka-ui  |     receive.buffer.bytes = 65536
kafka-ui  |     reconnect.backoff.max.ms = 1000
kafka-ui  |     reconnect.backoff.ms = 50
kafka-ui  |     request.timeout.ms = 30000
kafka-ui  |     retries = 2147483647
kafka-ui  |     retry.backoff.ms = 100
kafka-ui  |     sasl.client.callback.handler.class = null
kafka-ui  |     sasl.jaas.config = null
kafka-ui  |     sasl.kerberos.kinit.cmd = /usr/bin/kinit
kafka-ui  |     sasl.kerberos.min.time.before.relogin = 60000
kafka-ui  |     sasl.kerberos.service.name = null
kafka-ui  |     sasl.kerberos.ticket.renew.jitter = 0.05
kafka-ui  |     sasl.kerberos.ticket.renew.window.factor = 0.8
kafka-ui  |     sasl.login.callback.handler.class = null
kafka-ui  |     sasl.login.class = null
kafka-ui  |     sasl.login.refresh.buffer.seconds = 300
kafka-ui  |     sasl.login.refresh.min.period.seconds = 60
kafka-ui  |     sasl.login.refresh.window.factor = 0.8
kafka-ui  |     sasl.login.refresh.window.jitter = 0.05
kafka-ui  |     sasl.mechanism = GSSAPI
kafka-ui  |     security.protocol = SSL
kafka-ui  |     security.providers = null
kafka-ui  |     send.buffer.bytes = 131072
kafka-ui  |     socket.connection.setup.timeout.max.ms = 30000
kafka-ui  |     socket.connection.setup.timeout.ms = 10000
kafka-ui  |     ssl.cipher.suites = null
kafka-ui  |     ssl.enabled.protocols = [TLSv1.2, TLSv1.3]
kafka-ui  |     ssl.endpoint.identification.algorithm = https
kafka-ui  |     ssl.engine.factory.class = null
kafka-ui  |     ssl.key.password = null
kafka-ui  |     ssl.keymanager.algorithm = SunX509
kafka-ui  |     ssl.keystore.certificate.chain = null
kafka-ui  |     ssl.keystore.key = null
kafka-ui  |     ssl.keystore.location = /opt/java/openjdk/lib/security/keystore.jks
kafka-ui  |     ssl.keystore.password = [hidden]
kafka-ui  |     ssl.keystore.type = JKS
kafka-ui  |     ssl.protocol = TLSv1.3
kafka-ui  |     ssl.provider = null
kafka-ui  |     ssl.secure.random.implementation = null
kafka-ui  |     ssl.trustmanager.algorithm = PKIX
kafka-ui  |     ssl.truststore.certificates = null
kafka-ui  |     ssl.truststore.location = /opt/java/openjdk/lib/security/kafka-trust.pfx
kafka-ui  |     ssl.truststore.password = [hidden]
kafka-ui  |     ssl.truststore.type = PKCS12
kafka-ui  |
kafka-ui  | 2022-02-05 21:51:49,087 INFO  [parallel-3] o.a.k.c.a.AdminClientConfig: AdminClientConfig values:
kafka-ui  |     bootstrap.servers = [d5pbrc-kfc002lk.corp.dev:9092]
kafka-ui  |     client.dns.lookup = use_all_dns_ips
kafka-ui  |     client.id =
kafka-ui  |     connections.max.idle.ms = 300000
kafka-ui  |     default.api.timeout.ms = 60000
kafka-ui  |     metadata.max.age.ms = 300000
kafka-ui  |     metric.reporters = []
kafka-ui  |     metrics.num.samples = 2
kafka-ui  |     metrics.recording.level = INFO
kafka-ui  |     metrics.sample.window.ms = 30000
kafka-ui  |     receive.buffer.bytes = 65536
kafka-ui  |     reconnect.backoff.max.ms = 1000
kafka-ui  |     reconnect.backoff.ms = 50
kafka-ui  |     request.timeout.ms = 30000
kafka-ui  |     retries = 2147483647
kafka-ui  |     retry.backoff.ms = 100
kafka-ui  |     sasl.client.callback.handler.class = null
kafka-ui  |     sasl.jaas.config = null
kafka-ui  |     sasl.kerberos.kinit.cmd = /usr/bin/kinit
kafka-ui  |     sasl.kerberos.min.time.before.relogin = 60000
kafka-ui  |     sasl.kerberos.service.name = null
kafka-ui  |     sasl.kerberos.ticket.renew.jitter = 0.05
kafka-ui  |     sasl.kerberos.ticket.renew.window.factor = 0.8
kafka-ui  |     sasl.login.callback.handler.class = null
kafka-ui  |     sasl.login.class = null
kafka-ui  |     sasl.login.refresh.buffer.seconds = 300
kafka-ui  |     sasl.login.refresh.min.period.seconds = 60
kafka-ui  |     sasl.login.refresh.window.factor = 0.8
kafka-ui  |     sasl.login.refresh.window.jitter = 0.05
kafka-ui  |     sasl.mechanism = GSSAPI
kafka-ui  |     security.protocol = SSL
kafka-ui  |     security.providers = null
kafka-ui  |     send.buffer.bytes = 131072
kafka-ui  |     socket.connection.setup.timeout.max.ms = 30000
kafka-ui  |     socket.connection.setup.timeout.ms = 10000
kafka-ui  |     ssl.cipher.suites = null
kafka-ui  |     ssl.enabled.protocols = [TLSv1.2, TLSv1.3]
kafka-ui  |     ssl.endpoint.identification.algorithm = https
kafka-ui  |     ssl.engine.factory.class = null
kafka-ui  |     ssl.key.password = null
kafka-ui  |     ssl.keymanager.algorithm = SunX509
kafka-ui  |     ssl.keystore.certificate.chain = null
kafka-ui  |     ssl.keystore.key = null
kafka-ui  |     ssl.keystore.location = /opt/java/openjdk/lib/security/keystore.jks
kafka-ui  |     ssl.keystore.password = [hidden]
kafka-ui  |     ssl.keystore.type = JKS
kafka-ui  |     ssl.protocol = TLSv1.3
kafka-ui  |     ssl.provider = null
kafka-ui  |     ssl.secure.random.implementation = null
kafka-ui  |     ssl.trustmanager.algorithm = PKIX
kafka-ui  |     ssl.truststore.certificates = null
kafka-ui  |     ssl.truststore.location = /opt/java/openjdk/lib/security/omni-tech-client.jks
kafka-ui  |     ssl.truststore.password = [hidden]
kafka-ui  |     ssl.truststore.type = JKS
kafka-ui  |
kafka-ui  | 2022-02-05 21:51:49,088 INFO  [parallel-1] o.a.k.c.a.AdminClientConfig: AdminClientConfig values:
kafka-ui  |     bootstrap.servers = [d5tbcr-kfc001lk.corp.dev:9092]
kafka-ui  |     client.dns.lookup = use_all_dns_ips
kafka-ui  |     client.id =
kafka-ui  |     connections.max.idle.ms = 300000
kafka-ui  |     default.api.timeout.ms = 60000
kafka-ui  |     metadata.max.age.ms = 300000
kafka-ui  |     metric.reporters = []
kafka-ui  |     metrics.num.samples = 2
kafka-ui  |     metrics.recording.level = INFO
kafka-ui  |     metrics.sample.window.ms = 30000
kafka-ui  |     receive.buffer.bytes = 65536
kafka-ui  |     reconnect.backoff.max.ms = 1000
kafka-ui  |     reconnect.backoff.ms = 50
kafka-ui  |     request.timeout.ms = 30000
kafka-ui  |     retries = 2147483647
kafka-ui  |     retry.backoff.ms = 100
kafka-ui  |     sasl.client.callback.handler.class = null
kafka-ui  |     sasl.jaas.config = null
kafka-ui  |     sasl.kerberos.kinit.cmd = /usr/bin/kinit
kafka-ui  |     sasl.kerberos.min.time.before.relogin = 60000
kafka-ui  |     sasl.kerberos.service.name = null
kafka-ui  |     sasl.kerberos.ticket.renew.jitter = 0.05
kafka-ui  |     sasl.kerberos.ticket.renew.window.factor = 0.8
kafka-ui  |     sasl.login.callback.handler.class = null
kafka-ui  |     sasl.login.class = null
kafka-ui  |     sasl.login.refresh.buffer.seconds = 300
kafka-ui  |     sasl.login.refresh.min.period.seconds = 60
kafka-ui  |     sasl.login.refresh.window.factor = 0.8
kafka-ui  |     sasl.login.refresh.window.jitter = 0.05
kafka-ui  |     sasl.mechanism = GSSAPI
kafka-ui  |     security.protocol = PLAINTEXT
kafka-ui  |     security.providers = null
kafka-ui  |     send.buffer.bytes = 131072
kafka-ui  |     socket.connection.setup.timeout.max.ms = 30000
kafka-ui  |     socket.connection.setup.timeout.ms = 10000
kafka-ui  |     ssl.cipher.suites = null
kafka-ui  |     ssl.enabled.protocols = [TLSv1.2, TLSv1.3]
kafka-ui  |     ssl.endpoint.identification.algorithm = https
kafka-ui  |     ssl.engine.factory.class = null
kafka-ui  |     ssl.key.password = null
kafka-ui  |     ssl.keymanager.algorithm = SunX509
kafka-ui  |     ssl.keystore.certificate.chain = null
kafka-ui  |     ssl.keystore.key = null
kafka-ui  |     ssl.keystore.location = null
kafka-ui  |     ssl.keystore.password = null
kafka-ui  |     ssl.keystore.type = JKS
kafka-ui  |     ssl.protocol = TLSv1.3
kafka-ui  |     ssl.provider = null
kafka-ui  |     ssl.secure.random.implementation = null
kafka-ui  |     ssl.trustmanager.algorithm = PKIX
kafka-ui  |     ssl.truststore.certificates = null
kafka-ui  |     ssl.truststore.location = null
kafka-ui  |     ssl.truststore.password = null
kafka-ui  |     ssl.truststore.type = JKS
kafka-ui  |
kafka-ui  | 2022-02-05 21:51:49,295 INFO  [parallel-1] o.a.k.c.u.AppInfoParser: Kafka version: 2.8.0
kafka-ui  | 2022-02-05 21:51:49,295 INFO  [parallel-1] o.a.k.c.u.AppInfoParser: Kafka commitId: ebb1d6e21cc92130
kafka-ui  | 2022-02-05 21:51:49,295 INFO  [parallel-1] o.a.k.c.u.AppInfoParser: Kafka startTimeMs: 1644097909291
kafka-ui  | 2022-02-05 21:51:50,180 INFO  [parallel-3] o.a.k.c.u.AppInfoParser: Kafka version: 2.8.0
kafka-ui  | 2022-02-05 21:51:50,181 INFO  [parallel-3] o.a.k.c.u.AppInfoParser: Kafka commitId: ebb1d6e21cc92130
kafka-ui  | 2022-02-05 21:51:50,182 INFO  [parallel-3] o.a.k.c.u.AppInfoParser: Kafka startTimeMs: 1644097910180
kafka-ui  | 2022-02-05 21:51:50,248 INFO  [parallel-2] o.a.k.c.u.AppInfoParser: Kafka version: 2.8.0
kafka-ui  | 2022-02-05 21:51:50,248 INFO  [parallel-2] o.a.k.c.u.AppInfoParser: Kafka commitId: ebb1d6e21cc92130
kafka-ui  | 2022-02-05 21:51:50,248 INFO  [parallel-2] o.a.k.c.u.AppInfoParser: Kafka startTimeMs: 1644097910248
kafka-ui  | 2022-02-05 21:51:51,017 DEBUG [kafka-admin-client-thread | adminclient-3] c.p.k.u.s.ClustersMetricsScheduler: Metrics updated for cluster: TB
kafka-ui  | 2022-02-05 21:51:51,622 ERROR [kafka-admin-client-thread | adminclient-2] c.p.k.u.s.MetricsService: Failed to collect cluster FB info
kafka-ui  | java.lang.IllegalStateException: Error while creating AdminClient for Cluster FB
kafka-ui  |     at com.provectus.kafka.ui.service.AdminClientServiceImpl.lambda$createAdminClient$3(AdminClientServiceImpl.java:44)
kafka-ui  |     at reactor.core.publisher.Mono.lambda$onErrorMap$31(Mono.java:3732)
kafka-ui  |     at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94)
kafka-ui  |     at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onError(FluxMapFuseable.java:140)
kafka-ui  |     at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondError(MonoFlatMap.java:192)
kafka-ui  |     at reactor.core.publisher.MonoFlatMap$FlatMapInner.onError(MonoFlatMap.java:259)
kafka-ui  |     at reactor.core.publisher.MonoCreate$DefaultMonoSink.error(MonoCreate.java:194)
kafka-ui  |     at com.provectus.kafka.ui.service.ReactiveAdminClient.lambda$toMono$1(ReactiveAdminClient.java:97)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$WhenCompleteBiConsumer.accept(KafkaFutureImpl.java:175)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$WhenCompleteBiConsumer.accept(KafkaFutureImpl.java:162)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl.completeExceptionally(KafkaFutureImpl.java:238)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$Applicant.accept(KafkaFutureImpl.java:61)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$Applicant.accept(KafkaFutureImpl.java:49)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl.completeExceptionally(KafkaFutureImpl.java:238)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$Applicant.accept(KafkaFutureImpl.java:61)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$Applicant.accept(KafkaFutureImpl.java:49)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl.completeExceptionally(KafkaFutureImpl.java:238)
kafka-ui  |     at org.apache.kafka.common.KafkaFuture$AllOfAdapter.accept(KafkaFuture.java:73)
kafka-ui  |     at org.apache.kafka.common.KafkaFuture$AllOfAdapter.accept(KafkaFuture.java:57)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl.completeExceptionally(KafkaFutureImpl.java:238)
kafka-ui  |     at org.apache.kafka.clients.admin.KafkaAdminClient$9.handleResponse(KafkaAdminClient.java:2126)
kafka-ui  |     at org.apache.kafka.clients.admin.KafkaAdminClient$AdminClientRunnable.handleResponses(KafkaAdminClient.java:1189)
kafka-ui  |     at org.apache.kafka.clients.admin.KafkaAdminClient$AdminClientRunnable.processRequests(KafkaAdminClient.java:1341)
kafka-ui  |     at org.apache.kafka.clients.admin.KafkaAdminClient$AdminClientRunnable.run(KafkaAdminClient.java:1264)
kafka-ui  |     at java.base/java.lang.Thread.run(Thread.java:830)
kafka-ui  | Caused by: org.apache.kafka.common.errors.ClusterAuthorizationException: Cluster authorization failed.
kafka-ui  | 2022-02-05 21:51:51,623 DEBUG [kafka-admin-client-thread | adminclient-2] c.p.k.u.s.ClustersMetricsScheduler: Metrics updated for cluster: FB
kafka-ui  | 2022-02-05 21:51:51,881 ERROR [kafka-admin-client-thread | adminclient-1] c.p.k.u.s.MetricsService: Failed to collect cluster AF info
kafka-ui  | java.lang.IllegalStateException: Error while creating AdminClient for Cluster AF
kafka-ui  |     at com.provectus.kafka.ui.service.AdminClientServiceImpl.lambda$createAdminClient$3(AdminClientServiceImpl.java:44)
kafka-ui  |     at reactor.core.publisher.Mono.lambda$onErrorMap$31(Mono.java:3732)
kafka-ui  |     at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94)
kafka-ui  |     at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onError(FluxMapFuseable.java:140)
kafka-ui  |     at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondError(MonoFlatMap.java:192)
kafka-ui  |     at reactor.core.publisher.MonoFlatMap$FlatMapInner.onError(MonoFlatMap.java:259)
kafka-ui  |     at reactor.core.publisher.MonoCreate$DefaultMonoSink.error(MonoCreate.java:194)
kafka-ui  |     at com.provectus.kafka.ui.service.ReactiveAdminClient.lambda$toMono$1(ReactiveAdminClient.java:97)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$WhenCompleteBiConsumer.accept(KafkaFutureImpl.java:175)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$WhenCompleteBiConsumer.accept(KafkaFutureImpl.java:162)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl.completeExceptionally(KafkaFutureImpl.java:238)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$Applicant.accept(KafkaFutureImpl.java:61)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$Applicant.accept(KafkaFutureImpl.java:49)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl.completeExceptionally(KafkaFutureImpl.java:238)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$Applicant.accept(KafkaFutureImpl.java:61)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$Applicant.accept(KafkaFutureImpl.java:49)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl.completeExceptionally(KafkaFutureImpl.java:238)
kafka-ui  |     at org.apache.kafka.common.KafkaFuture$AllOfAdapter.accept(KafkaFuture.java:73)
kafka-ui  |     at org.apache.kafka.common.KafkaFuture$AllOfAdapter.accept(KafkaFuture.java:57)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl.completeExceptionally(KafkaFutureImpl.java:238)
kafka-ui  |     at org.apache.kafka.clients.admin.KafkaAdminClient$9.handleResponse(KafkaAdminClient.java:2126)
kafka-ui  |     at org.apache.kafka.clients.admin.KafkaAdminClient$AdminClientRunnable.handleResponses(KafkaAdminClient.java:1189)
kafka-ui  |     at org.apache.kafka.clients.admin.KafkaAdminClient$AdminClientRunnable.processRequests(KafkaAdminClient.java:1341)
kafka-ui  |     at org.apache.kafka.clients.admin.KafkaAdminClient$AdminClientRunnable.run(KafkaAdminClient.java:1264)
kafka-ui  |     at java.base/java.lang.Thread.run(Thread.java:830)
kafka-ui  | Caused by: org.apache.kafka.common.errors.ClusterAuthorizationException: Cluster authorization failed.
kafka-ui  | 2022-02-05 21:51:51,881 DEBUG [kafka-admin-client-thread | adminclient-1] c.p.k.u.s.ClustersMetricsScheduler: Metrics updated for cluster: AF


Issue comments:
Hi, thanks for reaching out.

An example comes within #1568. 

Let me know if you more questions!
Hi, thank you for the answer.

An example is located [here](https://github.com/provectus/kafka-ui/blob/master/documentation/compose/kafka-ssl.yml).
Hi, no changes on my side.
I tried to set pass like file containing the password, but only get error:
```
kafka-ui  | Caused by: java.security.UnrecoverableKeyException: Password verification failed
kafka-ui  |     at java.base/sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:790)
```
After that return pass like string, and keep same error like first message

**docker-compose**
version: '3'

services:
  kafka-ui:
    image: provectuslabs/kafka-ui:master
    container_name: kafka-ui
    ports:
      - "8080:8080"
    restart: always
    volumes:
      - ./cert:/opt/java/openjdk/lib/security
    environment:
      KAFKA_CLUSTERS_0_NAME: TBCR_DSO
      KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: d5tbcr-kfc002lk.corp.dev:9092,d5tbcr-kfc003lk.corp.dev:9092,d5tbcr-kfc004lk.corp.dev:9092
      KAFKA_CLUSTERS_0_PROPERTIES_SECURITY_PROTOCOL: SSL
      KAFKA_CLUSTERS_0_PROPERTIES_SSL_KEYSTORE_LOCATION: /opt/java/openjdk/lib/security/tbcr-keystore.jks
      KAFKA_CLUSTERS_0_PROPERTIES_SSL_KEYSTORE_PASSWORD: pass_as_string
      KAFKA_CLUSTERS_0_PROPERTIES_SSL_TRUSTSTORE_LOCATION: /opt/java/openjdk/lib/security/tbcr-omni-tech-client.jks
      KAFKA_CLUSTERS_0_PROPERTIES_SSL_TRUSTSTORE_PASSWORD: pass_as_string


**logs**
kafka-ui  | 2022-02-09 10:08:12,188 INFO  [parallel-1] o.a.k.c.a.AdminClientConfig: AdminClientConfig values:
kafka-ui  |     bootstrap.servers = [d5tbcr-kfc002lk.corp.dev:9092, d5tbcr-kfc003lk.corp.dev:9092, d5tbcr-kfc004lk.corp.dev:9092]
kafka-ui  |     client.dns.lookup = use_all_dns_ips
kafka-ui  |     client.id =
kafka-ui  |     connections.max.idle.ms = 300000
kafka-ui  |     default.api.timeout.ms = 60000
kafka-ui  |     metadata.max.age.ms = 300000
kafka-ui  |     metric.reporters = []
kafka-ui  |     metrics.num.samples = 2
kafka-ui  |     metrics.recording.level = INFO
kafka-ui  |     metrics.sample.window.ms = 30000
kafka-ui  |     receive.buffer.bytes = 65536
kafka-ui  |     reconnect.backoff.max.ms = 1000
kafka-ui  |     reconnect.backoff.ms = 50
kafka-ui  |     request.timeout.ms = 30000
kafka-ui  |     retries = 2147483647
kafka-ui  |     retry.backoff.ms = 100
kafka-ui  |     sasl.client.callback.handler.class = null
kafka-ui  |     sasl.jaas.config = null
kafka-ui  |     sasl.kerberos.kinit.cmd = /usr/bin/kinit
kafka-ui  |     sasl.kerberos.min.time.before.relogin = 60000
kafka-ui  |     sasl.kerberos.service.name = null
kafka-ui  |     sasl.kerberos.ticket.renew.jitter = 0.05
kafka-ui  |     sasl.kerberos.ticket.renew.window.factor = 0.8
kafka-ui  |     sasl.login.callback.handler.class = null
kafka-ui  |     sasl.login.class = null
kafka-ui  |     sasl.login.refresh.buffer.seconds = 300
kafka-ui  |     sasl.login.refresh.min.period.seconds = 60
kafka-ui  |     sasl.login.refresh.window.factor = 0.8
kafka-ui  |     sasl.login.refresh.window.jitter = 0.05
kafka-ui  |     sasl.mechanism = GSSAPI
kafka-ui  |     security.protocol = SSL
kafka-ui  |     security.providers = null
kafka-ui  |     send.buffer.bytes = 131072
kafka-ui  |     socket.connection.setup.timeout.max.ms = 30000
kafka-ui  |     socket.connection.setup.timeout.ms = 10000
kafka-ui  |     ssl.cipher.suites = null
kafka-ui  |     ssl.enabled.protocols = [TLSv1.2, TLSv1.3]
kafka-ui  |     ssl.endpoint.identification.algorithm = https
kafka-ui  |     ssl.engine.factory.class = null
kafka-ui  |     ssl.key.password = null
kafka-ui  |     ssl.keymanager.algorithm = SunX509
kafka-ui  |     ssl.keystore.certificate.chain = null
kafka-ui  |     ssl.keystore.key = null
kafka-ui  |     ssl.keystore.location = /opt/java/openjdk/lib/security/tbcr-keystore.jks
kafka-ui  |     ssl.keystore.password = [hidden]
kafka-ui  |     ssl.keystore.type = JKS
kafka-ui  |     ssl.protocol = TLSv1.3
kafka-ui  |     ssl.provider = null
kafka-ui  |     ssl.secure.random.implementation = null
kafka-ui  |     ssl.trustmanager.algorithm = PKIX
kafka-ui  |     ssl.truststore.location = /opt/java/openjdk/lib/security/tbcr-omni-tech-client.jks
kafka-ui  |     ssl.truststore.password = [hidden]
kafka-ui  |     ssl.truststore.type = JKS
kafka-ui  | 2022-02-09 10:08:12,290 INFO  [parallel-1] o.a.k.c.u.AppInfoParser: Kafka version: 2.8.0
kafka-ui  | 2022-02-09 10:08:12,290 INFO  [parallel-1] o.a.k.c.u.AppInfoParser: Kafka commitId: ebb1d6e21cc92130
kafka-ui  | 2022-02-09 10:08:12,290 INFO  [parallel-1] o.a.k.c.u.AppInfoParser: Kafka startTimeMs: 1644401292290
kafka-ui  | 2022-02-09 10:08:14,397 ERROR [kafka-admin-client-thread | adminclient-32] c.p.k.u.s.MetricsService: Failed
to collect cluster TBCR_DSO info
kafka-ui  | java.lang.IllegalStateException: Error while creating AdminClient for Cluster TBCR_DSO
kafka-ui  |     at com.provectus.kafka.ui.service.AdminClientServiceImpl.lambda$createAdminClient$3(AdminClientServiceIm
pl.java:44)
kafka-ui  |     at reactor.core.publisher.Mono.lambda$onErrorMap$31(Mono.java:3732)
kafka-ui  |     at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94)
kafka-ui  |     at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onError(FluxMapFuseable.java:140)
kafka-ui  |     at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondError(MonoFlatMap.java:192)
kafka-ui  |     at reactor.core.publisher.MonoFlatMap$FlatMapInner.onError(MonoFlatMap.java:259)
kafka-ui  |     at reactor.core.publisher.MonoCreate$DefaultMonoSink.error(MonoCreate.java:194)
kafka-ui  |     at com.provectus.kafka.ui.service.ReactiveAdminClient.lambda$toMono$1(ReactiveAdminClient.java:97)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$WhenCompleteBiConsumer.accept(KafkaFutureImpl.java:
175)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$WhenCompleteBiConsumer.accept(KafkaFutureImpl.java:
162)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl.completeExceptionally(KafkaFutureImpl.java:238)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$Applicant.accept(KafkaFutureImpl.java:61)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$Applicant.accept(KafkaFutureImpl.java:49)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl.completeExceptionally(KafkaFutureImpl.java:238)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$Applicant.accept(KafkaFutureImpl.java:61)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl$Applicant.accept(KafkaFutureImpl.java:49)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl.completeExceptionally(KafkaFutureImpl.java:238)
kafka-ui  |     at org.apache.kafka.common.KafkaFuture$AllOfAdapter.accept(KafkaFuture.java:73)
kafka-ui  |     at org.apache.kafka.common.KafkaFuture$AllOfAdapter.accept(KafkaFuture.java:57)
kafka-ui  |     at org.apache.kafka.common.internals.KafkaFutureImpl.completeExceptionally(KafkaFutureImpl.java:238)
kafka-ui  |     at org.apache.kafka.clients.admin.KafkaAdminClient$9.handleResponse(KafkaAdminClient.java:2126)
kafka-ui  |     at org.apache.kafka.clients.admin.KafkaAdminClient$AdminClientRunnable.handleResponses(KafkaAdminClient.
java:1189)
kafka-ui  |     at org.apache.kafka.clients.admin.KafkaAdminClient$AdminClientRunnable.processRequests(KafkaAdminClient.
java:1341)
kafka-ui  |     at org.apache.kafka.clients.admin.KafkaAdminClient$AdminClientRunnable.run(KafkaAdminClient.java:1264)
kafka-ui  |     at java.base/java.lang.Thread.run(Thread.java:830)
kafka-ui  | Caused by: org.apache.kafka.common.errors.ClusterAuthorizationException: Cluster authorization failed.
kafka-ui  | 2022-02-09 10:08:14,397 DEBUG [kafka-admin-client-thread | adminclient-32] c.p.k.u.s.ClustersMetricsSchedule
r: Metrics updated for cluster: TBCR_DSO

@manifiko Mind hoping on our discord? I'll explain how to set it up with your configuration.
https://discord.gg/4DWzD7pGE5
Hi @manifiko, Did you find any solution for this issue? I am too facing same problem and my SSL configuration works fine with Conduktor and few other tool which uses AdminClient but doesn't seems to be working with Kafka-ui
@SukhmeetSethi hey, feel free to open up a new discussion or we can discuss your problem in our discord.
For us the problem is we noticed from the startup message that the "ssl.key.password" parameter is not loaded. 

Using the 'docker-compose-ssl.yaml' one now to test. What is the environment variable to pass that ?
> For us the problem is we noticed from the startup message that the "ssl.key.password" parameter is not loaded.
> 
> Using the 'docker-compose-ssl.yaml' one now to test. What is the environment variable to pass that ?

If someone is looking for it, this is the environment variable to set _"ssl.key.password"_ :
`KAFKA_CLUSTERS_0_PROPERTIES_SSL_KEY_PASSWORD`

Pull request review comments:
I think we dont need this constants here, pls move/embed to KafkaSSLManager
minor, but I think naming is vague and too general here
Why do we need this special config? Could we just push these params?
Discussed, found the problem, turns out it does work with properties.
location of this this file is not obvious ( /etc/kafka/secrets/kafka.keystore.jks )

Let's make it more explicit. 
let's name it kafka.truststore.jks inside docker too)
done
done, what I could

Patch that resolved the issue:
diff --git a/README.md b/README.md
index 10e61d65c0f..55deb5f4a0f 100644
--- a/README.md
+++ b/README.md
@@ -65,6 +65,18 @@ To run UI for Apache Kafka, you can use a pre-built Docker image or build it loc
 
 We have plenty of [docker-compose files](documentation/compose/DOCKER_COMPOSE.md) as examples. They're built for various configuration stacks.
 
+# Guides
+
+- [SSO configuration](documentation/guides/SSO.md)
+- [AWS IAM configuration](documentation/guides/AWS_IAM.md)
+- [Docker-compose files](documentation/guides/yaml-description.md)
+- [Connection to a secure broker]()
+
+## Connecting to a Secure Broker
+
+The app supports TLS (SSL) and SASL connections for [encryption and authentication](http://kafka.apache.org/090/documentation.html#security). <br/>
+An example is located [here](documentation/compose/kafka-ssl.yml).
+
 ### Configuration File
 Example of how to configure clusters in the [application-local.yml](https://github.com/provectus/kafka-ui/blob/master/kafka-ui-api/src/main/resources/application-local.yml) configuration file:
 
@@ -97,7 +109,7 @@ kafka:
 
 Configure as many clusters as you need by adding their configs below separated with `-`.
 
-## Running From Docker Image
+## Running a Docker Image
 The official Docker image for UI for Apache Kafka is hosted here: [hub.docker.com/r/provectuslabs/kafka-ui](https://hub.docker.com/r/provectuslabs/kafka-ui).
 
 Launch Docker container in the background:
@@ -131,18 +143,6 @@ Check [building.md](documentation/project/contributing/building.md)
 
 Check [running.md](documentation/project/contributing/running.md)
 
-# Guides
-
-- [SSO configuration](documentation/guides/SSO.md)
-- [AWS IAM configuration](documentation/guides/AWS_IAM.md)
-- [Docker-compose files](documentation/guides/yaml-description.md)
-
-## Connecting to a Secure Broker
-
-UI for Apache Kafka supports TLS (SSL) and SASL connections for [encryption and authentication](http://kafka.apache.org/090/documentation.html#security). This can be configured by providing a combination of the following files (placed into the Kafka root directory):
-
-To be continued
-
 ## Liveliness and readiness probes
 Liveliness and readiness endpoint is at `/actuator/health`.
 Info endpoint (build info) is located at `/actuator/info`.
diff --git a/documentation/compose/DOCKER_COMPOSE.md b/documentation/compose/DOCKER_COMPOSE.md
index c7379efe9e1..02a3a6e157e 100644
--- a/documentation/compose/DOCKER_COMPOSE.md
+++ b/documentation/compose/DOCKER_COMPOSE.md
@@ -1,12 +1,13 @@
 # Descriptions of docker-compose configurations (*.yaml)
 
-1. [kafka-ui.yaml](https://github.com/provectus/kafka-ui/blob/master/docker/kafka-ui.yaml) - Default configuration with 2 kafka clusters with two nodes of Schema Registry, one kafka-connect and a few dummy topics.
-2. [kafka-clusters-only.yaml](https://github.com/provectus/kafka-ui/blob/master/docker/kafka-clusters-only.yaml) - A configuration for development purposes, everything besides `kafka-ui` itself (to be run locally).
-3. [kafka-cluster-sr-auth.yaml](https://github.com/provectus/kafka-ui/blob/master/docker/kafka-cluster-sr-auth.yaml) - Schema registry with authentication.
-4. [kafka-ui-auth-context.yaml](https://github.com/provectus/kafka-ui/blob/master/docker/kafka-ui-auth-context.yaml) - Basic (username/password) authentication with custom path (URL) (issue 861).
-5. [kafka-ui-connectors.yaml](https://github.com/provectus/kafka-ui/blob/master/docker/kafka-ui-connectors.yaml) - Configuration with different connectors (github-source, s3, sink-activities, source-activities) and Ksql functionality.
-6. [kafka-ui-jmx-secured.yml](https://github.com/provectus/kafka-ui/blob/master/docker/kafka-ui-jmx-secured.yml) - Kafka’s JMX with SSL and authentication.
-7. [kafka-ui-reverse-proxy.yaml](https://github.com/provectus/kafka-ui/blob/master/docker/kafka-ui-reverse-proxy.yaml) - An example for using the app behind a proxy (like nginx).
-8. [kafka-ui-sasl.yaml](https://github.com/provectus/kafka-ui/blob/master/docker/kafka-ui-sasl.yaml) - SASL auth for Kafka.
-9. [kafka-ui-traefik-proxy.yaml](https://github.com/provectus/kafka-ui/blob/master/docker/kafka-ui-traefik-proxy.yaml) - Traefik specific proxy configuration.
-10. [kafka-ui-zookeeper-ssl.yml](https://github.com/provectus/kafka-ui/blob/master/docker/kafka-ui-zookeeper-ssl.yml) - SSL enabled zookeeper.
+1. [kafka-ui.yaml](./kafka-ui.yaml) - Default configuration with 2 kafka clusters with two nodes of Schema Registry, one kafka-connect and a few dummy topics.
+2. [kafka-clusters-only.yaml](./kafka-clusters-only.yaml) - A configuration for development purposes, everything besides `kafka-ui` itself (to be run locally).
+3. [kafka-ui-ssl.yml](./kafka-ssl.yml) - Connect to Kafka via TLS/SSL
+4. [kafka-cluster-sr-auth.yaml](./kafka-cluster-sr-auth.yaml) - Schema registry with authentication.
+5. [kafka-ui-auth-context.yaml](./kafka-ui-auth-context.yaml) - Basic (username/password) authentication with custom path (URL) (issue 861).
+6. [kafka-ui-connectors.yaml](./kafka-ui-connectors.yaml) - Configuration with different connectors (github-source, s3, sink-activities, source-activities) and Ksql functionality.
+7. [kafka-ui-jmx-secured.yml](./kafka-ui-jmx-secured.yml) - Kafka’s JMX with SSL and authentication.
+8. [kafka-ui-reverse-proxy.yaml](./kafka-ui-reverse-proxy.yaml) - An example for using the app behind a proxy (like nginx).
+9. [kafka-ui-sasl.yaml](./kafka-ui-sasl.yaml) - SASL auth for Kafka.
+10. [kafka-ui-traefik-proxy.yaml](./kafka-ui-traefik-proxy.yaml) - Traefik specific proxy configuration.
+11. [kafka-ui-zookeeper-ssl.yml](./kafka-ui-zookeeper-ssl.yml) - SSL enabled zookeeper.
diff --git a/documentation/compose/kafka-ssl.yml b/documentation/compose/kafka-ssl.yml
new file mode 100644
index 00000000000..b298cdc9d65
--- /dev/null
+++ b/documentation/compose/kafka-ssl.yml
@@ -0,0 +1,58 @@
+---
+version: '3.4'
+services:
+
+  kafka-ui:
+    container_name: kafka-ui
+    image: provectuslabs/kafka-ui:latest
+    ports:
+      - 8080:8080
+    depends_on:
+      - zookeeper0
+      - kafka0
+    environment:
+      KAFKA_CLUSTERS_0_NAME: local
+      KAFKA_CLUSTERS_0_PROPERTIES_SECURITY_PROTOCOL: SSL
+      KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka0:29092 # SSL LISTENER!
+      KAFKA_CLUSTERS_0_ZOOKEEPER: zookeeper0:2181
+      KAFKA_CLUSTERS_0_PROPERTIES_SSL_TRUSTSTORE_LOCATION: /kafka.truststore.jks
+      KAFKA_CLUSTERS_0_PROPERTIES_SSL_TRUSTSTORE_PASSWORD: secret # A FILE WITH THE TRUSTSTORE PASSWORD
+    volumes:
+      - ./ssl/kafka.truststore.jks:/kafka.truststore.jks
+
+  zookeeper0:
+    image: confluentinc/cp-zookeeper:6.0.1
+    environment:
+      ZOOKEEPER_CLIENT_PORT: 2181
+      ZOOKEEPER_TICK_TIME: 2000
+    ports:
+      - 2181:2181
+
+  kafka0:
+    image: confluentinc/cp-kafka:6.0.1
+    hostname: kafka0
+    depends_on:
+      - zookeeper0
+    ports:
+      - '9092:9092'
+    environment:
+      KAFKA_BROKER_ID: 1
+      KAFKA_ZOOKEEPER_CONNECT: zookeeper0:2181
+      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
+      KAFKA_ADVERTISED_LISTENERS: SSL://kafka0:29092,PLAINTEXT_HOST://localhost:9092
+      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: SSL:SSL,PLAINTEXT_HOST:PLAINTEXT
+      KAFKA_INTER_BROKER_LISTENER_NAME: SSL
+      KAFKA_SECURITY_PROTOCOL: SSL
+      KAFKA_SSL_ENABLED_MECHANISMS: PLAIN,SSL
+      KAFKA_SSL_KEYSTORE_FILENAME: kafka.keystore.jks
+      KAFKA_SSL_KEYSTORE_CREDENTIALS: creds
+      KAFKA_SSL_KEY_CREDENTIALS: creds
+      KAFKA_SSL_TRUSTSTORE_FILENAME: kafka.truststore.jks
+      KAFKA_SSL_TRUSTSTORE_CREDENTIALS: creds
+      #KAFKA_SSL_CLIENT_AUTH: 'required'
+      KAFKA_SSL_CLIENT_AUTH: "requested"
+      KAFKA_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM: '' # COMMON NAME VERIFICATION IS DISABLED SERVER-SIDE
+    volumes:
+      - ./ssl/creds:/etc/kafka/secrets/creds
+      - ./ssl/kafka.truststore.jks:/etc/kafka/secrets/kafka.truststore.jks
+      - ./ssl/kafka.keystore.jks:/etc/kafka/secrets/kafka.keystore.jks
\ No newline at end of file
diff --git a/documentation/compose/ssl/creds b/documentation/compose/ssl/creds
new file mode 100644
index 00000000000..536aca34dba
--- /dev/null
+++ b/documentation/compose/ssl/creds
@@ -0,0 +1,1 @@
+secret
\ No newline at end of file
diff --git a/documentation/compose/ssl/generate_certs.sh b/documentation/compose/ssl/generate_certs.sh
new file mode 100644
index 00000000000..ebb916657bd
--- /dev/null
+++ b/documentation/compose/ssl/generate_certs.sh
@@ -0,0 +1,175 @@
+#!/usr/bin/env bash
+
+set -eu
+
+KEYSTORE_FILENAME="kafka.keystore.jks"
+VALIDITY_IN_DAYS=3650
+DEFAULT_TRUSTSTORE_FILENAME="kafka.truststore.jks"
+TRUSTSTORE_WORKING_DIRECTORY="truststore"
+KEYSTORE_WORKING_DIRECTORY="keystore"
+CA_CERT_FILE="ca-cert"
+KEYSTORE_SIGN_REQUEST="cert-file"
+KEYSTORE_SIGN_REQUEST_SRL="ca-cert.srl"
+KEYSTORE_SIGNED_CERT="cert-signed"
+
+export COUNTRY=US
+export STATE=IL
+export ORGANIZATION_UNIT=SE
+export CITY=Chicago
+export PASSWORD=secret
+
+COUNTRY=$COUNTRY
+STATE=$STATE
+OU=$ORGANIZATION_UNIT
+CN=kafka0 # COMMON NAME VERIFICATION GOES BRR
+LOCATION=$CITY
+PASS=$PASSWORD
+
+function file_exists_and_exit() {
+  echo "'$1' cannot exist. Move or delete it before"
+  echo "re-running this script."
+  exit 1
+}
+
+if [ -e "$KEYSTORE_WORKING_DIRECTORY" ]; then
+  file_exists_and_exit $KEYSTORE_WORKING_DIRECTORY
+fi
+
+if [ -e "$CA_CERT_FILE" ]; then
+  file_exists_and_exit $CA_CERT_FILE
+fi
+
+if [ -e "$KEYSTORE_SIGN_REQUEST" ]; then
+  file_exists_and_exit $KEYSTORE_SIGN_REQUEST
+fi
+
+if [ -e "$KEYSTORE_SIGN_REQUEST_SRL" ]; then
+  file_exists_and_exit $KEYSTORE_SIGN_REQUEST_SRL
+fi
+
+if [ -e "$KEYSTORE_SIGNED_CERT" ]; then
+  file_exists_and_exit $KEYSTORE_SIGNED_CERT
+fi
+
+echo "Welcome to the Kafka SSL keystore and trust store generator script."
+
+trust_store_file=""
+trust_store_private_key_file=""
+
+  if [ -e "$TRUSTSTORE_WORKING_DIRECTORY" ]; then
+    file_exists_and_exit $TRUSTSTORE_WORKING_DIRECTORY
+  fi
+
+  mkdir $TRUSTSTORE_WORKING_DIRECTORY
+  echo
+  echo "OK, we'll generate a trust store and associated private key."
+  echo
+  echo "First, the private key."
+  echo
+
+  openssl req -new -x509 -keyout $TRUSTSTORE_WORKING_DIRECTORY/ca-key \
+    -out $TRUSTSTORE_WORKING_DIRECTORY/ca-cert -days $VALIDITY_IN_DAYS -nodes \
+    -subj "/C=$COUNTRY/ST=$STATE/L=$LOCATION/O=$OU/CN=$CN"
+
+  trust_store_private_key_file="$TRUSTSTORE_WORKING_DIRECTORY/ca-key"
+
+  echo
+  echo "Two files were created:"
+  echo " - $TRUSTSTORE_WORKING_DIRECTORY/ca-key -- the private key used later to"
+  echo "   sign certificates"
+  echo " - $TRUSTSTORE_WORKING_DIRECTORY/ca-cert -- the certificate that will be"
+  echo "   stored in the trust store in a moment and serve as the certificate"
+  echo "   authority (CA). Once this certificate has been stored in the trust"
+  echo "   store, it will be deleted. It can be retrieved from the trust store via:"
+  echo "   $ keytool -keystore <trust-store-file> -export -alias CARoot -rfc"
+
+  echo
+  echo "Now the trust store will be generated from the certificate."
+  echo
+
+  keytool -keystore $TRUSTSTORE_WORKING_DIRECTORY/$DEFAULT_TRUSTSTORE_FILENAME \
+    -alias CARoot -import -file $TRUSTSTORE_WORKING_DIRECTORY/ca-cert \
+    -noprompt -dname "C=$COUNTRY, ST=$STATE, L=$LOCATION, O=$OU, CN=$CN" -keypass $PASS -storepass $PASS -storetype JKS
+
+  trust_store_file="$TRUSTSTORE_WORKING_DIRECTORY/$DEFAULT_TRUSTSTORE_FILENAME"
+
+  echo
+  echo "$TRUSTSTORE_WORKING_DIRECTORY/$DEFAULT_TRUSTSTORE_FILENAME was created."
+
+  # don't need the cert because it's in the trust store.
+  rm $TRUSTSTORE_WORKING_DIRECTORY/$CA_CERT_FILE
+
+echo
+echo "Continuing with:"
+echo " - trust store file:        $trust_store_file"
+echo " - trust store private key: $trust_store_private_key_file"
+
+mkdir $KEYSTORE_WORKING_DIRECTORY
+
+echo
+echo "Now, a keystore will be generated. Each broker and logical client needs its own"
+echo "keystore. This script will create only one keystore. Run this script multiple"
+echo "times for multiple keystores."
+echo
+echo "     NOTE: currently in Kafka, the Common Name (CN) does not need to be the FQDN of"
+echo "           this host. However, at some point, this may change. As such, make the CN"
+echo "           the FQDN. Some operating systems call the CN prompt 'first / last name'"
+
+# To learn more about CNs and FQDNs, read:
+# https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/X509ExtendedTrustManager.html
+
+keytool -keystore $KEYSTORE_WORKING_DIRECTORY/$KEYSTORE_FILENAME \
+  -alias localhost -validity $VALIDITY_IN_DAYS -genkey -keyalg RSA \
+   -noprompt -dname "C=$COUNTRY, ST=$STATE, L=$LOCATION, O=$OU, CN=$CN" -keypass $PASS -storepass $PASS -storetype JKS
+
+echo
+echo "'$KEYSTORE_WORKING_DIRECTORY/$KEYSTORE_FILENAME' now contains a key pair and a"
+echo "self-signed certificate. Again, this keystore can only be used for one broker or"
+echo "one logical client. Other brokers or clients need to generate their own keystores."
+
+echo
+echo "Fetching the certificate from the trust store and storing in $CA_CERT_FILE."
+echo
+
+keytool -keystore $trust_store_file -export -alias CARoot -rfc -file $CA_CERT_FILE -keypass $PASS -storepass $PASS
+
+echo
+echo "Now a certificate signing request will be made to the keystore."
+echo
+keytool -keystore $KEYSTORE_WORKING_DIRECTORY/$KEYSTORE_FILENAME -alias localhost \
+  -certreq -file $KEYSTORE_SIGN_REQUEST -keypass $PASS -storepass $PASS
+
+echo
+echo "Now the trust store's private key (CA) will sign the keystore's certificate."
+echo
+openssl x509 -req -CA $CA_CERT_FILE -CAkey $trust_store_private_key_file \
+  -in $KEYSTORE_SIGN_REQUEST -out $KEYSTORE_SIGNED_CERT \
+  -days $VALIDITY_IN_DAYS -CAcreateserial
+# creates $KEYSTORE_SIGN_REQUEST_SRL which is never used or needed.
+
+echo
+echo "Now the CA will be imported into the keystore."
+echo
+keytool -keystore $KEYSTORE_WORKING_DIRECTORY/$KEYSTORE_FILENAME -alias CARoot \
+  -import -file $CA_CERT_FILE -keypass $PASS -storepass $PASS -noprompt
+rm $CA_CERT_FILE # delete the trust store cert because it's stored in the trust store.
+
+echo
+echo "Now the keystore's signed certificate will be imported back into the keystore."
+echo
+keytool -keystore $KEYSTORE_WORKING_DIRECTORY/$KEYSTORE_FILENAME -alias localhost -import \
+  -file $KEYSTORE_SIGNED_CERT -keypass $PASS -storepass $PASS
+
+echo
+echo "All done!"
+echo
+echo "Deleting intermediate files. They are:"
+echo " - '$KEYSTORE_SIGN_REQUEST_SRL': CA serial number"
+echo " - '$KEYSTORE_SIGN_REQUEST': the keystore's certificate signing request"
+echo "   (that was fulfilled)"
+echo " - '$KEYSTORE_SIGNED_CERT': the keystore's certificate, signed by the CA, and stored back"
+echo "    into the keystore"
+
+  rm $KEYSTORE_SIGN_REQUEST_SRL
+  rm $KEYSTORE_SIGN_REQUEST
+  rm $KEYSTORE_SIGNED_CERT


Test patch: