honeypot 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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