Skip to content
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

[NEW] Rate limiting for user registering #23732

Merged
merged 18 commits into from
Nov 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion app/api/server/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { DDPCommon } from 'meteor/ddp-common';
import { DDP } from 'meteor/ddp';
import { Accounts } from 'meteor/accounts-base';
import { Restivus } from 'meteor/nimble:restivus';
import { RateLimiter } from 'meteor/rate-limit';
import _ from 'underscore';
import { RateLimiter } from 'meteor/rate-limit';

import { Logger } from '../../../server/lib/logger/Logger';
import { getRestPayload } from '../../../server/lib/logger/logPayloads';
Expand Down Expand Up @@ -447,6 +447,14 @@ export class APIClass extends Restivus {
});
}

updateRateLimiterDictionaryForRoute(route, numRequestsAllowed, intervalTimeInMS) {
if (rateLimiterDictionary[route]) {
rateLimiterDictionary[route].options.numRequestsAllowed = numRequestsAllowed ?? rateLimiterDictionary[route].options.numRequestsAllowed;
rateLimiterDictionary[route].options.intervalTimeInMS = intervalTimeInMS ?? rateLimiterDictionary[route].options.intervalTimeInMS;
API.v1.reloadRoutesToRefreshRateLimiter();
}
}

_initAuth() {
const loginCompatibility = (bodyParams, request) => {
// Grab the username or email that the user is logging in with
Expand Down Expand Up @@ -771,6 +779,7 @@ settings.watch('API_Enable_Rate_Limiter_Limit_Calls_Default', (value) => {
API.v1.reloadRoutesToRefreshRateLimiter();
});


settings.watch('Prometheus_API_User_Agent', (value) => {
prometheusAPIUserAgent = value;
});
13 changes: 12 additions & 1 deletion app/api/server/v1/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { setUserStatus } from '../../../../imports/users-presence/server/activeU
import { resetTOTP } from '../../../2fa/server/functions/resetTOTP';
import { Team } from '../../../../server/sdk';


API.v1.addRoute('users.create', { authRequired: true }, {
post() {
check(this.bodyParams, {
Expand Down Expand Up @@ -283,7 +284,11 @@ API.v1.addRoute('users.list', { authRequired: true }, {
},
});

API.v1.addRoute('users.register', { authRequired: false }, {
API.v1.addRoute('users.register', { authRequired: false,
ostjen marked this conversation as resolved.
Show resolved Hide resolved
rateLimiterOptions: {
numRequestsAllowed: settings.get('Rate_Limiter_Limit_RegisterUser'),
intervalTimeInMS: settings.get('API_Enable_Rate_Limiter_Limit_Time_Default'),
} }, {
post() {
if (this.userId) {
return API.v1.failure('Logged in users can not register again.');
Expand Down Expand Up @@ -944,3 +949,9 @@ API.v1.addRoute('users.logout', { authRequired: true }, {
});
},
});

settings.watch('Rate_Limiter_Limit_RegisterUser', (value) => {
const userRegisterRoute = '/api/v1/users.registerpost';

API.v1.updateRateLimiterDictionaryForRoute(userRegisterRoute, value);
});
8 changes: 6 additions & 2 deletions app/lib/server/startup/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2977,7 +2977,7 @@ settingsRegistry.addGroup('Setup_Wizard', function() {
});

settingsRegistry.addGroup('Rate Limiter', function() {
this.section('DDP Rate Limiter', function() {
this.section('DDP_Rate_Limiter', function() {
this.add('DDP_Rate_Limit_IP_Enabled', true, { type: 'boolean' });
this.add('DDP_Rate_Limit_IP_Requests_Allowed', 120000, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_IP_Enabled', value: true } });
this.add('DDP_Rate_Limit_IP_Interval_Time', 60000, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_IP_Enabled', value: true } });
Expand All @@ -2999,12 +2999,16 @@ settingsRegistry.addGroup('Rate Limiter', function() {
this.add('DDP_Rate_Limit_Connection_By_Method_Interval_Time', 10000, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_Connection_By_Method_Enabled', value: true } });
});

this.section('API Rate Limiter', function() {
this.section('API_Rate_Limiter', function() {
this.add('API_Enable_Rate_Limiter', true, { type: 'boolean' });
this.add('API_Enable_Rate_Limiter_Dev', true, { type: 'boolean', enableQuery: { _id: 'API_Enable_Rate_Limiter', value: true } });
this.add('API_Enable_Rate_Limiter_Limit_Calls_Default', 10, { type: 'int', enableQuery: { _id: 'API_Enable_Rate_Limiter', value: true } });
this.add('API_Enable_Rate_Limiter_Limit_Time_Default', 60000, { type: 'int', enableQuery: { _id: 'API_Enable_Rate_Limiter', value: true } });
});

this.section('Feature_Limiting', function() {
this.add('Rate_Limiter_Limit_RegisterUser', 1, { type: 'int', enableQuery: { _id: 'API_Enable_Rate_Limiter', value: true } });
});
});

settingsRegistry.addGroup('Troubleshoot', function() {
Expand Down
5 changes: 5 additions & 0 deletions packages/rocketchat-i18n/i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,8 @@
"API_Enable_Rate_Limiter_Dev": "Enable Rate Limiter in development",
"API_Enable_Rate_Limiter_Dev_Description": "Should limit the amount of calls to the endpoints in the development environment?",
"API_Enable_Rate_Limiter_Limit_Calls_Default": "Default number calls to the rate limiter",
"Rate_Limiter_Limit_RegisterUser": "Default number calls to the rate limiter for registering a user",
"Rate_Limiter_Limit_RegisterUser_Description": "Number of default calls for user registering endpoints(REST and real-time API's), allowed within the time range defined in the API Rate Limiter section.",
"API_Enable_Rate_Limiter_Limit_Calls_Default_Description": "Number of default calls for each endpoint of the REST API, allowed within the time range defined below",
"API_Enable_Rate_Limiter_Limit_Time_Default": "Default time limit for the rate limiter (in ms)",
"API_Enable_Rate_Limiter_Limit_Time_Default_Description": "Default timeout to limit the number of calls at each endpoint of the REST API(in ms)",
Expand All @@ -385,6 +387,7 @@
"API_Personal_Access_Tokens_Regenerate_Modal": "If you lost or forgot your token, you can regenerate it, but remember that all applications that use this token should be updated",
"API_Personal_Access_Tokens_Remove_Modal": "Are you sure you wish to remove this personal access token?",
"API_Personal_Access_Tokens_To_REST_API": "Personal access tokens to REST API",
"API_Rate_Limiter": "API Rate Limiter",
"API_Shield_Types": "Shield Types",
"API_Shield_Types_Description": "Types of shields to enable as a comma separated list, choose from `online`, `channel` or `*` for all",
"API_Shield_user_require_auth": "Require authentication for users shields",
Expand Down Expand Up @@ -1340,6 +1343,7 @@
"Days": "Days",
"DB_Migration": "Database Migration",
"DB_Migration_Date": "Database Migration Date",
"DDP_Rate_Limit": "DDP Rate Limit",
"DDP_Rate_Limit_Connection_By_Method_Enabled": "Limit by Connection per Method: enabled",
"DDP_Rate_Limit_Connection_By_Method_Interval_Time": "Limit by Connection per Method: interval time",
"DDP_Rate_Limit_Connection_By_Method_Requests_Allowed": "Limit by Connection per Method: requests allowed",
Expand Down Expand Up @@ -1837,6 +1841,7 @@
"Favorite_Rooms": "Enable Favorite Rooms",
"Favorites": "Favorites",
"Feature_Depends_on_Livechat_Visitor_navigation_as_a_message_to_be_enabled": "This feature depends on \"Send Visitor Navigation History as a Message\" to be enabled.",
"Feature_Limiting": "Feature Limiting",
"Features": "Features",
"Features_Enabled": "Features Enabled",
"Feature_Disabled": "Feature Disabled",
Expand Down
7 changes: 6 additions & 1 deletion packages/rocketchat-i18n/i18n/pt-BR.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,9 @@
"API_Enable_Rate_Limiter_Dev": "Ativar limitador de taxa em desenvolvimento",
"API_Enable_Rate_Limiter_Dev_Description": "Deve limitar a quantidade de chamadas para os endpoints no ambiente de desenvolvimento?",
"API_Enable_Rate_Limiter_Limit_Calls_Default": "Número padrão de chamadas para o limitador de taxa",
"API_Enable_Rate_Limiter_Limit_Calls_Default_Description": "Número de chamadas padrão para cada endpoint da API REST, permitido dentro do intervalo de tempo definido abaixo",
"Rate_Limiter_Limit_RegisterUser": "Número de chamadas para as endpoint de registro de usuário",
"Rate_Limiter_Limit_RegisterUser_Description": "Número de chamadas padrão para as endpoints de registro de usuário(API REST e real-time), permitido dentro do intervalo de tempo definido abaixo",
"API_Enable_Rate_Limiter_Limit_Calls_Default_Description": "Número de chamadas padrão para cada endpoint da API REST, permitido dentro do intervalo de tempo definido na seção de limitação de taxa.",
"API_Enable_Rate_Limiter_Limit_Time_Default": "Limite de tempo padrão para o limitador de taxa (em ms)",
"API_Enable_Rate_Limiter_Limit_Time_Default_Description": "Tempo limite padrão para limitar o número de chamadas em cada endpoint da API REST (em ms)",
"API_Enable_Shields": "Ativar Protetores",
Expand All @@ -385,6 +387,7 @@
"API_Personal_Access_Tokens_Regenerate_Modal": "Se perdeu ou esqueceu o seu código, pode recuperá-lo, mas lembre-se de que todos os aplicativos que usam esse código devem ser atualizados",
"API_Personal_Access_Tokens_Remove_Modal": "Tem certeza de que deseja remover este código de acesso pessoal?",
"API_Personal_Access_Tokens_To_REST_API": "Código de acesso pessoal para API REST",
"API_Rate_Limiter": "Limitação de Taxa de API",
"API_Shield_Types": "Tipos de escudo",
"API_Shield_Types_Description": "Tipos de escudos para habilitar como uma lista separada por vírgulas, escolha entre `online`, `channel` ou `*` para todos",
"API_Shield_user_require_auth": "Exigir autenticaçāo para escudos de usuários",
Expand Down Expand Up @@ -1340,6 +1343,7 @@
"Days": "Dias",
"DB_Migration": "Migração de Banco de Dados",
"DB_Migration_Date": "Data da migração do banco de dados",
"DDP_Rate_Limit": "Limitação de Taxa de DDP",
"DDP_Rate_Limit_Connection_By_Method_Enabled": "Limite por Conexão por Método: Habilitado",
"DDP_Rate_Limit_Connection_By_Method_Interval_Time": "Limite por Conexão por Método: Intervalo de tempo",
"DDP_Rate_Limit_Connection_By_Method_Requests_Allowed": "Limite por Conexão por Método: Solicitações permitidas",
Expand Down Expand Up @@ -1838,6 +1842,7 @@
"Favorites": "Favoritos",
"Feature_Depends_on_Livechat_Visitor_navigation_as_a_message_to_be_enabled": "Esse recurso depende de \"Enviar histórico de navegação do visitante como uma mensagem\" para ser ativado.",
"Features": "Funcionalidades",
"Feature_Limiting": "Limitação de funcionalidades",
"Features_Enabled": "Funcionalidades habilitadas",
"Feature_Disabled": "Funcionalidade desabilitada",
"Federation": "Federação",
Expand Down
18 changes: 17 additions & 1 deletion server/methods/registerUser.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import { Accounts } from 'meteor/accounts-base';
import s from 'underscore.string';
import { DDPRateLimiter } from 'meteor/ddp-rate-limiter';

import { Users } from '../../app/models';
import { settings } from '../../app/settings';
import { validateEmailDomain, passwordPolicy } from '../../app/lib';
import { validateEmailDomain, passwordPolicy, RateLimiter } from '../../app/lib';
import { validateInviteToken } from '../../app/invites/server/functions/validateInviteToken';

Meteor.methods({
Expand Down Expand Up @@ -97,3 +98,18 @@ Meteor.methods({
return userId;
},
});

let registerUserRuleId = RateLimiter.limitMethod('registerUser',
settings.get('Rate_Limiter_Limit_RegisterUser'),
settings.get('API_Enable_Rate_Limiter_Limit_Time_Default'), {
userId() { return true; },
});


settings.watch('Rate_Limiter_Limit_RegisterUser', (value) => {
// remove old DDP rate limiter rule and create a new one with the updated setting value
DDPRateLimiter.removeRule(registerUserRuleId);
registerUserRuleId = RateLimiter.limitMethod('registerUser', value, settings.get('API_Enable_Rate_Limiter_Limit_Time_Default'), {
userId() { return true; },
});
});