Securing Mosquitto MQTT Server with Certificates on a Local Network

Required Information

  • DietPi version | v9.7.1
  • Distro version | bookworm
  • Kernel version | Linux pihole 6.6.44-current-rockchip64 #1 SMP PREEMPT Sat Aug 3 06:54:42 UTC 2024 aarch64 GNU/Linux
  • Architecture | arm64
  • SBC model | NanoPi R5S/R5C (aarch64)
  • Power supply used | 5V 3A
  • SD card used | ineternal emmc
  • There is wifi/Bluetoothh card installed
    AW-CB375NF Wireless NIC for Jetson Xavier NX/Orin NX/Orin Nano Board etc. 2.4G / 5GHz 300Mbps / 867Mbps Dual-Band Wi-Fi 5, RTL8822CE-CG Core, Bluetooth 5.0, M.2 A/E NGFF

Hi everyone,

I’m trying to secure my Mosquitto MQTT server running on a local network using TLS certificates. I’ve written a bash script (see below) to generate a CA, server, and client certificates. The certificates work, but I’m unsure about the best practices for setting the Common Name (CN), especially for enabling access via both hostname and IP address.

My Setup:

  • Local network with devices accessing the MQTT server via hostname (e.g., hostname.fritz.box) and its IP address.
  • MQTT server is hosted on a DietPi system.
  • The script generates certificates with the CN set as hostname.fritz.box (default from hostname).

Questions:

  1. How should I configure the CN to support access via both hostname and IP address?
    Should I use the hostname, the IP address, or something else for the server certificate’s CN? I’ve read about Subject Alternative Names (SAN), but I’m unsure how to implement it correctly in my script.
  2. Are there additional steps needed to improve security?
    For example, configuring Mosquitto or updating my script to align with best practices for certificate generation and management.

Script for Certificate Generation:

bash

Code kopieren

#!/bin/bash

# Directory to store certificates
CERT_DIR="./mosquitto_certs"
mkdir -p "$CERT_DIR"

# Certificate properties
DAYS=3650  # Validity of certificates (10 years)
COUNTRY="US"
STATE="CA"
LOCALITY="San Francisco"
ORGANIZATION="private"
ORG_UNIT="IT"

# Get the system's hostname and append .fritz.box for use as default CN
DEFAULT_CN="$(hostname).fritz.box"

# Function to generate the CA certificate
generate_ca() {
    echo "Generating Certification Authority (CA)..."
    openssl req -x509 -newkey rsa:2048 -days $DAYS -nodes -keyout "$CERT_DIR/ca.key" -out "$CERT_DIR/ca.crt" -subj "/C=$COUNTRY/ST=$STATE/L=$LOCALITY/O=$ORGANIZATION/OU=$ORG_UNIT/CN=mosquitto-ca"
    echo "CA certificate and key generated: $CERT_DIR/ca.crt, $CERT_DIR/ca.key"
    chmod 644 "$CERT_DIR/ca.crt" "$CERT_DIR/ca.key"
}

# Function to generate a Server certificate
generate_server() {
    if [ ! -f "$CERT_DIR/ca.crt" ] || [ ! -f "$CERT_DIR/ca.key" ]; then
        echo "CA certificate (ca.crt) or key (ca.key) is missing! Please create a CA first."
        return
    fi

    echo "Generating Server certificate..."
    openssl genrsa -out "$CERT_DIR/server.key" 2048
    openssl req -new -key "$CERT_DIR/server.key" -out "$CERT_DIR/server.csr" -subj "/C=$COUNTRY/ST=$STATE/L=$LOCALITY/O=$ORGANIZATION/OU=$ORG_UNIT/CN=$DEFAULT_CN"
    openssl x509 -req -in "$CERT_DIR/server.csr" -CA "$CERT_DIR/ca.crt" -CAkey "$CERT_DIR/ca.key" -CAcreateserial -out "$CERT_DIR/server.crt" -days $DAYS
    echo "Server certificate and key generated: $CERT_DIR/server.crt, $CERT_DIR/server.key"
    chmod 644 "$CERT_DIR/server.crt" "$CERT_DIR/server.key"
    rm "$CERT_DIR/server.csr"
}

# Function to generate a Client certificate
generate_client() {
    if [ ! -f "$CERT_DIR/ca.crt" ] || [ ! -f "$CERT_DIR/ca.key" ]; then
        echo "CA certificate (ca.crt) or key (ca.key) is missing! Please create a CA first."
        return
    fi

    read -p "Enter the name for the client certificate (e.g., client1): " CLIENT_NAME
    if [ -z "$CLIENT_NAME" ]; then
        echo "Client name cannot be empty!"
        return
    fi

    CLIENT_CN="${CLIENT_NAME}.fritz.box"
    CLIENT_KEY="$CERT_DIR/${CLIENT_NAME}.key"
    CLIENT_CRT="$CERT_DIR/${CLIENT_NAME}.crt"

    echo "Generating Client certificate for $CLIENT_NAME..."
    openssl genrsa -out "$CLIENT_KEY" 2048
    openssl req -new -key "$CLIENT_KEY" -out "$CERT_DIR/${CLIENT_NAME}.csr" -subj "/C=$COUNTRY/ST=$STATE/L=$LOCALITY/O=$ORGANIZATION/OU=$ORG_UNIT/CN=$CLIENT_CN"
    openssl x509 -req -in "$CERT_DIR/${CLIENT_NAME}.csr" -CA "$CERT_DIR/ca.crt" -CAkey "$CERT_DIR/ca.key" -CAcreateserial -out "$CLIENT_CRT" -days $DAYS
    echo "Client certificate and key generated: $CLIENT_CRT, $CLIENT_KEY"
    chmod 644 "$CLIENT_CRT" "$CLIENT_KEY"
    rm "$CERT_DIR/${CLIENT_NAME}.csr"
}

# Menu
while true; do
    echo "Certificate Management Menu"
    echo "1. Create Certification Authority (CA)"
    echo "2. Create Server Certificate"
    echo "3. Create Client Certificate"
    echo "4. Exit"
    read -p "Enter your choice: " choice

    case $choice in
        1) generate_ca ;;
        2) generate_server ;;
        3) generate_client ;;
        4) echo "Exiting..."; exit ;;
        *) echo "Invalid choice. Please select a valid option." ;;
    esac
done

When I connect via IP I get this error

Hostname/IP does not match certificate's altnames: IP: ip of server is not in the cert's list

Any guidance, examples, or corrections would be greatly appreciated. Thank you!

You’d need to use the IP address in the CN or SAN.