Failed to use HTTPS for the kibana server

Hi !

I want to enable HTTPS for my kibana.
I installed ELK (v9.0.0) with docker compose (v2) on my host 10.0.70.195 and I want to add more security to my stack.
But now, I can't connect to the kibana interface via HTTPS. Elasticsearch is up on https://10.0.70.195:9200 with my elastic creds.

I have a .env in my project with all env variables.

docker-compose.yml :

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    container_name: elasticsearch
    environment:
      - node.name=es-node
      - cluster-name=docker-cluster
      - bootstrap.memory_lock=true
      - network.host=0.0.0.0
      - ingest.geoip.downloader.enabled=false
      - discovery.type=single-node
      - xpack.security.authc.realms.native.native1.enabled=false
      - xpack.security.enabled=true

      # Sécurité SSL transport
      - xpack.security.transport.ssl.enabled=true
      - xpack.security.transport.ssl.keystore.path=/usr/share/elasticsearch/config/http.p12
      - xpack.security.transport.ssl.keystore.password=""
      - xpack.security.transport.ssl.certificate_authorities=/usr/share/elasticsearch/config/certs/elasticsearch-ca.pem

      # Sécurité SSL HTTP
      - xpack.security.http.ssl.enabled=true
      - xpack.security.http.ssl.keystore.path=/usr/share/elasticsearch/config/http.p12
      - xpack.security.http.ssl.truststore.path=/usr/share/elasticsearch/config/http.p12

      - ES_JAVA_OPTS=-Xms1g -Xmx1g
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
    
    ulimits:
      memlock:
        soft: -1
        hard: -1

    volumes:
      - esdata:/usr/share/elasticsearch/data
      - ./certs/elasticsearch/http.p12:/usr/share/elasticsearch/config/http.p12
      - ./certs/kibana/elasticsearch-ca.pem:/usr/share/elasticsearch/config/certs/elasticsearch-ca.pem

    ports:
      - ${ES_PORT}:9200

    healthcheck:
      test: ["CMD-SHELL", "curl -k -u ${ELASTIC_USERNAME}:${ELASTIC_PASSWORD} https://10.0.70.195:9200"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 20s

    networks:
      - elk

  kibana:
    image: docker.elastic.co/kibana/kibana:${STACK_VERSION}
    container_name: kibana
    environment:
      - xpack.reporting.roles.enabled=false
      - ELASTICSEARCH_HOSTS=https://elasticsearch:9200
      - ELASTICSEARCH_USERNAME=${KIBANA_USERNAME}
      - ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD}
      - XPACK_SECURITY_ENCRYPTIONKEY=${XPACK_SECURITY_ENCRYPTIONKEY}
      - XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=${XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY}
      - XPACK_REPORTING_ENCRYPTIONKEY=${XPACK_REPORTING_ENCRYPTIONKEY}

      - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=/usr/share/kibana/config/certs/elasticsearch-ca.pem
      - ELASTICSEARCH_SSL_VERIFICATIONMODE=none

      # Configuration de la sécurité HTTPS
      - SERVER_SSL_ENABLED=true
      - SERVER_SSL_CERTIFICATE=/usr/share/kibana/config/kibana-server.crt
      - SERVER_SSL_KEY=/usr/share/kibana/config/kibana-server.key
      - SERVER_SSL_KEYSTORE_PASSWORD=""

    volumes:
      - ./certs/elasticsearch/http.p12:/usr/share/kibana/config/http.p12
      - ./certs/kibana/elasticsearch-ca.pem:/usr/share/kibana/config/certs/elasticsearch-ca.pem
      - ./kibana/kibana.yml:/usr/share/kibana/config/kibana.yml:z
      - ./kibana-server/kibana-server.crt:/usr/share/kibana/config/kibana-server.crt
      - ./kibana-server/kibana-server.key:/usr/share/kibana/config/kibana-server.key

    ports:
      - "5601:5601"

    depends_on:
      elasticsearch:
        condition: service_healthy

    networks:
      - elk

  logstash:
    image: docker.elastic.co/logstash/logstash:${STACK_VERSION}
    container_name: logstash
    environment:
      - ELASTIC_USERNAME=${ELASTIC_USERNAME}
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}

    volumes:
      - ./logstash/logstash.conf:/usr/share/logstash/pipeline/logstash.conf
      - ./logstash/pipeline/pfsense.conf:/usr/share/logstash/pipeline/pfsense.conf
      - ./logstash/pipeline/mailcow.conf:/usr/share/logstash/pipeline/mailcow.conf
      - ./logstash/config/pipelines.yml:/usr/share/logstash/config/pipelines.yml
      - ./certs/kibana/elasticsearch-ca.pem:/usr/share/logstash/config/certs/elasticsearch-ca.pem
    ports:
      - "5044:5044"
      - "5151:5151/udp"
      - "5140:5140/udp"

    depends_on:
      elasticsearch:
        condition: service_healthy

    networks:
      - elk

