strimzi / strimzi-kafka-operator

Apache Kafka® running on Kubernetes
https://strimzi.io/
Apache License 2.0
4.78k stars 1.28k forks source link

MirrorMaker2: Keystore password incorrect #4331

Closed wiegandf closed 3 years ago

wiegandf commented 3 years ago

Describe the bug I am trying to use MirrorMaker2 with strimzi operator to mirror to an AWS MSK cluster. I am using TLS authentication on MSK side and the certificates are referenced in the YAML below. It looks like MM2 is creating the keystore correctly but then fails to read from it.

2021-01-28 09:50:19,108 ERROR Scheduler for MirrorCheckpointConnector caught exception in scheduled task: creating internal topics (org.apache.kafka.connect.mirror.Scheduler) [Scheduler for MirrorCheckpointConnector-creating internal topics]
org.apache.kafka.common.KafkaException: Failed to create new KafkaAdminClient
    at org.apache.kafka.clients.admin.KafkaAdminClient.createInternal(KafkaAdminClient.java:535)
    at org.apache.kafka.clients.admin.Admin.create(Admin.java:75)
    at org.apache.kafka.connect.util.TopicAdmin.<init>(TopicAdmin.java:216)
    at org.apache.kafka.connect.mirror.MirrorUtils.createCompactedTopic(MirrorUtils.java:108)
    at org.apache.kafka.connect.mirror.MirrorUtils.createSinglePartitionCompactedTopic(MirrorUtils.java:114)
    at org.apache.kafka.connect.mirror.MirrorCheckpointConnector.createInternalTopics(MirrorCheckpointConnector.java:163)
    at org.apache.kafka.connect.mirror.Scheduler.run(Scheduler.java:93)
    at org.apache.kafka.connect.mirror.Scheduler.executeThread(Scheduler.java:112)
    at org.apache.kafka.connect.mirror.Scheduler.lambda$execute$2(Scheduler.java:63)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: org.apache.kafka.common.KafkaException: Failed to load SSL keystore /tmp/kafka/clusters/target.keystore.p12 of type PKCS12
    at org.apache.kafka.common.security.ssl.DefaultSslEngineFactory$FileBasedStore.load(DefaultSslEngineFactory.java:377)
    at org.apache.kafka.common.security.ssl.DefaultSslEngineFactory$FileBasedStore.<init>(DefaultSslEngineFactory.java:349)
    at org.apache.kafka.common.security.ssl.DefaultSslEngineFactory.createKeystore(DefaultSslEngineFactory.java:299)
    at org.apache.kafka.common.security.ssl.DefaultSslEngineFactory.configure(DefaultSslEngineFactory.java:161)
    at org.apache.kafka.common.security.ssl.SslFactory.instantiateSslEngineFactory(SslFactory.java:136)
    at org.apache.kafka.common.security.ssl.SslFactory.configure(SslFactory.java:93)
    at org.apache.kafka.common.network.SslChannelBuilder.configure(SslChannelBuilder.java:72)
    at org.apache.kafka.common.network.ChannelBuilders.create(ChannelBuilders.java:157)
    at org.apache.kafka.common.network.ChannelBuilders.clientChannelBuilder(ChannelBuilders.java:73)
    at org.apache.kafka.clients.ClientUtils.createChannelBuilder(ClientUtils.java:105)
    at org.apache.kafka.clients.admin.KafkaAdminClient.createInternal(KafkaAdminClient.java:508)
    ... 14 more
Caused by: java.io.IOException: keystore password was incorrect
    at java.base/sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:2117)
    at java.base/sun.security.util.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:222)
    at java.base/java.security.KeyStore.load(KeyStore.java:1479)
    at org.apache.kafka.common.security.ssl.DefaultSslEngineFactory$FileBasedStore.load(DefaultSslEngineFactory.java:374)
    ... 24 more
Caused by: java.security.UnrecoverableKeyException: failed to decrypt safe contents entry: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
    ... 28 more

When I try to open the keystore, it works when I don't enter any password:

[kafka@mirrormaker-poc-mirrormaker2-6ddf9c48f9-rt6md clusters]$ keytool -list -v -keystore target.keystore.p12
Enter keystore password:

