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]