volumes:
  esdata:

networks:
  elk:
    driver: bridge

I've created a self-signed certificate, as I don't yet have a CA in my company.

Kibana logs are clean, elasticsearch logs contains more WARN like this one :

elasticsearch  | {"@timestamp":"2025-05-06T13:39:09.333Z", "log.level": "WARN", "message":"caught exception while handling client http traffic, closing connection Netty4HttpChannel{localAddress=/192.168.48.2:9200, remoteAddress=/192.168.48.1:53538}", "ecs.version": "1.2.0","service.name":"ES_ECS","event.dataset":"elasticsearch.server","process.thread.name":"elasticsearch[es-node][transport_worker][T#4]","log.logger":"org.elasticsearch.http.AbstractHttpServerTransport","elasticsearch.cluster.uuid":"ZmpHv6aCTM2P9so-ZCATUQ","elasticsearch.node.id":"RE7JhgHcQt-HLzsjXNse6Q","elasticsearch.node.name":"es-node","elasticsearch.cluster.name":"docker-cluster","error.type":"io.netty.handler.codec.DecoderException","error.message":"javax.net.ssl.SSLHandshakeException: (certificate_unknown) Received fatal alert: certificate_unknown","error.stack_trace":"io.netty.handler.codec.DecoderException: javax.net.ssl.SSLHandshakeException: (certificate_unknown) Received fatal alert: certificate_unknown\n\tat io.netty.codec@4.1.118.Final/io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:500)\n\tat io.netty.codec@4.1.118.Final/io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290)\n\tat io.netty.transport@4.1.118.Final/io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)\n\tat io.netty.transport@4.1.118.Final/io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)\n\tat io.netty.transport@4.1.118.Final/io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)\n\tat io.netty.transport@4.1.118.Final/io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1357)\n\tat io.netty.transport@4.1.118.Final/io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)\n\tat io.netty.transport@4.1.118.Final/io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)\n\tat io.netty.transport@4.1.118.Final/io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:868)\n\tat io.netty.transport@4.1.118.Final/io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)\n\tat io.netty.transport@4.1.118.Final/io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:796)\n\tat io.netty.transport@4.1.118.Final/io.netty.channel.nio.NioEventLoop.processSelectedKeysPlain(NioEventLoop.java:697)\n\tat io.netty.transport@4.1.118.Final/io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:660)\n\tat io.netty.transport@4.1.118.Final/io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)\n\tat io.netty.common@4.1.118.Final/io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998)\n\tat io.netty.common@4.1.118.Final/io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)\n\tat java.base/java.lang.Thread.run(Thread.java:1447)\nCaused by: javax.net.ssl.SSLHandshakeException: (certificate_unknown) Received fatal alert: certificate_unknown\n\tat java.base/sun.security.ssl.Alert.createSSLException(Alert.java:130)\n\tat java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)\n\tat java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:363)\n\tat java.base/sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:287)\n\tat java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:202)\n\tat java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:172)\n\tat java.base/sun.security.ssl.SSLEngineImpl.decode(SSLEngineImpl.java:734)\n\tat java.base/sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:689)\n\tat java.base/sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:504)\n\tat java.base/sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:480)\n\tat java.base/javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:673)\n\tat io.netty.handler@4.1.118.Final/io.netty.handler.ssl.SslHandler$SslEngineType$3.unwrap(SslHandler.java:309)\n\tat io.netty.handler@4.1.118.Final/io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1485)\n\tat io.netty.handler@4.1.118.Final/io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1378)\n\tat io.netty.handler@4.1.118.Final/io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1427)\n\tat io.netty.codec@4.1.118.Final/io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:530)\n\tat io.netty.codec@4.1.118.Final/io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:469)\n\t... 16 more\n"}

When I want to connect to : https://10.0.70.195:5601
I have this :

If you have any ideas, I'd love to hear them !

Thanks !

What do you have in this kibana.yml ?

kibana.yml :

server.host=0.0.0.0
elasticsearch.hosts=["https://10.0.70.195:9200"]

ES has HTTPS connection, you must set in kibana.yml as well. Check this.
Most likely you have to set elasticsearch.username and elasticsearch.password in kibana.yml

But these variables are set in docker-compose.yml

Actually, something not ok with certificate: SLHandshakeException: (certificate_unknown) inside ES. Maybe CA is missing, permission or something similar.

I changed the CA, because it not contains CA:true, now is ok with it !

Thanks for your help