2222#include < grpcpp/ext/proto_server_reflection_plugin.h>
2323#include < grpcpp/grpcpp.h>
2424#include < grpcpp/health_check_service_interface.h>
25+ #include < grpcpp/support/server_interceptor.h>
2526#include < regex>
2627#include < atomic>
28+ #include < cstdlib>
2729#include < mutex>
2830#include < signal.h>
2931#include < thread>
@@ -37,6 +39,72 @@ using grpc::Server;
3739using grpc::ServerBuilder;
3840using grpc::ServerContext;
3941using grpc::Status;
42+
43+ // gRPC bearer token auth interceptor for distributed mode.
44+ // Reads LOCALAI_GRPC_AUTH_TOKEN from the environment. When set, rejects
45+ // requests without a matching "authorization: Bearer <token>" metadata header.
46+ class TokenAuthInterceptor : public grpc ::experimental::Interceptor {
47+ public:
48+ explicit TokenAuthInterceptor (grpc::experimental::ServerRpcInfo* info, const std::string& token)
49+ : token_(token) {}
50+
51+ void Intercept (grpc::experimental::InterceptorBatchMethods* methods) override {
52+ if (methods->QueryInterceptionHookPoint (
53+ grpc::experimental::InterceptionHookPoint::POST_RECV_INITIAL_METADATA )) {
54+ auto * metadata = methods->GetRecvInitialMetadata ();
55+ bool authenticated = false ;
56+ if (metadata) {
57+ auto it = metadata->find (" authorization" );
58+ if (it != metadata->end ()) {
59+ std::string expected = " Bearer " + token_;
60+ std::string got (it->second .data (), it->second .size ());
61+ // Constant-time comparison
62+ if (expected.size () == got.size () &&
63+ CRYPTO_memcmp (expected.data (), got.data (), expected.size ()) == 0 ) {
64+ authenticated = true ;
65+ }
66+ }
67+ }
68+ if (!authenticated) {
69+ methods->FailHijackedSendMessage ();
70+ // Hijack the RPC and return UNAUTHENTICATED
71+ methods->Hijack ();
72+ methods->ModifySendStatus (grpc::Status (grpc::StatusCode::UNAUTHENTICATED , " invalid token" ));
73+ methods->Proceed ();
74+ return ;
75+ }
76+ }
77+ methods->Proceed ();
78+ }
79+
80+ private:
81+ std::string token_;
82+
83+ // Minimal constant-time comparison (avoids OpenSSL dependency)
84+ static int CRYPTO_memcmp (const void * a, const void * b, size_t n) {
85+ const unsigned char * pa = static_cast <const unsigned char *>(a);
86+ const unsigned char * pb = static_cast <const unsigned char *>(b);
87+ unsigned char result = 0 ;
88+ for (size_t i = 0 ; i < n; i++) {
89+ result |= pa[i] ^ pb[i];
90+ }
91+ return result;
92+ }
93+ };
94+
95+ class TokenAuthInterceptorFactory : public grpc ::experimental::ServerInterceptorFactoryInterface {
96+ public:
97+ explicit TokenAuthInterceptorFactory (const std::string& token) : token_(token) {}
98+
99+ grpc::experimental::Interceptor* CreateServerInterceptor (
100+ grpc::experimental::ServerRpcInfo* info) override {
101+ return new TokenAuthInterceptor (info, token_);
102+ }
103+
104+ private:
105+ std::string token_;
106+ };
107+
40108// END LocalAI
41109
42110
@@ -2765,6 +2833,16 @@ int main(int argc, char** argv) {
27652833 builder.SetMaxMessageSize (50 * 1024 * 1024 ); // 50MB
27662834 builder.SetMaxSendMessageSize (50 * 1024 * 1024 ); // 50MB
27672835 builder.SetMaxReceiveMessageSize (50 * 1024 * 1024 ); // 50MB
2836+
2837+ // Add bearer token auth interceptor if LOCALAI_GRPC_AUTH_TOKEN is set
2838+ const char * auth_token = std::getenv (" LOCALAI_GRPC_AUTH_TOKEN" );
2839+ if (auth_token != nullptr && auth_token[0 ] != ' \0 ' ) {
2840+ std::vector<std::unique_ptr<grpc::experimental::ServerInterceptorFactoryInterface>> creators;
2841+ creators.push_back (std::make_unique<TokenAuthInterceptorFactory>(auth_token));
2842+ builder.experimental ().SetInterceptorCreators (std::move (creators));
2843+ std::cout << " gRPC auth enabled via LOCALAI_GRPC_AUTH_TOKEN" << std::endl;
2844+ }
2845+
27682846 std::unique_ptr<Server> server (builder.BuildAndStart ());
27692847 // run the HTTP server in a thread - see comment below
27702848 std::thread t ([&]()
0 commit comments