After seeing how to set up transport layer security for the Mosquitto MQTT broker by using the Transport layer security on this post, we need to see how to setup client authentication (only authorized clients can connect to the broker) either by using the common user/password based authentication method or using client certificates.
Authentication by user and password:
First let’s enable authentication to the broker by setting up user and password authentication. For enabling this kind of authentication we need to modify the broker configuration (/etc/mosquitto/mosquitto.conf in my case) file and change the following entries:
allow_anonymous false password_file /etc/mosquitto/passwd_mqtt
Before restarting the MQTT broker we need to add some users to the passwd_mqtt file with the command mosquitto_passwd:
mosquitto_passwd -c passwd_mqtt user1
The -c parameter is for creating the initial password file, if doesn’t exist, otherwise it will overwrite it!. For adding new users or update the passwords just run the command without the -c parameter.
After restarting the broker will should not be able to logon anonymously:
# mosquitto_pub --cafile /etc/mosquitto/certs/ca.crt -h localhost -t "test" -m "message" -p 8883 -d Client mosqpub/25688-pcortex sending CONNECT Client mosqpub/25688-pcortex received CONNACK Connection Refused: not authorised. Error: The connection was refused.
But providing the user name and password:
# mosquitto_pub --cafile /etc/mosquitto/certs/ca.crt -h localhost -t "test" -m "message" -p 8883 -d -u user1 -P secret Client mosqpub/25709-pcortex sending CONNECT Client mosqpub/25709-pcortex received CONNACK Client mosqpub/25709-pcortex sending PUBLISH (d0, q0, r0, m1, 'test', ... (7 bytes)) Client mosqpub/25709-pcortex sending DISCONNECT
For subscription, the same has to be done, again by providing a user and password:
# mosquitto_sub -t \$SYS/broker/bytes/\# -v --cafile /etc/mosquitto/certs/ca.crt -p 8883 -u user1 -P secret $SYS/broker/bytes/received 217 $SYS/broker/bytes/sent 20
Authentication by using client certificates
Using client certificates, signed by a certificate authority, assures the client identity. The certificate authority used must be the same used by the server certificates and is only supported over TLS/SSL.
For using client certificates for authentication, we need to change the listener configuration for TLS/SSL by adding the following directives:
# MQTT over TLS/SSL listener 8883 cafile /etc/mosquitto/certs/ca.crt certfile /etc/mosquitto/certs/hostname.crt keyfile /etc/mosquitto/certs/hostname.key require_certificate true use_identity_as_username true
The require_certificate directive with the value true means that clients must now provide a client certificate to connect.
The use_identity_as_username means that the user name of the connecting user is taken from the CN (Common Name) property of the certificate, otherwise we still need to provide an user and password.
With the above new configuration, we now can’t access the broker with user/password token:
mosquitto_pub --cafile /etc/mosquitto/certs/ca.crt -h localhost -t "test" -m "message" -p 8883 -d -u user1 -P secret Client mosqpub/26549-pcortex sending CONNECT Error: A TLS error occurred.
the result on the log file is:
1478537500: New connection from 127.0.0.1 on port 8883. 1478537500: OpenSSL Error: error:140890C7:SSL routines:ssl3_get_client_certificate:peer did not return a certificate 1478537500: Socket error on client , disconnecting.
For accessing now the broker we must provide a client certificate and a private key. The client certificate must be generated from the same CA (Certificate authority) that created the server certificate, otherwise, the client certificate can’t be validated, and the connection fails:
1478537629: New connection from 127.0.0.1 on port 8883. 1478537629: OpenSSL Error: error:14089086:SSL routines:ssl3_get_client_certificate:certificate verify failed 1478537629: Socket error on client , disconnecting.
So on the same directory where the ca.crt and ca.key are residing execute the script to create the client certificate. For example for creating a client certificate for user1, we need to execute the following command:
# ./gen_CA.sh client user1
And we should have three files, two of them the user1.crt, the user certificate, and the user1.key, the user1 private key.
We can now logon to the broker:
# mosquitto_pub --cafile /etc/mosquitto/certs/ca.crt -h localhost -t "test" -m "message" -p 8883 -d --cert user1.crt --key user1.key Client mosqpub/30264-pcortex sending CONNECT Client mosqpub/30264-pcortex received CONNACK Client mosqpub/30264-pcortex sending PUBLISH (d0, q0, r0, m1, 'test', ... (7 bytes)) Client mosqpub/30264-pcortex sending DISCONNECT
If we check the log, we have the following:
1478539830: New client connected from 127.0.0.1 as mosqpub/27159-pcortex (c1, k60, u'user1'). 1478539830: Client mosqpub/27159-pcortex disconnected. 1478601507: New connection from 127.0.0.1 on port 8883.
We can see that the broker extracted the username from the certificate property CN. If we don’t use the use_identity_as_username as true we need to provide the username and password and the client certificate. In this case, the certificate is validated, and the user used to logon might not be the same as the one defined on the CN certificate property. So without this directive we either need to allow anonymous logon again or define user and passwords.
Additional thoughts:
With user and password authentication we can revoke access to an user, by deleting it from the password file or changing the password.
But what about the authentication based on client certificates? As long the certificate is valid, the user can logon at will, since the broker will always accept it until the end of the certificate validation date.
The solution for this is to revocate the client certificate so when it is used, the broker rejects it. For this functionality, most Certificate Authorities provide the revocation list by providing either by CRL (Certificate Revocation list) file, or by OSCP (Online Status Certificate Protocol), and the server checks the client certificate on this list before allowing access. Mosquitto broker only works with CRL files.
We need to modify the listener configuration to verify the client certificates if it was revocated or not:
# MQTT over TLS/SSL listener 8883 cafile /etc/mosquitto/certs/ca.crt certfile /etc/mosquitto/certs/hostname.crt keyfile /etc/mosquitto/certs/hostname.key require_certificate true use_identity_as_username true crlfile /etc/mosquitto/certs/ca.crl
The issue now is that the script that we are using for generating certificates doesn’t generate CRLs, and so we can’t revocate certificates by using this script.
We can do it by hand, but that wouldn’t make our life much easier, since revocating certificates means that the CA server must now track all certificates and their status.
One possible solution is to use scripts that allow to create (a not very secure) CA: easy-ca. These scripts will replace the genCA.sh script and allow the revocation of certificates.
Getting the easy-CA scripts:
git clone https://github.com/fcgdam/easy-ca.git
I’m omitting a lot of output that these scripts do, namely asking for the Organization name, CA passwords and so on.
Somewhere where we find suitable we create our CA:
Generating the CA:
# cd ~ # ~/easy-ca/create-root-ca -d CA # cd CA
Then at the CA directory we created, we can now create our broker certificate and user certificate. Make sure that the names make sense.
Generating the broker certificate:
# bin/create-ssl -s brokername # bin/create-client -c user1
At the end we have the following files:
ca/ca.crt
We now have all the needed files for setting up our broker with support for CRL.
# sudo -s # cp ~/CA/ca/ca.crt /etc/mosquitto/certs # cp ~/CA/crl/ca.crl /etc/mosquitto/certs # cp ~/CA/certs/brokername.server.crt /etc/mosquitto/certs/brokername.crt # cp ~/CA/private/brokername.server.key /etc/mosquitto/certs/brokername.key
And restarting the broker we have the new certificates in place.
We can test now by using our CA user certificate:
# cd ~/CA # mosquitto_pub --cafile /etc/mosquitto/certs/ca.crt -h localhost -t "test" -m "message" -p 8883 -d --cert certs/user1.client.crt --key private/user1.client.key Client mosqpub/7424-pcortex sending CONNECT Client mosqpub/7424-pcortex received CONNACK Client mosqpub/7424-pcortex sending PUBLISH (d0, q0, r0, m1, 'test', ... (7 bytes)) Client mosqpub/7424-pcortex sending DISCONNECT
It works. Now let’s revoke the user certificate:
# bin/revoke-cert -c certs/user1.client.crt Revoking certificate 'certs/user1.client.crt' Reason for revocation: 1. unspecified 2. keyCompromise 3. CACompromise 4. affiliationChanged 5. superseded 6. cessationOfOperation 7. certificateHold Enter 1-7 [1]: 2 You are about to revoke this certificate with reason 'keyCompromise'. Are you SURE you wish to continue? [y/N]: y Using configuration from conf/ca.conf Revoking Certificate 02. Data Base Updated Using configuration from conf/ca.conf Server certificate revoked.
We need to copy the new CRL to the directory where mosquitto expects the CRL:
# sudo cp crl/ca.crl /etc/mosquitto/certs
And after restarting the broker we have:
mosquitto_pub --cafile /etc/mosquitto/certs/ca.crt -h localhost -t "test" -m "message" -p 8883 -d --cert certs/user1.client.crt --key private/user1.client.key Client mosqpub/7640-pcortex sending CONNECT Error: A TLS error occurred.
And on the logs:
1478625909: OpenSSL Error: error:14089086:SSL routines:ssl3_get_client_certificate:certificate verify failed 1478625909: Socket error on client , disconnecting. 1478625910: New connection from 127.0.0.1 on port 8883.