log_sense 1.5.1 → 1.6.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.org +51 -1
- data/Gemfile.lock +15 -6
- data/LICENSE.txt +9 -2
- data/README.org +113 -36
- data/Rakefile +6 -6
- data/exe/log_sense +110 -39
- data/ip_locations/dbip-country-lite.sqlite3 +0 -0
- data/lib/log_sense/aggregator.rb +191 -0
- data/lib/log_sense/apache_aggregator.rb +122 -0
- data/lib/log_sense/apache_log_line_parser.rb +23 -21
- data/lib/log_sense/apache_log_parser.rb +15 -12
- data/lib/log_sense/apache_report_shaper.rb +309 -0
- data/lib/log_sense/emitter.rb +80 -506
- data/lib/log_sense/ip_locator.rb +24 -12
- data/lib/log_sense/options_checker.rb +24 -0
- data/lib/log_sense/options_parser.rb +84 -50
- data/lib/log_sense/rails_aggregator.rb +69 -0
- data/lib/log_sense/rails_log_parser.rb +82 -68
- data/lib/log_sense/rails_report_shaper.rb +183 -0
- data/lib/log_sense/report_shaper.rb +105 -0
- data/lib/log_sense/templates/_cdn_links.html.erb +11 -0
- data/lib/log_sense/templates/_command_invocation.html.erb +4 -0
- data/lib/log_sense/templates/_log_structure.html.erb +13 -4
- data/lib/log_sense/templates/_output_table.html.erb +54 -2
- data/lib/log_sense/templates/_output_table.txt.erb +3 -1
- data/lib/log_sense/templates/_rails.css.erb +7 -0
- data/lib/log_sense/templates/_report_data.html.erb +4 -4
- data/lib/log_sense/templates/_summary.html.erb +14 -5
- data/lib/log_sense/templates/_summary.txt.erb +2 -2
- data/lib/log_sense/templates/{rails.html.erb → report_html.erb} +19 -37
- data/lib/log_sense/templates/{apache.txt.erb → report_txt.erb} +1 -1
- data/lib/log_sense/version.rb +1 -1
- data/lib/log_sense.rb +19 -9
- data/log_sense.gemspec +21 -21
- data/{apache-screenshot.png → screenshots/apache-screenshot.png} +0 -0
- data/screenshots/rails-screenshot.png +0 -0
- metadata +21 -16
- data/lib/log_sense/apache_data_cruncher.rb +0 -141
- data/lib/log_sense/rails_data_cruncher.rb +0 -141
- data/lib/log_sense/templates/apache.html.erb +0 -115
- data/lib/log_sense/templates/rails.txt.erb +0 -22
- data/sample_logs/empty_log.log +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7d269dedfbb6ec6eae3a77491cc5ec7ca6241f388f2658964541cdd3983b8298
|
|
4
|
+
data.tar.gz: 6f24d23c8d06430b3605aad90522e08818cd18fd6c71c5fe90823cdc9483c81e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a63f715b281101a6f61029da3e2bcf5db4d47a537af562507b0184d6c6755eed436afed729c31cc95255bf064cbe9f487917c96d78199bbee25e6d4189469951
|
|
7
|
+
data.tar.gz: 77c62a24c3c81067dd0288ad606b732e0f112d0d1710b326be9e894aa72a93db4cb0a188c93b54ada728c2eeb0cf7378fb7033e13982c9a0932414c8455d2703
|
data/CHANGELOG.org
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
#+AUTHOR: Adolfo Villafiorita
|
|
3
3
|
#+STARTUP: showall
|
|
4
4
|
|
|
5
|
+
* 1.6.0
|
|
6
|
+
|
|
7
|
+
- [User] New output format =ufw= generates directives to blacklist IPs
|
|
8
|
+
requesting URLs matching a pattern. For users of the Uncomplicated
|
|
9
|
+
Firewall.
|
|
10
|
+
- [User] new option =--no-geo= skips geolocation, which is terribly
|
|
11
|
+
costly in the current implementation.
|
|
12
|
+
- [User] Updated DB-IP country file to Dec 2022 version.
|
|
13
|
+
- [User] Changed name of SQLite output format to sqlite3
|
|
14
|
+
- [User] It is now possible to start analysis from a sqlite3 DB
|
|
15
|
+
generated by log_sense, breaking parsing and generation in two
|
|
16
|
+
steps.
|
|
17
|
+
- [User] Check for correctness of I/O formats before launching
|
|
18
|
+
analysis
|
|
19
|
+
- [User] Streak report has been renames Session. Limited the number
|
|
20
|
+
of URLs shown in each session, to avoid buffer?/memory overflows
|
|
21
|
+
when an IP requests a massive amount of URLs.
|
|
22
|
+
- [User] Added an IP-per-hour visits report.
|
|
23
|
+
- [Code] A rather extensive refactoring of the source code to
|
|
24
|
+
remove code duplications and improve code structure.
|
|
25
|
+
- [Code] Rubocop-ped various files
|
|
26
|
+
- [Code] Added text renderer to DataTable, which sanitizes input and
|
|
27
|
+
further reduces risks of XSS and log poisoning attacks
|
|
28
|
+
- [Code] CDN links have been ported into the Emitter module and used
|
|
29
|
+
in the Embedded Ruby Templates (erbs). This simplifies version
|
|
30
|
+
updates of Javascript libraries used in reports.
|
|
31
|
+
|
|
32
|
+
* 1.5.2
|
|
33
|
+
|
|
34
|
+
- [User] Updated DB-IP country file.
|
|
35
|
+
- [User] Added reports "Missed Pages by IP" and "Missed Resources by
|
|
36
|
+
IP". It can help pinpoint attack sources.
|
|
37
|
+
- [User] Added report "Combined Platform", which puts together
|
|
38
|
+
Browser, OS, and IP.
|
|
39
|
+
- [User] Summary now shows total size transferred.
|
|
40
|
+
- [User] Added link to DB-IP page for IPs in some tables.
|
|
41
|
+
- [User] Added count of IPs by Country.
|
|
42
|
+
- [User] Improved textual report: values in cells holding multiple
|
|
43
|
+
values (e.g. IPs) are now shown in distinct lines in the cell. A new
|
|
44
|
+
option -r limits the number of lines shown per cell.
|
|
45
|
+
- [Default] The number of rows initially shown in HTML reports is now 25.
|
|
46
|
+
- [Default] Default for number of entries in textual report is now
|
|
47
|
+
100 (used to be 900).
|
|
48
|
+
- [Fixed] The size column in HTML reports is now sorted numerically.
|
|
49
|
+
- [Code] Improved performances of DataTable rendering, using the
|
|
50
|
+
dataRender flag.
|
|
51
|
+
- [Code] Use trim_mode in ERB to avoid empty lines in HTML output.
|
|
52
|
+
- [Code] Moved to the debug gem.
|
|
53
|
+
- [Gem] Updated email and author's name.
|
|
54
|
+
|
|
5
55
|
* 1.5.1
|
|
6
56
|
|
|
7
57
|
- [User] Option --input-files allows to specify input files
|
|
@@ -16,7 +66,7 @@
|
|
|
16
66
|
- [User] Present Unique Visits / day as integer
|
|
17
67
|
- [User] Added Country and Streaks report for rails
|
|
18
68
|
- [User] Changed Streak report in Apache
|
|
19
|
-
- [Gem] Updated
|
|
69
|
+
- [Gem] Updated DB-IP
|
|
20
70
|
- [Gem] Updated Bundle
|
|
21
71
|
- [Code] Refactored all reports, so that they are specified
|
|
22
72
|
in the same way
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
log_sense (1.
|
|
4
|
+
log_sense (1.5.3)
|
|
5
5
|
browser
|
|
6
6
|
ipaddr
|
|
7
7
|
iso_country_codes
|
|
@@ -12,21 +12,30 @@ GEM
|
|
|
12
12
|
remote: https://rubygems.org/
|
|
13
13
|
specs:
|
|
14
14
|
browser (5.3.1)
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
debug (1.6.2)
|
|
16
|
+
irb (>= 1.3.6)
|
|
17
|
+
reline (>= 0.3.1)
|
|
18
|
+
io-console (0.5.11)
|
|
19
|
+
ipaddr (1.2.5)
|
|
20
|
+
irb (1.4.1)
|
|
21
|
+
reline (>= 0.3.0)
|
|
17
22
|
iso_country_codes (0.7.8)
|
|
23
|
+
mini_portile2 (2.8.0)
|
|
18
24
|
minitest (5.15.0)
|
|
19
25
|
rake (12.3.3)
|
|
20
|
-
|
|
26
|
+
reline (0.3.1)
|
|
27
|
+
io-console (~> 0.5)
|
|
28
|
+
sqlite3 (1.5.4)
|
|
29
|
+
mini_portile2 (~> 2.8.0)
|
|
21
30
|
terminal-table (3.0.2)
|
|
22
31
|
unicode-display_width (>= 1.1.1, < 3)
|
|
23
|
-
unicode-display_width (2.
|
|
32
|
+
unicode-display_width (2.3.0)
|
|
24
33
|
|
|
25
34
|
PLATFORMS
|
|
26
35
|
ruby
|
|
27
36
|
|
|
28
37
|
DEPENDENCIES
|
|
29
|
-
|
|
38
|
+
debug
|
|
30
39
|
log_sense!
|
|
31
40
|
minitest
|
|
32
41
|
rake (~> 12.0)
|
data/LICENSE.txt
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
The MIT License (MIT)
|
|
2
|
-
|
|
1
|
+
The source code is distributed under the terms of the MIT License (MIT)
|
|
3
2
|
Copyright (c) 2021 Shair.Tech
|
|
4
3
|
|
|
5
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
@@ -19,3 +18,11 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
19
18
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
19
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
20
|
THE SOFTWARE.
|
|
21
|
+
|
|
22
|
+
==============================================================================
|
|
23
|
+
LogSense uses data from The free DB-IP Lite database for geolocation purposes.
|
|
24
|
+
|
|
25
|
+
The DB-IP Lite Database is licensed under a Creative Commons Attribution 4.0
|
|
26
|
+
International License.
|
|
27
|
+
|
|
28
|
+
For more information: https://db-ip.com
|
data/README.org
CHANGED
|
@@ -9,29 +9,62 @@ Rails logs. Written in Ruby, it runs from the command line, it is
|
|
|
9
9
|
fast, and it can be installed on any system with a relatively recent
|
|
10
10
|
version of Ruby. We tested on Ruby 2.6.9, Ruby 3.0.x and later.
|
|
11
11
|
|
|
12
|
-
LogSense reports the following data:
|
|
12
|
+
When generating reports, LogSense reports the following data:
|
|
13
13
|
|
|
14
14
|
- Visitors, hits, unique visitors, bandwidth used
|
|
15
15
|
- Most accessed HTML pages
|
|
16
16
|
- Most accessed resources
|
|
17
|
+
- Missed resources (also by IP) which helps highlight
|
|
18
|
+
potential attacks
|
|
17
19
|
- Response statuses
|
|
18
20
|
- Referers
|
|
19
21
|
- OS, browsers, and devices
|
|
20
|
-
- IP Country location, thanks to the
|
|
22
|
+
- IP Country location, thanks to the DP-IP lite country DB
|
|
21
23
|
- Streaks: resources accessed by a given IP over time
|
|
22
24
|
- Performance of Rails requests
|
|
25
|
+
|
|
26
|
+
A special output format =ufw= generates rules for the [[https://launchpad.net/ufw][Uncomplicated
|
|
27
|
+
Firewall]] to blacklist IPs requesting URLs matching a specific pattern.
|
|
23
28
|
|
|
24
29
|
Filters from the command line allow to analyze specific periods and
|
|
25
30
|
distinguish traffic generated by self polls and crawlers.
|
|
26
31
|
|
|
27
|
-
LogSense generates HTML, txt, and SQLite outputs.
|
|
32
|
+
LogSense generates HTML, txt, ufw, and SQLite outputs.
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
** Apache Report Structure
|
|
30
35
|
|
|
31
36
|
#+ATTR_HTML: :width 80%
|
|
32
|
-
[[file:./apache-screenshot.png]]
|
|
37
|
+
[[file:./screenshots/apache-screenshot.png]]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
** Rails Report Structure
|
|
41
|
+
|
|
42
|
+
#+ATTR_HTML: :width 80%
|
|
43
|
+
[[file:./screenshots/rails-screenshot.png]]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
** UFW Report
|
|
33
47
|
|
|
48
|
+
The output format =ufw= generates directives for Uncomplicated
|
|
49
|
+
Firewall blacklisting IPs requesting URLs matching a given pattern.
|
|
34
50
|
|
|
51
|
+
We use it to blacklist IPs requesting WordPress login pages on our
|
|
52
|
+
websites... since we don't use WordPress for our websites.
|
|
53
|
+
|
|
54
|
+
*Example*
|
|
55
|
+
|
|
56
|
+
#+begin_src
|
|
57
|
+
$ log_sense -f apache -t ufw -i apache.log
|
|
58
|
+
# /users/sign_in/xmlrpc.php?rsd
|
|
59
|
+
ufw deny from 20.212.3.206
|
|
60
|
+
|
|
61
|
+
# /wp-login.php /wordpress/wp-login.php /blog/wp-login.php /wp/wp-login.php
|
|
62
|
+
ufw deny from 185.255.134.18
|
|
63
|
+
|
|
64
|
+
...
|
|
65
|
+
#+end_src
|
|
66
|
+
|
|
67
|
+
|
|
35
68
|
* An important word of warning
|
|
36
69
|
|
|
37
70
|
[[https://owasp.org/www-community/attacks/Log_Injection][Log poisoning]] is a technique whereby attackers send requests with invalidated
|
|
@@ -46,9 +79,10 @@ opened or code executed.
|
|
|
46
79
|
* Motivation
|
|
47
80
|
|
|
48
81
|
LogSense moves along the lines of tools such as [[https://goaccess.io/][GoAccess]] (which
|
|
49
|
-
strongly inspired the development of Log Sense) and [[https://umami.is/][Umami]],
|
|
50
|
-
*privacy* and *data-ownership*: the data generated by
|
|
51
|
-
stored on your computer and owned by you (like it should
|
|
82
|
+
strongly inspired the development of Log Sense) and [[https://umami.is/][Umami]], both
|
|
83
|
+
focusing on *privacy* and *data-ownership*: the data generated by
|
|
84
|
+
LogSense is stored on your computer and owned by you (like it should
|
|
85
|
+
be)[fn:1].
|
|
52
86
|
|
|
53
87
|
LogSense is also inspired by *static websites generators*: statistics
|
|
54
88
|
are generated from the command line and accessed as static HTML files.
|
|
@@ -74,41 +108,84 @@ generated files are then made available on a private area on the web.
|
|
|
74
108
|
#+RESULTS:
|
|
75
109
|
#+begin_example
|
|
76
110
|
Usage: log_sense [options] [logfile ...]
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
- txt
|
|
102
|
-
- html
|
|
111
|
+
--title=TITLE Title to use in the report
|
|
112
|
+
-f, --input-format=FORMAT Input format (either rails or apache)
|
|
113
|
+
-i, --input-files=file,file, Input files (can also be passed directly)
|
|
114
|
+
-t, --output-format=FORMAT Output format: html, org, txt, sqlite.
|
|
115
|
+
-o, --output-file=OUTPUT_FILE Output file
|
|
116
|
+
-b, --begin=DATE Consider entries after or on DATE
|
|
117
|
+
-e, --end=DATE Consider entries before or on DATE
|
|
118
|
+
-l, --limit=N Limit to the N most requested resources (defaults to 100)
|
|
119
|
+
-w, --width=WIDTH Maximum width of long columns in textual reports
|
|
120
|
+
-r, --rows=ROWS Maximum number of rows for columns with multiple entries in textual reports
|
|
121
|
+
-p, --pattern=PATTERN Pattern to use with ufw report to decide IP to blacklist
|
|
122
|
+
-c, --crawlers=POLICY Decide what to do with crawlers (applies to Apache Logs)
|
|
123
|
+
--no-selfpolls Ignore self poll entries (requests from ::1; applies to Apache Logs)
|
|
124
|
+
-n, --no-geog Do not geolocate entries
|
|
125
|
+
--verbose Inform about progress (output to STDERR)
|
|
126
|
+
-v, --version Prints version information
|
|
127
|
+
-h, --help Prints this help
|
|
128
|
+
|
|
129
|
+
This is version 1.6.0
|
|
130
|
+
|
|
131
|
+
Output formats:
|
|
132
|
+
|
|
133
|
+
- rails: txt, html, sqlite3, ufw
|
|
134
|
+
- apache: txt, html, sqlite3, ufw
|
|
103
135
|
#+end_example
|
|
104
136
|
|
|
105
137
|
Examples:
|
|
106
138
|
|
|
107
139
|
#+begin_example sh
|
|
108
140
|
log_sense -f apache -i access.log -t txt > access-data.txt
|
|
109
|
-
log_sense -f rails -i production.log -t html -o performance.
|
|
141
|
+
log_sense -f rails -i production.log -t html -o performance.html
|
|
110
142
|
#+end_example
|
|
111
143
|
|
|
144
|
+
* Code Structure
|
|
145
|
+
|
|
146
|
+
The code implements a pipeline, with the following steps:
|
|
147
|
+
|
|
148
|
+
1. *Parser:* parses a log to a SQLite3 database. The database
|
|
149
|
+
contains a table with a list of events, and, in the case of Rails
|
|
150
|
+
report, a table with the errors.
|
|
151
|
+
2. *Aggregator:* takes as input a SQLite DB and aggregates data,
|
|
152
|
+
typically performing "group by", which are simpler to generate in
|
|
153
|
+
Ruby, rather than in SQL. The module outputs a Hash, with
|
|
154
|
+
different reporting data.
|
|
155
|
+
3. *GeoLocator:* add country information to all the reporting data
|
|
156
|
+
which has an IP as one the fields.
|
|
157
|
+
4. *Shaper:* makes (geolocated) aggregated data (e.g. Hashes and
|
|
158
|
+
such), into Array of Arrays, simplifying the structure of the code
|
|
159
|
+
building the reports.
|
|
160
|
+
5. *Emitter* generates reports from shaped data using ERB.
|
|
161
|
+
|
|
162
|
+
The architecture and the structure of the code is far from being nice,
|
|
163
|
+
for historical reason and for a bunch of small differences existing
|
|
164
|
+
between the input and the outputs to be generated. This usually ends
|
|
165
|
+
up with modifications to the code that have to be replicated in
|
|
166
|
+
different parts of the code and in interferences.
|
|
167
|
+
|
|
168
|
+
Among the points I would like to address:
|
|
169
|
+
|
|
170
|
+
- The execution pipeline in the main script has a few exceptions to
|
|
171
|
+
manage SQLite reading/dumping and ufw report. A linear structure
|
|
172
|
+
would be a lot nicer.
|
|
173
|
+
- Two different classes are defined for steps 1, 2, and 4, to manage,
|
|
174
|
+
respectively, Apache and Rails logs. These classes inherit from a
|
|
175
|
+
common ancestor (e.g. ApacheParser and RailsParser both inherit from
|
|
176
|
+
Parser), but there is still too little code shared. A nicer
|
|
177
|
+
approach would be that of identifying a common DB structure and
|
|
178
|
+
unify the pipeline up to (or including) the generation of
|
|
179
|
+
reports. There are a bunch of small different things to highlight in
|
|
180
|
+
reports, which still make this difficult. For instance, the country
|
|
181
|
+
report for Apache reports size of TX data, which is not available
|
|
182
|
+
for Rail reports.
|
|
183
|
+
- Geolocation could become a lot more efficient if performed in
|
|
184
|
+
SQLite, rather than in Ruby
|
|
185
|
+
- The distinction between Aggregation, Shaping, and Emission is a too
|
|
186
|
+
fine-grained and it would be nice to be able to cleanly remove one
|
|
187
|
+
of the steps.
|
|
188
|
+
|
|
112
189
|
|
|
113
190
|
* Change Log
|
|
114
191
|
|
|
@@ -138,7 +215,7 @@ the known bugs.)
|
|
|
138
215
|
|
|
139
216
|
* License
|
|
140
217
|
|
|
141
|
-
|
|
218
|
+
Source code distributed under the terms of the [[http://opensource.org/licenses/MIT][MIT License]].
|
|
142
219
|
|
|
143
220
|
Geolocation is made possible by the DB-IP.com IP to City database,
|
|
144
221
|
released under a CC license.
|
data/Rakefile
CHANGED
|
@@ -9,18 +9,18 @@ end
|
|
|
9
9
|
require_relative './lib/log_sense/ip_locator.rb'
|
|
10
10
|
|
|
11
11
|
desc "Convert Geolocation DB to sqlite"
|
|
12
|
-
task :
|
|
13
|
-
filename =
|
|
12
|
+
task :dbip, [:filename] do |tasks, args|
|
|
13
|
+
filename = args[:filename]
|
|
14
14
|
|
|
15
15
|
if !File.exist? filename
|
|
16
16
|
puts "Error. Could not find: #{filename}"
|
|
17
17
|
puts
|
|
18
18
|
puts 'I see the following files:'
|
|
19
19
|
puts Dir.glob("ip_locations/dbip-country-lite*").map { |x| "- #{x}\n" }
|
|
20
|
-
puts
|
|
21
|
-
puts
|
|
22
|
-
puts
|
|
23
|
-
puts
|
|
20
|
+
puts
|
|
21
|
+
puts "1. Download (if necessary) a more recent version from: https://db-ip.com/db/download/ip-to-country-lite"
|
|
22
|
+
puts "2. Save downloaded file to ip_locations/"
|
|
23
|
+
puts "3. Relaunch with YYYY-MM"
|
|
24
24
|
|
|
25
25
|
exit
|
|
26
26
|
else
|
data/exe/log_sense
CHANGED
|
@@ -1,82 +1,153 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "log_sense"
|
|
4
|
+
require "sqlite3"
|
|
4
5
|
|
|
5
6
|
#
|
|
6
7
|
# Parse Command Line Arguments
|
|
7
8
|
#
|
|
8
9
|
|
|
9
10
|
# this better be here... OptionsParser consumes ARGV
|
|
10
|
-
@command_line = ARGV.join(
|
|
11
|
-
@options
|
|
12
|
-
@
|
|
11
|
+
@command_line = ARGV.join(" ")
|
|
12
|
+
@options = LogSense::OptionsParser.parse ARGV
|
|
13
|
+
@input_filenames = @options[:input_filenames] + ARGV
|
|
14
|
+
@output_filename = @options[:output_filename]
|
|
13
15
|
|
|
14
16
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
+
# Check correctness of input data.
|
|
18
|
+
#
|
|
19
|
+
|
|
20
|
+
#
|
|
21
|
+
# Check input files
|
|
17
22
|
#
|
|
18
|
-
@input_filenames = @options[:input_filenames] + ARGV
|
|
19
23
|
@non_existing = @input_filenames.reject { |x| File.exist?(x) }
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
if @non_existing.any?
|
|
26
|
+
warn "Error: some input file(s) \"#{@non_existing.join(", ")}\" do not exist"
|
|
27
|
+
exit 1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
#
|
|
31
|
+
# Special condition: sqlite3 requires a single file as input
|
|
32
|
+
#
|
|
33
|
+
if @input_filenames.size > 0 &&
|
|
34
|
+
File.extname(@input_filenames.first) == "sqlite3" &&
|
|
35
|
+
@input_filenames.size > 1
|
|
36
|
+
warn "Error: you can pass only one sqlite3 file as input"
|
|
37
|
+
exit 1
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
#
|
|
41
|
+
# Supported input/output chains
|
|
42
|
+
#
|
|
43
|
+
iformat = @options[:input_format]
|
|
44
|
+
oformat = @options[:output_format]
|
|
45
|
+
|
|
46
|
+
if !LogSense::OptionsChecker::compatible?(iformat, oformat)
|
|
47
|
+
warn "Error: don't know how to make #{iformat} into #{oformat}."
|
|
48
|
+
warn "Possible transformation chains:"
|
|
49
|
+
warn LogSense::OptionsChecker.chains_to_s
|
|
23
50
|
exit 1
|
|
24
51
|
end
|
|
25
|
-
@input_files = @input_filenames.empty? ? [$stdin] : @input_filenames.map { |x| File.open(x, 'r') }
|
|
26
52
|
|
|
27
53
|
#
|
|
28
|
-
#
|
|
54
|
+
# Do the work
|
|
29
55
|
#
|
|
30
56
|
|
|
31
57
|
@started_at = Time.now
|
|
32
58
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
when 'rails'
|
|
38
|
-
parser_klass = LogSense::RailsLogParser
|
|
39
|
-
cruncher_klass = LogSense::RailsDataCruncher
|
|
59
|
+
if @input_filenames.size > 0 &&
|
|
60
|
+
File.extname(@input_filenames.first) == ".sqlite3"
|
|
61
|
+
warn "Reading SQLite3 DB ..." if @options[:verbose]
|
|
62
|
+
@db = SQLite3::Database.open @input_filenames.first
|
|
40
63
|
else
|
|
41
|
-
|
|
42
|
-
|
|
64
|
+
warn "Parsing ..." if @options[:verbose]
|
|
65
|
+
@input_files = if @input_filenames.empty?
|
|
66
|
+
[$stdin]
|
|
67
|
+
else
|
|
68
|
+
@input_filenames.map { |fname| File.open(fname, "r") }
|
|
69
|
+
end
|
|
70
|
+
class_name = "LogSense::#{@options[:input_format].capitalize}LogParser"
|
|
71
|
+
parser_class = Object.const_get class_name
|
|
72
|
+
parser = parser_class.new
|
|
73
|
+
@db = parser.parse @input_files
|
|
43
74
|
end
|
|
44
75
|
|
|
45
|
-
|
|
46
|
-
|
|
76
|
+
if @options[:output_format] == "sqlite3"
|
|
77
|
+
warn "Saving SQLite3 DB ..." if @options[:verbose]
|
|
47
78
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
ddb = SQLite3::Database.new(@output_file || 'db.sqlite3')
|
|
51
|
-
b = SQLite3::Backup.new(ddb, 'main', @db, 'main')
|
|
79
|
+
ddb = SQLite3::Database.new(@output_filename || "db.sqlite3")
|
|
80
|
+
b = SQLite3::Backup.new(ddb, "main", @db, "main")
|
|
52
81
|
b.step(-1) #=> DONE
|
|
53
82
|
b.finish
|
|
83
|
+
|
|
84
|
+
exit 0
|
|
85
|
+
elsif @options[:output_format] == "ufw"
|
|
86
|
+
pattern = @options[:pattern] || "php"
|
|
87
|
+
|
|
88
|
+
if @options[:input_format] == "rails"
|
|
89
|
+
query = "select distinct event.ip,event.url
|
|
90
|
+
from error join event
|
|
91
|
+
where event.log_id = error.log_id and
|
|
92
|
+
event.url like '%#{pattern}%'"
|
|
93
|
+
else
|
|
94
|
+
query = "select distinct ip,path from logline
|
|
95
|
+
where path like '%#{pattern}%'"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
ips = @db.execute query
|
|
99
|
+
ips_and_urls = ips.group_by { |x| x[0] }.transform_values { |x|
|
|
100
|
+
x.map { |y| y[1..-1] }.flatten
|
|
101
|
+
}
|
|
102
|
+
ips_and_urls.each do |ip, urls|
|
|
103
|
+
puts "# #{urls[0..10].uniq.join(' ')}"
|
|
104
|
+
puts "ufw deny from #{ip}"
|
|
105
|
+
puts
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
exit 0
|
|
54
109
|
else
|
|
55
|
-
|
|
56
|
-
|
|
110
|
+
warn "Aggregating data ..." if @options[:verbose]
|
|
111
|
+
class_name = "LogSense::#{@options[:input_format].capitalize}Aggregator"
|
|
112
|
+
aggr_class = Object.const_get class_name
|
|
113
|
+
aggr = aggr_class.new(@db, @options)
|
|
114
|
+
@data = aggr.aggregate
|
|
57
115
|
|
|
58
|
-
|
|
59
|
-
|
|
116
|
+
if @options[:geolocation]
|
|
117
|
+
warn "Geolocating ..." if @options[:verbose]
|
|
118
|
+
@data = LogSense::IpLocator.geolocate @data
|
|
60
119
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
120
|
+
warn "Grouping IPs by country ..." if @options[:verbose]
|
|
121
|
+
country_col = @data[:ips][0].size - 1
|
|
122
|
+
@data[:countries] = @data[:ips].group_by { |x| x[country_col] }
|
|
123
|
+
else
|
|
124
|
+
@data[:countries] = {}
|
|
125
|
+
end
|
|
64
126
|
|
|
65
127
|
@ended_at = Time.now
|
|
66
128
|
@duration = @ended_at - @started_at
|
|
67
129
|
|
|
68
130
|
@data = @data.merge({
|
|
69
131
|
command: @command_line,
|
|
70
|
-
filenames:
|
|
132
|
+
filenames: @input_filenames,
|
|
71
133
|
log_files: @input_files,
|
|
72
134
|
started_at: @started_at,
|
|
73
135
|
ended_at: @ended_at,
|
|
74
136
|
duration: @duration,
|
|
75
137
|
width: @options[:width]
|
|
76
138
|
})
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
139
|
+
|
|
140
|
+
if @options[:verbose]
|
|
141
|
+
warn "I have the following keys in data: "
|
|
142
|
+
warn @data.keys.sort.map { |key| "#{key}: #{@data[key].class}" }.join("\n")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
warn "Shaping data for output ..." if @options[:verbose]
|
|
146
|
+
class_name = "LogSense::#{@options[:input_format].capitalize}ReportShaper"
|
|
147
|
+
shaper_class = Object.const_get class_name
|
|
148
|
+
shaper = shaper_class.new
|
|
149
|
+
@reports = shaper.shape @data
|
|
150
|
+
|
|
151
|
+
warn "Emitting..." if @options[:verbose]
|
|
152
|
+
puts LogSense::Emitter.emit @reports, @data, @options
|
|
82
153
|
end
|
|
Binary file
|