]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Fix runner api rate limit bypass
authorChocobozzz <me@florianbigard.com>
Tue, 20 Jun 2023 12:17:34 +0000 (14:17 +0200)
committerChocobozzz <me@florianbigard.com>
Tue, 20 Jun 2023 12:17:34 +0000 (14:17 +0200)
26 files changed:
server/controllers/api/abuse.ts
server/controllers/api/accounts.ts
server/controllers/api/blocklist.ts
server/controllers/api/bulk.ts
server/controllers/api/config.ts
server/controllers/api/custom-page.ts
server/controllers/api/index.ts
server/controllers/api/jobs.ts
server/controllers/api/metrics.ts
server/controllers/api/oauth-clients.ts
server/controllers/api/overviews.ts
server/controllers/api/plugins.ts
server/controllers/api/runners/index.ts
server/controllers/api/runners/jobs-files.ts
server/controllers/api/runners/jobs.ts
server/controllers/api/runners/manage-runners.ts
server/controllers/api/runners/registration-tokens.ts
server/controllers/api/search/index.ts
server/controllers/api/server/index.ts
server/controllers/api/users/index.ts
server/controllers/api/video-channel-sync.ts
server/controllers/api/video-channel.ts
server/controllers/api/video-playlist.ts
server/controllers/api/videos/index.ts
server/middlewares/rate-limiter.ts
server/tests/api/runners/runner-common.ts

