116 lines
3.4 KiB
Ruby
116 lines
3.4 KiB
Ruby
require 'rack/attack'
|
|
require 'netaddr'
|
|
|
|
class Rack::Attack
|
|
class Request
|
|
TOKEN = 'travis.access_token'.freeze
|
|
|
|
def travis_token
|
|
env.fetch(TOKEN)
|
|
end
|
|
|
|
def authenticated?
|
|
env.include? TOKEN
|
|
end
|
|
|
|
def identifier
|
|
authenticated? ? travis_token.to_s : ip
|
|
end
|
|
end
|
|
|
|
def self.bantime(value)
|
|
case Travis.env
|
|
when "production" then value
|
|
when "staging" then 10 # ban for 10 seconds on staging
|
|
else 1
|
|
end
|
|
end
|
|
|
|
POST_SAFELIST = [
|
|
"/auth/handshake",
|
|
"/auth/post_message",
|
|
"/auth/post_message/iframe"
|
|
]
|
|
|
|
GITHUB_CIDR = NetAddr::CIDR.create('192.30.252.0/22')
|
|
|
|
safelist('safelist build status images') do |request|
|
|
/\.(png|svg)$/.match(request.path)
|
|
end
|
|
|
|
# https://help.github.com/articles/what-ip-addresses-does-github-use-that-i-should-safelist/
|
|
safelist('safelist anything coming from github') do |request|
|
|
request.ip && GITHUB_CIDR.contains?(request.ip)
|
|
end
|
|
|
|
####
|
|
# Whitelisted IP addresses
|
|
safelist('safelist client requesting from redis') do |request|
|
|
# TODO: deprecate :api_whitelisted_ips in favour of api_safelisted_ips
|
|
Travis.redis.sismember(:api_whitelisted_ips, request.ip) || Travis.redis.sismember(:api_safelisted_ips, request.ip)
|
|
end
|
|
|
|
####
|
|
# Ban based on: IP address
|
|
# Ban time: indefinite
|
|
# Ban after: manually banned
|
|
blocklist('block client requesting from redis') do |request|
|
|
# TODO: deprecate :api_blacklisted_ips in favour of api_blocklisted_ips
|
|
Travis.redis.sismember(:api_blacklisted_ips, request.ip) || Travis.redis.sismember(:api_blocklisted_ips, request.ip)
|
|
end
|
|
|
|
####
|
|
# Ban based on: IP address or access token
|
|
# Ban time: 5 hours
|
|
# Ban after: 10 POST requests within five minutes to /auth/github
|
|
blocklist('hammering /auth/github') do |request|
|
|
Rack::Attack::Allow2Ban.filter(request.identifier, maxretry: 2, findtime: 5.minutes, bantime: bantime(5.hours)) do
|
|
request.post? and request.path == '/auth/github'
|
|
end
|
|
end
|
|
|
|
####
|
|
# Ban based on: IP address or access token
|
|
# Ban time: 1 hour
|
|
# Ban after: 10 POST requests within 30 seconds
|
|
blocklist('spamming with POST requests') do |request|
|
|
Rack::Attack::Allow2Ban.filter(request.identifier, maxretry: 10, findtime: 30.seconds, bantime: bantime(1.hour)) do
|
|
request.post? and not POST_SAFELIST.include? request.path
|
|
end
|
|
end
|
|
|
|
|
|
###
|
|
# Throttle: unauthenticated requests to /auth/github - 1 per minute
|
|
# Scoped by: IP address
|
|
throttle('req/ip/1min', limit: 1, period: 1.minute) do |request|
|
|
request.ip unless request.authenticated? and request.path == '/auth/github'
|
|
end
|
|
|
|
###
|
|
# Throttle: unauthenticated requests - 500 per minute
|
|
# Scoped by: IP address
|
|
throttle('req/ip/1min', limit: 500, period: 1.minute) do |request|
|
|
request.ip unless request.authenticated?
|
|
end
|
|
|
|
###
|
|
# Throttle: authenticated requests - 2000 per minute
|
|
# Scoped by: access token
|
|
throttle('req/token/1min', limit: 2000, period: 1.minute) do |request|
|
|
request.identifier
|
|
end
|
|
|
|
if ENV["MEMCACHIER_SERVERS"]
|
|
cache.store = Dalli::Client.new(
|
|
ENV["MEMCACHIER_SERVERS"].split(","),
|
|
username: ENV["MEMCACHIER_USERNAME"],
|
|
password: ENV["MEMCACHIER_PASSWORD"],
|
|
failover: true,
|
|
socket_timeout: 1.5,
|
|
socket_failure_delay: 0.2)
|
|
else
|
|
cache.store = ActiveSupport::Cache::MemoryStore.new
|
|
end
|
|
end
|