forked from fedi/mastodon
b1d4471e36
The previous rate limit allowed to post media so fast that it is possible to fill up the disk space even before an administrator notices. The new rate limit is configured so that it takes 24 hours to eat 10 gigabytes: 10 * 1024 / 8 / (24 * 60 / 30) = 27 (which rounded to 30) The period is set long so that it does not prevent from attaching several media to one post, which would happen in a short period. For example, if the period is 5 minutes, the rate limit would be: 10 * 1024 / 8 / (24 * 60 / 5) = 4 This long period allows to lift the limit up.
78 lines
2 KiB
Ruby
78 lines
2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'doorkeeper/grape/authorization_decorator'
|
|
|
|
class Rack::Attack
|
|
class Request
|
|
def authenticated_token
|
|
return @token if defined?(@token)
|
|
|
|
@token = Doorkeeper::OAuth::Token.authenticate(
|
|
Doorkeeper::Grape::AuthorizationDecorator.new(self),
|
|
*Doorkeeper.configuration.access_token_methods
|
|
)
|
|
end
|
|
|
|
def authenticated_user_id
|
|
authenticated_token&.resource_owner_id
|
|
end
|
|
|
|
def unauthenticated?
|
|
!authenticated_user_id
|
|
end
|
|
|
|
def api_request?
|
|
path.start_with?('/api')
|
|
end
|
|
|
|
def web_request?
|
|
!api_request?
|
|
end
|
|
end
|
|
|
|
PROTECTED_PATHS = %w(
|
|
/auth/sign_in
|
|
/auth
|
|
/auth/password
|
|
).freeze
|
|
|
|
PROTECTED_PATHS_REGEX = Regexp.union(PROTECTED_PATHS.map { |path| /\A#{Regexp.escape(path)}/ })
|
|
|
|
# Always allow requests from localhost
|
|
# (blocklist & throttles are skipped)
|
|
Rack::Attack.safelist('allow from localhost') do |req|
|
|
# Requests are allowed if the return value is truthy
|
|
'127.0.0.1' == req.ip || '::1' == req.ip
|
|
end
|
|
|
|
throttle('throttle_authenticated_api', limit: 300, period: 5.minutes) do |req|
|
|
req.api_request? && req.authenticated_user_id
|
|
end
|
|
|
|
throttle('throttle_unauthenticated_api', limit: 7_500, period: 5.minutes) do |req|
|
|
req.ip if req.api_request?
|
|
end
|
|
|
|
throttle('throttle_media', limit: 30, period: 30.minutes) do |req|
|
|
req.authenticated_user_id if req.post? && req.path.start_with('/api/v1/media')
|
|
end
|
|
|
|
throttle('protected_paths', limit: 25, period: 5.minutes) do |req|
|
|
req.ip if req.post? && req.path =~ PROTECTED_PATHS_REGEX
|
|
end
|
|
|
|
self.throttled_response = lambda do |env|
|
|
now = Time.now.utc
|
|
match_data = env['rack.attack.match_data']
|
|
|
|
headers = {
|
|
'Content-Type' => 'application/json',
|
|
'X-RateLimit-Limit' => match_data[:limit].to_s,
|
|
'X-RateLimit-Remaining' => '0',
|
|
'X-RateLimit-Reset' => (now + (match_data[:period] - now.to_i % match_data[:period])).iso8601(6),
|
|
}
|
|
|
|
[429, headers, [{ error: I18n.t('errors.429') }.to_json]]
|
|
end
|
|
end
|