@@ -175,7 +175,7 @@ func StartConsoleServer(logger *zap.Logger, startupLogger *zap.Logger, db *sql.D
175
175
serverOpts := []grpc.ServerOption {
176
176
//grpc.StatsHandler(&ocgrpc.ServerHandler{IsPublicEndpoint: true}),
177
177
grpc .MaxRecvMsgSize (int (config .GetConsole ().MaxMessageSizeBytes )),
178
- grpc .UnaryInterceptor (consoleInterceptorFunc (logger , config , consoleSessionCache )),
178
+ grpc .UnaryInterceptor (consoleInterceptorFunc (logger , config , consoleSessionCache , loginAttemptCache )),
179
179
}
180
180
grpcServer := grpc .NewServer (serverOpts ... )
181
181
@@ -438,7 +438,7 @@ func (s *ConsoleServer) Stop() {
438
438
s .grpcServer .GracefulStop ()
439
439
}
440
440
441
- func consoleInterceptorFunc (logger * zap.Logger , config Config , sessionCache SessionCache ) func (context.Context , interface {}, * grpc.UnaryServerInfo , grpc.UnaryHandler ) (interface {}, error ) {
441
+ func consoleInterceptorFunc (logger * zap.Logger , config Config , sessionCache SessionCache , loginAttmeptCache LoginAttemptCache ) func (context.Context , interface {}, * grpc.UnaryServerInfo , grpc.UnaryHandler ) (interface {}, error ) {
442
442
return func (ctx context.Context , req interface {}, info * grpc.UnaryServerInfo , handler grpc.UnaryHandler ) (interface {}, error ) {
443
443
if info .FullMethod == "/nakama.console.Console/Authenticate" {
444
444
// Skip authentication check for Login endpoint.
@@ -464,7 +464,7 @@ func consoleInterceptorFunc(logger *zap.Logger, config Config, sessionCache Sess
464
464
return nil , status .Error (codes .Unauthenticated , "Console authentication required." )
465
465
}
466
466
467
- if ctx , ok = checkAuth (ctx , config , auth [0 ], sessionCache ); ! ok {
467
+ if ctx , ok = checkAuth (ctx , logger , config , auth [0 ], sessionCache , loginAttmeptCache ); ! ok {
468
468
return nil , status .Error (codes .Unauthenticated , "Console authentication invalid." )
469
469
}
470
470
role := ctx .Value (ctxConsoleRoleKey {}).(console.UserRole )
@@ -478,7 +478,7 @@ func consoleInterceptorFunc(logger *zap.Logger, config Config, sessionCache Sess
478
478
}
479
479
}
480
480
481
- func checkAuth (ctx context.Context , config Config , auth string , sessionCache SessionCache ) (context.Context , bool ) {
481
+ func checkAuth (ctx context.Context , logger * zap. Logger , config Config , auth string , sessionCache SessionCache , loginAttemptCache LoginAttemptCache ) (context.Context , bool ) {
482
482
const basicPrefix = "Basic "
483
483
const bearerPrefix = "Bearer "
484
484
@@ -488,9 +488,24 @@ func checkAuth(ctx context.Context, config Config, auth string, sessionCache Ses
488
488
if ! ok {
489
489
return ctx , false
490
490
}
491
-
492
- if username != config .GetConsole ().Username || password != config .GetConsole ().Password {
493
- // Username and/or password do not match.
491
+ ip , _ := extractClientAddressFromContext (logger , ctx )
492
+ if ! loginAttemptCache .Allow (username , ip ) {
493
+ return ctx , false
494
+ }
495
+ if username == config .GetConsole ().Username {
496
+ if password != config .GetConsole ().Password {
497
+ // Admin password does not match.
498
+ if lockout , until := loginAttemptCache .Add (config .GetConsole ().Username , ip ); lockout != LockoutTypeNone {
499
+ switch lockout {
500
+ case LockoutTypeAccount :
501
+ logger .Info (fmt .Sprintf ("Console admin account locked until %v." , until ))
502
+ case LockoutTypeIp :
503
+ logger .Info (fmt .Sprintf ("Console admin IP locked until %v." , until ))
504
+ }
505
+ }
506
+ return ctx , false
507
+ }
508
+ } else {
494
509
return ctx , false
495
510
}
496
511
0 commit comments