bind_log_analyzer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +6 -0
- data/README.md +126 -0
- data/Rakefile +110 -0
- data/bin/bind_log_analyzer +98 -0
- data/bind_log_analyzer.gemspec +24 -0
- data/db/migrate/20120329151738_create_logs.rb +11 -0
- data/doc/database.yml +7 -0
- data/doc/update_bind_log_analyzer.sh +16 -0
- data/lib/bind_log_analyzer/base.rb +87 -0
- data/lib/bind_log_analyzer/connector.rb +47 -0
- data/lib/bind_log_analyzer/exceptions.rb +4 -0
- data/lib/bind_log_analyzer/version.rb +3 -0
- data/lib/bind_log_analyzer.rb +6 -0
- data/lib/models/log.rb +2 -0
- data/spec/bind_log_analyzer_spec.rb +86 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- metadata +101 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# Bind Log Analyzer
|
2
|
+
|
3
|
+
Simple analysis and SQL storage for Bind DNS server's logs
|
4
|
+
|
5
|
+
## Requirements
|
6
|
+
|
7
|
+
This gem was tested with:
|
8
|
+
|
9
|
+
- ruby-1.9.3-p125
|
10
|
+
- rubygem (1.8.15)
|
11
|
+
- bundler (1.0.21)
|
12
|
+
- activerecord (3.2.2)
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Just install the gem:
|
17
|
+
|
18
|
+
gem install bind_log_analyzer
|
19
|
+
|
20
|
+
The gem requires **active_record** but you probably need to install the right adapter. As example, if you'll use MySQL, install the **mysql2** gem.
|
21
|
+
|
22
|
+
## Configuration
|
23
|
+
|
24
|
+
### Bind
|
25
|
+
|
26
|
+
To configure **Bind** add these lines to _/etc/bind/named.conf.options_ (or whatever your s.o. and bind installation require)
|
27
|
+
|
28
|
+
logging{
|
29
|
+
channel "querylog" {
|
30
|
+
file "/var/log/bind/query.log";
|
31
|
+
print-time yes;
|
32
|
+
};
|
33
|
+
|
34
|
+
category queries { querylog; };
|
35
|
+
};
|
36
|
+
|
37
|
+
Restart bind and make sure than the _query.log_ file contains lines as this:
|
38
|
+
|
39
|
+
28-Mar-2012 16:48:19.694 client 192.168.10.38#58767: query: www.github.com IN A + (192.168.10.1)
|
40
|
+
|
41
|
+
or the regexp will fail :(
|
42
|
+
|
43
|
+
### Database
|
44
|
+
|
45
|
+
To store the logs you can use every database supported by ActiveRecord. Just create a database and a user with the right privileges. You can provide the -s flag to *BindLogAnalyzer* to make it create the table. Otherwise create it by yourself.
|
46
|
+
This is the MySQL CREATE TABLE syntax:
|
47
|
+
|
48
|
+
CREATE TABLE `logs` (
|
49
|
+
`id` int(11) NOT NULL AUTO_INCREMENT,
|
50
|
+
`date` datetime NOT NULL,
|
51
|
+
`client` varchar(255) NOT NULL,
|
52
|
+
`query` varchar(255) NOT NULL,
|
53
|
+
`q_type` varchar(255) NOT NULL,
|
54
|
+
`server` varchar(255) NOT NULL,
|
55
|
+
PRIMARY KEY (`id`)
|
56
|
+
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1;
|
57
|
+
|
58
|
+
## Usage
|
59
|
+
|
60
|
+
Use the provided --help to get various options available. This is the default help:
|
61
|
+
|
62
|
+
-h, --help Display this screen
|
63
|
+
-v, --verbose LEVEL Enables verbose output. Use level 1 for WARN, 2 for INFO and 3 for DEBUG
|
64
|
+
-s, --setup Creates the needed tables in the database.
|
65
|
+
-f, --file FILE Indicates the log file to parse. It's mandatory.
|
66
|
+
-c, --config CONFIG A yaml file containing the database configurations under the "database" entry
|
67
|
+
-a, --adapter ADAPTER The database name to save the logs
|
68
|
+
-d, --database DATABASE The database name to save the logs
|
69
|
+
-H, --host HOST The address (IP, hostname or path) of the database
|
70
|
+
-P, --port PORT The port of the database
|
71
|
+
-u, --user USER The username to be used to connect to the database
|
72
|
+
-p, --password PASSWORD The password of the user
|
73
|
+
|
74
|
+
There's only one mandatory argument which is **--file FILE**. With this flag you pass the Bind log file to analyze to *BindLogAnalyzer*.
|
75
|
+
|
76
|
+
The first time you launch *BindLogAnalyzer* you can use the **-s|--setup** flag to make it create the table (using ActiveRecord::Migration).
|
77
|
+
The database credentials can be provided using the needed flags or creating a YAML file containing all the informations under the **database** key. This is an example:
|
78
|
+
|
79
|
+
database:
|
80
|
+
adapter: mysql2
|
81
|
+
database: bindloganalyzer
|
82
|
+
host: localhost
|
83
|
+
port: 3306
|
84
|
+
username: root
|
85
|
+
password:
|
86
|
+
|
87
|
+
## Automatization
|
88
|
+
|
89
|
+
A good way to use this script is to let it be launched by **logrotate** so create the _/etc/logrotate.d/bind_ file with this content:
|
90
|
+
|
91
|
+
/var/log/named/query.log {
|
92
|
+
weekly
|
93
|
+
missingok
|
94
|
+
rotate 8
|
95
|
+
compress
|
96
|
+
delaycompress
|
97
|
+
notifempty
|
98
|
+
create 644 bind bind
|
99
|
+
postrotate
|
100
|
+
if [ -e /var/log/named/query.log.1 ]; then
|
101
|
+
exec su - YOUR_USER -c '/usr/local/bin/update_bind_log_analyzer.sh /var/log/named/query.log.1'
|
102
|
+
fi
|
103
|
+
endscript
|
104
|
+
}
|
105
|
+
|
106
|
+
The script **/usr/local/bin/update_bind_log_analyzer.sh** can be wherever you prefer. Its typical content if you use RVM and a dedicated gemset for *BindLogAnalyzer*, can be:
|
107
|
+
|
108
|
+
#!/bin/bash
|
109
|
+
|
110
|
+
# *************************** #
|
111
|
+
# EDIT THESE VARS #
|
112
|
+
# *************************** #
|
113
|
+
BLA_RVM_GEMSET="1.9.3-p125@bind_log_analyzer"
|
114
|
+
BLA_USER="my_username"
|
115
|
+
BLA_DB_FILE="/etc/bind_log_analyzer/database.yml"
|
116
|
+
|
117
|
+
# *************************** #
|
118
|
+
# DO NOT EDIT BELOW THIS LINE #
|
119
|
+
# *************************** #
|
120
|
+
. /home/$BLA_USER/.rvm/scripts/rvm && source "/home/$BLA_USER/.rvm/scripts/rvm"
|
121
|
+
rvm use $BLA_RVM_GEMSET
|
122
|
+
bind_log_analyzer --config $BLA_DB_FILE --file $1
|
123
|
+
|
124
|
+
## To do
|
125
|
+
|
126
|
+
- Add a web interface to show the queries (with awesome graphs, obviously :)
|
data/Rakefile
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
require "bind_log_analyzer/version"
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
require "bundler/gem_tasks"
|
8
|
+
|
9
|
+
require 'yaml'
|
10
|
+
require 'logger'
|
11
|
+
require 'active_record'
|
12
|
+
|
13
|
+
namespace :spec do
|
14
|
+
desc "Run all specs"
|
15
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
16
|
+
#t.pattern = 'spec/**/*_spec.rb'
|
17
|
+
t.rspec_opts = ['--options', 'spec/spec.opts']
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Generate code coverage"
|
21
|
+
RSpec::Core::RakeTask.new(:coverage) do |t|
|
22
|
+
#t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
23
|
+
require 'simplecov'
|
24
|
+
SimpleCov.start
|
25
|
+
#t.rcov = true
|
26
|
+
#t.rcov_opts = ['--exclude', 'spec']
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
task :build do
|
31
|
+
system "gem build bind_log_analyzer.gemspec"
|
32
|
+
end
|
33
|
+
|
34
|
+
task :release => :build do
|
35
|
+
system "gem push pkg/bind_log_analyzer-#{BindLogAnalyzer::VERSION}.gem"
|
36
|
+
end
|
37
|
+
|
38
|
+
namespace :db do
|
39
|
+
def create_database config
|
40
|
+
options = {:charset => 'utf8', :collation => 'utf8_unicode_ci'}
|
41
|
+
|
42
|
+
create_db = lambda do |config|
|
43
|
+
ActiveRecord::Base.establish_connection config.merge('database' => nil)
|
44
|
+
ActiveRecord::Base.connection.create_database config['database'], options
|
45
|
+
ActiveRecord::Base.establish_connection config
|
46
|
+
end
|
47
|
+
|
48
|
+
begin
|
49
|
+
create_db.call config
|
50
|
+
rescue Mysql::Error => sqlerr
|
51
|
+
if sqlerr.errno == 1405
|
52
|
+
print "#{sqlerr.error}. \nPlease provide the root password for your mysql installation\n>"
|
53
|
+
root_password = $stdin.gets.strip
|
54
|
+
|
55
|
+
grant_statement = <<-SQL
|
56
|
+
GRANT ALL PRIVILEGES ON #{config['database']}.*
|
57
|
+
TO '#{config['username']}'@'localhost'
|
58
|
+
IDENTIFIED BY '#{config['password']}' WITH GRANT OPTION;
|
59
|
+
SQL
|
60
|
+
|
61
|
+
create_db.call config.merge('database' => nil, 'username' => 'root', 'password' => root_password)
|
62
|
+
else
|
63
|
+
$stderr.puts sqlerr.error
|
64
|
+
$stderr.puts "Couldn't create database for #{config.inspect}, charset: utf8, collation: utf8_unicode_ci"
|
65
|
+
$stderr.puts "(if you set the charset manually, make sure you have a matching collation)" if config['charset']
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
task :environment do
|
71
|
+
DATABASE_ENV = ENV['DATABASE_ENV'] || 'development'
|
72
|
+
MIGRATIONS_DIR = ENV['MIGRATIONS_DIR'] || 'db/migrate'
|
73
|
+
end
|
74
|
+
|
75
|
+
task :configuration => :environment do
|
76
|
+
@config = YAML.load_file('config/databases.yml')[DATABASE_ENV]
|
77
|
+
end
|
78
|
+
|
79
|
+
task :configure_connection => :configuration do
|
80
|
+
ActiveRecord::Base.establish_connection @config
|
81
|
+
ActiveRecord::Base.logger = Logger.new STDOUT if @config['logger']
|
82
|
+
end
|
83
|
+
|
84
|
+
desc 'Create the database from config/database.yml for the current DATABASE_ENV'
|
85
|
+
task :create => :configure_connection do
|
86
|
+
create_database @config
|
87
|
+
end
|
88
|
+
|
89
|
+
desc 'Drops the database for the current DATABASE_ENV'
|
90
|
+
task :drop => :configure_connection do
|
91
|
+
ActiveRecord::Base.connection.drop_database @config['database']
|
92
|
+
end
|
93
|
+
|
94
|
+
desc 'Migrate the database (options: VERSION=x, VERBOSE=false).'
|
95
|
+
task :migrate => :configure_connection do
|
96
|
+
ActiveRecord::Migration.verbose = true
|
97
|
+
ActiveRecord::Migrator.migrate MIGRATIONS_DIR, ENV['VERSION'] ? ENV['VERSION'].to_i : nil
|
98
|
+
end
|
99
|
+
|
100
|
+
desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
|
101
|
+
task :rollback => :configure_connection do
|
102
|
+
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
103
|
+
ActiveRecord::Migrator.rollback MIGRATIONS_DIR, step
|
104
|
+
end
|
105
|
+
|
106
|
+
desc "Retrieves the current schema version number"
|
107
|
+
task :version => :configure_connection do
|
108
|
+
puts "Current version: #{ActiveRecord::Migrator.current_version}"
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bind_log_analyzer"
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
@filename = nil
|
7
|
+
@log_level = 0
|
8
|
+
@database_confs = nil
|
9
|
+
@setup_database = false
|
10
|
+
@db_yaml = nil
|
11
|
+
@db_adapter = 'mysql2'
|
12
|
+
@db_host = nil
|
13
|
+
@db_database = nil
|
14
|
+
@db_port = nil
|
15
|
+
@db_username = nil
|
16
|
+
@db_password = nil
|
17
|
+
|
18
|
+
optparse = OptionParser.new do |opts|
|
19
|
+
# Set a banner, displayed at the top
|
20
|
+
# of the help screen.
|
21
|
+
opts.banner = "Usage: ./#{File.basename(__FILE__)} (--file|-f FILENAME [--setup|-s] [--verbose|-v (1|2|3)] [--database|-d database_name] [--config|-c database_yaml_configurations] [--host|-H database_hostname] [--port|-p database_port] [--user|-u database_username] [--password|-p database_password] | --help|-h)"
|
22
|
+
|
23
|
+
# This displays the help screen
|
24
|
+
opts.on( '-h', '--help', 'Display this screen' ) do
|
25
|
+
puts opts
|
26
|
+
exit
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on( '-v', '--verbose LEVEL', "Enables verbose output. Use level 1 for WARN, 2 for INFO and 3 for DEBUG" ) do |opt|
|
30
|
+
case
|
31
|
+
when 1 === opt.to_i
|
32
|
+
@log_level = 1
|
33
|
+
when 2 === opt.to_i
|
34
|
+
@log_level = 2
|
35
|
+
when opt.to_i >= 3
|
36
|
+
@log_level = 3
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on( '-s', '--setup', "Creates the needed tables in the database." ) do |opt|
|
41
|
+
@setup_database = true
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on( '-f', '--file FILE', "Indicates the log file to parse. It's mandatory." ) do |opt|
|
45
|
+
@filename = opt
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on( '-c', '--config CONFIG', 'A yaml file containing the database configurations under the "database" entry' ) do |opt|
|
49
|
+
@db_yaml = opt
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on( '-a', '--adapter ADAPTER', 'The database name to save the logs' ) do |opt|
|
53
|
+
@db_adapter = opt
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.on( '-d', '--database DATABASE', 'The database name to save the logs' ) do |opt|
|
57
|
+
@db_database = opt
|
58
|
+
end
|
59
|
+
|
60
|
+
opts.on( '-H', '--host HOST', 'The address (IP, hostname or path) of the database' ) do |opt|
|
61
|
+
@db_host = opt
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on( '-P', '--port PORT', 'The port of the database' ) do |opt|
|
65
|
+
@db_port = opt
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.on( '-u', '--user USER', 'The username to be used to connect to the database' ) do |opt|
|
69
|
+
@db_username = opt
|
70
|
+
end
|
71
|
+
|
72
|
+
opts.on( '-p', '--password PASSWORD', 'The password of the user' ) do |opt|
|
73
|
+
@db_password = opt
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
optparse.parse!
|
78
|
+
|
79
|
+
if @db_yaml
|
80
|
+
@database_confs = @db_yaml
|
81
|
+
elsif @db_username || @db_password || @db_host || @db_port || @db_database
|
82
|
+
@database_confs = {
|
83
|
+
adapter: @db_adapter,
|
84
|
+
username: @db_username,
|
85
|
+
password: @db_password,
|
86
|
+
host: @db_host,
|
87
|
+
port: @db_port,
|
88
|
+
database: @db_database
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
if @filename
|
93
|
+
@base = BindLogAnalyzer::Base.new(@database_confs, @filename, @setup_database, @log_level)
|
94
|
+
@base.analyze
|
95
|
+
else
|
96
|
+
puts optparse.banner
|
97
|
+
exit -1
|
98
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "bind_log_analyzer/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "bind_log_analyzer"
|
7
|
+
s.version = BindLogAnalyzer::VERSION
|
8
|
+
s.authors = ["Tommaso Visconti"]
|
9
|
+
s.email = ["tommaso.visconti@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/tommyblue/Bind-Log-Analyzer"
|
11
|
+
s.summary = %q{Log analysis and SQL storage for Bind DNS server}
|
12
|
+
s.description = %q{BindLogAnalyzer analyzes a Bind query log file and stores it's data into a SQL database (ActiveRecord is used for this feature)}
|
13
|
+
|
14
|
+
s.rubyforge_project = "bind_log_analyzer"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency "activerecord"
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
s.add_development_dependency "simplecov"
|
24
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class CreateLogs < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :logs do |t|
|
4
|
+
t.datetime :date, :null => false
|
5
|
+
t.string :client, :null => false
|
6
|
+
t.string :query, :null => false
|
7
|
+
t.string :q_type, :null => false
|
8
|
+
t.string :server, :null => false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/doc/database.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# *************************** #
|
4
|
+
# EDIT THESE VARS #
|
5
|
+
# *************************** #
|
6
|
+
BLA_PATH="~/Devel/bind-log-analyzer"
|
7
|
+
BLA_RVM_GEMSET="1.9.3-p125@bind_log_analyzer"
|
8
|
+
BLA_USER="my_username"
|
9
|
+
|
10
|
+
# *************************** #
|
11
|
+
# DO NOT EDIT BELOW THIS LINE #
|
12
|
+
# *************************** #
|
13
|
+
cd $BLA_PATH
|
14
|
+
. /home/$BLA_USER/.rvm/scripts/rvm && source "/home/$BLA_USER/.rvm/scripts/rvm"
|
15
|
+
rvm use $BLA_RVM_GEMSET
|
16
|
+
$BLA_PATH/bin/bind_log_analyzer -f $1
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'bind_log_analyzer/exceptions'
|
2
|
+
require 'bind_log_analyzer/connector'
|
3
|
+
require 'models/log'
|
4
|
+
|
5
|
+
module BindLogAnalyzer
|
6
|
+
class Base
|
7
|
+
include BindLogAnalyzer::Connector
|
8
|
+
|
9
|
+
attr_reader :log_filename, :database_confs
|
10
|
+
|
11
|
+
def initialize(database_confs = nil, logfile = nil, setup_database = false, log_level = 0)
|
12
|
+
if database_confs
|
13
|
+
if database_confs.instance_of?(Hash)
|
14
|
+
@database_confs = database_confs
|
15
|
+
else
|
16
|
+
# Load the yaml file
|
17
|
+
if FileTest.exists?(database_confs)
|
18
|
+
@database_confs = YAML::load(File.open(database_confs))['database']
|
19
|
+
else
|
20
|
+
raise BindLogAnalyzer::DatabaseConfsNotValid, "The indicated YAML file doesn't exist or is invalid"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
else
|
24
|
+
# Tries to find the yaml file or prints an error
|
25
|
+
filename = File.join(File.dirname(__FILE__), 'database.yml')
|
26
|
+
if FileTest.exists?(filename)
|
27
|
+
@database_confs = YAML::load(File.open(filename))['database']
|
28
|
+
else
|
29
|
+
raise BindLogAnalyzer::DatabaseConfsNotValid, "Can't find valid database configurations"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
@stored_queries = 0
|
34
|
+
self.logfile = logfile if logfile
|
35
|
+
setup_db(@database_confs, setup_database, log_level)
|
36
|
+
end
|
37
|
+
|
38
|
+
def logfile=(logfile)
|
39
|
+
@log_filename = logfile if FileTest.exists?(logfile)
|
40
|
+
end
|
41
|
+
|
42
|
+
def logfile
|
43
|
+
@log_filename
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse_line(line)
|
47
|
+
query = {}
|
48
|
+
regexp = %r{^(\d{2}-\w{3}-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d{3})\s+client\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})#\d+:\s+query:\s+(.*)\s+IN\s+(\w+)\s+\+\s+\((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\)$}
|
49
|
+
|
50
|
+
parsed_line = line.scan(regexp)
|
51
|
+
if parsed_line.size > 0
|
52
|
+
# Parse timestamp
|
53
|
+
parsed_timestamp = Date._strptime(parsed_line[0][0], "%d-%b-%Y %H:%M:%S.%L")
|
54
|
+
query_time = Time.local(parsed_timestamp[:year], parsed_timestamp[:mon], parsed_timestamp[:mday], parsed_timestamp[:hour], parsed_timestamp[:min], parsed_timestamp[:sec], parsed_timestamp[:sec_fraction], parsed_timestamp[:zone])
|
55
|
+
|
56
|
+
query[:date] = query_time
|
57
|
+
query[:client] = parsed_line[0][1]
|
58
|
+
query[:query] = parsed_line[0][2]
|
59
|
+
query[:q_type] = parsed_line[0][3]
|
60
|
+
query[:server] = parsed_line[0][4]
|
61
|
+
|
62
|
+
query
|
63
|
+
else
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def store_query(query)
|
69
|
+
log = Log.new(query)
|
70
|
+
@stored_queries += 1 if log.save
|
71
|
+
end
|
72
|
+
|
73
|
+
def analyze
|
74
|
+
return false unless @log_filename
|
75
|
+
|
76
|
+
lines = 0
|
77
|
+
File.new(@log_filename).each do |line|
|
78
|
+
query = self.parse_line(line)
|
79
|
+
if query
|
80
|
+
self.store_query(query)
|
81
|
+
lines += 1
|
82
|
+
end
|
83
|
+
end
|
84
|
+
puts "Analyzed #{lines} lines and correctly stored #{@stored_queries} logs"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module BindLogAnalyzer
|
5
|
+
module Connector
|
6
|
+
|
7
|
+
def setup_db(database_params, setup_database = false, log_level = 0)
|
8
|
+
@database_params = database_params
|
9
|
+
|
10
|
+
self.connect
|
11
|
+
|
12
|
+
migrate_tables if setup_database
|
13
|
+
|
14
|
+
self.load_environment
|
15
|
+
if log_level > 0
|
16
|
+
|
17
|
+
log_level_class = {
|
18
|
+
1 => Logger::WARN,
|
19
|
+
2 => Logger::INFO,
|
20
|
+
3 => Logger::DEBUG
|
21
|
+
}
|
22
|
+
|
23
|
+
log = Logger.new STDOUT
|
24
|
+
log.level = log_level_class[log_level]
|
25
|
+
ActiveRecord::Base.logger = log
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def migrate_tables
|
30
|
+
ActiveRecord::Migration.verbose = true
|
31
|
+
migrations_dir = File.join(File.dirname(__FILE__), '..', '..', 'db/migrate')
|
32
|
+
ActiveRecord::Migrator.migrate migrations_dir
|
33
|
+
end
|
34
|
+
|
35
|
+
def connect
|
36
|
+
ActiveRecord::Base.establish_connection(@database_params)
|
37
|
+
end
|
38
|
+
|
39
|
+
def connected?
|
40
|
+
ActiveRecord::Base.connected?
|
41
|
+
end
|
42
|
+
|
43
|
+
def load_environment
|
44
|
+
Dir.glob('./lib/models/*').each { |r| require r }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/models/log.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe BindLogAnalyzer do
|
4
|
+
before :all do
|
5
|
+
@db_params = YAML::load(File.open(File.join(File.dirname(__FILE__), '..', 'config', 'databases.yml')))['database']
|
6
|
+
|
7
|
+
# Create a test logfile
|
8
|
+
@filename = 'test_file.log'
|
9
|
+
@doc = <<EOF
|
10
|
+
28-Mar-2012 16:48:32.411 client 192.168.10.37#60303: query: github.com IN A + (192.168.10.1)
|
11
|
+
28-Mar-2012 16:48:32.412 client 192.168.10.201#60303: query: google.com IN AAAA + (192.168.10.1)
|
12
|
+
28-Mar-2012 16:48:32.898 client 192.168.10.114#53309: query: www.nasa.gov IN A + (192.168.10.1)
|
13
|
+
EOF
|
14
|
+
|
15
|
+
File.open(@filename, 'w') { |f| f.write(@doc) } unless FileTest.exists?(@filename)
|
16
|
+
end
|
17
|
+
|
18
|
+
after :all do
|
19
|
+
#delete the test logfile
|
20
|
+
File.delete(@filename) if FileTest.exists?(@filename)
|
21
|
+
end
|
22
|
+
|
23
|
+
before :each do
|
24
|
+
end
|
25
|
+
|
26
|
+
#it "can't be instantiated without database params" do
|
27
|
+
# base = BindLogAnalyzer::Base.new
|
28
|
+
# ??
|
29
|
+
#end
|
30
|
+
|
31
|
+
it "can be instantiated without a logfile" do
|
32
|
+
base = BindLogAnalyzer::Base.new(@db_params)
|
33
|
+
base.logfile.should == nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it "permit setting a logfile from initializer" do
|
37
|
+
@base = BindLogAnalyzer::Base.new(@db_params, @filename)
|
38
|
+
@base.logfile.should == @filename
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should update the logfile name" do
|
42
|
+
@base = BindLogAnalyzer::Base.new(@db_params, @filename)
|
43
|
+
|
44
|
+
# Create a new dummy file
|
45
|
+
@new_filename = 'new_test_file.log'
|
46
|
+
doc = ''
|
47
|
+
File.open(@new_filename, 'w') { |f| f.write(doc) } unless FileTest.exists?(@new_filename)
|
48
|
+
|
49
|
+
@base.logfile = @new_filename
|
50
|
+
@base.logfile.should == @new_filename
|
51
|
+
|
52
|
+
# Delete the file
|
53
|
+
File.delete(@new_filename)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "shouldn't update the logfile name if it doesn't exist" do
|
57
|
+
@base = BindLogAnalyzer::Base.new(@db_params, @filename)
|
58
|
+
@base.logfile = 'unexisting_test_file'
|
59
|
+
@base.logfile.should_not == 'unexisting_test_file'
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should correctly parse a line" do
|
63
|
+
@base = BindLogAnalyzer::Base.new(@db_params, @filename)
|
64
|
+
line = "28-Mar-2012 16:48:32.412 client 192.168.10.201#60303: query: google.com IN AAAA + (192.168.10.1)"
|
65
|
+
test_line = {
|
66
|
+
date: Time.local('2012','mar',28, 16, 48, 31),
|
67
|
+
client: "192.168.10.201",
|
68
|
+
query: "google.com",
|
69
|
+
type: "AAAA",
|
70
|
+
server: "192.168.10.1"
|
71
|
+
}
|
72
|
+
parsed_line = @base.parse_line(line)
|
73
|
+
parsed_line.should == test_line
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should be connected after setup_db is called" do
|
77
|
+
@base = BindLogAnalyzer::Base.new(@db_params, @filename)
|
78
|
+
@base.connected?.should == true
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should be possible to instantiate a Log class after BindLogAnalyzer::Base initialization" do
|
82
|
+
@base = BindLogAnalyzer::Base.new(@db_params, @filename)
|
83
|
+
log = Log.new
|
84
|
+
log.class.should_not == NilClass
|
85
|
+
end
|
86
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bind_log_analyzer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tommaso Visconti
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: &8286660 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *8286660
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &8286020 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *8286020
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: simplecov
|
38
|
+
requirement: &8285380 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *8285380
|
47
|
+
description: BindLogAnalyzer analyzes a Bind query log file and stores it's data into
|
48
|
+
a SQL database (ActiveRecord is used for this feature)
|
49
|
+
email:
|
50
|
+
- tommaso.visconti@gmail.com
|
51
|
+
executables:
|
52
|
+
- bind_log_analyzer
|
53
|
+
extensions: []
|
54
|
+
extra_rdoc_files: []
|
55
|
+
files:
|
56
|
+
- .gitignore
|
57
|
+
- Gemfile
|
58
|
+
- README.md
|
59
|
+
- Rakefile
|
60
|
+
- bin/bind_log_analyzer
|
61
|
+
- bind_log_analyzer.gemspec
|
62
|
+
- db/migrate/20120329151738_create_logs.rb
|
63
|
+
- doc/database.yml
|
64
|
+
- doc/update_bind_log_analyzer.sh
|
65
|
+
- lib/bind_log_analyzer.rb
|
66
|
+
- lib/bind_log_analyzer/base.rb
|
67
|
+
- lib/bind_log_analyzer/connector.rb
|
68
|
+
- lib/bind_log_analyzer/exceptions.rb
|
69
|
+
- lib/bind_log_analyzer/version.rb
|
70
|
+
- lib/models/log.rb
|
71
|
+
- spec/bind_log_analyzer_spec.rb
|
72
|
+
- spec/spec.opts
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
homepage: https://github.com/tommyblue/Bind-Log-Analyzer
|
75
|
+
licenses: []
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ! '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements: []
|
93
|
+
rubyforge_project: bind_log_analyzer
|
94
|
+
rubygems_version: 1.8.15
|
95
|
+
signing_key:
|
96
|
+
specification_version: 3
|
97
|
+
summary: Log analysis and SQL storage for Bind DNS server
|
98
|
+
test_files:
|
99
|
+
- spec/bind_log_analyzer_spec.rb
|
100
|
+
- spec/spec.opts
|
101
|
+
- spec/spec_helper.rb
|