diff options
author | Chocobozzz <me@florianbigard.com> | 2018-03-29 10:58:24 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-03-29 11:03:30 +0200 |
commit | 490b595a01c5824ff63ffb87f0efdfca95f4bf3b (patch) | |
tree | 3ad716fbb97a8b4ee946ad907202b82934a33d7c | |
parent | 23f4c3d412974fa5fda52589d1192e098e260f1a (diff) | |
download | PeerTube-490b595a01c5824ff63ffb87f0efdfca95f4bf3b.tar.gz PeerTube-490b595a01c5824ff63ffb87f0efdfca95f4bf3b.tar.zst PeerTube-490b595a01c5824ff63ffb87f0efdfca95f4bf3b.zip |
Prevent brute force login attack
-rw-r--r-- | client/src/app/core/auth/auth.service.ts | 8 | ||||
-rw-r--r-- | client/src/app/shared/rest/rest-extractor.service.ts | 36 | ||||
-rw-r--r-- | client/src/app/signup/signup.component.ts | 2 | ||||
-rw-r--r-- | config/default.yaml | 6 | ||||
-rw-r--r-- | config/production.yaml.example | 6 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | server.ts | 4 | ||||
-rw-r--r-- | server/controllers/api/users.ts | 14 | ||||
-rw-r--r-- | server/controllers/api/videos/index.ts | 2 | ||||
-rw-r--r-- | server/initializers/checker.ts | 1 | ||||
-rw-r--r-- | server/initializers/constants.ts | 9 | ||||
-rw-r--r-- | server/initializers/installer.ts | 2 | ||||
-rw-r--r-- | server/tests/api/server/reverse-proxy.ts | 82 | ||||
-rw-r--r-- | server/tests/utils/videos/videos.ts | 11 | ||||
-rw-r--r-- | support/docker/production/config/production.yaml | 8 | ||||
-rw-r--r-- | yarn.lock | 22 |
16 files changed, 191 insertions, 24 deletions
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index f5ca2fcdc..d31c61496 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts | |||
@@ -66,8 +66,12 @@ export class AuthService { | |||
66 | }, | 66 | }, |
67 | 67 | ||
68 | error => { | 68 | error => { |
69 | let errorMessage = `Cannot retrieve OAuth Client credentials: ${error.text}. \n` | 69 | let errorMessage = error.message |
70 | errorMessage += 'Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.' | 70 | |
71 | if (error.status === 403) { | ||
72 | errorMessage = `Cannot retrieve OAuth Client credentials: ${error.text}. \n` | ||
73 | errorMessage += 'Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.' | ||
74 | } | ||
71 | 75 | ||
72 | // We put a bigger timeout | 76 | // We put a bigger timeout |
73 | // This is an important message | 77 | // This is an important message |
diff --git a/client/src/app/shared/rest/rest-extractor.service.ts b/client/src/app/shared/rest/rest-extractor.service.ts index ad08a32f8..b1e22a76c 100644 --- a/client/src/app/shared/rest/rest-extractor.service.ts +++ b/client/src/app/shared/rest/rest-extractor.service.ts | |||
@@ -42,25 +42,33 @@ export class RestExtractor { | |||
42 | console.error('An error occurred:', errorMessage) | 42 | console.error('An error occurred:', errorMessage) |
43 | } else if (err.status !== undefined) { | 43 | } else if (err.status !== undefined) { |
44 | // A server-side error occurred. | 44 | // A server-side error occurred. |
45 | if (err.error) { | 45 | if (err.error && err.error.errors) { |
46 | if (err.error.errors) { | 46 | const errors = err.error.errors |
47 | const errors = err.error.errors | 47 | const errorsArray: string[] = [] |
48 | const errorsArray: string[] = [] | 48 | |
49 | 49 | Object.keys(errors).forEach(key => { | |
50 | Object.keys(errors).forEach(key => { | 50 | errorsArray.push(errors[key].msg) |
51 | errorsArray.push(errors[key].msg) | 51 | }) |
52 | }) | 52 | |
53 | 53 | errorMessage = errorsArray.join('. ') | |
54 | errorMessage = errorsArray.join('. ') | 54 | } else if (err.error && err.error.error) { |
55 | } else if (err.error.error) { | 55 | errorMessage = err.error.error |
56 | errorMessage = err.error.error | ||
57 | } | ||
58 | } else if (err.status === 413) { | 56 | } else if (err.status === 413) { |
59 | errorMessage = 'Request is too large for the server. Please contact you administrator if you want to increase the limit size.' | 57 | errorMessage = 'Request is too large for the server. Please contact you administrator if you want to increase the limit size.' |
58 | } else if (err.status === 429) { | ||
59 | const secondsLeft = err.headers.get('retry-after') | ||
60 | if (secondsLeft) { | ||
61 | const minutesLeft = Math.floor(parseInt(secondsLeft, 10) / 60) | ||
62 | errorMessage = 'Too many attempts, please try again after ' + minutesLeft + ' minutes.' | ||
63 | } else { | ||
64 | errorMessage = 'Too many attempts, please try again later.' | ||
65 | } | ||
66 | } else if (err.status === 500) { | ||
67 | errorMessage = 'Server error. Please retry later.' | ||
60 | } | 68 | } |
61 | 69 | ||
62 | errorMessage = errorMessage ? errorMessage : 'Unknown error.' | 70 | errorMessage = errorMessage ? errorMessage : 'Unknown error.' |
63 | console.error(`Backend returned code ${err.status}, body was: ${errorMessage}`) | 71 | console.error(`Backend returned code ${err.status}, errorMessage is: ${errorMessage}`) |
64 | } else { | 72 | } else { |
65 | errorMessage = err | 73 | errorMessage = err |
66 | } | 74 | } |
diff --git a/client/src/app/signup/signup.component.ts b/client/src/app/signup/signup.component.ts index 93d73a11e..1f3e2e146 100644 --- a/client/src/app/signup/signup.component.ts +++ b/client/src/app/signup/signup.component.ts | |||
@@ -101,7 +101,7 @@ export class SignupComponent extends FormReactive implements OnInit { | |||
101 | const lines = [ | 101 | const lines = [ |
102 | SignupComponent.getApproximateTime(fullHdSeconds) + ' of full HD videos', | 102 | SignupComponent.getApproximateTime(fullHdSeconds) + ' of full HD videos', |
103 | SignupComponent.getApproximateTime(hdSeconds) + ' of HD videos', | 103 | SignupComponent.getApproximateTime(hdSeconds) + ' of HD videos', |
104 | SignupComponent.getApproximateTime(normalSeconds) + ' of normal quality videos' | 104 | SignupComponent.getApproximateTime(normalSeconds) + ' of average quality videos' |
105 | ] | 105 | ] |
106 | 106 | ||
107 | this.quotaHelpIndication = lines.join('<br />') | 107 | this.quotaHelpIndication = lines.join('<br />') |
diff --git a/config/default.yaml b/config/default.yaml index 26fc5c128..bf772faf6 100644 --- a/config/default.yaml +++ b/config/default.yaml | |||
@@ -6,6 +6,12 @@ webserver: | |||
6 | hostname: 'localhost' | 6 | hostname: 'localhost' |
7 | port: 9000 | 7 | port: 9000 |
8 | 8 | ||
9 | # Proxies to trust to get real client IP | ||
10 | # If you run PeerTube just behind a local proxy (nginx), keep 'loopback' | ||
11 | # If you run PeerTube behind a remote proxy, add the proxy IP address (or subnet) | ||
12 | trust_proxy: | ||
13 | - 'loopback' | ||
14 | |||
9 | # Your database name will be "peertube"+database.suffix | 15 | # Your database name will be "peertube"+database.suffix |
10 | database: | 16 | database: |
11 | hostname: 'localhost' | 17 | hostname: 'localhost' |
diff --git a/config/production.yaml.example b/config/production.yaml.example index 43cacee3b..d362e0b8a 100644 --- a/config/production.yaml.example +++ b/config/production.yaml.example | |||
@@ -7,6 +7,12 @@ webserver: | |||
7 | hostname: 'example.com' | 7 | hostname: 'example.com' |
8 | port: 443 | 8 | port: 443 |
9 | 9 | ||
10 | # Proxies to trust to get real client IP | ||
11 | # If you run PeerTube just behind a local proxy (nginx), keep 'loopback' | ||
12 | # If you run PeerTube behind a remote proxy, add the proxy IP address (or subnet) | ||
13 | trust_proxy: | ||
14 | - 'loopback' | ||
15 | |||
10 | # Your database name will be "peertube"+database.suffix | 16 | # Your database name will be "peertube"+database.suffix |
11 | database: | 17 | database: |
12 | hostname: 'localhost' | 18 | hostname: 'localhost' |
diff --git a/package.json b/package.json index 8a013e2d5..933bd7aa3 100644 --- a/package.json +++ b/package.json | |||
@@ -65,6 +65,7 @@ | |||
65 | "create-torrent": "^3.24.5", | 65 | "create-torrent": "^3.24.5", |
66 | "express": "^4.12.4", | 66 | "express": "^4.12.4", |
67 | "express-oauth-server": "^2.0.0", | 67 | "express-oauth-server": "^2.0.0", |
68 | "express-rate-limit": "^2.11.0", | ||
68 | "express-validator": "^5.0.0", | 69 | "express-validator": "^5.0.0", |
69 | "fluent-ffmpeg": "^2.1.0", | 70 | "fluent-ffmpeg": "^2.1.0", |
70 | "js-yaml": "^3.5.4", | 71 | "js-yaml": "^3.5.4", |
@@ -104,6 +105,7 @@ | |||
104 | "@types/chai": "^4.0.4", | 105 | "@types/chai": "^4.0.4", |
105 | "@types/config": "^0.0.34", | 106 | "@types/config": "^0.0.34", |
106 | "@types/express": "^4.0.35", | 107 | "@types/express": "^4.0.35", |
108 | "@types/express-rate-limit": "^2.9.3", | ||
107 | "@types/kue": "^0.11.8", | 109 | "@types/kue": "^0.11.8", |
108 | "@types/lodash": "^4.14.64", | 110 | "@types/lodash": "^4.14.64", |
109 | "@types/magnet-uri": "^5.1.1", | 111 | "@types/magnet-uri": "^5.1.1", |
@@ -48,6 +48,9 @@ if (errorMessage !== null) { | |||
48 | throw new Error(errorMessage) | 48 | throw new Error(errorMessage) |
49 | } | 49 | } |
50 | 50 | ||
51 | // Trust our proxy (IP forwarding...) | ||
52 | app.set('trust proxy', CONFIG.TRUST_PROXY) | ||
53 | |||
51 | // ----------- Database ----------- | 54 | // ----------- Database ----------- |
52 | 55 | ||
53 | // Initialize database and models | 56 | // Initialize database and models |
@@ -81,6 +84,7 @@ if (isTestInstance()) { | |||
81 | ) { | 84 | ) { |
82 | return (cors({ | 85 | return (cors({ |
83 | origin: 'http://localhost:3000', | 86 | origin: 'http://localhost:3000', |
87 | exposedHeaders: 'Retry-After', | ||
84 | credentials: true | 88 | credentials: true |
85 | }))(req, res, next) | 89 | }))(req, res, next) |
86 | } | 90 | } |
diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts index 583376c38..5e96d789e 100644 --- a/server/controllers/api/users.ts +++ b/server/controllers/api/users.ts | |||
@@ -2,12 +2,13 @@ import * as express from 'express' | |||
2 | import 'multer' | 2 | import 'multer' |
3 | import { extname, join } from 'path' | 3 | import { extname, join } from 'path' |
4 | import * as uuidv4 from 'uuid/v4' | 4 | import * as uuidv4 from 'uuid/v4' |
5 | import * as RateLimit from 'express-rate-limit' | ||
5 | import { UserCreate, UserRight, UserRole, UserUpdate, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../shared' | 6 | import { UserCreate, UserRight, UserRole, UserUpdate, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../shared' |
6 | import { retryTransactionWrapper } from '../../helpers/database-utils' | 7 | import { retryTransactionWrapper } from '../../helpers/database-utils' |
7 | import { processImage } from '../../helpers/image-utils' | 8 | import { processImage } from '../../helpers/image-utils' |
8 | import { logger } from '../../helpers/logger' | 9 | import { logger } from '../../helpers/logger' |
9 | import { createReqFiles, getFormattedObjects } from '../../helpers/utils' | 10 | import { createReqFiles, getFormattedObjects } from '../../helpers/utils' |
10 | import { AVATARS_SIZE, CONFIG, IMAGE_MIMETYPE_EXT, sequelizeTypescript } from '../../initializers' | 11 | import { AVATARS_SIZE, CONFIG, IMAGE_MIMETYPE_EXT, RATES_LIMIT, sequelizeTypescript } from '../../initializers' |
11 | import { updateActorAvatarInstance } from '../../lib/activitypub' | 12 | import { updateActorAvatarInstance } from '../../lib/activitypub' |
12 | import { sendUpdateActor } from '../../lib/activitypub/send' | 13 | import { sendUpdateActor } from '../../lib/activitypub/send' |
13 | import { Emailer } from '../../lib/emailer' | 14 | import { Emailer } from '../../lib/emailer' |
@@ -43,6 +44,11 @@ import { OAuthTokenModel } from '../../models/oauth/oauth-token' | |||
43 | import { VideoModel } from '../../models/video/video' | 44 | import { VideoModel } from '../../models/video/video' |
44 | 45 | ||
45 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR }) | 46 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR }) |
47 | const loginRateLimiter = new RateLimit({ | ||
48 | windowMs: RATES_LIMIT.LOGIN.WINDOW_MS, | ||
49 | max: RATES_LIMIT.LOGIN.MAX, | ||
50 | delayMs: 0 | ||
51 | }) | ||
46 | 52 | ||
47 | const usersRouter = express.Router() | 53 | const usersRouter = express.Router() |
48 | 54 | ||
@@ -136,7 +142,11 @@ usersRouter.post('/:id/reset-password', | |||
136 | asyncMiddleware(resetUserPassword) | 142 | asyncMiddleware(resetUserPassword) |
137 | ) | 143 | ) |
138 | 144 | ||
139 | usersRouter.post('/token', token, success) | 145 | usersRouter.post('/token', |
146 | loginRateLimiter, | ||
147 | token, | ||
148 | success | ||
149 | ) | ||
140 | // TODO: Once https://github.com/oauthjs/node-oauth2-server/pull/289 is merged, implement revoke token route | 150 | // TODO: Once https://github.com/oauthjs/node-oauth2-server/pull/289 is merged, implement revoke token route |
141 | 151 | ||
142 | // --------------------------------------------------------------------------- | 152 | // --------------------------------------------------------------------------- |
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index c0a8ac118..552e5edac 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -353,7 +353,7 @@ function getVideo (req: express.Request, res: express.Response) { | |||
353 | async function viewVideo (req: express.Request, res: express.Response) { | 353 | async function viewVideo (req: express.Request, res: express.Response) { |
354 | const videoInstance = res.locals.video | 354 | const videoInstance = res.locals.video |
355 | 355 | ||
356 | const ip = req.headers['x-real-ip'] as string || req.ip | 356 | const ip = req.ip |
357 | const exists = await Redis.Instance.isViewExists(ip, videoInstance.uuid) | 357 | const exists = await Redis.Instance.isViewExists(ip, videoInstance.uuid) |
358 | if (exists) { | 358 | if (exists) { |
359 | logger.debug('View for ip %s and video %s already exists.', ip, videoInstance.uuid) | 359 | logger.debug('View for ip %s and video %s already exists.', ip, videoInstance.uuid) |
diff --git a/server/initializers/checker.ts b/server/initializers/checker.ts index cd93f19a9..45f1d79c3 100644 --- a/server/initializers/checker.ts +++ b/server/initializers/checker.ts | |||
@@ -20,6 +20,7 @@ function checkConfig () { | |||
20 | function checkMissedConfig () { | 20 | function checkMissedConfig () { |
21 | const required = [ 'listen.port', | 21 | const required = [ 'listen.port', |
22 | 'webserver.https', 'webserver.hostname', 'webserver.port', | 22 | 'webserver.https', 'webserver.hostname', 'webserver.port', |
23 | 'trust_proxy', | ||
23 | 'database.hostname', 'database.port', 'database.suffix', 'database.username', 'database.password', | 24 | 'database.hostname', 'database.port', 'database.suffix', 'database.username', 'database.password', |
24 | 'redis.hostname', 'redis.port', 'redis.auth', | 25 | 'redis.hostname', 'redis.port', 'redis.auth', |
25 | 'smtp.hostname', 'smtp.port', 'smtp.username', 'smtp.password', 'smtp.tls', 'smtp.from_address', | 26 | 'smtp.hostname', 'smtp.port', 'smtp.username', 'smtp.password', 'smtp.tls', 'smtp.from_address', |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 284acf8f3..986fed099 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -127,6 +127,7 @@ const CONFIG = { | |||
127 | URL: '', | 127 | URL: '', |
128 | HOST: '' | 128 | HOST: '' |
129 | }, | 129 | }, |
130 | TRUST_PROXY: config.get<string[]>('trust_proxy'), | ||
130 | LOG: { | 131 | LOG: { |
131 | LEVEL: config.get<string>('log.level') | 132 | LEVEL: config.get<string>('log.level') |
132 | }, | 133 | }, |
@@ -234,6 +235,13 @@ const CONSTRAINTS_FIELDS = { | |||
234 | } | 235 | } |
235 | } | 236 | } |
236 | 237 | ||
238 | const RATES_LIMIT = { | ||
239 | LOGIN: { | ||
240 | WINDOW_MS: 5 * 60 * 1000, // 5 minutes | ||
241 | MAX: 10 // 10 attempts | ||
242 | } | ||
243 | } | ||
244 | |||
237 | let VIDEO_VIEW_LIFETIME = 60000 * 60 // 1 hour | 245 | let VIDEO_VIEW_LIFETIME = 60000 * 60 // 1 hour |
238 | const VIDEO_TRANSCODING_FPS = { | 246 | const VIDEO_TRANSCODING_FPS = { |
239 | MIN: 10, | 247 | MIN: 10, |
@@ -468,6 +476,7 @@ export { | |||
468 | USER_PASSWORD_RESET_LIFETIME, | 476 | USER_PASSWORD_RESET_LIFETIME, |
469 | IMAGE_MIMETYPE_EXT, | 477 | IMAGE_MIMETYPE_EXT, |
470 | SCHEDULER_INTERVAL, | 478 | SCHEDULER_INTERVAL, |
479 | RATES_LIMIT, | ||
471 | JOB_COMPLETED_LIFETIME, | 480 | JOB_COMPLETED_LIFETIME, |
472 | VIDEO_VIEW_LIFETIME | 481 | VIDEO_VIEW_LIFETIME |
473 | } | 482 | } |
diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts index d2f6c7c8c..f0adf8c9e 100644 --- a/server/initializers/installer.ts +++ b/server/initializers/installer.ts | |||
@@ -112,7 +112,7 @@ async function createOAuthAdminIfNotExist () { | |||
112 | // Our password is weak so do not validate it | 112 | // Our password is weak so do not validate it |
113 | validatePassword = false | 113 | validatePassword = false |
114 | } else { | 114 | } else { |
115 | password = passwordGenerator(8, true) | 115 | password = passwordGenerator(16, true) |
116 | } | 116 | } |
117 | 117 | ||
118 | const userData = { | 118 | const userData = { |
diff --git a/server/tests/api/server/reverse-proxy.ts b/server/tests/api/server/reverse-proxy.ts new file mode 100644 index 000000000..aa4b3ae81 --- /dev/null +++ b/server/tests/api/server/reverse-proxy.ts | |||
@@ -0,0 +1,82 @@ | |||
1 | /* tslint:disable:no-unused-expression */ | ||
2 | |||
3 | import 'mocha' | ||
4 | import * as chai from 'chai' | ||
5 | import { About } from '../../../../shared/models/server/about.model' | ||
6 | import { CustomConfig } from '../../../../shared/models/server/custom-config.model' | ||
7 | import { deleteCustomConfig, getAbout, getVideo, killallServers, login, reRunServer, uploadVideo, userLogin, viewVideo } from '../../utils' | ||
8 | const expect = chai.expect | ||
9 | |||
10 | import { | ||
11 | getConfig, | ||
12 | flushTests, | ||
13 | runServer, | ||
14 | registerUser, getCustomConfig, setAccessTokensToServers, updateCustomConfig | ||
15 | } from '../../utils/index' | ||
16 | |||
17 | describe('Test application behind a reverse proxy', function () { | ||
18 | let server = null | ||
19 | let videoId | ||
20 | |||
21 | before(async function () { | ||
22 | this.timeout(30000) | ||
23 | |||
24 | await flushTests() | ||
25 | server = await runServer(1) | ||
26 | await setAccessTokensToServers([ server ]) | ||
27 | |||
28 | const { body } = await uploadVideo(server.url, server.accessToken, {}) | ||
29 | videoId = body.video.uuid | ||
30 | }) | ||
31 | |||
32 | it('Should view a video only once with the same IP by default', async function () { | ||
33 | await viewVideo(server.url, videoId) | ||
34 | await viewVideo(server.url, videoId) | ||
35 | |||
36 | const { body } = await getVideo(server.url, videoId) | ||
37 | expect(body.views).to.equal(1) | ||
38 | }) | ||
39 | |||
40 | it('Should view a video 2 times with the X-Forwarded-For header set', async function () { | ||
41 | await viewVideo(server.url, videoId, 204, '0.0.0.1,127.0.0.1') | ||
42 | await viewVideo(server.url, videoId, 204, '0.0.0.2,127.0.0.1') | ||
43 | |||
44 | const { body } = await getVideo(server.url, videoId) | ||
45 | expect(body.views).to.equal(3) | ||
46 | }) | ||
47 | |||
48 | it('Should view a video only once with the same client IP in the X-Forwarded-For header', async function () { | ||
49 | await viewVideo(server.url, videoId, 204, '0.0.0.4,0.0.0.3,::ffff:127.0.0.1') | ||
50 | await viewVideo(server.url, videoId, 204, '0.0.0.5,0.0.0.3,127.0.0.1') | ||
51 | |||
52 | const { body } = await getVideo(server.url, videoId) | ||
53 | expect(body.views).to.equal(4) | ||
54 | }) | ||
55 | |||
56 | it('Should view a video two times with a different client IP in the X-Forwarded-For header', async function () { | ||
57 | await viewVideo(server.url, videoId, 204, '0.0.0.8,0.0.0.6,127.0.0.1') | ||
58 | await viewVideo(server.url, videoId, 204, '0.0.0.8,0.0.0.7,127.0.0.1') | ||
59 | |||
60 | const { body } = await getVideo(server.url, videoId) | ||
61 | expect(body.views).to.equal(6) | ||
62 | }) | ||
63 | |||
64 | it('Should rate limit logins', async function () { | ||
65 | const user = { username: 'root', password: 'fail' } | ||
66 | |||
67 | for (let i = 0; i < 9; i++) { | ||
68 | await userLogin(server, user, 400) | ||
69 | } | ||
70 | |||
71 | await userLogin(server, user, 429) | ||
72 | }) | ||
73 | |||
74 | after(async function () { | ||
75 | process.kill(-server.app.pid) | ||
76 | |||
77 | // Keep the logs if the test failed | ||
78 | if (this['ok']) { | ||
79 | await flushTests() | ||
80 | } | ||
81 | }) | ||
82 | }) | ||
diff --git a/server/tests/utils/videos/videos.ts b/server/tests/utils/videos/videos.ts index 424f41ed8..9bda53371 100644 --- a/server/tests/utils/videos/videos.ts +++ b/server/tests/utils/videos/videos.ts | |||
@@ -85,13 +85,18 @@ function getVideo (url: string, id: number | string, expectedStatus = 200) { | |||
85 | .expect(expectedStatus) | 85 | .expect(expectedStatus) |
86 | } | 86 | } |
87 | 87 | ||
88 | function viewVideo (url: string, id: number | string, expectedStatus = 204) { | 88 | function viewVideo (url: string, id: number | string, expectedStatus = 204, xForwardedFor?: string) { |
89 | const path = '/api/v1/videos/' + id + '/views' | 89 | const path = '/api/v1/videos/' + id + '/views' |
90 | 90 | ||
91 | return request(url) | 91 | const req = request(url) |
92 | .post(path) | 92 | .post(path) |
93 | .set('Accept', 'application/json') | 93 | .set('Accept', 'application/json') |
94 | .expect(expectedStatus) | 94 | |
95 | if (xForwardedFor) { | ||
96 | req.set('X-Forwarded-For', xForwardedFor) | ||
97 | } | ||
98 | |||
99 | return req.expect(expectedStatus) | ||
95 | } | 100 | } |
96 | 101 | ||
97 | function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = 200) { | 102 | function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = 200) { |
diff --git a/support/docker/production/config/production.yaml b/support/docker/production/config/production.yaml index 41272ba26..7b6de32e5 100644 --- a/support/docker/production/config/production.yaml +++ b/support/docker/production/config/production.yaml | |||
@@ -7,6 +7,14 @@ webserver: | |||
7 | hostname: undefined | 7 | hostname: undefined |
8 | port: 443 | 8 | port: 443 |
9 | 9 | ||
10 | # Proxies to trust to get real client IP | ||
11 | # If you run PeerTube just behind a local proxy (nginx), keep 'loopback' | ||
12 | # If you run PeerTube behind a remote proxy, add the proxy IP address (or subnet) | ||
13 | trust_proxy: | ||
14 | - 'loopback' | ||
15 | - 'linklocal' | ||
16 | - 'uniquelocal' | ||
17 | |||
10 | # Your database name will be "peertube"+database.suffix | 18 | # Your database name will be "peertube"+database.suffix |
11 | database: | 19 | database: |
12 | hostname: 'db' | 20 | hostname: 'db' |
@@ -63,6 +63,12 @@ | |||
63 | version "1.2.0" | 63 | version "1.2.0" |
64 | resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" | 64 | resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" |
65 | 65 | ||
66 | "@types/express-rate-limit@^2.9.3": | ||
67 | version "2.9.3" | ||
68 | resolved "https://registry.yarnpkg.com/@types/express-rate-limit/-/express-rate-limit-2.9.3.tgz#e83a548bf251ad12ca49055c22d3f2da4e16b62d" | ||
69 | dependencies: | ||
70 | "@types/express" "*" | ||
71 | |||
66 | "@types/express-serve-static-core@*": | 72 | "@types/express-serve-static-core@*": |
67 | version "4.11.1" | 73 | version "4.11.1" |
68 | resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.11.1.tgz#f6f7212382d59b19d696677bcaa48a37280f5d45" | 74 | resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.11.1.tgz#f6f7212382d59b19d696677bcaa48a37280f5d45" |
@@ -1140,6 +1146,10 @@ cliui@^3.2.0: | |||
1140 | strip-ansi "^3.0.1" | 1146 | strip-ansi "^3.0.1" |
1141 | wrap-ansi "^2.0.0" | 1147 | wrap-ansi "^2.0.0" |
1142 | 1148 | ||
1149 | clone@^1.0.2: | ||
1150 | version "1.0.4" | ||
1151 | resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" | ||
1152 | |||
1143 | closest-to@~2.0.0: | 1153 | closest-to@~2.0.0: |
1144 | version "2.0.0" | 1154 | version "2.0.0" |
1145 | resolved "https://registry.yarnpkg.com/closest-to/-/closest-to-2.0.0.tgz#bb2a860edb7769b62d04821748ae50da24dbefaa" | 1155 | resolved "https://registry.yarnpkg.com/closest-to/-/closest-to-2.0.0.tgz#bb2a860edb7769b62d04821748ae50da24dbefaa" |
@@ -1569,6 +1579,12 @@ deep-extend@~0.4.0: | |||
1569 | version "0.4.2" | 1579 | version "0.4.2" |
1570 | resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" | 1580 | resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" |
1571 | 1581 | ||
1582 | defaults@^1.0.3: | ||
1583 | version "1.0.3" | ||
1584 | resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" | ||
1585 | dependencies: | ||
1586 | clone "^1.0.2" | ||
1587 | |||
1572 | define-property@^0.2.5: | 1588 | define-property@^0.2.5: |
1573 | version "0.2.5" | 1589 | version "0.2.5" |
1574 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" | 1590 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" |
@@ -1914,6 +1930,12 @@ express-oauth-server@^2.0.0: | |||
1914 | express "^4.13.3" | 1930 | express "^4.13.3" |
1915 | oauth2-server "3.0.0" | 1931 | oauth2-server "3.0.0" |
1916 | 1932 | ||
1933 | express-rate-limit@^2.11.0: | ||
1934 | version "2.11.0" | ||
1935 | resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-2.11.0.tgz#092122218c86eddb56fb350f431e522fb8024ea9" | ||
1936 | dependencies: | ||
1937 | defaults "^1.0.3" | ||
1938 | |||
1917 | express-validator@^5.0.0: | 1939 | express-validator@^5.0.0: |
1918 | version "5.0.3" | 1940 | version "5.0.3" |
1919 | resolved "https://registry.yarnpkg.com/express-validator/-/express-validator-5.0.3.tgz#c31176740f216c5ce043d6e20c7afa1db1a2691e" | 1941 | resolved "https://registry.yarnpkg.com/express-validator/-/express-validator-5.0.3.tgz#c31176740f216c5ce043d6e20c7afa1db1a2691e" |