eclipse / paho.mqtt.m2mqtt

Eclipse Public License 1.0
512 stars 303 forks source link

TLS not working for dockerized MQTT broker #142

Open rauleteee opened 6 months ago

rauleteee commented 6 months ago

Issue Summary:

I have a Dockerized MQTT broker server where I want to add TLS connections using certificates from a private CA (root-ca.cer has signed server.cer as a trust chain ). I have configured the server and client code with certificates, and the server configuration seems correct. However, I'm encountering an error during the TLS handshake when the client tries to connect to the MQTT broker. It is important to say that the broker accepts connections when running the server C# program and client C# program locally, the problem comes when dockerizing it.

Server Code (Publisher):

                var certificate = new X509Certificate2(Constants.PATH_SERVER_CERT, Constants.CERT_PASSWORD);
                var certificateCA = new X509Certificate2(Constants.PATH_CA_CERT, Constants.CERT_PASSWORD);

                MqttClient client = new MqttClient(BrokerAddress, 8883, true,
                                       certificateCA,
                                       certificate,
                                       MqttSslProtocols.TLSv1_2, RemoteCertificateValidationCallback);

                client.Connect(Guid.NewGuid().ToString());

                client.Publish(topic, Encoding.UTF8.GetBytes(message), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);

Client Code (Subscriber):

  // Cargar el certificado PFX
                var certificate = new X509Certificate2(Constants.PATH_CLIENT_CERT, Constants.CERT_PASSWORD);
                var certificateCA = new X509Certificate2(Constants.PATH_CA_CERT, Constants.CERT_PASSWORD);

                // Crear cliente MQTT con soporte TLS
                MqttClient client = new MqttClient(BrokerAddress, 8883, true,
                                       certificateCA,
                                       certificate,
                                       MqttSslProtocols.TLSv1_2, RemoteCertificateValidationCallback);

                Console.WriteLine($"INFO: BROKER ADDRESS: {BrokerAddress}");

                client.MqttMsgPublishReceived += (sender, e) =>
                {
 //...
                };

                client.Connect(Guid.NewGuid().ToString());

                client.Subscribe(new string[] { topic }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });

                Console.WriteLine($"{Colors.YELLOW}{Colors.BOLD}[info]{Colors.RESET} Subscribed to topic {Colors.BOLD}[{topic}]{Colors.RESET}");
            }
            catch (MqttClientException ex)
            {
                Console.WriteLine($"{Colors.RED}{Colors.BOLD}[MQTT Connection Exception]{Colors.RESET} {ex}");
                // Manejo específico para errores de conexión MQTT
            }
            catch (SocketException ex)
            {
                Console.WriteLine($"{Colors.RED}{Colors.BOLD}[Socket Exception]{Colors.RESET} {ex}");
                // Manejo específico para errores de Socket
            }
            catch (Exception ex)
            {
                Console.WriteLine($"{Colors.RED}{Colors.BOLD}[General Exception]{Colors.RESET} {ex}");
                // Manejo para cualquier otro tipo de error no capturado específicamente
            }
        }

RemoteCertificateValidationCallback returns always true. It works in local mode, not in dockerized mode.

Mosquitto Configuration (mosquitto.conf):

persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log

# TLS
certfile /mosquitto/certs/server.crt
keyfile /mosquitto/certs/server.key

listener 8883 0.0.0.0

## Authentication ##
# By default, Mosquitto >=2.0 allows only authenticated connections. Change to true to enable anonymous connections.
require_certificate true
allow_anonymous true
log_type all
connection_messages true

# password_file /mosquitto/config/password.txt

Server's Docker compose:

version: '3.8'

services:
  mosquitto:
    image: eclipse-mosquitto:1.6
    container_name: mosquitto
    environment:
      - TZ=Europe/Madrid
    volumes:
      - ./mosquitto/config:/mosquitto/config
      - ./mosquitto/data:/mosquitto/data
      - ./mosquitto/log:/mosquitto/log
      - ./mosquitto/certs:/mosquitto/certs
    ports:
      - "8883:8883"
    networks:
      - mls_network
    restart: unless-stopped

  mls_server:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: mls_server
    environment:
      - BROKER_IP_ADDRESS=mosquitto
    ports:
      - "10003:10003"
    depends_on:
      - mosquitto
    networks:
      - mls_network
    volumes:
      - ./mls-server/keys:/mls-server/mls-server/keys
      - ./mls-server/data:/mls-server/mls-server/data
    restart: on-failure
networks:
  mls_network:
    driver: bridge
    external: true

Client's Docker compose:

version: '3.8'

services:
  mls_client:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: mls_client
    environment:
      BROKER_IP_ADDRESS: ${BROKER_IP_ADDRESS}
      SERVER_IP: ${SERVER_IP}
      N: ${N}
      ALGORITHM: ${ALGORITHM}
      TZ: Europe/Madrid
    env_file:
      - .env
    ports:
      - "10003"
      - "8883"
    networks:
      - mls_network
    volumes:
      - ./mls-client/keys:/mls-client/keys
      - ./mls-client/data:/mls-client/data
      - ./mls-client/scripts:/mls-client/scripts
    restart: on-failure
networks:
  mls_network:
    driver: bridge
    external: true

Mosquitto log:

I have tried to see if there are uploaded certs as follows:

sudo docker exec -it 4a91e96b0825 ls /mosquitto/certs
server.crt  server.key

As you can see, they are in the correct place, but mosquitto cannot use them.

1703761378: mosquitto version 1.6.15 starting
1703761378: Config loaded from /mosquitto/config/mosquitto.conf.
1703761378: Opening ipv4 listen socket on port 8883.
1703761378: mosquitto version 1.6.15 running
1703761558: mosquitto version 1.6.15 terminating
1703761558: Saving in-memory database to /mosquitto/data/mosquitto.db.
1703761821: mosquitto version 1.6.15 starting
1703761821: Config loaded from /mosquitto/config/mosquitto.conf.
1703761821: Opening ipv4 listen socket on port 8883.
1703761821: mosquitto version 1.6.15 running
1703761959: New connection from 172.22.0.4 on port 8883.
1703761959: Client <unknown> disconnected due to protocol error.
1703761959: New connection from 172.22.0.4 on port 8883.
1703761959: Client <unknown> disconnected due to protocol error.

Error Message:

[Exception] Exception in MQTT: uPLibrary.Networking.M2Mqtt.Exceptions.MqttConnectionException: Exception connecting to the broker
...
mls_client    | [General Exception] uPLibrary.Networking.M2Mqtt.Exceptions.MqttConnectionException: Exception connecting to the broker
mls_client    |  ---> System.IO.IOException: Unable to read data from the transport connection: Connection reset by peer.
mls_client    |  ---> System.Net.Sockets.SocketException (104): Connection reset by peer
mls_client    |    at System.Net.Sockets.NetworkStream.Read(Span`1 buffer)
mls_client    |    --- End of inner exception stack trace ---
mls_client    |    at System.Net.Sockets.NetworkStream.Read(Span`1 buffer)
mls_client    |    at System.Net.Security.SyncReadWriteAdapter.ReadAsync(Stream stream, Memory`1 buffer, CancellationToken cancellationToken)
mls_client    |    at System.Net.Security.SslStream.EnsureFullTlsFrameAsync[TIOAdapter](CancellationToken cancellationToken, Int32 estimatedSize)
mls_client    |    at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
mls_client    |    at System.Net.Security.SslStream.ReceiveHandshakeFrameAsync[TIOAdapter](CancellationToken cancellationToken)
mls_client    |    at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken)
mls_client    |    at System.Net.Security.SslStream.AuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions)
mls_client    |    at uPLibrary.Networking.M2Mqtt.MqttNetworkChannel.Connect()
mls_client    |    at uPLibrary.Networking.M2Mqtt.MqttClient.Connect(String clientId, String username, String password, Boolean willRetain, Byte willQosLevel, Boolean willFlag, String willTopic, String willMessage, Boolean cleanSession, UInt16 keepAlivePeriod)
mls_client    |    --- End of inner exception stack trace ---
mls_client    |    at uPLibrary.Networking.M2Mqtt.MqttClient.Connect(String clientId, String username, String password, Boolean willRetain, Byte willQosLevel, Boolean willFlag, String willTopic, String willMessage, Boolean cleanSession, UInt16 keepAlivePeriod)
mls_client    |    at uPLibrary.Networking.M2Mqtt.MqttClient.Connect(String clientId)
mls_client    |    at Idbotic.MlsClient.Utilities.Mqtt.SubscribeToMqttTopic(String topic) in /mls-client/src/Mqtt.cs:line 60

...

The server certificates (server.crt, server.key) have been successfully copied to the Mosquitto container's path, and their existence has been confirmed.

I appreciate any guidance or suggestions to troubleshoot and resolve this problem.