honeypot 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -2,46 +2,59 @@
2
2
 
3
3
  Catch bad guys when they stick their hands in the honey.
4
4
 
5
- == models
5
+ == rails 3 only
6
6
 
7
- copy gemdir/models/*.rb to your models dir
7
+ uses rack
8
8
 
9
- == requestables interface
9
+ == honeypot models
10
10
 
11
- requestables (like User, Vote, etc.) should define #actor
11
+ honeypots (aka requestables like User, Vote, etc.) should define #actor
12
12
 
13
13
  class User < ActiveRecord::Base
14
- include RemoteRequestLogger
14
+ has_many :votes
15
+ include Honeypot
15
16
  def actor; self; end
16
17
  end
17
18
 
18
19
  class Vote < ActiveRecord::Base
19
20
  belongs_to :user
20
- include RemoteRequestLogger
21
+ include Honeypot
21
22
  def actor; user; end
22
23
  end
23
24
 
24
25
  == usage in controllers
25
26
 
26
- put this in your application controller so it has a maximum chance of grabbing the ip
27
+ when somebody touches a honeypot, make sure to log it:
27
28
 
28
- class ApplicationController < ActionController::Base
29
- # this is safe enough for engineyard cloud, where internal ip addresses always start with 10.
30
- def ensure_remote_ip
31
- session[:remote_ip] = request.remote_ip unless request.remote_ip.starts_with?('10.')
29
+ class UsersController < ApplicationController
30
+ def create
31
+ # [...]
32
+ @user.log_remote_request(session, request)
33
+ # [...]
34
+ end
35
+ end
36
+
37
+ class VotesController < ApplicationController
38
+ def create
39
+ # [...]
40
+ @vote.log_remote_request(session, request)
41
+ # [...]
32
42
  end
33
- prepend_before_filter :ensure_remote_ip
34
43
  end
35
44
 
36
- then you invoke log_remote_request with both session and request
45
+ and be creative...
37
46
 
38
- class SessionsController < ApplicationController
47
+ class SessionController < ApplicationController
48
+ # notice when a User logs in
39
49
  def create
40
- # @user = [...]
41
- @user.log_remote_request session, request
50
+ # [...]
51
+ current_user.log_remote_request(session, request)
52
+ # [...]
42
53
  end
43
54
  end
44
55
 
56
+ you pass both the session and the request so that you have the maximum chance of getting a real remote ip
57
+
45
58
  == migration
46
59
 
47
60
  create_table "remote_hosts" do |t|
@@ -69,39 +82,9 @@ then you invoke log_remote_request with both session and request
69
82
  add_index "remote_requests", ["remote_host_id"], :name => "index_remote_requests_on_remote_host_id"
70
83
  add_index "remote_requests", ["requestable_type", "requestable_id"], :name => "index_remote_requests_on_requestable"
71
84
 
72
- == helper for active-scaffold
73
-
74
- def remote_requests_column(record)
75
- str = '<ul>'
76
- str << record.remote_requests.map { |x| content_tag :li, x.to_label }.join
77
- str << '</ul>'
78
- str
79
- end
85
+ == Acknowledgements
80
86
 
81
- == remote hosts controller with active-scaffold
82
-
83
- class RemoteHostsController < ApplicationController
84
- allow_access :admin
85
- layout 'admin'
86
- helper :remote_hosts
87
- active_scaffold :remote_host do |config|
88
- config.list.per_page = 100
89
- config.actions.exclude :search, :create, :update, :delete
90
- config.columns.add :remote_requests_count
91
- config.columns[:remote_requests_count].sort_by :sql => "(SELECT COUNT(remote_requests.id) FROM remote_requests WHERE remote_requests.remote_host_id = remote_hosts.id)"
92
- config.columns.add :last_remote_request_at
93
- config.columns[:last_remote_request_at].sort_by :sql => "(SELECT MAX(remote_requests.updated_at) FROM remote_requests WHERE remote_requests.remote_host_id = remote_hosts.id)"
94
- config.list.columns = %w{
95
- hostname
96
- ip_address
97
- country_code
98
- state_name
99
- city
100
- remote_requests_count
101
- last_remote_request_at
102
- }
103
- end
104
- end
87
+ in production use at http://brighterplanet.com
105
88
 
106
89
  == Copyright
107
90
 
data/Rakefile CHANGED
@@ -10,8 +10,10 @@ begin
10
10
  gem.email = "seamus@abshere.net"
11
11
  gem.homepage = "http://github.com/seamusabshere/honeypot"
12
12
  gem.authors = ["Seamus Abshere"]
13
- gem.add_dependency 'fast_timestamp', '0.0.4'
14
- gem.add_dependency 'geokit', '1.5.0'
13
+ gem.add_dependency 'fast_timestamp', '>=0.0.4'
14
+ gem.add_dependency 'geokit', '>=1.5.0'
15
+ gem.add_dependency 'activesupport', '>=3.0.0beta2'
16
+ gem.add_dependency 'activerecord', '>=3.0.0beta2'
15
17
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
18
  end
17
19
  Jeweler::GemcutterTasks.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
data/honeypot.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{honeypot}
8
- s.version = "0.0.1"
8
+ s.version = "0.0.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Seamus Abshere"]
12
- s.date = %q{2010-02-02}
12
+ s.date = %q{2010-05-10}
13
13
  s.description = %q{Catch bad guys when they stick their hands in the honey.}
14
14
  s.email = %q{seamus@abshere.net}
15
15
  s.extra_rdoc_files = [
@@ -25,16 +25,18 @@ Gem::Specification.new do |s|
25
25
  "VERSION",
26
26
  "honeypot.gemspec",
27
27
  "lib/honeypot.rb",
28
- "lib/remote_host.rb",
29
- "lib/remote_request.rb",
30
- "lib/remote_request_logger.rb",
28
+ "lib/honeypot/ipaddr_ext.rb",
29
+ "lib/honeypot/rack.rb",
30
+ "lib/honeypot/railtie.rb",
31
+ "lib/honeypot/remote_host.rb",
32
+ "lib/honeypot/remote_request.rb",
31
33
  "test/helper.rb",
32
34
  "test/test_honeypot.rb"
33
35
  ]
34
36
  s.homepage = %q{http://github.com/seamusabshere/honeypot}
35
37
  s.rdoc_options = ["--charset=UTF-8"]
36
38
  s.require_paths = ["lib"]
37
- s.rubygems_version = %q{1.3.5}
39
+ s.rubygems_version = %q{1.3.6}
38
40
  s.summary = %q{Track remote requests to catch fraud.}
39
41
  s.test_files = [
40
42
  "test/helper.rb",
@@ -46,15 +48,21 @@ Gem::Specification.new do |s|
46
48
  s.specification_version = 3
47
49
 
48
50
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
49
- s.add_runtime_dependency(%q<fast_timestamp>, ["= 0.0.4"])
50
- s.add_runtime_dependency(%q<geokit>, ["= 1.5.0"])
51
+ s.add_runtime_dependency(%q<fast_timestamp>, [">= 0.0.4"])
52
+ s.add_runtime_dependency(%q<geokit>, [">= 1.5.0"])
53
+ s.add_runtime_dependency(%q<activesupport>, [">= 3.0.0beta2"])
54
+ s.add_runtime_dependency(%q<activerecord>, [">= 3.0.0beta2"])
51
55
  else
52
- s.add_dependency(%q<fast_timestamp>, ["= 0.0.4"])
53
- s.add_dependency(%q<geokit>, ["= 1.5.0"])
56
+ s.add_dependency(%q<fast_timestamp>, [">= 0.0.4"])
57
+ s.add_dependency(%q<geokit>, [">= 1.5.0"])
58
+ s.add_dependency(%q<activesupport>, [">= 3.0.0beta2"])
59
+ s.add_dependency(%q<activerecord>, [">= 3.0.0beta2"])
54
60
  end
55
61
  else
56
- s.add_dependency(%q<fast_timestamp>, ["= 0.0.4"])
57
- s.add_dependency(%q<geokit>, ["= 1.5.0"])
62
+ s.add_dependency(%q<fast_timestamp>, [">= 0.0.4"])
63
+ s.add_dependency(%q<geokit>, [">= 1.5.0"])
64
+ s.add_dependency(%q<activesupport>, [">= 3.0.0beta2"])
65
+ s.add_dependency(%q<activerecord>, [">= 3.0.0beta2"])
58
66
  end
59
67
  end
60
68
 
data/lib/honeypot.rb CHANGED
@@ -1,4 +1,48 @@
1
- # auto-loaded by rails
2
- # require 'remote_host'
3
- # require 'remote_request'
4
- # require 'remote_request_logger'
1
+ require 'ipaddr'
2
+ require 'set'
3
+ require 'active_support'
4
+ require 'active_record'
5
+ require 'honeypot/ipaddr_ext'
6
+ require 'honeypot/remote_request'
7
+ require 'honeypot/remote_host'
8
+ require 'honeypot/rack'
9
+
10
+ require 'honeypot/railtie' if defined?(Rails::Railtie)
11
+
12
+ module Honeypot
13
+ def self.included(base)
14
+ base.class_eval do
15
+ has_many :remote_requests, :as => :requestable, :dependent => :destroy
16
+ has_many :remote_hosts, :through => :remote_requests, :uniq => true
17
+ end
18
+ end
19
+
20
+ def log_remote_request(session, request)
21
+ effective_ip_address = session['honeypot.last_known_remote_ip'].present? ? session['honeypot.last_known_remote_ip'] : request.remote_ip
22
+ remote_host = RemoteHost.find_or_create_by_ip_address effective_ip_address
23
+ remote_request = remote_requests.find_or_create_by_remote_host_id remote_host.id
24
+ remote_request.last_http_referer = request.referer if request.referer.present?
25
+ remote_request.last_request_uri = request.request_uri if request.request_uri.present?
26
+ remote_request.increment :hits
27
+ remote_request.save!
28
+ true
29
+ end
30
+
31
+ def related_requestables(seen_remote_host_ids = [])
32
+ set = Set.new
33
+ conditions = seen_remote_host_ids.present? ? [ "remote_hosts.id NOT IN (?)", seen_remote_host_ids ] : nil
34
+ remote_hosts.where(conditions).find_in_batches do |batch|
35
+ batch.each do |remote_host|
36
+ seen_remote_host_ids << remote_host.id
37
+ remote_host.remote_requests.all(:include => :requestable).each do |remote_request|
38
+ set << remote_request.requestable
39
+ end
40
+ end
41
+ end
42
+ if respond_to?(:actor) and actor != self
43
+ set += actor.related_requestables(seen_remote_host_ids)
44
+ end
45
+ set.delete self
46
+ set
47
+ end
48
+ end
@@ -0,0 +1,18 @@
1
+ # http://codesnippets.joyent.com/posts/show/7546
2
+ class IPAddr
3
+ PRIVATE_RANGES = [
4
+ IPAddr.new('127.0.0.1/32'),
5
+ IPAddr.new('10.0.0.0/8'),
6
+ IPAddr.new('172.16.0.0/12'),
7
+ IPAddr.new('192.168.0.0/16')
8
+ ]
9
+
10
+ def private?
11
+ return false unless self.ipv4?
12
+ PRIVATE_RANGES.any? { |ipr| ipr.include? self }
13
+ end
14
+
15
+ def public?
16
+ !private?
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ # inspired by hoptoad_notifier
2
+ # http://charlesmaxwood.com/sessions-in-rack-and-rails-metal/
3
+ module Honeypot
4
+ # Middleware for Rack applications. Remote hosts will be tied together with remote requests.
5
+ class Rack
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ session = env['rack.session']
12
+ remote_ip = IPAddr.new env['action_dispatch.remote_ip'].to_s
13
+ session['honeypot.last_known_remote_ip'] = remote_ip.to_s if remote_ip.public?
14
+ @app.call env
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ require 'honeypot'
2
+ require 'rails'
3
+
4
+ module Honeypot
5
+ class Railtie < Rails::Railtie
6
+ initializer "honeypot.configure_rails_initialization" do |app|
7
+ app.middleware.use Honeypot::Rack
8
+ end
9
+ end
10
+ end
@@ -1,15 +1,6 @@
1
1
  class RemoteHost < ActiveRecord::Base
2
2
  has_many :remote_requests, :dependent => :destroy
3
3
 
4
- # start active-scaffold interface
5
- def remote_requests_count
6
- remote_requests.count
7
- end
8
- def last_remote_request_at
9
- remote_requests.calculate :max, :updated_at
10
- end
11
- # end active-scaffold interface
12
-
13
4
  include FastTimestamp
14
5
 
15
6
  def lookup_hostname
@@ -1,6 +1,4 @@
1
1
  class RemoteRequest < ActiveRecord::Base
2
2
  belongs_to :requestable, :polymorphic => :true
3
3
  belongs_to :remote_host
4
-
5
- delegate :to_label, :to => :requestable
6
4
  end
@@ -1,7 +1,10 @@
1
1
  require 'helper'
2
2
 
3
3
  class TestHoneypot < Test::Unit::TestCase
4
- def test_something_for_real
5
- flunk "hey buddy, you should probably rename this file and start testing for real"
4
+ def test_ip_is_recognized_as_public_or_private
5
+ assert IPAddr.new('192.168.1.1').private?
6
+ assert IPAddr.new('10.0.0.2').private?
7
+ assert IPAddr.new('172.16.0.2').private?
8
+ assert IPAddr.new('88.122.122.122').public?
6
9
  end
7
10
  end
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: honeypot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 2
9
+ version: 0.0.2
5
10
  platform: ruby
6
11
  authors:
7
12
  - Seamus Abshere
@@ -9,29 +14,65 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-02-02 00:00:00 -05:00
17
+ date: 2010-05-10 00:00:00 -04:00
13
18
  default_executable:
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
21
  name: fast_timestamp
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
20
24
  requirements:
21
- - - "="
25
+ - - ">="
22
26
  - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ - 0
30
+ - 4
23
31
  version: 0.0.4
24
- version:
32
+ type: :runtime
33
+ version_requirements: *id001
25
34
  - !ruby/object:Gem::Dependency
26
35
  name: geokit
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
30
38
  requirements:
31
- - - "="
39
+ - - ">="
32
40
  - !ruby/object:Gem::Version
41
+ segments:
42
+ - 1
43
+ - 5
44
+ - 0
33
45
  version: 1.5.0
34
- version:
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: activesupport
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 3
57
+ - 0
58
+ - 0beta2
59
+ version: 3.0.0beta2
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: activerecord
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 3
71
+ - 0
72
+ - 0beta2
73
+ version: 3.0.0beta2
74
+ type: :runtime
75
+ version_requirements: *id004
35
76
  description: Catch bad guys when they stick their hands in the honey.
36
77
  email: seamus@abshere.net
37
78
  executables: []
@@ -50,9 +91,11 @@ files:
50
91
  - VERSION
51
92
  - honeypot.gemspec
52
93
  - lib/honeypot.rb
53
- - lib/remote_host.rb
54
- - lib/remote_request.rb
55
- - lib/remote_request_logger.rb
94
+ - lib/honeypot/ipaddr_ext.rb
95
+ - lib/honeypot/rack.rb
96
+ - lib/honeypot/railtie.rb
97
+ - lib/honeypot/remote_host.rb
98
+ - lib/honeypot/remote_request.rb
56
99
  - test/helper.rb
57
100
  - test/test_honeypot.rb
58
101
  has_rdoc: true
@@ -68,18 +111,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
68
111
  requirements:
69
112
  - - ">="
70
113
  - !ruby/object:Gem::Version
114
+ segments:
115
+ - 0
71
116
  version: "0"
72
- version:
73
117
  required_rubygems_version: !ruby/object:Gem::Requirement
74
118
  requirements:
75
119
  - - ">="
76
120
  - !ruby/object:Gem::Version
121
+ segments:
122
+ - 0
77
123
  version: "0"
78
- version:
79
124
  requirements: []
80
125
 
81
126
  rubyforge_project:
82
- rubygems_version: 1.3.5
127
+ rubygems_version: 1.3.6
83
128
  signing_key:
84
129
  specification_version: 3
85
130
  summary: Track remote requests to catch fraud.
@@ -1,36 +0,0 @@
1
- module RemoteRequestLogger
2
- def self.included(base)
3
- base.class_eval do
4
- has_many :remote_requests, :as => :requestable, :dependent => :destroy
5
- has_many :remote_hosts, :through => :remote_requests, :uniq => true
6
- end
7
- end
8
-
9
- def log_remote_request(session, request)
10
- effective_ip_address = session[:remote_ip].present? ? session[:remote_ip] : request.remote_ip
11
- x = remote_requests.find_or_create_by_remote_host_id RemoteHost.find_or_create_by_ip_address(effective_ip_address).id
12
- x.last_http_referer = request.referer if request.referer.present?
13
- x.last_request_uri = request.request_uri if request.request_uri.present?
14
- x.increment :hits
15
- x.save!
16
- true
17
- end
18
-
19
- def related_requestables(seen_remote_host_ids = [])
20
- set = Set.new
21
- conditions = seen_remote_host_ids.present? ? [ "remote_hosts.id NOT IN (?)", seen_remote_host_ids ] : nil
22
- remote_hosts.scoped(:conditions => conditions).find_in_batches do |batch|
23
- batch.each do |remote_host|
24
- seen_remote_host_ids << remote_host.id
25
- remote_host.remote_requests.all(:include => :requestable).each do |remote_request|
26
- set << remote_request.requestable
27
- end
28
- end
29
- end
30
- if respond_to?(:actor) and actor != self
31
- set += actor.related_requestables(seen_remote_host_ids)
32
- end
33
- set.delete self
34
- set
35
- end
36
- end