]> git.openstreetmap.org Git - chef.git/blob - cookbooks/tile/templates/default/tile-ratelimit.erb
Add IP based rate limiting to the render servers
[chef.git] / cookbooks / tile / templates / default / tile-ratelimit.erb
1 #!/usr/bin/ruby
2
3 require "apache_log_regex"
4 require "date"
5 require "file-tail"
6 require "gdbm"
7 require "lru_redux"
8
9 REQUESTS_PER_SECOND = <%= node[:tile][:ratelimit][:requests_per_second] %>
10 BLOCK_AT = <%= node[:tile][:ratelimit][:maximum_backlog] %>
11 UNBLOCK_AT = BLOCK_AT / 2
12
13 parser = ApacheLogRegex.new('%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"')
14 clients = LruRedux::Cache.new(1000000)
15
16 def decay_count(client, time)
17   decay = (time.to_i - client[:last_update]) * REQUESTS_PER_SECOND
18
19   client[:request_count] = [client[:request_count] - decay, 0].max
20   client[:last_update] = time.to_i
21 end
22
23 def write_blocked_ips(clients)
24   time = Time.now
25
26   File.open("/srv/tile.openstreetmap.org/conf/ip.map.new", "w") do |file|
27     clients.each do |address, client|
28       decay_count(client, time)
29
30       if client[:request_count] >= UNBLOCK_AT
31         file.puts "#{address} blocked"
32       elsif client.has_key?(:blocked_at)
33         puts "Unblocked #{address}"
34
35         client.delete(:blocked_at)
36       end
37     end
38   end
39
40   File.rename("/srv/tile.openstreetmap.org/conf/ip.map.new",
41               "/srv/tile.openstreetmap.org/conf/ip.map")
42
43   time + 900
44 end
45
46 next_check = write_blocked_ips(clients)
47
48 File::Tail::Logfile.tail("/var/log/apache2/access.log") do |line|
49   begin
50     hash = parser.parse!(line)
51
52     address = hash["%a"]
53
54     next if address == "127.0.0.1" || address == "::1"
55
56     time = Time.now
57
58     client = clients.getset(address) do
59       { :request_count => 0, :last_update => 0 }
60     end
61
62     decay_count(client, time)
63
64     client[:request_count] = client[:request_count] + 1
65
66     if client[:request_count] > BLOCK_AT && !client.has_key?(:blocked_at)
67       puts "Blocked #{address}"
68
69       client[:blocked_at] = time
70
71       next_check = time
72     elsif client[:request_count] < UNBLOCK_AT && client.has_key?(:blocked_at)
73       puts "Unblocked #{address}"
74
75       client.delete(:blocked_at)
76
77       next_check = time
78     end
79
80     if time >= next_check
81       next_check = write_blocked_ips(clients)
82     end
83   rescue ApacheLogRegex::ParseError
84     # nil
85   end
86 end