Closed dentex closed 1 year ago
I had a quick look at the code while on the train. The NEED_CLIENT_AUTH
property is only evaluated for SSL connections. While at first it may seem sensible to not allow passwords to be sent over non-encrypted connections, this fails to take into account that Moquette may be behind an SSL-terminating proxy. Thus even though Moquette may not know it, the connection may still be secured.
This problem also does not happen when explicitly setting an Authenticator and AuthorizorPolicy using the four-parameter Server.start()
method.
Hello. Thanks for reaching out. Are you saying that this issue can be avoided by setting an Authenticator and AuthorizerPolicy using an alternate Server.start() method? Would this be a way to explicitly set authentication and authorization policies for the connection, rather than relying on the NEED_CLIENT_AUTH property?
Yes, the alternative start method seems to be a good workaround for this problem for now.
Using the alternative start method you can pass your own implementations of the Authenticator and AuthorizorPolicy. This means you can use any source for your user database, like a relational database, or ldap server. The NEED_CLIENT_AUTH does not need to be set for this to work.
I think I may have implemented the 3 additional parameters for the Server.start()
method.
public static class MySslContextCreator implements ISslContextCreator {
@Override
public SslContext initSSLContext() {
SelfSignedCertificate cert;
try {
cert = new SelfSignedCertificate();
} catch (CertificateException e) {
throw new RuntimeException(e);
}
try {
Log.d(DEBUG_TAG, "Using certificate: " + cert.cert().getSubjectX500Principal().getName());
return SslContextBuilder.forServer(cert.key(), cert.cert())
.sslProvider(SslProvider.JDK)
.build();
} catch (SSLException e) {
throw new RuntimeException(e);
}
}
}
public static class MyAuthenticator implements IAuthenticator {
@Override
public boolean checkValid(String clientId, String username, byte[] password) {
String passwordString = new String(password, StandardCharsets.UTF_8);
String un = App.settings.getString(App.getCtx().getString(R.string.mqtt_username), App.default_username);
String pw = App.settings.getString(App.getCtx().getString(R.string.mqtt_password), App.default_password);
boolean isValid = un.equals(username) && pw.equals(passwordString);
Log.v(DEBUG_TAG, "checkValid: " + clientId + " " + username + " -> " + isValid);
return isValid;
}
}
public static class MyAuthorizatorPolicy implements IAuthorizatorPolicy {
@Override
public boolean canWrite(Topic topic, String user, String client) {
return true;
}
@Override
public boolean canRead(Topic topic, String user, String client) {
return true;
}
}
doing
server.startServer(memoryConfig, interceptors, new MySslContextCreator(), new MyAuthenticator(), new MyAuthorizatorPolicy());
I noticed that the behavior is the same as before. If, when configuring the client, I don't add username and password, I can connect anyway. Basically, the logic in MyAuthenticator
is skipped.
The logcat whould print checkValid: CLIENT_ID USERNAME -> false
... but this happens only if I choose wrong credentials (or checkValid -> true for correct credentials).
@hylkevds can you provide any insight? Thanks.
I think I may have found how to circumvent the issue: use ALLOW_ANONYMOUS_PROPERTY_NAME.
boolean isAuthEnabled = App.settings.getBoolean(...) //get the boolean from the app's settings.
props.setProperty(BrokerConstants.ALLOW_ANONYMOUS_PROPERTY_NAME, String.valueOf(!isAuthEnabled));
...and although settings also props.setProperty(BrokerConstants.NEED_CLIENT_AUTH, String.valueOf(isAuthEnabled));
would make sense, it's irrelevant.
need_client_auth
property is used to authenticate the client's TLS certificate.
This means that if the client's certificate could be authenticated (or validated) against the CA authorities or the certificates present in the broker's trust store, it's permitted to open a connect to the server. This doesn't mean that it's authenticated in terms of MQTT protocol user/password credential pair.
That's credentials are instead validated against the authenticator provided, which could reject also if the TLS authentication was successful.
Got it. Thanks.
I close it as question. If you want to contribute describing better this in the doc, feel free to create a PR to https://github.com/moquette-io/moquette/blob/gh-pages
Expected behavior
If I enable authentication in the server config, I would expect a client to be unable to connect, unless it uses username and password.
Actual behavior
If I enable authentication in the server config, it correctly detects connection attempts with wrong username or password, refusing to connect, but if I simply disable auth in the client, it can connect anyway to the server, regardless of its auth settings.
Steps to reproduce
I'm configuring a broker server like this (platform: Android):
Let me know if I need to write down also the client configuration (
org.eclipse.paho.client.mqttv3
).Minimal yet complete reproducer code (or URL to code) or complete log file
The logs are from my app; I'm not sure what kind of logs are requested.
Here I'm publishing a message on the server's app build (it shares the connection properties with the server, they cannot be different) Maybe an unusual use case, but I have a "publisher" build of the app that enables the server and a 'publishing only' client and a "subscriber" build that is receiving messages only.
Now the "subscriber" build of the app, first connecting without authentication
here, I'm enabling auth preferences, but a wrong password is used
...and a correct password:
Moquette MQTT version
0.16
JVM version (e.g.
java -version
)11.0.15
OS version (e.g.
uname -a
)Android, the version actually doesn't matter, realdevice, or emulator. This is the emulator:
Linux localhost 4.14.175-g6f3fc9538452 #1 SMP PREEMPT Wed Apr 8 17:38:09 UTC 2020 i686