Собственно поэтому и решил написать небольшую надстройку, для занесения нежелательных 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, но появляется вероятность отсеить не того пользователя.