-
Notifications
You must be signed in to change notification settings - Fork 110
Authentication and authorization of pub/sub #896
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
535039c
to
e923cc2
Compare
I closed the PR because it seems the build / checks seem to get triggered on your account as well. |
Codecov Report
@@ Coverage Diff @@
## master #896 +/- ##
==========================================
+ Coverage 83.05% 83.23% +0.17%
==========================================
Files 63 64 +1
Lines 9929 10310 +381
==========================================
+ Hits 8247 8582 +335
- Misses 1682 1728 +46 |
@redboltz Progress on the authentication/authorization is quite good. I would like to implement also the certificate based authentication. But for this, i would need access to the asio ssl socket to enable the certificate callback on a specific connection (set_verify_mode / set_verify_callback). How can I configure this callback for a specific connection ? |
Sounds good!
I think that "for a specific connection" is difficult. mqtt_cpp/example/tls_both_client_cert.cpp Lines 347 to 376 in bed6d65
The server applies client_cert authentication for all incoming connections.
I think that 2 is better. But I'm not sure it is possible. |
You can also set the verify_callback on a specific stream: That way you can use the certificate on the specific stream. I know this is possible, i have done this in another (not opensource) project. Users would login based on their client client certificate. However I need access to ssl_stream after the server has accepted the new connection. |
I'm not sure the following getter can be used for the purpose: mqtt_cpp/include/mqtt/server.hpp Lines 351 to 365 in 6c76ccb
If it is possible, how to decide? Server has no information about client_cert or not. |
After the accept of the connection has occurred: The set_verify_callback needs to be called on the tls stream of the specific connection. This should be done before the handshake is performed on the connection: mqtt_cpp/include/mqtt/server.hpp Lines 399 to 411 in abd28e4
So I guess I need to add in server.hpp, i may understand now. |
I don't understand what you mean yet. Line 19 in bed6d65
Am I missing something? I think that you want to implement mixed TLS connections. How to mix the two authentication ?? My question is that. |
Maybe I understand what is the reason of confusion. I guess you think that load_verify_file should be set for each incoming connections. mqtt_cpp/include/mqtt/server.hpp Line 358 in bed6d65
https://github.com/redboltz/mqtt_cpp/blob/bed6d658ad3502caebc532c01ac397553f78ef7e/example/tls_both_client_cert.cpp |
The actual client socket is part of boost::asio::ssl::verify_context in the callback of the server? |
My idea was to pass on the CNAME as the username to the mqtt login. |
It's https://www.boost.org/doc/libs/1_77_0/doc/html/boost_asio/reference/ssl__stream.html |
Yes some of customizable field (default could be CNAME) is used as UserName in the broker. If there is no way to distinguish them, the solution is always continue UserName and Password authentication. |
But how to get the CNAME into broker, such that is used as default value for login ? Using map with socket as key? (socket -> cname) |
Now, I understand about the difficulty.
I think that we need to add something to They stores context. mqtt_cpp/include/mqtt/server.hpp Line 427 in 7ac3e0c
class server_tls {
public:
using verify_callback_t =
std::function<void(
bool,
boost::asio::ssl::verify_context&,
socket_t // additional parameter
>;
void set_verify_callback(verify_callback_t cb) {
verify_cb_ = force_move(cb); // just store the callback
}
private:
verify_callback_t verify_cb_;
} And at mqtt_cpp/include/mqtt/server.hpp Line 371 in 7ac3e0c
auto socket = std::make_shared<socket_t>(ioc_con, ctx_);
ctx_.set_verify_callback(
[this, socket] // copy capture socket shared_ptr
(bool preverified, boost::asio::ssl::verify_context& ctx) {
if (verify_cb_) {
return verify_cb_(preverified, ctx, force_move(socket));
}
else {
return false;
}
}
);
} Now, broker can access to the socket (shared_ptr). I think that this is similar approach you mentioned (I'm not 100% sure) |
I noticed that even if we provide set_verify_callback wrapper (socket parameter version), the callback setter (broker) needs to find corresponding endpoint by the socket. Maybe map is needed. It is not efficient. I have a question. boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12);
// ...
ctx.set_verify_callback(
[]
(bool preverified,
boost::asio::ssl::verify_context& vctx) {
std::cout << "[clicrt] preverified:" << std::boolalpha << preverified << std::endl;
if (!preverified) return false;
int depth = X509_STORE_CTX_get_error_depth(vctx.native_handle());
std::cout << "[clicrt] depth:" << depth << std::endl;
if (depth > 0) return true;
X509* cert = X509_STORE_CTX_get_current_cert(vctx.native_handle());
X509_NAME* name = X509_get_subject_name(cert);
std::string cname;
cname.resize(0xffff);
auto size = X509_NAME_get_text_by_NID(name, NID_commonName, &cname[0], static_cast<int>(cname.size()));
cname.resize(static_cast<std::size_t>(size));
std::cout << "[clicrt] CNAME:" << cname << std::endl;
return true;
}
); We can extract CNAME from vctx in the varify callback. |
No, map is not a very nice solution. Other possibility is to store it as metadata with the socket. From a socket you can obtain the certificate information. For TCP socket and TLS socket without certificate, this information is not available. |
i am not sure, but i don't think that is possible |
How about this ? At mqtt_cpp/include/mqtt/server.hpp Line 371 in 7ac3e0c
Capture username to mqtt_cpp/include/mqtt/server.hpp Line 408 in 7ac3e0c
sp->set_preauthed_user_name(*username); And add endpoint to public:
void set_preauthed_user_name(std::string user_name) {
preauthed_user_name_.emplace(force_move(user_name));
}
optional<std::string> get_preauthed_user_name() const {
return preauthed_user_name_;
}
private:
optional<std::string> preauthed_user_name_; And from the broker, mqtt_cpp/include/mqtt/broker/broker.hpp Lines 1003 to 1014 in 7ac3e0c
if (auto preauthed_user_name_opt = ep.get_preauthed_user_name()) {
// Got already authenticated UserName
} |
Yes, i do think that may be the best option. |
Ok, please go forward on this way. |
I have it working, only thing is, when CNAME does not match any username in the auth file:
|
Thanks. Invalid login is good because the client already indicated client_cert authentication. It is important that if client doesn't set client cert, then anonymous or username and password authentication should work. |
In order to do that, the broker needs to distinguish something like the following:
However, I don't know it can be. But if asio cannot distinguish 1 and 2, then , continue to username and password authentication. It is acceptable compromization. |
I think this has to do with the preverified boolean, but not sure how this works. I think this is behaviour that is happening now:
|
bool preverified, // True if the certificate passed pre-verification. I understand as follows:
So the first two can't be distinguished. It is OK. I think that the latter is better. Because the error reporting is unified to password authentication and keep simple implementation on server_tls and server_tls_ws. |
Can you try again ? I think this is because the peer was getting validated, but tls_client has no certificate. |
I got the same result. Still tls_client cannot connect to broker but can connect tls_server. |
When I remove auth.json but config set auth.json (by default) then, I got infinity loop at the following point:
|
That was the reason of the error
I place auth.json. Then I got the same error. When I execute tls_client again, then I got
It seems that it is expected result. I run tls_client again and again, then I got expected result. |
I just create #920. It adds client certificate related options to command option example: using client certificate
using username and password
|
#920 merged |
|
That is strange. Maybe related to asio tls handling? |
I was not able to reproduce this, but I added an error message in the broker when the auth file was not opened/loaded. |
maybe this is related ? |
Hmm, I'm not sure. On 29a8519 , the problem still happens.
Only broker first time is ng. So I guess that the difference between tls_server and broker has a hint for analize. Is this reproduced on your environment? |
Yes, I see the same behaviour under windows / msvc |
My environment is I guess that it is not an environmental problem. |
I think the issue is in tls_client. If I use mqttbox as client, it works first time. Maybe try mosquitto client also. |
Also on linux (ubuntu) I don't see this issue. It connects the first time .. |
Maybe I found the problem on the broker.cpp. Now re-checking. |
Something uninitialised? |
Fixed. The first |
I will have a look |
I made a separate listen for all server types, to be consistent. This fix seems to work. |
Confirmed. Thanks. |
Co-authored-by: Takatoshi Kondo <redboltz@gmail.com>
Confirmed. I think that the PR is ready to merge except cording style fix. |
I have working:
https://github.com/redboltz/mqtt_cpp/pull/896/files#diff-44ba049abad6813622f466f2db254566065420da4d9a478215ec06979243b7eaR130-R147
Still to do: