},
serverVersion: 'Unknown',
signup: {
- allowed: false
+ allowed: false,
+ allowedForCurrentIP: false
},
transcoding: {
enabledResolutions: []
}
isRegistrationAllowed () {
- return this.serverService.getConfig().signup.allowed
+ return this.serverService.getConfig().signup.allowed &&
+ this.serverService.getConfig().signup.allowedForCurrentIP
}
getFirstAdminRightAvailable () {
signup:
enabled: false
limit: 10 # When the limit is reached, registrations are disabled. -1 == unlimited
+ filters:
+ cidr: # You can specify CIDR ranges to whitelist (empty = no filtering) or blacklist
+ whitelist: []
+ blacklist: []
user:
# Default value of maximum video BYTES the user can upload (does not take into account transcoded files).
signup:
enabled: false
limit: 10 # When the limit is reached, registrations are disabled. -1 == unlimited
+ filters:
+ cidr: # You can specify CIDR ranges to whitelist (empty = no filtering) or blacklist
+ whitelist: []
+ blacklist: []
user:
# Default value of maximum video BYTES the user can upload (does not take into account transcoded files).
"express-rate-limit": "^2.11.0",
"express-validator": "^5.0.0",
"fluent-ffmpeg": "^2.1.0",
+ "ipaddr.js": "https://github.com/whitequark/ipaddr.js.git#8e69afeb4053ee32447a101845f860848280eca5",
+ "is-cidr": "^2.0.5",
"iso-639-3": "^1.0.1",
"js-yaml": "^3.5.4",
"jsonld": "^1.0.1",
import { About } from '../../../shared/models/server/about.model'
import { CustomConfig } from '../../../shared/models/server/custom-config.model'
import { unlinkPromise, writeFilePromise } from '../../helpers/core-utils'
-import { isSignupAllowed } from '../../helpers/utils'
+import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/utils'
import { CONFIG, CONSTRAINTS_FIELDS, reloadConfig } from '../../initializers'
import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares'
import { customConfigUpdateValidator } from '../../middlewares/validators/config'
async function getConfig (req: express.Request, res: express.Response, next: express.NextFunction) {
const allowed = await isSignupAllowed()
+ const allowedForCurrentIP = isSignupAllowedForCurrentIP(req.ip)
const enabledResolutions = Object.keys(CONFIG.TRANSCODING.RESOLUTIONS)
.filter(key => CONFIG.TRANSCODING.RESOLUTIONS[key] === true)
},
serverVersion: packageJSON.version,
signup: {
- allowed
+ allowed,
+ allowedForCurrentIP
},
transcoding: {
enabledResolutions
authenticate,
ensureUserHasRight,
ensureUserRegistrationAllowed,
+ ensureUserRegistrationAllowedForIP,
paginationValidator,
setDefaultPagination,
setDefaultSort,
usersRouter.post('/register',
asyncMiddleware(ensureUserRegistrationAllowed),
+ ensureUserRegistrationAllowedForIP,
asyncMiddleware(usersRegisterValidator),
asyncMiddleware(registerUserRetryWrapper)
)
import { Model } from 'sequelize-typescript'
+import * as ipaddr from 'ipaddr.js'
+const isCidr = require('is-cidr')
import { ResultList } from '../../shared'
import { VideoResolution } from '../../shared/models/videos'
import { CONFIG } from '../initializers'
return totalUsers < CONFIG.SIGNUP.LIMIT
}
+function isSignupAllowedForCurrentIP (ip: string) {
+ const addr = ipaddr.parse(ip)
+ let excludeList = [ 'blacklist' ]
+ let matched: string
+
+ // if there is a valid, non-empty whitelist, we exclude all unknown adresses too
+ if (CONFIG.SIGNUP.FILTERS.CIDR.WHITELIST.filter(cidr => isCidr(cidr)).length > 0) {
+ excludeList.push('unknown')
+ }
+
+ if (addr.kind() === 'ipv4') {
+ const addrV4 = ipaddr.IPv4.parse(ip)
+ const rangeList = {
+ whitelist: CONFIG.SIGNUP.FILTERS.CIDR.WHITELIST.filter(cidr => isCidr.v4(cidr))
+ .map(cidr => ipaddr.IPv4.parseCIDR(cidr)),
+ blacklist: CONFIG.SIGNUP.FILTERS.CIDR.BLACKLIST.filter(cidr => isCidr.v4(cidr))
+ .map(cidr => ipaddr.IPv4.parseCIDR(cidr))
+ }
+ matched = ipaddr.subnetMatch(addrV4, rangeList, 'unknown')
+ } else if (addr.kind() === 'ipv6') {
+ const addrV6 = ipaddr.IPv6.parse(ip)
+ const rangeList = {
+ whitelist: CONFIG.SIGNUP.FILTERS.CIDR.WHITELIST.filter(cidr => isCidr.v6(cidr))
+ .map(cidr => ipaddr.IPv6.parseCIDR(cidr)),
+ blacklist: CONFIG.SIGNUP.FILTERS.CIDR.BLACKLIST.filter(cidr => isCidr.v6(cidr))
+ .map(cidr => ipaddr.IPv6.parseCIDR(cidr))
+ }
+ matched = ipaddr.subnetMatch(addrV6, rangeList, 'unknown')
+ }
+
+ return !excludeList.includes(matched)
+}
+
function computeResolutionsToTranscode (videoFileHeight: number) {
const resolutionsEnabled: number[] = []
const configResolutions = CONFIG.TRANSCODING.RESOLUTIONS
generateRandomString,
getFormattedObjects,
isSignupAllowed,
+ isSignupAllowedForCurrentIP,
computeResolutionsToTranscode,
resetSequelizeInstance,
getServerActor,
'storage.avatars', 'storage.videos', 'storage.logs', 'storage.previews', 'storage.thumbnails', 'storage.torrents', 'storage.cache',
'log.level',
'user.video_quota',
- 'cache.previews.size', 'admin.email', 'signup.enabled', 'signup.limit', 'transcoding.enabled', 'transcoding.threads',
+ 'cache.previews.size', 'admin.email',
+ 'signup.enabled', 'signup.limit', 'signup.filters.cidr.whitelist', 'signup.filters.cidr.blacklist',
+ 'transcoding.enabled', 'transcoding.threads',
'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route',
'instance.default_nsfw_policy', 'instance.robots',
'services.twitter.username', 'services.twitter.whitelisted'
},
SIGNUP: {
get ENABLED () { return config.get<boolean>('signup.enabled') },
- get LIMIT () { return config.get<number>('signup.limit') }
+ get LIMIT () { return config.get<number>('signup.limit') },
+ FILTERS: {
+ CIDR: {
+ get WHITELIST () { return config.get<string[]>('signup.filters.cidr.whitelist') },
+ get BLACKLIST () { return config.get<string[]>('signup.filters.cidr.blacklist') }
+ }
+ }
},
USER: {
get VIDEO_QUOTA () { return config.get<number>('user.video_quota') }
} from '../../helpers/custom-validators/users'
import { isVideoExist } from '../../helpers/custom-validators/videos'
import { logger } from '../../helpers/logger'
-import { isSignupAllowed } from '../../helpers/utils'
-import { CONSTRAINTS_FIELDS } from '../../initializers'
+import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/utils'
+import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers'
import { Redis } from '../../lib/redis'
import { UserModel } from '../../models/account/user'
import { areValidationErrors } from './utils'
}
]
+const ensureUserRegistrationAllowedForIP = [
+ async (req: express.Request, res: express.Response, next: express.NextFunction) => {
+ const allowed = isSignupAllowedForCurrentIP(req.ip)
+
+ if (allowed === false) {
+ return res.status(403)
+ .send({ error: 'You are not on a network authorized for registration.' })
+ .end()
+ }
+
+ return next()
+ }
+]
+
const usersAskResetPasswordValidator = [
body('email').isEmail().not().isEmpty().withMessage('Should have a valid email'),
usersUpdateMeValidator,
usersVideoRatingValidator,
ensureUserRegistrationAllowed,
+ ensureUserRegistrationAllowedForIP,
usersGetValidator,
usersUpdateMyAvatarValidator,
usersAskResetPasswordValidator,
}
signup: {
- allowed: boolean
+ allowed: boolean,
+ allowedForCurrentIP: boolean
}
transcoding: {
version "1.1.3"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.3.tgz#710193264bb05c77b8c90d02f5aaf22216a667b2"
+cidr-regex@^2.0.8:
+ version "2.0.8"
+ resolved "https://registry.yarnpkg.com/cidr-regex/-/cidr-regex-2.0.8.tgz#c79bae6223d241c0860d93bfde1fb1c1c4fdcab6"
+ dependencies:
+ ip-regex "^2.1.0"
+
circular-json@^0.3.1:
version "0.3.3"
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
version "1.0.0"
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
+ip-regex@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
+
ip-set@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/ip-set/-/ip-set-1.0.1.tgz#633b66d0bd6c8d0de968d053263c9120d3b6727e"
version "1.7.0"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.7.0.tgz#2206ed334afc32e01fed3ee838b6b2521068b9d2"
+"ipaddr.js@https://github.com/whitequark/ipaddr.js.git#8e69afeb4053ee32447a101845f860848280eca5":
+ version "1.7.0"
+ resolved "https://github.com/whitequark/ipaddr.js.git#8e69afeb4053ee32447a101845f860848280eca5"
+
ipv6-normalize@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/ipv6-normalize/-/ipv6-normalize-1.0.1.tgz#1b3258290d365fa83239e89907dde4592e7620a8"
dependencies:
ci-info "^1.0.0"
+is-cidr@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/is-cidr/-/is-cidr-2.0.5.tgz#13227927d71865d1177fe0e5b60e6ddd3dee0034"
+ dependencies:
+ cidr-regex "^2.0.8"
+
is-data-descriptor@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"