Passwordless auth with client key+cert and Enterprise Security Extension

In our use-case we have clients connecting to the broker using a client certificate + key and we want to use the Enterprise Security Extension for role-based access control, with an MSSQL backend.

Right now I have managed to configure the broker so that we can correctly connect using cert+key, and then we extract the common name from the cert and use this as the authentication-key.
The problem is that we still need to provide a username and password in order to be able to connect, when cert+key should be enough for our case.

How can we configure the ese-extension to allow for this case?

There is the “allow-all-authentication-manager” that would allow everyone with a cert to connect and then in theory the CN could be used to fetch the roles for the client, but I’m not sure how to achieve that.
When I use “sql-authentication-manager” the “authorization-role-key” seems to automatically resolve to the users roles but not when using the “allow-all-authentication-manager”.

Our config currently looks something like this:

    <pipelines>
        <listener-pipeline listener="tls-listener">
            <authentication-preprocessors>
                <x509-preprocessor prefix="{{" postfix="}}">
                    <x509-extractions>
                        <x509-extraction>
                            <x509-field>subject-common-name</x509-field>
                            <ese-variable>authentication-key</ese-variable>
                        </x509-extraction>
                    </x509-extractions>
                </x509-preprocessor>
                <plain-preprocessor>
                    <transformations>
                        <transformation>
                            <from>mqtt-password</from>
                            <to>authentication-byte-secret</to>
                        </transformation>
                    </transformations>
                </plain-preprocessor>
            </authentication-preprocessors>

            <sql-authentication-manager>
                <realm>mssql-backend</realm>
            </sql-authentication-manager>

            <sql-authorization-manager>
                <realm>mssql-backend</realm>
                <use-authorization-key>false</use-authorization-key>
                <use-authorization-role-key>true</use-authorization-role-key>
            </sql-authorization-manager>

        </listener-pipeline>
    </pipelines>

(I’ve also tried setting an empty password in the database (both an empty field an an empty hashed string) but that didnt work)

Edit:
A possible hack I tried that worked:

  <plain-preprocessor>
    <transformations>
      <transformation encoding="UTF8">
      <from>authentication-key</from>
      <to>authentication-byte-secret</to>
    </transformation>
  </transformations>
</plain-preprocessor>

and then just create users with username==password. Maybe there is a better way though :slight_smile:

Hello @tbrn,

Thank you for the outreach!

x509 extraction is most definitely a method that is supported with the ESE extension. Collaborating with our team, it does look like the “allow-all-authentication-manager” is a key component for this configuration.

Comparing pipelines from a known-good test deployment, and the pipeline you have outlined here, it looks like the major differences are between the additional plain-preprocessor used for transformation, and the inclusion of an additional tag for the allow-all-authentication-manager.

​
    <pipelines>
      <listener-pipeline listener="mqtts-int">
​
        <allow-all-authentication-manager/>
​
        <authorization-preprocessors>
          <x509-preprocessor prefix="{{" postfix="}}">
            <x509-extractions>        
              <x509-extraction>
                <x509-field>subject-common-name</x509-field>
                <ese-variable>authorization-key</ese-variable>
              </x509-extraction>
            </x509-extractions>
          </x509-preprocessor>
​
​
          <logging-preprocessor>
            <message>The content of the authorization-key ESE-Variable: ${authorization-key}</message>
            <level>info</level>
            <name>ese</name>
          </logging-preprocessor>
        </authorization-preprocessors>
​
​
        <file-authorization-manager>
          <realm>permission-file-client-cert</realm>
          <use-authorization-key>true</use-authorization-key>
          <use-authorization-role-key>true</use-authorization-role-key>
        </file-authorization-manager>
        
      </listener-pipeline>
    </pipelines>

Some more specific information regarding x509 implementation, and core ESE concepts can be found here, as well.

Best,
Aaron from HiveMQ Team

Hi, thank you. Unfortunately I get similar issues with this setup in that the authorization-role-key does not get set:

2023-06-08 20:07:53,374 INFO  - authorization-key = commonName | authorization-role-key =

We use the SQL based authentication/authorization, and it feels like the query that sets authorization-role-key is only run when there is an sql-authentication-manager present. :thinking:

Hello @tbrn ,

Confirming with our team, I was able to verify that roles are set during authentication, but the allow-all authentication manager is unable to provide roles.

The workaround by supplying username==password is effective, if as you mentioned a bit hack-y. If additional viable configurations become available from further review, we will make sure to update here.

In the meantime, please feel free to reach out if you have any further questions, or if there is anything more we can assist with!

Best,
Aaron from the HiveMQ Team