*****************  WARNING WARNING WARNING  *****************
* The integrity of the information stored in your keystore  *
* has NOT been verified!  In order to verify its integrity, *
* you must provide your keystore password.                  *
*****************  WARNING WARNING WARNING  *****************

Keystore type: PKCS12
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: msk-dev-certificate-and-key/cert
Creation date: Jan 28, 2021
Entry type: PrivateKeyEntry

*******************************************
*******************************************

When I enter any arbitrary password I'll get an error similar to MM2 stacktrace:

[kafka@mirrormaker-poc-mirrormaker2-6ddf9c48f9-rt6md clusters]$ keytool -list -v -keystore target.keystore.p12
Enter keystore password:
keytool error: java.io.IOException: keystore password was incorrect
java.io.IOException: keystore password was incorrect
    at java.base/sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:2117)
    at java.base/sun.security.util.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:222)
    at java.base/java.security.KeyStore.load(KeyStore.java:1479)
    at java.base/sun.security.tools.keytool.Main.doCommands(Main.java:1064)
    at java.base/sun.security.tools.keytool.Main.run(Main.java:409)
    at java.base/sun.security.tools.keytool.Main.main(Main.java:402)
Caused by: java.security.UnrecoverableKeyException: failed to decrypt safe contents entry: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
    ... 6 more

Environment (please complete the following information):

YAML files and logs

apiVersion: kafka.strimzi.io/v1alpha1
kind: KafkaMirrorMaker2
metadata:
  name: mirrormaker-poc
spec:
  logging:
    type: inline
    loggers:
      connect.root.logger.level: DEBUG
  template:
    pod:
      metadata:
        annotations:
          sidecar.istio.io/inject: "false"
  version: 2.7.0
  replicas: 1
  connectCluster: "target"
  clusters:
    - alias: "source"
      bootstrapServers: ...,...,...
    - alias: "target"
      bootstrapServers: ...,...,...
      authentication:
        type: tls
        certificateAndKey:
          certificate: cert
          key: key
          secretName: msk-dev-certificate-and-key
      tls:
        trustedCertificates: []
      config:
        config.storage.replication.factor: 3
        offset.storage.replication.factor: 3
        status.storage.replication.factor: 3
  mirrors:
  - ...

Additional context As it seems that kafka_mirror_maker_tls_prepare_certificates.sh is creating the keystores, I also tried to set CERTS_STORE_PASSWORD but it gave me the same error.

scholzj commented 3 years ago

Can you describe the steps for reproducing this? It does not seem to be happening to me.

scholzj commented 3 years ago

I tried it with the following YAML:

apiVersion: kafka.strimzi.io/v1beta1
kind: Kafka
metadata:
  name: my-source
spec:
  kafka:
    version: 2.7.0
    replicas: 3
    listeners:
      - name: tls
        port: 9093
        type: internal
        tls: true
        authentication:
          type: tls
#    authorization:
#      type: simple
    config:
      offsets.topic.replication.factor: 3
      transaction.state.log.replication.factor: 3
      transaction.state.log.min.isr: 2
      log.message.format.version: "2.7"
      inter.broker.protocol.version: "2.7"
    storage:
      type: jbod
      volumes:
      - id: 0
        type: persistent-claim
        size: 100Gi
        deleteClaim: true
  zookeeper:
    replicas: 3
    storage:
      type: persistent-claim
      size: 100Gi
      deleteClaim: true
  entityOperator:
    topicOperator: {}
    userOperator: {}
---

apiVersion: kafka.strimzi.io/v1beta1
kind: Kafka
metadata:
  name: my-target
spec:
  kafka:
    version: 2.7.0
    replicas: 3
    listeners:
      - name: tls
        port: 9093
        type: internal
        tls: true
        authentication:
          type: tls
#    authorization:
#      type: simple
    config:
      offsets.topic.replication.factor: 3
      transaction.state.log.replication.factor: 3
      transaction.state.log.min.isr: 2
      log.message.format.version: "2.7"
      inter.broker.protocol.version: "2.7"
    storage:
      type: jbod
      volumes:
        - id: 0
          type: persistent-claim
          size: 100Gi
          deleteClaim: true
  zookeeper:
    replicas: 3
    storage:
      type: persistent-claim
      size: 100Gi
      deleteClaim: true
  entityOperator:
    topicOperator: {}
    userOperator: {}
