· 8 years ago · Apr 18, 2018, 11:44 AM
1require 'ipaddr'
2
3module Rack
4 #
5 # BanHammer is a Rack middleware app that restricts access to your server
6 # using a black-list of IPv4/IPv6 addresses and ranges.
7 #
8 # MIT License - Hal Brodigan (postmodern.mod3 at gmail.com)
9 #
10 class BanHammer
11
12 DEFAULT_MESSAGE = %{
13 <html>
14 <head>
15 <title>Banned</title>
16 </head>
17 <body>
18 <h1>Banned</h1>
19
20 <p>Your IP address has been banned by BanHammer. Please contact the site administrators for information on why you were banned.</p>
21 </body>
22 </html>
23 }
24
25 #
26 # Initializes the ban hammer.
27 #
28 # @param [#call] app
29 # The Rack application to protect.
30 #
31 # @param [Hash] options
32 # Additional options.
33 #
34 # @option options [Array<String>] :banned ([])
35 # The list of IPv4/IPv6 addresses and netmasked ranges that are banned.
36 #
37 # @option options [String] :message ('')
38 # A message to display to those banned.
39 #
40 # @option options [String] :content_type ('text/html')
41 # The Content-Type of the ban message.
42 #
43 # @example
44 # use BanHammer, :banned => ['219.140.118.33/24'],
45 # :message => %{
46 # <html>
47 # <head>
48 # <title>Banned</title>
49 # </head>
50 #
51 # <body>
52 # <h1>Not even seven proxies can protect you from the BAN HAMMER.</h1>
53 # </body>
54 # </html>
55 # }
56 #
57 def initialize(app,options={})
58 @app = app
59 @banned = []
60
61 if options[:banned]
62 options[:banned].each do |ip|
63 @banned << IPAddr.new(ip)
64 end
65 end
66
67 @response = [
68 403,
69 {'Content-Type' => (options[:content_type] || 'text/html')},
70 [options[:message] || DEFAULT_MESSAGE]
71 ]
72 end
73
74 def call(env)
75 remote_addr = IPAddr.new(env['REMOTE_ADDR'])
76
77 @banned.each do |range|
78 return @response if range.include?(remote_addr)
79 end
80
81 @app.call(env)
82 end
83
84 end
85end