mirror of
https://framagit.org/les/gancio.git
synced 2025-02-01 00:52:01 +01:00
introduce a new instance api rate limiter
This commit is contained in:
parent
c54630a2f4
commit
a9c9fd38a5
5 changed files with 47 additions and 22 deletions
|
@ -689,7 +689,7 @@ const eventController = {
|
||||||
...pagination,
|
...pagination,
|
||||||
replacements
|
replacements
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
log.error('[EVENT]', e)
|
log.error('[EVENT]' + String(e))
|
||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
const rateLimit = require('express-rate-limit');
|
|
||||||
const log = require('../../log')
|
const log = require('../../log')
|
||||||
const nominatim = require('../../services/geocoding/nominatim')
|
const nominatim = require('../../services/geocoding/nominatim')
|
||||||
const photon = require('../../services/geocoding/photon')
|
const photon = require('../../services/geocoding/photon')
|
||||||
|
@ -8,15 +7,6 @@ let d = 0 // departure time
|
||||||
let h = 0 // hit geocoding provider time (aka Latency)
|
let h = 0 // hit geocoding provider time (aka Latency)
|
||||||
|
|
||||||
const geocodingController = {
|
const geocodingController = {
|
||||||
/**
|
|
||||||
* TODO: replace/merge with a general 'instance rate-limiter' or 'instance api-related rate-limiter' when this will be defined
|
|
||||||
*/
|
|
||||||
instanceApiRateLimiter: rateLimit({
|
|
||||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
||||||
max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
|
|
||||||
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
|
|
||||||
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
|
||||||
}),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit provider api usage.
|
* Limit provider api usage.
|
||||||
|
|
|
@ -3,8 +3,6 @@ const { Place, Event } = require('../models/models')
|
||||||
const eventController = require('./event')
|
const eventController = require('./event')
|
||||||
const exportController = require('./export')
|
const exportController = require('./export')
|
||||||
|
|
||||||
const { version } = require('../../../package.json')
|
|
||||||
|
|
||||||
const log = require('../../log')
|
const log = require('../../log')
|
||||||
const { Op, where, col, fn, cast } = require('sequelize')
|
const { Op, where, col, fn, cast } = require('sequelize')
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ const oauthController = require('./controller/oauth')
|
||||||
const announceController = require('./controller/announce')
|
const announceController = require('./controller/announce')
|
||||||
const pluginController = require('./controller/plugins')
|
const pluginController = require('./controller/plugins')
|
||||||
const geocodingController = require('./controller/geocoding')
|
const geocodingController = require('./controller/geocoding')
|
||||||
|
const { DDOSProtectionApiRateLimiter, SPAMProtectionApiRateLimiter } = require('./limiter')
|
||||||
const helpers = require('../helpers')
|
const helpers = require('../helpers')
|
||||||
const storage = require('./storage')
|
const storage = require('./storage')
|
||||||
|
|
||||||
|
@ -30,6 +31,10 @@ module.exports = () => {
|
||||||
api.use(express.urlencoded({ extended: false }))
|
api.use(express.urlencoded({ extended: false }))
|
||||||
api.use(express.json())
|
api.use(express.json())
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'test') {
|
||||||
|
api.use(DDOSProtectionApiRateLimiter)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (config.status !== 'READY') {
|
if (config.status !== 'READY') {
|
||||||
|
|
||||||
|
@ -66,12 +71,12 @@ module.exports = () => {
|
||||||
api.get('/ping', (_req, res) => res.sendStatus(200))
|
api.get('/ping', (_req, res) => res.sendStatus(200))
|
||||||
api.get('/user', isAuth, (req, res) => res.json(req.user))
|
api.get('/user', isAuth, (req, res) => res.json(req.user))
|
||||||
|
|
||||||
api.post('/user/recover', userController.forgotPassword)
|
api.post('/user/recover', SPAMProtectionApiRateLimiter, userController.forgotPassword)
|
||||||
api.post('/user/check_recover_code', userController.checkRecoverCode)
|
api.post('/user/check_recover_code', userController.checkRecoverCode)
|
||||||
api.post('/user/recover_password', userController.updatePasswordWithRecoverCode)
|
api.post('/user/recover_password', SPAMProtectionApiRateLimiter, userController.updatePasswordWithRecoverCode)
|
||||||
|
|
||||||
// register and add users
|
// register and add users
|
||||||
api.post('/user/register', userController.register)
|
api.post('/user/register', SPAMProtectionApiRateLimiter, userController.register)
|
||||||
api.post('/user', isAdmin, userController.create)
|
api.post('/user', isAdmin, userController.create)
|
||||||
|
|
||||||
// update user
|
// update user
|
||||||
|
@ -127,7 +132,7 @@ module.exports = () => {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// allow anyone to add an event (anon event has to be confirmed, TODO: flood protection)
|
// allow anyone to add an event (anon event has to be confirmed, TODO: flood protection)
|
||||||
api.post('/event', eventController.isAnonEventAllowed, upload.single('image'), eventController.add)
|
api.post('/event', eventController.isAnonEventAllowed, SPAMProtectionApiRateLimiter, upload.single('image'), eventController.add)
|
||||||
|
|
||||||
// api.get('/event/search', eventController.search)
|
// api.get('/event/search', eventController.search)
|
||||||
|
|
||||||
|
@ -141,8 +146,8 @@ module.exports = () => {
|
||||||
api.get('/event/meta', eventController.searchMeta)
|
api.get('/event/meta', eventController.searchMeta)
|
||||||
|
|
||||||
// add event notification TODO
|
// add event notification TODO
|
||||||
api.post('/event/notification', eventController.addNotification)
|
// api.post('/event/notification', eventController.addNotification)
|
||||||
api.delete('/event/notification/:code', eventController.delNotification)
|
// api.delete('/event/notification/:code', eventController.delNotification)
|
||||||
|
|
||||||
api.post('/settings', isAdmin, settingsController.setRequest)
|
api.post('/settings', isAdmin, settingsController.setRequest)
|
||||||
api.get('/settings', isAdmin, settingsController.getAll)
|
api.get('/settings', isAdmin, settingsController.getAll)
|
||||||
|
@ -173,8 +178,8 @@ module.exports = () => {
|
||||||
api.put('/place', isAdmin, placeController.updatePlace)
|
api.put('/place', isAdmin, placeController.updatePlace)
|
||||||
|
|
||||||
// - GEOCODING
|
// - GEOCODING
|
||||||
api.get('/placeOSM/Nominatim/:place_details', helpers.isGeocodingEnabled, geocodingController.instanceApiRateLimiter, geocodingController.nominatimRateLimit, geocodingController._nominatim)
|
api.get('/placeOSM/Nominatim/:place_details', helpers.isGeocodingEnabled, geocodingController.nominatimRateLimit, geocodingController._nominatim)
|
||||||
api.get('/placeOSM/Photon/:place_details', helpers.isGeocodingEnabled, geocodingController.instanceApiRateLimiter, geocodingController.photonRateLimit, geocodingController._photon)
|
api.get('/placeOSM/Photon/:place_details', helpers.isGeocodingEnabled, geocodingController.photonRateLimit, geocodingController._photon)
|
||||||
|
|
||||||
// - TAGS
|
// - TAGS
|
||||||
api.get('/tags', isAdmin, tagController.getAll)
|
api.get('/tags', isAdmin, tagController.getAll)
|
||||||
|
@ -215,7 +220,7 @@ module.exports = () => {
|
||||||
// OAUTH
|
// OAUTH
|
||||||
api.get('/clients', isAuth, oauthController.getClients)
|
api.get('/clients', isAuth, oauthController.getClients)
|
||||||
api.get('/client/:client_id', isAuth, oauthController.getClient)
|
api.get('/client/:client_id', isAuth, oauthController.getClient)
|
||||||
api.post('/client', oauthController.createClient)
|
api.post('/client', SPAMProtectionApiRateLimiter, oauthController.createClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
api.use((_req, res) => res.sendStatus(404))
|
api.use((_req, res) => res.sendStatus(404))
|
||||||
|
|
32
server/api/limiter.js
Normal file
32
server/api/limiter.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
const rateLimit = require('express-rate-limit')
|
||||||
|
const log = require('../log')
|
||||||
|
|
||||||
|
const next = (req, res, next) => next()
|
||||||
|
|
||||||
|
const instanceApiRateLimiter = {
|
||||||
|
|
||||||
|
DDOSProtectionApiRateLimiter: (process.env.NODE_ENV === 'test' ? next : rateLimit({
|
||||||
|
windowMs: 60 * 1000, // 5 minutes
|
||||||
|
max: 100, // Limit each IP to 100 requests per `window` (here, per 5 minutes)
|
||||||
|
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
|
||||||
|
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
||||||
|
handler: (request, response, next, options) => {
|
||||||
|
log.warn(`DDOS protection api rate limiter: > 100req/minute/ip ${request.ip}`)
|
||||||
|
return response.status(options.statusCode).send(options.message)
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
|
||||||
|
SPAMProtectionApiRateLimiter: (process.env.NODE_ENV === 'test' ? next : rateLimit({
|
||||||
|
windowMs: 5 * 60 * 1000, // 10 minutes
|
||||||
|
max: 3, // Limit each IP to 3 requests per `window` (here, per 15 minutes)
|
||||||
|
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
|
||||||
|
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
||||||
|
handler: (request, response, next, options) => {
|
||||||
|
log.warn(`SPAM protection api rate limiter: 3req/5min/ip ${request.ip}`)
|
||||||
|
return response.status(options.statusCode).send(options.message)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = instanceApiRateLimiter
|
Loading…
Reference in a new issue