index d6211cc83e807a0c7591b61564f841c9fbf062ee..d582f198d1cf9e16e23a72734622184dea2451da 100644 (file)
@@ -16,6 +16,7 @@ import {
   abusesSortValidator,
   abuseUpdateValidator,
   addAbuseMessageValidator,
+  apiRateLimiter,
   asyncMiddleware,
   asyncRetryTransactionMiddleware,
   authenticate,
@@ -32,6 +33,8 @@ import { AccountModel } from '../../models/account/account'
 
 const abuseRouter = express.Router()
 
+abuseRouter.use(apiRateLimiter)
+
 abuseRouter.get('/',
   openapiOperationDoc({ operationId: 'getAbuses' }),
   authenticate,
index 2d86d393ceae5067a298e0b20ca61da76993f28b..96f36bf6fe9db79225516eb691b791c77adac9ed 100644 (file)
@@ -9,6 +9,7 @@ import { getFormattedObjects } from '../../helpers/utils'
 import { JobQueue } from '../../lib/job-queue'
 import { Hooks } from '../../lib/plugins/hooks'
 import {
+  apiRateLimiter,
   asyncMiddleware,
   authenticate,
   commonVideosFiltersValidator,
@@ -41,6 +42,8 @@ import { VideoPlaylistModel } from '../../models/video/video-playlist'
 
 const accountsRouter = express.Router()
 
+accountsRouter.use(apiRateLimiter)
+
 accountsRouter.get('/',
   paginationValidator,
   accountsSortValidator,
index 1e936ad106d458d9b367701f61448866aca35134..dee12b108cdc8f7a441f056770a9f16f9b7df30f 100644 (file)
@@ -1,15 +1,17 @@
 import express from 'express'
 import { handleToNameAndHost } from '@server/helpers/actors'
+import { logger } from '@server/helpers/logger'
 import { AccountBlocklistModel } from '@server/models/account/account-blocklist'
 import { getServerActor } from '@server/models/application/application'
 import { ServerBlocklistModel } from '@server/models/server/server-blocklist'
 import { MActorAccountId, MUserAccountId } from '@server/types/models'
 import { BlockStatus } from '@shared/models'
-import { asyncMiddleware, blocklistStatusValidator, optionalAuthenticate } from '../../middlewares'
-import { logger } from '@server/helpers/logger'
+import { apiRateLimiter, asyncMiddleware, blocklistStatusValidator, optionalAuthenticate } from '../../middlewares'
 
 const blocklistRouter = express.Router()
 
+blocklistRouter.use(apiRateLimiter)
+
 blocklistRouter.get('/status',
   optionalAuthenticate,
   blocklistStatusValidator,
index 51292175bfc31497fd8a993e9bedd9a3a6cb748f..c41c7d378743c5c371923d6795b2d1e870ba3f4c 100644 (file)
@@ -4,10 +4,12 @@ import { bulkRemoveCommentsOfValidator } from '@server/middlewares/validators/bu
 import { VideoCommentModel } from '@server/models/video/video-comment'
 import { HttpStatusCode } from '@shared/models'
 import { BulkRemoveCommentsOfBody } from '@shared/models/bulk/bulk-remove-comments-of-body.model'
-import { asyncMiddleware, authenticate } from '../../middlewares'
+import { apiRateLimiter, asyncMiddleware, authenticate } from '../../middlewares'
 
 const bulkRouter = express.Router()
 
+bulkRouter.use(apiRateLimiter)
+
 bulkRouter.post('/remove-comments-of',
   authenticate,
   asyncMiddleware(bulkRemoveCommentsOfValidator),
index 3b6230f4acdd2df3b4900246f393faf83d077945..228eae109ac485f09601204085cb564697115f4e 100644 (file)
@@ -8,11 +8,13 @@ import { auditLoggerFactory, CustomConfigAuditView, getAuditIdFromRes } from '..
 import { objectConverter } from '../../helpers/core-utils'
 import { CONFIG, reloadConfig } from '../../initializers/config'
 import { ClientHtml } from '../../lib/client-html'
-import { asyncMiddleware, authenticate, ensureUserHasRight, openapiOperationDoc } from '../../middlewares'
+import { apiRateLimiter, asyncMiddleware, authenticate, ensureUserHasRight, openapiOperationDoc } from '../../middlewares'
 import { customConfigUpdateValidator, ensureConfigIsEditable } from '../../middlewares/validators/config'
 
 const configRouter = express.Router()
 
+configRouter.use(apiRateLimiter)
+
 const auditLogger = auditLoggerFactory('config')
 
 configRouter.get('/',
index d1c672f3fc0a62f6a637caeaafe5dfbc0717dd82..f4e1a0e79a6ad2bd90c3a088dcfcd983bbd9052f 100644 (file)
@@ -2,10 +2,12 @@ import express from 'express'
 import { ServerConfigManager } from '@server/lib/server-config-manager'
 import { ActorCustomPageModel } from '@server/models/account/actor-custom-page'
 import { HttpStatusCode, UserRight } from '@shared/models'
-import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares'
+import { apiRateLimiter, asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares'
 
 const customPageRouter = express.Router()
 
+customPageRouter.use(apiRateLimiter)
+
 customPageRouter.get('/homepage/instance',
   asyncMiddleware(getInstanceHomepage)
 )
index 646f9597e79ba1cc21ffb325c52f605722bed0e1..31f1a56f99473eda6fbc0c85ed7f2e228664da42 100644 (file)
@@ -1,9 +1,8 @@
 import cors from 'cors'
 import express from 'express'
-import { buildRateLimiter } from '@server/middlewares'
+
 import { HttpStatusCode } from '../../../shared/models'
 import { badRequest } from '../../helpers/express-utils'
-import { CONFIG } from '../../initializers/config'
 import { abuseRouter } from './abuse'
 import { accountsRouter } from './accounts'
 import { blocklistRouter } from './blocklist'
@@ -32,12 +31,6 @@ apiRouter.use(cors({
   credentials: true
 }))
 
-const apiRateLimiter = buildRateLimiter({
-  windowMs: CONFIG.RATES_LIMIT.API.WINDOW_MS,
-  max: CONFIG.RATES_LIMIT.API.MAX
-})
-apiRouter.use(apiRateLimiter)
-
 apiRouter.use('/server', serverRouter)
 apiRouter.use('/abuses', abuseRouter)
 apiRouter.use('/bulk', bulkRouter)
@@ -57,6 +50,8 @@ apiRouter.use('/plugins', pluginRouter)
 apiRouter.use('/custom-pages', customPageRouter)
 apiRouter.use('/blocklist', blocklistRouter)
 apiRouter.use('/runners', runnersRouter)
+
+// apiRouter.use(apiRateLimiter)
 apiRouter.use('/ping', pong)
 apiRouter.use('/*', badRequest)
 
index b63e2f962cf6ad37d9d0b4bfdd7954d1c3f4ab6d..c701bc970bca6db31b771b1bcd45c28d04237cec 100644 (file)
@@ -4,6 +4,7 @@ import { HttpStatusCode, Job, JobState, JobType, ResultList, UserRight } from '@
 import { isArray } from '../../helpers/custom-validators/misc'
 import { JobQueue } from '../../lib/job-queue'
 import {
+  apiRateLimiter,
   asyncMiddleware,
   authenticate,
   ensureUserHasRight,
@@ -17,6 +18,8 @@ import { listJobsValidator } from '../../middlewares/validators/jobs'
 
 const jobsRouter = express.Router()
 
+jobsRouter.use(apiRateLimiter)
+
 jobsRouter.post('/pause',
   authenticate,
   ensureUserHasRight(UserRight.MANAGE_JOBS),
index f66173875ffac2629af3680b5af0ba4761fe1466..909963fa7a6af118ea177f50da112dd28bee8301 100644 (file)
@@ -1,11 +1,13 @@
 import express from 'express'
+import { CONFIG } from '@server/initializers/config'
 import { OpenTelemetryMetrics } from '@server/lib/opentelemetry/metrics'
 import { HttpStatusCode, PlaybackMetricCreate } from '@shared/models'
-import { addPlaybackMetricValidator, asyncMiddleware } from '../../middlewares'
-import { CONFIG } from '@server/initializers/config'
+import { addPlaybackMetricValidator, apiRateLimiter, asyncMiddleware } from '../../middlewares'
 
 const metricsRouter = express.Router()
 
+metricsRouter.use(apiRateLimiter)
+
 metricsRouter.post('/playback',
   asyncMiddleware(addPlaybackMetricValidator),
   addPlaybackMetric
index eb7942fd68247ce9f093ecac92de189fcce54c2c..b619b78a39cf14ac2d9f6cc17d4810105ccaae8e 100644 (file)
@@ -4,10 +4,12 @@ import { OAuthClientModel } from '@server/models/oauth/oauth-client'
 import { HttpStatusCode, OAuthClientLocal } from '@shared/models'
 import { logger } from '../../helpers/logger'
 import { CONFIG } from '../../initializers/config'
-import { asyncMiddleware, openapiOperationDoc } from '../../middlewares'
+import { apiRateLimiter, asyncMiddleware, openapiOperationDoc } from '../../middlewares'
 
 const oauthClientsRouter = express.Router()
 
+oauthClientsRouter.use(apiRateLimiter)
+
 oauthClientsRouter.get('/local',
   openapiOperationDoc({ operationId: 'getOAuthClient' }),
   asyncMiddleware(getLocalClient)
index 34585e557f775e9ac97ee03065a3fe9a3eb5a816..fc616281e3e326fc6a1bfc368fe293e2f44d9d4a 100644 (file)
@@ -2,16 +2,18 @@ import express from 'express'
 import memoizee from 'memoizee'
 import { logger } from '@server/helpers/logger'
 import { Hooks } from '@server/lib/plugins/hooks'
+import { getServerActor } from '@server/models/application/application'
 import { VideoModel } from '@server/models/video/video'
 import { CategoryOverview, ChannelOverview, TagOverview, VideosOverview } from '../../../shared/models/overviews'
 import { buildNSFWFilter } from '../../helpers/express-utils'
 import { MEMOIZE_TTL, OVERVIEWS } from '../../initializers/constants'
-import { asyncMiddleware, optionalAuthenticate, videosOverviewValidator } from '../../middlewares'
+import { apiRateLimiter, asyncMiddleware, optionalAuthenticate, videosOverviewValidator } from '../../middlewares'
 import { TagModel } from '../../models/video/tag'
-import { getServerActor } from '@server/models/application/application'
 
 const overviewsRouter = express.Router()
 
+overviewsRouter.use(apiRateLimiter)
+
 overviewsRouter.get('/videos',
   videosOverviewValidator,
   optionalAuthenticate,
index e85fd6e116207c8642ff8bcec92b50f244ff196d..337b72b2f296625844cda792eee12c4a03c27849 100644 (file)
@@ -4,6 +4,7 @@ import { getFormattedObjects } from '@server/helpers/utils'
 import { listAvailablePluginsFromIndex } from '@server/lib/plugins/plugin-index'
 import { PluginManager } from '@server/lib/plugins/plugin-manager'
 import {
+  apiRateLimiter,
   asyncMiddleware,
   authenticate,
   availablePluginsSortValidator,
@@ -35,6 +36,8 @@ import {
 
 const pluginRouter = express.Router()
 
+pluginRouter.use(apiRateLimiter)
+
 pluginRouter.get('/available',
   openapiOperationDoc({ operationId: 'getAvailablePlugins' }),
   authenticate,
index c98ded354b79d3f31097978624114c751adb16cc..9998fe4cc09d281657903e5286fed1b87dbb9f27 100644 (file)
@@ -6,6 +6,8 @@ import { runnerRegistrationTokensRouter } from './registration-tokens'
 
 const runnersRouter = express.Router()
 
+// No api route limiter here, they are defined in child routers
+
 runnersRouter.use('/', manageRunnersRouter)
 runnersRouter.use('/', runnerJobsRouter)
 runnersRouter.use('/', runnerJobFilesRouter)
index 260d824a875a6cbc2c01210fc10656417238a156..4e69fb9023ada87a04c713ec7408b25af6c97be7 100644 (file)
@@ -3,7 +3,7 @@ import { logger, loggerTagsFactory } from '@server/helpers/logger'
 import { proxifyHLS, proxifyWebTorrentFile } from '@server/lib/object-storage'
 import { VideoPathManager } from '@server/lib/video-path-manager'
 import { getStudioTaskFilePath } from '@server/lib/video-studio'
-import { asyncMiddleware } from '@server/middlewares'
+import { apiRateLimiter, asyncMiddleware } from '@server/middlewares'
 import { jobOfRunnerGetValidator } from '@server/middlewares/validators/runners'
 import {
   runnerJobGetVideoStudioTaskFileValidator,
@@ -16,18 +16,21 @@ const lTags = loggerTagsFactory('api', 'runner')
 const runnerJobFilesRouter = express.Router()
 
 runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/max-quality',
+  apiRateLimiter,
   asyncMiddleware(jobOfRunnerGetValidator),
   asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
   asyncMiddleware(getMaxQualityVideoFile)
 )
 
 runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/previews/max-quality',
+  apiRateLimiter,
   asyncMiddleware(jobOfRunnerGetValidator),
   asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
   getMaxQualityVideoPreview
 )
 
 runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/studio/task-files/:filename',
+  apiRateLimiter,
   asyncMiddleware(jobOfRunnerGetValidator),
   asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
   runnerJobGetVideoStudioTaskFileValidator,
index 140f062bed225d4f0ef112fc3ba7de8d42d025fc..5d687e689594eb9c9e081bd28cf093f4e86003bb 100644 (file)
@@ -7,6 +7,7 @@ import { MIMETYPES } from '@server/initializers/constants'
 import { sequelizeTypescript } from '@server/initializers/database'
 import { getRunnerJobHandlerClass, updateLastRunnerContact } from '@server/lib/runners'
 import {
+  apiRateLimiter,
   asyncMiddleware,
   authenticate,
   ensureUserHasRight,
@@ -69,11 +70,13 @@ const runnerJobsRouter = express.Router()
 // ---------------------------------------------------------------------------
 
 runnerJobsRouter.post('/jobs/request',
+  apiRateLimiter,
   asyncMiddleware(getRunnerFromTokenValidator),
   asyncMiddleware(requestRunnerJob)
 )
 
 runnerJobsRouter.post('/jobs/:jobUUID/accept',
+  apiRateLimiter,
   asyncMiddleware(runnerJobGetValidator),
   acceptRunnerJobValidator,
   asyncMiddleware(getRunnerFromTokenValidator),
@@ -81,6 +84,7 @@ runnerJobsRouter.post('/jobs/:jobUUID/accept',
 )
 
 runnerJobsRouter.post('/jobs/:jobUUID/abort',
+  apiRateLimiter,
   asyncMiddleware(jobOfRunnerGetValidator),
   abortRunnerJobValidator,
   asyncMiddleware(abortRunnerJob)
@@ -88,6 +92,7 @@ runnerJobsRouter.post('/jobs/:jobUUID/abort',
 
 runnerJobsRouter.post('/jobs/:jobUUID/update',
   runnerJobUpdateVideoFiles,
+  apiRateLimiter, // Has to be after multer middleware to parse runner token
   asyncMiddleware(jobOfRunnerGetValidator),
   updateRunnerJobValidator,
   asyncMiddleware(updateRunnerJobController)
@@ -101,6 +106,7 @@ runnerJobsRouter.post('/jobs/:jobUUID/error',
 
 runnerJobsRouter.post('/jobs/:jobUUID/success',
   postRunnerJobSuccessVideoFiles,
+  apiRateLimiter, // Has to be after multer middleware to parse runner token
   asyncMiddleware(jobOfRunnerGetValidator),
   successRunnerJobValidator,
   asyncMiddleware(postRunnerJobSuccess)
index eb08c4b1d5209fa4494904d99df58bc8268f4e22..be7ebc0b3f04dd891b6e3c38886d5cc3a6835851 100644 (file)
@@ -2,6 +2,7 @@ import express from 'express'
 import { logger, loggerTagsFactory } from '@server/helpers/logger'
 import { generateRunnerToken } from '@server/helpers/token-generator'
 import {
+  apiRateLimiter,
   asyncMiddleware,
   authenticate,
   ensureUserHasRight,
@@ -19,15 +20,18 @@ const lTags = loggerTagsFactory('api', 'runner')
 const manageRunnersRouter = express.Router()
 
 manageRunnersRouter.post('/register',
+  apiRateLimiter,
   asyncMiddleware(registerRunnerValidator),
   asyncMiddleware(registerRunner)
 )
 manageRunnersRouter.post('/unregister',
+  apiRateLimiter,
   asyncMiddleware(getRunnerFromTokenValidator),
   asyncMiddleware(unregisterRunner)
 )
 
 manageRunnersRouter.delete('/:runnerId',
+  apiRateLimiter,
   authenticate,
   ensureUserHasRight(UserRight.MANAGE_RUNNERS),
   asyncMiddleware(deleteRunnerValidator),
@@ -35,6 +39,7 @@ manageRunnersRouter.delete('/:runnerId',
 )
 
 manageRunnersRouter.get('/',
+  apiRateLimiter,
   authenticate,
   ensureUserHasRight(UserRight.MANAGE_RUNNERS),
   paginationValidator,
index 5ac3773fe611f5699ccb7d42cfeacf54a6f2aac6..117ff271bbb21fdb8b0d45dcb7f0a5b345bb5c2b 100644 (file)
@@ -1,6 +1,8 @@
 import express from 'express'
+import { logger, loggerTagsFactory } from '@server/helpers/logger'
 import { generateRunnerRegistrationToken } from '@server/helpers/token-generator'
 import {
+  apiRateLimiter,
   asyncMiddleware,
   authenticate,
   ensureUserHasRight,
@@ -12,19 +14,20 @@ import {
 import { deleteRegistrationTokenValidator } from '@server/middlewares/validators/runners'
 import { RunnerRegistrationTokenModel } from '@server/models/runner/runner-registration-token'
 import { HttpStatusCode, ListRunnerRegistrationTokensQuery, UserRight } from '@shared/models'
-import { logger, loggerTagsFactory } from '@server/helpers/logger'
 
 const lTags = loggerTagsFactory('api', 'runner')
 
 const runnerRegistrationTokensRouter = express.Router()
 
 runnerRegistrationTokensRouter.post('/registration-tokens/generate',
+  apiRateLimiter,
   authenticate,
   ensureUserHasRight(UserRight.MANAGE_RUNNERS),
   asyncMiddleware(generateRegistrationToken)
 )
 
 runnerRegistrationTokensRouter.delete('/registration-tokens/:id',
+  apiRateLimiter,
   authenticate,
   ensureUserHasRight(UserRight.MANAGE_RUNNERS),
   asyncMiddleware(deleteRegistrationTokenValidator),
@@ -32,6 +35,7 @@ runnerRegistrationTokensRouter.delete('/registration-tokens/:id',
 )
 
 runnerRegistrationTokensRouter.get('/registration-tokens',
+  apiRateLimiter,
   authenticate,
   ensureUserHasRight(UserRight.MANAGE_RUNNERS),
   paginationValidator,
index 39efc0b10c0686f58f6bb55b684f30b3bfb5f331..4d395161c985f233aa89fe826236f50b84c53eaa 100644 (file)
@@ -1,10 +1,13 @@
 import express from 'express'
+import { apiRateLimiter } from '@server/middlewares'
 import { searchChannelsRouter } from './search-video-channels'
 import { searchPlaylistsRouter } from './search-video-playlists'
 import { searchVideosRouter } from './search-videos'
 
 const searchRouter = express.Router()
 
+searchRouter.use(apiRateLimiter)
+
 searchRouter.use('/', searchVideosRouter)
 searchRouter.use('/', searchChannelsRouter)
 searchRouter.use('/', searchPlaylistsRouter)
index b20718d0966443250fc619bb8ab0e7d8b1fea9b9..57f7d601c3ee3edecdcece8dd854830636e4de87 100644 (file)
@@ -1,4 +1,5 @@
 import express from 'express'
+import { apiRateLimiter } from '@server/middlewares'
 import { contactRouter } from './contact'
 import { debugRouter } from './debug'
 import { serverFollowsRouter } from './follows'
@@ -9,6 +10,8 @@ import { statsRouter } from './stats'
 
 const serverRouter = express.Router()
 
+serverRouter.use(apiRateLimiter)
+
 serverRouter.use('/', serverFollowsRouter)
 serverRouter.use('/', serverRedundancyRouter)
 serverRouter.use('/', statsRouter)
index 96366d68c28b9d074aab88ab8f6926d7174c5532..5eac6fd0f67b8794efb04041f078a54198c90504 100644 (file)
@@ -15,6 +15,7 @@ import { Redis } from '../../../lib/redis'
 import { buildUser, createUserAccountAndChannelAndPlaylist } from '../../../lib/user'
 import {
   adminUsersSortValidator,
+  apiRateLimiter,
   asyncMiddleware,
   asyncRetryTransactionMiddleware,
   authenticate,
@@ -50,6 +51,9 @@ import { twoFactorRouter } from './two-factor'
 const auditLogger = auditLoggerFactory('users')
 
 const usersRouter = express.Router()
+
+usersRouter.use(apiRateLimiter)
+
 usersRouter.use('/', emailVerificationRouter)
 usersRouter.use('/', registrationsRouter)
 usersRouter.use('/', twoFactorRouter)
index 03c54b59cb15b668cae2a46b11448527d2c4eae9..6b52ac7ddd03519ce29ad322831db9f945d15feb 100644 (file)
@@ -2,6 +2,7 @@ import express from 'express'
 import { auditLoggerFactory, getAuditIdFromRes, VideoChannelSyncAuditView } from '@server/helpers/audit-logger'
 import { logger } from '@server/helpers/logger'
 import {
+  apiRateLimiter,
   asyncMiddleware,
   asyncRetryTransactionMiddleware,
   authenticate,
@@ -17,6 +18,8 @@ import { HttpStatusCode, VideoChannelSyncState } from '@shared/models'
 const videoChannelSyncRouter = express.Router()
 const auditLogger = auditLoggerFactory('channel-syncs')
 
+videoChannelSyncRouter.use(apiRateLimiter)
+
 videoChannelSyncRouter.post('/',
   authenticate,
   ensureSyncIsEnabled,
index c6d144f79290a89ce4b474f110921d9cf6df00f0..cdafa31dc088dde66867e99c114fb80e65fcacf1 100644 (file)
@@ -19,6 +19,7 @@ import { JobQueue } from '../../lib/job-queue'
 import { deleteLocalActorImageFile, updateLocalActorImageFiles } from '../../lib/local-actor'
 import { createLocalVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel'
 import {
+  apiRateLimiter,
   asyncMiddleware,
   asyncRetryTransactionMiddleware,
   authenticate,
@@ -57,6 +58,8 @@ const reqBannerFile = createReqFiles([ 'bannerfile' ], MIMETYPES.IMAGE.MIMETYPE_
 
 const videoChannelRouter = express.Router()
 
+videoChannelRouter.use(apiRateLimiter)
+
 videoChannelRouter.get('/',
   paginationValidator,
   videoChannelsSortValidator,
index de32dec88d2a26015d8f11d2f1e0b0cc441cb303..fe00034ed4aca8ccedf95c631d49e3c38fd860e8 100644 (file)
@@ -25,6 +25,7 @@ import { sendCreateVideoPlaylist, sendDeleteVideoPlaylist, sendUpdateVideoPlayli
 import { getLocalVideoPlaylistActivityPubUrl, getLocalVideoPlaylistElementActivityPubUrl } from '../../lib/activitypub/url'
 import { updatePlaylistMiniatureFromExisting } from '../../lib/thumbnail'
 import {
+  apiRateLimiter,
   asyncMiddleware,
   asyncRetryTransactionMiddleware,
   authenticate,
@@ -52,6 +53,8 @@ const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIM
 
 const videoPlaylistRouter = express.Router()
 
+videoPlaylistRouter.use(apiRateLimiter)
+
 videoPlaylistRouter.get('/privacies', listVideoPlaylistPrivacies)
 
 videoPlaylistRouter.get('/',
index 41992155d6f7f5fa5e9abb9f6a50a5b67bbb31fe..a34325e79ea6fe71ec0eb50e233598abd6043e76 100644 (file)
@@ -15,6 +15,7 @@ import { sequelizeTypescript } from '../../../initializers/database'
 import { JobQueue } from '../../../lib/job-queue'
 import { Hooks } from '../../../lib/plugins/hooks'
 import {
+  apiRateLimiter,
   asyncMiddleware,
   asyncRetryTransactionMiddleware,
   authenticate,
@@ -50,6 +51,8 @@ import { viewRouter } from './view'
 const auditLogger = auditLoggerFactory('videos')
 const videosRouter = express.Router()
 
+videosRouter.use(apiRateLimiter)
+
 videosRouter.use('/', blacklistRouter)
 videosRouter.use('/', statsRouter)
 videosRouter.use('/', rateVideoRouter)
index 0e936028c2c43f3546d14c3da2201f968317e774..8257965dd35d2c3a449f428bf92bb70e0af6e804 100644 (file)
@@ -1,5 +1,6 @@
 import express from 'express'
 import RateLimit, { Options as RateLimitHandlerOptions } from 'express-rate-limit'
+import { CONFIG } from '@server/initializers/config'
 import { RunnerModel } from '@server/models/runner/runner'
 import { UserRole } from '@shared/models'
 import { optionalAuthenticate } from './auth'
@@ -39,6 +40,11 @@ export function buildRateLimiter (options: {
   })
 }
 
+export const apiRateLimiter = buildRateLimiter({
+  windowMs: CONFIG.RATES_LIMIT.API.WINDOW_MS,
+  max: CONFIG.RATES_LIMIT.API.MAX
+})
+
 // ---------------------------------------------------------------------------
 // Private
 // ---------------------------------------------------------------------------
index 5540241908fc8fa0d9998457172ef0c77c8abb1a..34a51abe7abb20e7c0ffcc61dacb9e9d66d8e2ea 100644 (file)
@@ -14,7 +14,6 @@ import {
 import {
   cleanupTests,
   createSingleServer,
-  makePostBodyRequest,
   PeerTubeServer,
   setAccessTokensToServers,
   setDefaultVideoChannel,
@@ -641,24 +640,47 @@ describe('Test runner common actions', function () {
         })
       })
 
-      it('Should rate limit an unknown runner', async function () {
-        const path = '/api/v1/ping'
-        const fields = { runnerToken: 'toto' }
+      it('Should rate limit an unknown runner, but not a registered one', async function () {
+        this.timeout(60000)
+
+        await server.videos.quickUpload({ name: 'video' })
+        await waitJobs([ server ])
+
+        const { job } = await server.runnerJobs.autoAccept({ runnerToken })
 
         for (let i = 0; i < 20; i++) {
           try {
-            await makePostBodyRequest({ url: server.url, path, fields, expectedStatus: HttpStatusCode.OK_200 })
+            await server.runnerJobs.request({ runnerToken })
+            await server.runnerJobs.update({ runnerToken, jobToken: job.jobToken, jobUUID: job.uuid })
           } catch {}
         }
 
-        await makePostBodyRequest({ url: server.url, path, fields, expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
-      })
+        // Invalid
+        {
+          await server.runnerJobs.request({ runnerToken: 'toto', expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
+          await server.runnerJobs.update({
+            runnerToken: 'toto',
+            jobToken: job.jobToken,
+            jobUUID: job.uuid,
+            expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429
+          })
+        }
 
-      it('Should not rate limit a registered runner', async function () {
-        const path = '/api/v1/ping'
+        // Not provided
+        {
+          await server.runnerJobs.request({ runnerToken: undefined, expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
+          await server.runnerJobs.update({
+            runnerToken: undefined,
+            jobToken: job.jobToken,
+            jobUUID: job.uuid,
+            expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429
+          })
+        }
 
-        for (let i = 0; i < 20; i++) {
-          await makePostBodyRequest({ url: server.url, path, fields: { runnerToken }, expectedStatus: HttpStatusCode.OK_200 })
+        // Registered
+        {
+          await server.runnerJobs.request({ runnerToken })
+          await server.runnerJobs.update({ runnerToken, jobToken: job.jobToken, jobUUID: job.uuid })
         }
       })
     })