honeycomb 0.0.1

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 ADDED
@@ -0,0 +1,72 @@
1
+ = honeycomb
2
+ Josh Grunzweig - jgrunzweig at trustwave dot com
3
+
4
+ == Introduction
5
+
6
+ Tool to manage and analyze data from the Dionaea Honeypot Project
7
+
8
+ The Dionaea Project is a great honeypot which originated from the Google Summer
9
+ of Code. The project aims at obtaining malware samples by emulating Microsoft
10
+ Windows services, however, has branched out since its create to emulate other
11
+ services as well, such as MySQL. This project was created out of the necessity
12
+ to monitor and manage multiple instances of the Dionaea on honeypots located
13
+ around the world.
14
+
15
+ You can view more information about the Dionaea at the following address:
16
+ http://dionaea.carnivore.it/
17
+
18
+
19
+ == Usage
20
+
21
+ # basic example... See how much disk space is located on all honeypots
22
+
23
+ require 'honeycomb'
24
+
25
+ all_pots = Honeycomb::Interact.new
26
+
27
+ all_pots.check_diskspace
28
+
29
+
30
+ # another example... See how many instances of a specific md5 there are
31
+
32
+ require 'honeycomb'
33
+
34
+ all_pots = Honeycomb::Interact.new
35
+
36
+ all_pots.all{Honeycomb::Download.all(:download_md5_hash => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")}
37
+
38
+ == Versioning
39
+ The current version of honeycomb at the time of writing is 0.0.1. Code will be
40
+ buggy. Features may be lacking. However, in time I hope to make this a pretty
41
+ functional/useful tool.
42
+
43
+
44
+ == Requirements
45
+ There is, unfortunately, a number of gem pre-requisites. Apologies for this,
46
+ but it's the only way I could do it.
47
+
48
+ * bundler
49
+
50
+ After bundler is installed, do a "bundle install" to get everything installed.
51
+
52
+
53
+ == Copyright
54
+ honeycomb - Tool to manage and analyze data from the Dionaea Honeypot Project
55
+ Josh Grunzweig
56
+ Copyright (C) 2011 Trustwave Holdings
57
+
58
+ This program is free software: you can redistribute it and/or modify it
59
+ under the terms of the GNU General Public License as published by the
60
+ Free Software Foundation, either version 3 of the License, or (at your
61
+ option) any later version.
62
+
63
+ This program is distributed in the hope that it will be useful, but
64
+ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
65
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
66
+ for more details.
67
+
68
+ You should have received a copy of the GNU General Public License along
69
+ with this program. If not, see <http://www.gnu.org/licenses/>.
70
+
71
+ See LICENSE.txt
72
+
data/Rakefile ADDED
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ require 'rake'
6
+
7
+ begin
8
+ Bundler.setup(:default, :development)
9
+ rescue Bundler::BundlerError => e
10
+ $stderr.puts e.message
11
+ $stderr.puts "Run `bundle install` to install missing gems"
12
+ exit e.status_code
13
+ end
14
+
15
+ Dir["tasks/*.rake"].each { |taskfile| load taskfile }
16
+
17
+ require 'jeweler'
18
+ Jeweler::Tasks.new do |gem|
19
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
20
+ gem.name = "honeycomb"
21
+ gem.homepage = "http://github.com/spiderlabs/honeycomb"
22
+ gem.license = "GNU v3"
23
+ gem.summary = %Q{Tool to manage and analyze data from the Dionaea Honeypot Project}
24
+ gem.description = %Q{Tool to manage and analyze data from the Dionaea Honeypot Project}
25
+ gem.email = "jgrunzweig at trustwave dot com"
26
+ gem.authors = ["Josh Grunzweig"]
27
+ # dependencies defined in Gemfile
28
+ end
29
+ Jeweler::RubygemsDotOrgTasks.new
30
+
31
+ require 'rspec/core'
32
+ require 'rspec/core/rake_task'
33
+ RSpec::Core::RakeTask.new(:spec) do |spec|
34
+ spec.pattern = FileList['spec/**/*_spec.rb']
35
+ end
36
+
37
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
38
+ spec.pattern = 'spec/**/*_spec.rb'
39
+ spec.rcov = true
40
+ end
41
+
42
+ task :default => :spec
43
+
44
+ require 'rdoc/task'
45
+ Rake::RDocTask.new do |rdoc|
46
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
47
+
48
+ rdoc.rdoc_dir = 'rdoc'
49
+ rdoc.title = "honeycomb #{version}"
50
+ rdoc.rdoc_files.include('README*')
51
+ rdoc.rdoc_files.include('lib/**/*.rb')
52
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
File without changes
File without changes
@@ -0,0 +1,11 @@
1
+ honey_config:
2
+ servers: ['honeypot1', 'honeypot2']
3
+ username: 'r00t'
4
+ key: 'path/to/key'
5
+ password: 'sekret'
6
+ path: 'path/to/dionaea'
7
+ # Uncomment to following to change default directory of
8
+ # honeycomb/data/binaries
9
+ # honeycomb/data/logsql
10
+ #download_binaries: 'where/to/store/binaries'
11
+ #download_databases: 'where/to/store/databases'
data/lib/honeycomb.rb ADDED
@@ -0,0 +1,24 @@
1
+ # honeycomb - Tool to manage and analyze data from the Dionaea Honeypot
2
+ # Project
3
+ # Josh Grunzweig
4
+ # Copyright (C) 2011 Trustwave Holdings
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify it
7
+ # under the terms of the GNU General Public License as published by the
8
+ # Free Software Foundation, either version 3 of the License, or (at your
9
+ # option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful, but
12
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
+ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
+ # for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along
17
+ # with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'honeycomb/interact'
21
+ require 'honeycomb/environment'
22
+ require 'honeycomb/default_setup'
23
+ require 'honeycomb/model'
24
+
@@ -0,0 +1,25 @@
1
+ # honeycomb - Tool to manage and analyze data from the Dionaea Honeypot
2
+ # Project
3
+ # Josh Grunzweig
4
+ # Copyright (C) 2011 Trustwave Holdings
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify it
7
+ # under the terms of the GNU General Public License as published by the
8
+ # Free Software Foundation, either version 3 of the License, or (at your
9
+ # option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful, but
12
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
+ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
+ # for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along
17
+ # with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'honeycomb'
21
+ require 'honeycomb/model'
22
+
23
+ Honeycomb::Env.read_config
24
+ Honeycomb::Model.setup!
25
+
@@ -0,0 +1,64 @@
1
+ # honeycomb - Tool to manage and analyze data from the Dionaea Honeypot
2
+ # Project
3
+ # Josh Grunzweig
4
+ # Copyright (C) 2011 Trustwave Holdings
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify it
7
+ # under the terms of the GNU General Public License as published by the
8
+ # Free Software Foundation, either version 3 of the License, or (at your
9
+ # option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful, but
12
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
+ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
+ # for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along
17
+ # with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'pathname'
21
+ require 'yaml'
22
+
23
+ module Honeycomb
24
+
25
+ module Environment
26
+ CONFIG = {}
27
+ CFGFILE = Pathname.new(__FILE__).dirname.dirname.dirname.expand_path.join('etc').join('config.yml')
28
+
29
+ # TODO: Comment
30
+ def self.load_config(hash)
31
+ hash.each do |k,v|
32
+ if v.is_a?(String)
33
+ v = v.gsub(/\$([A-Z][A-Z0-9_]*)\$/) do |v|
34
+ var = $1
35
+ if const_defined?(var)
36
+ const_get(var).to_s
37
+ else
38
+ raise("Invalid variable referenced in configuration: #{v}")
39
+ end
40
+ end
41
+ end
42
+ CONFIG[k.to_s] = v
43
+ end
44
+ end
45
+
46
+ # TODO: Comment
47
+ def self.read_config(file=CFGFILE)
48
+ if h = YAML.load_file(file)
49
+ if h.is_a?(Hash)
50
+ load_config(h)
51
+ else
52
+ raise("invalid honeycomb config file format")
53
+ end
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ Env = Environment
60
+
61
+ end
62
+
63
+
64
+
@@ -0,0 +1,20 @@
1
+ # honeycomb - Tool to manage and analyze data from the Dionaea Honeypot
2
+ # Project
3
+ # Josh Grunzweig
4
+ # Copyright (C) 2011 Trustwave Holdings
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify it
7
+ # under the terms of the GNU General Public License as published by the
8
+ # Free Software Foundation, either version 3 of the License, or (at your
9
+ # option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful, but
12
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
+ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
+ # for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along
17
+ # with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'honeycomb/interact/interact'
@@ -0,0 +1,392 @@
1
+ # honeycomb - Tool to manage and analyze data from the Dionaea Honeypot
2
+ # Project
3
+ # Josh Grunzweig
4
+ # Copyright (C) 2011 Trustwave Holdings
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify it
7
+ # under the terms of the GNU General Public License as published by the
8
+ # Free Software Foundation, either version 3 of the License, or (at your
9
+ # option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful, but
12
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
+ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
+ # for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along
17
+ # with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'net/ssh'
21
+ require 'net/scp'
22
+ require 'open3'
23
+
24
+ module Honeycomb
25
+
26
+ class Interact
27
+
28
+ attr_accessor :db_path, :bin_path, :base_path, :username, :servers, :key
29
+
30
+ # This initializes a Honeycomb::Interact object and sets all the necessary
31
+ # variables which are used by other methods of the object.
32
+ #
33
+ # Variables and their purpose:
34
+ # * db_path - Path where databases are stored/saved
35
+ # * bin_path - Path where binaries are stored/saved
36
+ # * username - Username to connect to remote honeypot servers
37
+ # * key - Path to private key which is used for connections to honeypot
38
+ # servers
39
+ # * servers - Array of servers to connect to
40
+ # * base_path - Base location where Dionaea is installed to (Default per
41
+ # installation instructions: /opt/dionaea)
42
+ def initialize(db_path = nil, bin_path = nil, username = nil, key = nil,
43
+ servers = nil, base_path = nil)
44
+ self.db_path = Honeycomb::Env::CONFIG[:download_databases] ||
45
+ self.db_path = Pathname.new(__FILE__).dirname.dirname.dirname.dirname.expand_path.join('data').join('logsql/').to_s ||
46
+ db_path
47
+ self.bin_path = Honeycomb::Env::CONFIG[:download_binaries] ||
48
+ self.bin_path = Pathname.new(__FILE__).dirname.dirname.dirname.dirname.expand_path.join('data').join('binaries/').to_s ||
49
+ bin_path
50
+ self.username = Honeycomb::Env::CONFIG["honey_config"]["username"] ||
51
+ username
52
+ self.key = Honeycomb::Env::CONFIG["honey_config"]["key"] || key
53
+ self.servers = Honeycomb::Env::CONFIG["honey_config"]["servers"] ||
54
+ servers
55
+ self.base_path = Honeycomb::Env::CONFIG["honey_config"]["path"] ||
56
+ base_path
57
+ end
58
+
59
+ # This method will attempt to download all binaries from all servers
60
+ # specified in Honeycomb::Interact.servers.
61
+ #
62
+ # It will attempt to store all binaries into the folder specified in
63
+ # Honeycomb::Interact.bin_path.
64
+ #
65
+ # Additionally, rsync is utilized to transfer these files. It was
66
+ # chosen to use rsync over scp in order to limit the amount of
67
+ # bandwidth used between the client and servers.
68
+ #
69
+ # Arguments:
70
+ # * server - Array of servers to query
71
+ def download_binaries(server = self.servers)
72
+ server.each do |server|
73
+ tries = 0
74
+ puts "Downloading binaries from #{server} ..."
75
+ begin
76
+ Open3::popen3("rsync -v --force --ignore-errors --times -r -u -e \"ssh -i #{self.key}\" #{self.username}@#{server}:#{self.base_path}/var/dionaea/binaries/ #{self.bin_path}") { |stdin, stdout, stderr|
77
+ puts stdout.read.strip
78
+ puts stderr.read.strip
79
+ }
80
+ rescue
81
+ tries += 1
82
+ retry if tries <= 3
83
+ puts "Unable to connect. Moving on ..."
84
+ next
85
+ end
86
+ end
87
+ end
88
+
89
+ # This method will attempt to download all databases from all servers
90
+ # specified in Honeycomb::Interact.servers.
91
+ #
92
+ # It will attempt to store all binaries into the folder specified in
93
+ # Honeycomb::Interact.db_path.
94
+ #
95
+ # Additionally, scp is utilized to transfer these files. During tests,
96
+ # it was discovered that rsync had less than ideal results when
97
+ # downloading these files. While the transfer would appear to occur
98
+ # without error, the databases were often found to be corrupt.
99
+ #
100
+ # Arguments:
101
+ # * server - Array of servers to query
102
+ def download_databases(server = self.servers)
103
+ server.each do |server|
104
+ tries = 0
105
+ begin
106
+ Net::SSH.start(server, self.username, :keys => self.key) do |session|
107
+ puts "Downloading database from #{server} ..."
108
+ session.scp.download!(base_path + "/var/dionaea/logsql.sqlite",
109
+ self.db_path + "#{server}.sqlite")
110
+ end
111
+ rescue Errno::ETIMEDOUT
112
+ tries += 1
113
+ retry if tries <= 3
114
+ puts "Unable to connect. Moving on ..."
115
+ next
116
+ rescue Exception => e
117
+ puts "Error encountered: #{e.message}"
118
+ next
119
+ end
120
+ end
121
+ end
122
+
123
+ # This method will execute a command via ssh on all servers specified in
124
+ # the Honeycomb::Interact.servers variable. This command calls the internal
125
+ # ssh_command method in order to properly function.
126
+ #
127
+ # Argument:
128
+ # * command - Command to execute
129
+ #
130
+ # Returns:
131
+ # * Nothing
132
+ #
133
+ # Multiple strings with the results are outputted to the screen.
134
+ def execute_command(command)
135
+ response = self.ssh_command(command)
136
+ response.each do |server_hash|
137
+ puts "Executing #{command} on #{server_hash[:server]}:"
138
+ puts "\t#{server_hash[:result].gsub!(/\n/,"\n\t")}"
139
+ end
140
+ end # end execute_command
141
+
142
+ # This method will query the diskspace on all remote servers by calling
143
+ # the internal ssh_command method. It executes the command 'df -h /' and
144
+ # parses the results. The response is then parsed to return the total
145
+ # percentage of diskspace being used currently on each host.
146
+ #
147
+ # Arguments:
148
+ # * None
149
+ #
150
+ # Returns:
151
+ # * [ {:server => "Server Hostname", :result =>
152
+ #
153
+ # Multiple strings with the results are outputted to the screen.
154
+ def check_diskspace
155
+ response = self.ssh_command("df -h /")
156
+ all_usage = []
157
+ response.each do |server_hash|
158
+ usage = server_hash[:result]
159
+ if usage =~ /^(\/\w+)+.+\S+\s+\S+\s+\S+\s+(([0-9]+)%)/m
160
+ all_usage << {:server => server_hash[:server], :result => $2}
161
+ end
162
+ end
163
+ all_usage
164
+ end # end check_diskspace
165
+
166
+ # Only leaving this in here because it does a decent job of showing some
167
+ # of the stuff you can do with these DataMapper database bindings.
168
+ #
169
+ # This method was mainly created to be utilized for another project. It
170
+ # takes the md5 as in argument and performs a series of queries against
171
+ # all of the database to retrieve a large amount of data about that
172
+ # provided binary (in md5 checksum)
173
+ #
174
+ # Multiple encounters can be returned. These encounters are uniqued based
175
+ # on the URL of the download. Additionally, if duplicates of a given url
176
+ # are discovered, the encounter with the earliest timestamp is added to
177
+ # the hash.
178
+ #
179
+ # Argument:
180
+ # * md5 - MD5 String of the binary to be examined.
181
+ #
182
+ # Returns:
183
+ # * Hash -
184
+ # {:md5 => <md5_provided>,
185
+ # :encounters = [{ :url => <url_discovered>,
186
+ # :original_filename => <md5_provided>,
187
+ # :remote_host => <ip_address>,
188
+ # :source_timestamp => <Time>,
189
+ # :source => "Honeypot - <ip_address>",
190
+ # :virustotal => {:url => <url>,
191
+ # :timestamp => <timestamp_of_vt_scan>,
192
+ # :results => {:scanner => <scanner>,
193
+ # :result => <result>}
194
+ # }
195
+ # }
196
+ def self.get_md5_info(md5)
197
+ all_encounters = []
198
+ all_instances = self.all{Honeycomb::Download.all(:download_md5_hash => md5)}
199
+ all_instances.each do |instance|
200
+ all_encounters << {:url => instance.download_url}
201
+ end
202
+ num = 0
203
+ all_encounters.uniq! {|e| e[:url] }
204
+ all_encounters.each do |url|
205
+ all_connections = self.all{Honeycomb::Download.all(:download_md5_hash => md5,
206
+ :download_url => url[:url]).connections}
207
+ connection = {}
208
+ all_connections.each do |conn|
209
+ if not connection[:source_timestamp].nil?
210
+ if Time.at(connection[:source_timestamp]) > Time.at(conn.connection_timestamp.to_i)
211
+ connection[:source_timestamp] = Time.at(conn.connection_timestamp.to_i)
212
+ connection[:remote_host] = conn.remote_host
213
+ connection[:source] = conn.local_host
214
+ end
215
+ else
216
+ connection[:source_timestamp] = Time.at(conn.connection_timestamp.to_i)
217
+ connection[:remote_host] = conn.remote_host
218
+ connection[:source] = conn.local_host
219
+ end
220
+ end
221
+ all_encounters[num][:original_filename] = md5
222
+ all_encounters[num][:remote_host] = connection[:remote_host]
223
+ all_encounters[num][:source_timestamp] = connection[:source_timestamp]
224
+ all_encounters[num][:source] = "Honeypot - #{connection[:source]}"
225
+ virustotal_links = self.all{Honeycomb::Virustotal.first(:virustotal_md5_hash => md5).virustotal_permalink}
226
+ virustotal_timestamp = self.all{Honeycomb::Virustotal.first(:virustotal_md5_hash => md5).virustotal_timestamp}
227
+ virustotal_results = {}
228
+ self.all{Honeycomb::Virustotal.first(:virustotal_md5_hash => md5).virustotalscans}.each do |vtscan|
229
+ virustotal_results[:scanner] = vtscan.virustotalscan_scanner
230
+ virustotal_results[:result] = vtscan.virustotalscan_result
231
+ end
232
+ all_encounters[num][:virustotal] = {:url => virustotal_links,
233
+ :timestamp => virustotal_timestamp, :results => virustotal_results} if virustotal_links
234
+ num += 1
235
+ end
236
+ return {:md5 => md5, :encounters => all_encounters}
237
+ end
238
+
239
+
240
+
241
+ # This method will call the get_md5_info method on all binaries located
242
+ # in the provided directory. This information is then outputted to screen
243
+ # (soon to be changed).
244
+ #
245
+ # Again, only leaving this in here because it does a decent job of showing
246
+ # the kind of information you can pull.
247
+ #
248
+ # Argument:
249
+ # * dif - Directory of binaries
250
+ #
251
+ # Retruns:
252
+ # * Nothing
253
+ #
254
+ # Multiple items are outputted to the screen.
255
+ def self.get_all_md5_info(dir = Pathname.new(__FILE__).dirname.dirname.dirname.dirname.expand_path.join('data').join('binaries/').to_s)
256
+ results = []
257
+ all_binaries = Dir.entries(dir)
258
+ all_binaries.each do |bin|
259
+ if bin =~ /\w{32}/
260
+ require 'pp'
261
+ pp self.get_md5_info(bin)
262
+ end
263
+ end
264
+ end
265
+
266
+ # Used for executing a query against all databases at once.
267
+ def all(&block)
268
+ all_values = []
269
+ ::DataMapper::Repository.adapters.each do |repo|
270
+ if repo[0] == :default
271
+ next
272
+ end
273
+ begin
274
+ response = DataMapper.repository(repo[0]) {yield}
275
+ if response.kind_of?(DataMapper::Collection)
276
+ response.each do |x|
277
+ all_values << x
278
+ end
279
+ else
280
+ all_values << response if response
281
+ end
282
+ rescue Exception => e
283
+ #puts e.message
284
+ end
285
+ end
286
+ all_values
287
+ end
288
+
289
+ # Used for executing a query against a single database.
290
+ def individual(repo, &block)
291
+ all_values = []
292
+ begin
293
+ response = DataMapper.repository(repo[0]) {yield}
294
+ if response.kind_of?(DataMapper::Collection)
295
+ response.each do |x|
296
+ all_values << x
297
+ end
298
+ else
299
+ all_values << response if response
300
+ end
301
+ rescue Exception => e
302
+ #puts e.message
303
+ end
304
+ all_values
305
+ end
306
+
307
+ # This method was created mainly for my own benefit, but I figured I'd
308
+ # leave it in here in case anyone else would like to use it. Was doing
309
+ # some statistics for a co-worker who was looking for information about
310
+ # the IP addresses which has connected to my honeypots.
311
+ #
312
+ # It's a little clunky right now, and I ended up just performing actual
313
+ # SQL queries due to memory issues (Some of my databases are over 10 gigs
314
+ # in size). It will write a list of IP addresses and the number of times
315
+ # encountered to the following log file:
316
+ #
317
+ # ip_reputation.txt
318
+ #
319
+ # The format of the data is:
320
+ #
321
+ # <ip_address>,<count>
322
+ #
323
+ # In order from most encountered to least encountered. I also ignore
324
+ # people that have connected to these honeypots less than 200 times.
325
+ #
326
+ def self.ip_reputation_rule
327
+ all_connections_hash = {}
328
+
329
+ ::DataMapper::Repository.adapters.each do |repo|
330
+ if repo[0] == :default
331
+ next
332
+ end
333
+ response = repo[1].select("SELECT COUNT(remote_host), remote_host FROM connections WHERE connection_type = \"accept\" GROUP BY remote_host ORDER BY COUNT(remote_host)")
334
+ response.each do |struct|
335
+ ip = struct.to_a[1]
336
+ count = struct.to_a[0]
337
+ if all_connections_hash[ip]
338
+ all_connections_hash[ip] = all_connections_hash[ip].to_i +
339
+ count.to_i
340
+ else
341
+ all_connections_hash[ip] = count.to_i
342
+ end
343
+ end
344
+ end
345
+ all_connections_hash = all_connections_hash.sort_by { |k,v| -1*v }
346
+ File.open("ip_reputation.txt", 'w') do |f|
347
+ all_connections_hash.each do |ip,count|
348
+ if count > 200
349
+ f.write("#{ip},#{count}\n")
350
+ end
351
+ end
352
+ end
353
+ end
354
+
355
+ # This method is used internally by the execute_command method.
356
+ # It will take a command as an argument and execute it on ever server
357
+ # that is stored in Honeycomb::Interact.servers. The results are
358
+ # stored in a hash which is returned in an Array.
359
+ #
360
+ # Argument:
361
+ # * command - Command to be executed
362
+ #
363
+ # Returns:
364
+ # * Array of hashes -
365
+ # [{:server => <server_name>, :result => <result_of_command>}]
366
+ def ssh_command(command)
367
+ results = []
368
+ self.servers.each do |server|
369
+ begin
370
+ Net::SSH.start(server, self.username, :keys => self.key) do |session|
371
+ session.exec command do |ch, stream, data|
372
+ if stream == :stderr
373
+ results << {:server => server, :result => "ERROR: #{data}"}
374
+ else
375
+ results << {:server => server, :result => data}
376
+ end
377
+ end
378
+ end
379
+ rescue
380
+ next
381
+ end
382
+ end
383
+ return results
384
+ end
385
+
386
+
387
+ end # end Interact
388
+
389
+ end # end Honeycomb
390
+
391
+
392
+