Блокировка пользователей по ip адресу

Не так давно на один из моих сайтов стали проведить DDoS атаки, отсылая с разных ip адресов запросы на однин и тот же адрес. Количество запросов не такое большое, но сам action довольно тяжеловесный и занимает от 5 до 30 секунд.

Собственно поэтому и решил написать небольшую надстройку, для занесения нежелательных ip адресов в black-list.

У всех запросов была одна закономерность, запрос вываливался при валидации токена формы verify_authenticity_token. Поэтому и было решено начать оттуда.

Первое что было сделано, это логгирование "плохих" запросов (config/initializers/forgery_protection.rb):


module ActionController
module RequestForgeryProtection

def verify_authenticity_token
verified_request? || block_user! || raise(ActionController::InvalidAuthenticityToken)
end

def block_user!
BlockedIp.unverified_request!(request.remote_ip)
end

end

end


Далее собственно модель с тремя аттрибутами (ip адрес, количество попыток, статус) (apps/models/blocked_ip):

'
class BlockedIp < ActiveRecord::Base

before_save :change_status

STATUS_NONE = 'none'
STATUS_BLOCK = 'block'
STATUS_BLOCKED = 'blocked'

ALLOWED_ATEMTPS = 5

named_scope :pending_for_block, {:conditions => ["status = ?", STATUS_BLOCK]}

def BlockedIp.unverified_request!(ip_address)
ip = BlockedIp.find_by_ip_address(ip_address)
ip ||= BlockedIp.new(:ip_address => ip_address)
ip.violation!
false
end

def violation!
self.attempts += 1
self.save
end

def block!
`iptables -I INPUT -s #{self.ip_address} -j DROP`
self.update_attribute(:status, STATUS_BLOCKED)
AdminNotifier.deliver_ip_blocked(self)
self.save
end

private
def change_status
if self.attempts > ALLOWED_ATEMTPS and self.status != STATUS_BLOCKED
self.status = STATUS_BLOCK
end
end

end


Тут все довольно просто, есть три статуса:

* none - пока ничего страшного
* block - кандидат на блокирование
* blocked - заблокирован

Ну и последнее, так как команда iptables должна выполнятся в режиме root'a, то самым простым решением было написать rake таск и запускать его из рутового кронтаба:

$ sudo crontab -e
*/10 * * * * cd /your/apps/here && RAILS_ENV=production rake

И собственно сам rake таск:


namespace :maintenance do

desc "Block ips"
task :block_ips => :environment do
BlockedIp.pending_for_block.each do |ip|
ip.block!
end
end

end


В принципе при больших нагрузках можно снизить планку ALLOWED_ATEMTPS, но появляется вероятность отсеить не того пользователя.