mastodon/config/initializers/rack_attack.rb
Akihiko Odaki b1d4471e36 Throttle media post (#7337)
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.
2018-05-03 17:32:00 +02:00

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