This is an automated email from the ASF dual-hosted git repository.

chenBright pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new 76ff79be enforce ServerOptions.auth for redis, mongo and rtmp requests 
(#3598)
76ff79be is described below

commit 76ff79beee74e919174d36efa1a4da00bafadd8d
Author: UB <[email protected]>
AuthorDate: Sun Oct 18 09:33:21 2026 +0530

    enforce ServerOptions.auth for redis, mongo and rtmp requests (#3598)
---
 src/brpc/global.cpp                   |  2 +-
 src/brpc/policy/mongo_protocol.cpp    | 11 +++++++++
 src/brpc/policy/mongo_protocol.h      |  3 +++
 src/brpc/policy/redis_protocol.cpp    |  9 +++++++
 src/brpc/policy/rtmp_protocol.cpp     |  9 +++++++
 test/brpc_mongo_protocol_unittest.cpp | 36 +++++++++++++++++++++++++++
 test/brpc_redis_unittest.cpp          | 46 +++++++++++++++++++++++++++++++++++
 test/brpc_rtmp_unittest.cpp           | 36 +++++++++++++++++++++++++++
 8 files changed, 151 insertions(+), 1 deletion(-)

diff --git a/src/brpc/global.cpp b/src/brpc/global.cpp
index 0a0837e0..4cce93ed 100644
--- a/src/brpc/global.cpp
+++ b/src/brpc/global.cpp
@@ -575,7 +575,7 @@ static void GlobalInitializeOrDieImpl() {
     Protocol mongo_protocol = { ParseMongoMessage,
                                 nullptr, nullptr,
                                 ProcessMongoRequest, nullptr,
-                                nullptr, nullptr, nullptr,
+                                VerifyMongoRequest, nullptr, nullptr,
                                 CONNECTION_TYPE_POOLED, "mongo" };
     if (RegisterProtocol(PROTOCOL_MONGO, mongo_protocol) != 0) {
         exit(1);
diff --git a/src/brpc/policy/mongo_protocol.cpp 
b/src/brpc/policy/mongo_protocol.cpp
index 35647d79..1f8ff82c 100644
--- a/src/brpc/policy/mongo_protocol.cpp
+++ b/src/brpc/policy/mongo_protocol.cpp
@@ -171,6 +171,17 @@ ParseResult ParseMongoMessage(butil::IOBuf* source,
     return MakeMessage(msg);
 }
 
+bool VerifyMongoRequest(const InputMessageBase* msg_base) {
+    Server* server = (Server*)msg_base->arg();
+    if (server->options().auth) {
+        // The mongo protocol carries no credential that ServerOptions.auth
+        // could verify.
+        LOG(WARNING) << "mongo does not support authentication";
+        return false;
+    }
+    return true;
+}
+
 // Defined in baidu_rpc_protocol.cpp
 void EndRunningCallMethodInPool(
     ::google::protobuf::Service* service,
diff --git a/src/brpc/policy/mongo_protocol.h b/src/brpc/policy/mongo_protocol.h
index 3b8e6c44..8d473c7e 100644
--- a/src/brpc/policy/mongo_protocol.h
+++ b/src/brpc/policy/mongo_protocol.h
@@ -31,6 +31,9 @@ ParseResult ParseMongoMessage(butil::IOBuf* source, Socket* 
socket, bool read_eo
 // Actions to a (client) request in mongo format
 void ProcessMongoRequest(InputMessageBase* msg);
 
+// Verify authentication information in mongo format
+bool VerifyMongoRequest(const InputMessageBase* msg);
+
 } // namespace policy
 } // namespace brpc
 
diff --git a/src/brpc/policy/redis_protocol.cpp 
b/src/brpc/policy/redis_protocol.cpp
index e46bed95..18dbfd6f 100644
--- a/src/brpc/policy/redis_protocol.cpp
+++ b/src/brpc/policy/redis_protocol.cpp
@@ -133,6 +133,15 @@ ParseResult ParseRedisMessage(butil::IOBuf* source, 
Socket* socket,
             // was not enabled on this port at all.
             return MakeParseError(PARSE_ERROR_TRY_OTHERS);
         }
+        if (server->options().auth != nullptr && !socket->IsAuthenticated()) {
+            // Redis commands are handled right here rather than in
+            // ProcessRedisRequest(), so they never reach InputMessenger's
+            // authentication hook, and the protocol carries no credential
+            // that ServerOptions.auth could verify.
+            LOG(WARNING) << "Reject redis request from unauthenticated "
+                         << *socket;
+            return MakeParseError(PARSE_ERROR_ABSOLUTELY_WRONG);
+        }
         RedisConnContext* ctx = 
static_cast<RedisConnContext*>(socket->parsing_context());
         if (ctx == nullptr) {
             ctx = new RedisConnContext(rs);
diff --git a/src/brpc/policy/rtmp_protocol.cpp 
b/src/brpc/policy/rtmp_protocol.cpp
index fbc0ed0e..41ed2422 100644
--- a/src/brpc/policy/rtmp_protocol.cpp
+++ b/src/brpc/policy/rtmp_protocol.cpp
@@ -3490,6 +3490,15 @@ ParseResult ParseRtmpMessage(butil::IOBuf* source, 
Socket *socket, bool read_eof
         if (!r.is_ok()) {
             return r;
         }
+        if (server->options().auth != nullptr && !socket->IsAuthenticated()) {
+            // RTMP messages are handled inside Feed() rather than in a
+            // Process function, so they never reach InputMessenger's
+            // authentication hook, and the protocol carries no credential
+            // that ServerOptions.auth could verify.
+            LOG(WARNING) << "Reject rtmp connection from unauthenticated "
+                         << *socket;
+            return MakeParseError(PARSE_ERROR_ABSOLUTELY_WRONG);
+        }
         rtmp_ctx = new RtmpContext(nullptr, server);
         socket->reset_parsing_context(rtmp_ctx);
         // We don't need to customize app_connect at server-side.
diff --git a/test/brpc_mongo_protocol_unittest.cpp 
b/test/brpc_mongo_protocol_unittest.cpp
index 3ec22566..80bc4543 100644
--- a/test/brpc_mongo_protocol_unittest.cpp
+++ b/test/brpc_mongo_protocol_unittest.cpp
@@ -67,6 +67,13 @@ class MyEchoService : public ::brpc::policy::MongoService {
 class MyContext : public ::brpc::MongoContext {
 };
 
+class MyAuthenticator : public brpc::Authenticator {
+public:
+    int GenerateCredential(std::string*) const override { return 0; }
+    int VerifyCredential(const std::string&, const butil::EndPoint&,
+                         brpc::AuthContext*) const override { return 0; }
+};
+
 class MyMongoAdaptor : public brpc::MongoServiceAdaptor {
 public:
     virtual void SerializeError(int /*response_to*/, butil::IOBuf* out_buf) 
const {
@@ -204,6 +211,35 @@ TEST_F(MongoTest, process_request_from_internal_port) {
     ASSERT_EQ(1ll, _server._nerror_bvar.get_value());
 }
 
+TEST_F(MongoTest, verify_request) {
+    brpc::mongo_head_t header = { 0, 0, 0, 0 };
+    header.op_code = brpc::MONGO_OPCODE_REPLY;
+    header.message_length = sizeof(header) + EXP_REQUEST.length();
+    butil::IOBuf total_buf;
+    total_buf.append(static_cast<const void*>(&header), sizeof(header));
+    total_buf.append(EXP_REQUEST);
+    brpc::ParseResult req_pr = brpc::policy::ParseMongoMessage(
+        &total_buf, _socket.get(), false, &_server);
+    ASSERT_EQ(brpc::PARSE_OK, req_pr.error());
+    brpc::InputMessageBase* msg = req_pr.message();
+    if (msg->_socket == nullptr) {
+        _socket->ReAddress(&msg->_socket);
+    }
+    msg->_arg = &_server;
+
+    // Without ServerOptions.auth every request passes.
+    ASSERT_TRUE(brpc::policy::VerifyMongoRequest(msg));
+
+    // The mongo protocol carries no credential for ServerOptions.auth to
+    // verify, so requests on an authenticated server must be rejected.
+    MyAuthenticator auth;
+    _server._options.auth = &auth;
+    ASSERT_FALSE(brpc::policy::VerifyMongoRequest(msg));
+    _server._options.auth = nullptr;
+
+    msg->Destroy();
+}
+
 TEST_F(MongoTest, complete_flow) {
     butil::IOBuf request_buf;
     butil::IOBuf total_buf;
diff --git a/test/brpc_redis_unittest.cpp b/test/brpc_redis_unittest.cpp
index 610ea2ea..13806681 100644
--- a/test/brpc_redis_unittest.cpp
+++ b/test/brpc_redis_unittest.cpp
@@ -1576,6 +1576,52 @@ TEST_F(RedisTest, server_is_not_served_on_internal_port) 
{
     ASSERT_EQ(0, server.Join());
 }
 
+class MyServerAuthenticator : public brpc::Authenticator {
+public:
+    int GenerateCredential(std::string*) const override { return 0; }
+    int VerifyCredential(const std::string&, const butil::EndPoint&,
+                         brpc::AuthContext*) const override { return 0; }
+};
+
+TEST_F(RedisTest, server_rejects_unauthenticated_commands) {
+    std::string password = GeneratePassword();
+    RedisServiceImpl* rsimpl = new RedisServiceImpl(password);
+    std::unique_ptr<SetCommandHandler> sh(new SetCommandHandler(rsimpl));
+    std::unique_ptr<AuthCommandHandler> ah(new AuthCommandHandler(rsimpl));
+    rsimpl->AddCommandHandler("set", sh.get());
+    rsimpl->AddCommandHandler("auth", ah.get());
+
+    // Redis commands are handled inside ParseRedisMessage() and never reach
+    // InputMessenger's authentication hook. With ServerOptions.auth set they
+    // must be rejected, like VerifyNsheadRequest/VerifyThriftRequest do for
+    // the other protocols that carry no credential.
+    MyServerAuthenticator auth;
+    brpc::Server server;
+    brpc::ServerOptions server_options;
+    server_options.redis_service = rsimpl;
+    server_options.auth = &auth;
+    brpc::PortRange pr(8081, 8900);
+    ASSERT_EQ(0, server.Start("127.0.0.1", pr, &server_options));
+
+    brpc::ChannelOptions options;
+    options.protocol = brpc::PROTOCOL_REDIS;
+    options.max_retry = 0;
+    brpc::Channel channel;
+    ASSERT_EQ(0, channel.Init(
+        "127.0.0.1", server.listen_address().port, &options));
+
+    brpc::RedisRequest request;
+    brpc::RedisResponse response;
+    brpc::Controller cntl;
+    ASSERT_TRUE(request.AddCommand("set key1 value1"));
+    channel.CallMethod(nullptr, &cntl, &request, &response, nullptr);
+    ASSERT_TRUE(cntl.Failed());
+    ASSERT_EQ(0, response.reply_size());
+
+    ASSERT_EQ(0, server.Stop(0));
+    ASSERT_EQ(0, server.Join());
+}
+
 void* incr_thread(void* arg) {
     brpc::Channel* c = static_cast<brpc::Channel*>(arg);
     for (int i = 0; i < 5000; ++i) {
diff --git a/test/brpc_rtmp_unittest.cpp b/test/brpc_rtmp_unittest.cpp
index cbc21b7e..36e996b0 100644
--- a/test/brpc_rtmp_unittest.cpp
+++ b/test/brpc_rtmp_unittest.cpp
@@ -1250,6 +1250,42 @@ TEST(RtmpTest, 
rejected_create_stream_with_response_write_failure) {
     server.Join();
 }
 
+class MyServerAuthenticator : public brpc::Authenticator {
+public:
+    int GenerateCredential(std::string*) const override { return 0; }
+    int VerifyCredential(const std::string&, const butil::EndPoint&,
+                         brpc::AuthContext*) const override { return 0; }
+};
+
+TEST(RtmpTest, reject_unauthenticated_connection) {
+    PlayingDummyService rtmp_service;
+    MyServerAuthenticator auth;
+    brpc::Server server;
+    brpc::ServerOptions server_opt;
+    server_opt.rtmp_service = &rtmp_service;
+    server_opt.auth = &auth;
+    ASSERT_EQ(0, server.Start(0, &server_opt));
+
+    brpc::SocketId socket_id;
+    brpc::SocketOptions socket_options;
+    ASSERT_EQ(0, brpc::Socket::Create(socket_options, &socket_id));
+    brpc::SocketUniquePtr socket;
+    ASSERT_EQ(0, brpc::Socket::Address(socket_id, &socket));
+
+    // RTMP messages are handled inside the parser and never reach
+    // InputMessenger's authentication hook. With ServerOptions.auth set the
+    // connection must be rejected before any handshaking happens.
+    butil::IOBuf buf;
+    buf.append("\x03", 1);  // C0 (version)
+    brpc::ParseResult pr = brpc::policy::ParseRtmpMessage(
+        &buf, socket.get(), false, &server);
+    ASSERT_EQ(brpc::PARSE_ERROR_ABSOLUTELY_WRONG, pr.error());
+    ASSERT_EQ(nullptr, socket->parsing_context());
+
+    server.Stop(0);
+    server.Join();
+}
+
 TEST(RtmpTest, successfully_play_streams) {
     PlayingDummyService rtmp_service;
     brpc::Server server;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to