jzombie / docker-mqtt-mosquitto-cloudflare-tunnel

MQTT Broker via Cloudflare Tunnels, using Docker.
7 stars 0 forks source link

Encrypted retained messages #4

Closed jzombie closed 2 months ago

jzombie commented 2 months ago

Encrypting retained messages on the server side with Mosquitto requires a few additional steps, as Mosquitto does not natively support message encryption at rest. However, you can achieve this by combining Mosquitto with an external script or service that handles encryption and decryption.

Here are some approaches to encrypt retained messages on the server:

Approach 1: Using a Custom Plugin

You can create a custom Mosquitto plugin that encrypts messages before they are stored and decrypts them when they are retrieved.

  1. Install Mosquitto Development Libraries: Make sure you have the necessary development libraries for Mosquitto to build custom plugins.

    sudo apt-get install libmosquitto-dev
  2. Create a Custom Plugin: Write a plugin in C that uses a cryptographic library (e.g., OpenSSL) to encrypt and decrypt messages.

    #include <mosquitto.h>
    #include <openssl/evp.h>
    #include <string.h>
    
    static int encrypt_message(const char *input, char *output, size_t output_len) {
       // Implement encryption logic using OpenSSL
       return 0; // Return 0 on success
    }
    
    static int decrypt_message(const char *input, char *output, size_t output_len) {
       // Implement decryption logic using OpenSSL
       return 0; // Return 0 on success
    }
    
    int mosquitto_plugin_init(void **user_data, struct mosquitto_opt *opts, int opt_count) {
       // Initialization code
       return MOSQ_ERR_SUCCESS;
    }
    
    int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count) {
       // Cleanup code
       return MOSQ_ERR_SUCCESS;
    }
    
    int mosquitto_auth_plugin_version(void) {
       return MOSQ_AUTH_PLUGIN_VERSION;
    }
    
    int mosquitto_message_publish(void *user_data, struct mosquitto *mosq, const struct mosquitto_message *msg) {
       // Encrypt message
       char encrypted_msg[256];
       encrypt_message(msg->payload, encrypted_msg, sizeof(encrypted_msg));
       // Replace original payload with encrypted payload
       memcpy((void *)msg->payload, encrypted_msg, sizeof(encrypted_msg));
       return MOSQ_ERR_SUCCESS;
    }
    
    int mosquitto_message_receive(void *user_data, struct mosquitto *mosq, const struct mosquitto_message *msg) {
       // Decrypt message
       char decrypted_msg[256];
       decrypt_message(msg->payload, decrypted_msg, sizeof(decrypted_msg));
       // Replace encrypted payload with decrypted payload
       memcpy((void *)msg->payload, decrypted_msg, sizeof(decrypted_msg));
       return MOSQ_ERR_SUCCESS;
    }
  3. Compile the Plugin: Compile the plugin and configure Mosquitto to load it.

    gcc -fPIC -shared -o mosquitto_encryption_plugin.so mosquitto_encryption_plugin.c -lmosquitto -lssl -lcrypto
  4. Configure Mosquitto: Add the plugin to the Mosquitto configuration file.

    plugin /path/to/mosquitto_encryption_plugin.so

Approach 2: Using a Bridge with Encryption

Set up a Mosquitto bridge to another broker or service that handles encryption and decryption of messages.

  1. Configure Mosquitto Bridge: Add a bridge to the Mosquitto configuration file.

    connection encrypted_bridge
    address encrypted-broker.example.com:1883
    topic # both 0
  2. Encryption Service: Use an external service that subscribes to the Mosquitto broker, encrypts messages, and republishes them to the encrypted broker.

    import paho.mqtt.client as mqtt
    from cryptography.fernet import Fernet
    
    # Generate a key for encryption
    key = Fernet.generate_key()
    cipher_suite = Fernet(key)
    
    def on_message(client, userdata, msg):
       encrypted_payload = cipher_suite.encrypt(msg.payload)
       client.publish(msg.topic, encrypted_payload)
    
    client = mqtt.Client()
    client.on_message = on_message
    
    client.connect("localhost", 1883, 60)
    client.subscribe("#", 0)
    
    client.loop_forever()

Approach 3: Using SSL/TLS for Transport Encryption

While this does not encrypt messages at rest, it ensures that messages are encrypted during transport.

  1. Generate SSL/TLS Certificates: Use OpenSSL to generate certificates.

    openssl genrsa -out mosquitto.key 2048
    openssl req -new -key mosquitto.key -out mosquitto.csr
    openssl x509 -req -in mosquitto.csr -signkey mosquitto.key -out mosquitto.crt
  2. Configure Mosquitto for SSL/TLS: Add SSL/TLS configuration to the Mosquitto configuration file.

    listener 8883
    cafile /path/to/ca.crt
    certfile /path/to/mosquitto.crt
    keyfile /path/to/mosquitto.key
  3. Connect Clients Using SSL/TLS: Configure MQTT clients to connect using SSL/TLS.

    import paho.mqtt.client as mqtt
    
    client = mqtt.Client()
    client.tls_set(ca_certs="path/to/ca.crt", certfile="path/to/client.crt", keyfile="path/to/client.key")
    
    client.connect("localhost", 8883, 60)
    client.loop_forever()

Conclusion

While Mosquitto does not natively support encryption of retained messages at rest, you can achieve this through custom plugins, bridging to an encrypted broker, or using SSL/TLS for transport encryption. The best approach depends on your specific requirements and security policies.

jzombie commented 2 months ago

Loosely related: https://github.com/dmotz/trystero/issues/85

jzombie commented 2 months ago

I initially tried to make a C plugin for this but it seems that I couldn't bind directly to the storage I/O events, so I am using an encrypted storage mount instead.