---

apiVersion: kafka.strimzi.io/v1alpha1
kind: KafkaUser
metadata:
  name: my-source-mirror-maker
  labels:
    strimzi.io/cluster: my-source
spec:
  authentication:
    type: tls
---

apiVersion: kafka.strimzi.io/v1alpha1
kind: KafkaUser
metadata:
  name: my-target-mirror-maker
  labels:
    strimzi.io/cluster: my-target
spec:
  authentication:
    type: tls
---

apiVersion: kafka.strimzi.io/v1alpha1
kind: KafkaMirrorMaker2
metadata:
  name: my-mirror-maker-2
spec:
  version: 2.7.0
  replicas: 1
  connectCluster: "my-target"
  clusters:
    - alias: "my-source"
      bootstrapServers: my-source-kafka-bootstrap:9093
      tls:
        trustedCertificates:
          - secretName: my-source-cluster-ca-cert
            certificate: ca.crt
      authentication:
        certificateAndKey:
          certificate: user.crt
          key: user.key
          secretName: my-source-mirror-maker
        type: tls
    - alias: "my-target"
      bootstrapServers: my-target-kafka-bootstrap:9093
      tls:
        trustedCertificates:
          - secretName: my-target-cluster-ca-cert
            certificate: ca.crt
      authentication:
        certificateAndKey:
          certificate: user.crt
          key: user.key
          secretName: my-target-mirror-maker
        type: tls
      config:
        config.storage.replication.factor: 1
        offset.storage.replication.factor: 1
        status.storage.replication.factor: 1
  mirrors:
    - sourceCluster: "my-source"
      targetCluster: "my-target"
      sourceConnector:
        config:
          replication.factor: 1
          offset-syncs.topic.replication.factor: 1
          sync.topic.acls.enabled: "false"
      heartbeatConnector:
        config:
          heartbeats.topic.replication.factor: 1
      checkpointConnector:
        config:
          checkpoints.topic.replication.factor: 1
      topicsPattern: ".*"
      groupsPattern: ".*"

But all seems to work fine for me. It would be great if you could provide a full log or some more detailed steps to reproduce. Especially the log from the container start when it generates the keystores would be useful.

wiegandf commented 3 years ago

Hi, thanks so much for your support.

I can confirm that it seems to work when I spin up a new cluster with the operator and use self-generated certificates.

The certs which are not working are generated by terraform and are issued by our private AWS CA (aws pca). https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate is used to generate the certificates . https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acmpca_certificate_authority for the CA (with RSA_2048 and SHA256WITHRSA config).

The cert is saved to our vault with some python script:

certificate["PrivateKey"] = private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=serialization.BestAvailableEncryption(passphrase_encoded),
        ).decode("ascii")

Before creating the k8s secret for MM2, I'll decrypt the key manually via openssl rsa -in in.key -out out.key

I've attached the full logs to the post and meanwhile look how to better reproduce this. log.txt

scholzj commented 3 years ago

It looks like for some reason, the connector config file with the passwords did not generate. This is in your log:

Creating connector configuration:

While this is in mine:

Creating connector configuration:
# TLS / SSL
ssl.truststore.password=[hidden]
ssl.keystore.password=[hidden]

It looks like there is a bug in how the Mirror Maker 2 deployments handles the situation when you want to use the default TLS certificates from the JVM instead of passing your own. So basically this part is causing the issue.

      tls:
        trustedCertificates: []

I will have a look at it and see how it can be fixed. But in the meantime, as a workaround, you would need to create a secret with the public key of the CA used by your cluster and use it in the tls section:

      tls:
        trustedCertificates:
          - secretName: my-ca-secret
            certificate: ca.crt
scholzj commented 3 years ago

I opened a PR for this. I'm 99% sure it fixes the issue. But I do not have the exactly same environment with server certs signed by public CA and TLS client authentication. So if you want and have some test cluster to try it in, I can provide you with the images to try it.