I recently set up ChirpStack, and I’ve noticed that MQTT messages are published without encryption by default, making them vulnerable to eavesdropping and potential hacking. Are there any easy and quick ways to secure MQTT communication? Are there specific tools or configurations that you would recommend for encrypting MQTT messages to enhance security? Any advice on best practices for securing ChirpStack’s MQTT messages would be greatly appreciated!
What is your configuration that you see this happening?
General best practices are to put TLS in front of MQTT (usually run on port 8883), and to use either strong username/passwords or certificates to authenticate a gateway. Those should cover all your concerns.
Edit: Applying ACLs based on your organization and needs may also be a good idea.
I can do encryption with mosquitto but then I have problems with chirpstack, is there a page or an example that shows a way for mqtt encryption and chirpstack application?
Do you really think I haven’t analysed this connection?
If I had, I would have asked about the certificate, wouldn’t I? My question is how do I implement the mosquitto app in chirpstack after logging in with a password?
Buddy relax. Just trying to help.
You’d be surprised what people haven’t read before posting.
Besides your question doesn’t even make sense:
By mosquitto app do you mean an integration? Or simply having your gateways + chirpstack connect to the MQTT broker using username/pass?
After logging in where with a password? The Chirpstack UI? Or do you mean after configuring MQTT to require a password?
When I set a password on Mosquitto, ChirpStack fails to connect, and the system crashes. I just want to enable secure access to Mosquitto with a username and password, without adding certificates. My goal is for ChirpStack to connect to the MQTT broker using the username and password as well.
Could you please guide me on how to achieve this or suggest the steps I should follow?
Once you have configured a password file to be associated with your MQTT listener, all you need to do is configure Chirpstack and your GW Bridge / MQTT broker to use those credentials.
If you look at the example configuration options: Configuration - ChirpStack open-source LoRaWAN® Network Server documentation
In the regional.toml files (eu868 example) is where you find Chirpstack’s MQTT backend configuration (the integration.mqtt section in chirpstack.toml is an integration not a backend) You’re looking for the following section:
[regions.gateway.backend.mqtt]
# Event topic template.
event_topic="eu868/gateway/+/event/+"
# Command topic template.
command_topic="eu868/gateway/{{ gateway_id }}/command/{{ command }}"
# MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws)
server="tcp://localhost:1883"
# Connect with the given username (optional)
username=""
# Connect with the given password (optional)
password=""
Then in your chirpstack-gateway-bridge.toml you’re looking for the following section:
[integration.mqtt.auth.generic]
# MQTT servers.
#
# Configure one or multiple MQTT server to connect to. Each item must be in
# the following format: scheme://host:port where scheme is tcp, ssl or ws.
servers=[
"tcp://127.0.0.1:1883",
]
# Connect with the given username (optional)
username=""
# Connect with the given password (optional)
password=""
But to be clear this is not encryption it’s authentication. If you want a fully secured broker you will have to look into the certificates, self-signed or autogenerated by something like cert-bot.
If you only configure the broker to use a user/pass without using TLS encryption an eavesdropper could simply get the MQTT user/pass from your traffic and then connect themselves.
You can also use an encrypted connection without client certificates. The MQTT login credentials are then used.
However, the server certificate must be created and used as a minimum - > ca_cert=…
ChirpStack config
server="ssl://loraserver.***.de:8883"
# Connect with the given username (optional)
username="chirpstack"
# Connect with the given password (optional)
password="xxxxx"
# CA certificate file (not optional when using TLS!)
#
# Use this when setting up a secure connection (when server uses ssl://...)
# but the certificate used by the server is not trusted by any CA certificate
# on the server (e.g. when self generated).
ca_cert="/path_to/ca.crt"
# mqtt TLS certificate file (optional: if not declared, the above authentication is used)
# tls_cert="/path_to/client.crt"
# mqtt TLS key file (optional: if not declared, the above authentication is used)
# tls_key="/path_to/client-nopw.key"
This has been working for me for quite a while.
Use EMQX MQTT broker
Thank you so much Philipp,
i am not familliar with certification, kindly can you record screen for how to easiest way to create and apply to server?
i installed with Docker btw
Thanks alot
The guide I shared previously is the easiest way to create self-signed certs and apply them to Chirpstack.
If you’re willing to put in a bit more time into your setup to avoid needing to worry about certificate expiry and other headaches I would recommend Traefik. A reverse-proxy which can handle automatically generating certificates from LetsEncrypt to handle TLS and then proxy the traffic to your Chirpstack services.
If you are interested I can share my Traefik configuration.
Seconded, this is my approach. It’s a lot more straight-forward than becoming your own CA.
Hi Philipp, are you still keen to share your traefik configuration? I am interested in seeing it.
Just know that not everything will just work if you copy paste it. I’ve made a few changes to base Chirpstack, namely ports.
services:
reverse-proxy:
# The official v3 Traefik docker image
image: traefik:latest
command:
# Dashboard and Log
#- "--api.insecure=true"
#- "--log.level=DEBUG"
# Setup Docker Provider
- "--providers.docker=true"
# Make Containers not Exposed to WAN
- "--providers.docker.exposedbydefault=false"
# Set Entrypoints
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.mqtt.address=:8883"
# Create TLS Resolver for Chirptack Web Interface + Mosquitto Broker
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.myresolver.acme.email=liamp@ssicanada.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
restart: unless-stopped
ports:
# The HTTP port (gets redirected to HTTPS)
- "80:80"
# The HTTPs port
- "443:443"
# The MQTT port
- "8883:8883"
# The Traefik dashboard port (enabled by --api.insecure=true)
# - "8080:8080"
volumes:
# Allows Traefik to listen to the Docker events
- /var/run/docker.sock:/var/run/docker.sock
# Mount Certificate Folder
- ./letsencrypt:/letsencrypt
labels:
# Enable Traefik
- "traefik.enable=true"
networks:
default:
chirpstack:
image: chirpstack/chirpstack:latest
command: -c /etc/chirpstack
restart: unless-stopped
volumes:
# Mount Configuration
- ./configuration/chirpstack:/etc/chirpstack
# Mount Device Profiles
- ./lorawan-devices:/opt/lorawan-devices
depends_on:
- postgres
- mosquitto
- redis
environment:
- MQTT_BROKER_HOST=mosquitto
- REDIS_HOST=redis
- POSTGRESQL_HOST=postgres
labels:
# Enable Traefik
- "traefik.enable=true"
# Redirect traffic from HTTP to HTTPS
- "traefik.http.middlewares.myredirect.redirectscheme.scheme=https"
- "traefik.http.routers.chirpstack.middlewares=myredirect"
# Set HTTP Entrypoint and URL path
- "traefik.http.routers.chirpstack.rule=Host(`<your-server>.com`)"
- "traefik.http.routers.chirpstack.entrypoints=web"
# Point Router at Chirpstack Container
- "traefik.http.routers.chirpstack.service=chirpstack@docker"
# Set HTTPS Entrypoint and URL path
- "traefik.http.routers.chirpstack-secure.entrypoints=websecure"
- "traefik.http.routers.chirpstack-secure.rule=Host(`<your-server>.com`)"
# Enable TLS
- "traefik.http.routers.chirpstack-secure.tls.certresolver=myresolver"
- "traefik.http.routers.chirpstack-secure.tls=true"
# Set Backend Port
- "traefik.http.services.chirpstack.loadbalancer.server.port=8081"
- "traefik.http.services.chirpstack.loadbalancer.server.scheme=h2c"
networks:
default:
mosquitto:
image: eclipse-mosquitto:2
restart: unless-stopped
volumes:
- ./configuration/mosquitto/config/:/mosquitto/config/
labels:
# Create MQTT router
- "traefik.enable=true"
- "traefik.tcp.routers.mosquitto.rule=HostSNI(`<your-server>.com`)"
- "traefik.tcp.routers.mosquitto.entrypoints=mqtt"
# Add TLS to MQTT router
- "traefik.tcp.routers.mosquitto.tls=true"
- "traefik.tcp.routers.mosquitto.tls.certresolver=myresolver"
# Route to Mosquitto's Internal Unencrypted Port
- "traefik.tcp.services.mosquitto.loadbalancer.server.port=1884"
networks:
default:
postgres:
image: postgres:14-alpine
restart: unless-stopped
volumes:
- ./configuration/postgresql/initdb:/docker-entrypoint-initdb.d
- postgresqldata:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=root
networks:
default:
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --save 300 1 --save 60 100 --appendonly no
volumes:
- redisdata:/data
networks:
default:
volumes:
postgresqldata:
redisdata:
networks: # Force docker to give containers IP's in 192.168.x.x range to avoid any conflicts
default:
driver: bridge
ipam:
driver: default
config:
- subnet: 192.168.0.0/16
Thanks a lot Philipp !
Can I also ask you what is your configuration of mosquitto (inside your /configuration/mosquitto/config/
folder) please ? I’m still trying to decide whether to use traefik’s certificates for the mqtt or self-signed certificates as explained in the official documentation.
For my use case my mosquitto config is very simple. It just requires any connection on the unencrypted port (the only one open) to use a user/pass from a passwordfile. That way I can still guarantee only my gateways connect to the broker, while leaving all the TLS up to Traefik.
Your config was very hepful, thanks again!
Last question but what is the exact purpose of this line:
- "traefik.http.services.chirpstack.loadbalancer.server.scheme=h2c"
Is it for the gRPC API of chirpstack? I did not put it and it seems to me that it still works without it.
Its for the web interface and gRPC API (as both are on the same port)
That line specifies the protocol that Traefik should use to communicate with the backend service (chirpstack
in this case). The value h2c
stands for HTTP/2 Cleartext, which is an HTTP/2 protocol over a plain (non-encrypted) connection.
If you don’t specify this Traefik defaults to HTTP 1.1 cleartext.
Really I don’t think it makes a serious difference either way, I believe that was a remnant from testing that I never removed as using HTTP 2 does provide some benefits and it’s supported by Chirpstack.
In the end, it seems that this line is mandatory for the gRPC API to be accessible through the proxy. Without it, a 400 error is raised:
unexpected HTTP status code received from server: 400 (Bad Request); malformed header: missing HTTP content-type
.