logstash-filter-greynoise 0.1.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/CONTRIBUTORS +1 -1
- data/DEVELOPER.md +18 -1
- data/README.md +27 -8
- data/lib/logstash/filters/greynoise.rb +109 -26
- data/logstash-filter-greynoise.gemspec +5 -6
- data/spec/filters/greynoise_spec.rb +29 -15
- metadata +28 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a8f0682f3292b7e21b6aeb2200b45ad16d1e71b6f60f6c74db54e6a1a7d229d
|
4
|
+
data.tar.gz: 248adab329c505788c4a394a839fcaf0a63ca72b76c20a8b4407839206acf191
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f10678522e0686ffd212fa53275da708daa1900bbb4e13e982e91e491f08c2815d39f3d78160a1d9150960108d4377bf9aa4e59463fd7afe4614ac0adfb6056b
|
7
|
+
data.tar.gz: 8d07a3c74c739141c08ce930eed271b3c0ea70aa52d4023afbcf0ee9c1970f1df2485a5a365a94958836db8aabfac63b329fbb1334122505ff668656602c7caf
|
data/CHANGELOG.md
CHANGED
data/CONTRIBUTORS
CHANGED
@@ -2,7 +2,7 @@ The following is a list of people who have contributed ideas, code, bug
|
|
2
2
|
reports, or in general have helped logstash along its way.
|
3
3
|
|
4
4
|
Contributors:
|
5
|
-
*
|
5
|
+
* nicksherron - nsherron90@gmail.com
|
6
6
|
|
7
7
|
Note: If you've sent us patches, bug reports, or otherwise contributed to
|
8
8
|
Logstash, and you aren't on the list above and want to be, please let us know
|
data/DEVELOPER.md
CHANGED
@@ -1,2 +1,19 @@
|
|
1
1
|
# logstash-filter-greynoise
|
2
|
-
|
2
|
+
|
3
|
+
You can build this project on your machine locally or alternative use the docker file to build and run an environment in Docker.
|
4
|
+
|
5
|
+
To use Docker, run the following:
|
6
|
+
|
7
|
+
```
|
8
|
+
# build the docker container for local dev
|
9
|
+
make build-docker
|
10
|
+
|
11
|
+
# shell into docker environment
|
12
|
+
make shell-docker
|
13
|
+
|
14
|
+
# set GN key for tests
|
15
|
+
export GN_API_KEY=<GN key>
|
16
|
+
|
17
|
+
# run tests
|
18
|
+
bundle exec rspec
|
19
|
+
```
|
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
# Logstash
|
1
|
+
# Logstash Greynoise Filter
|
2
2
|
This is a filter plugin for [Logstash](https://github.com/elastic/logstash).
|
3
3
|
|
4
4
|
It is fully free and fully open source. The license is Apache 2.0, meaning you are pretty much free to use it however you want in whatever way.
|
5
5
|
|
6
6
|
## Documentation
|
7
7
|
|
8
|
-
The
|
8
|
+
The GreyNoise filter adds information about IP addresses from logstash events via the GreyNoise API.
|
9
9
|
|
10
10
|
GreyNoise is a system that collects and analyzes data on Internet-wide scanners.
|
11
11
|
GreyNoise collects data on benign scanners such as Shodan.io, as well as malicious actors like SSH and telnet worms.
|
@@ -19,24 +19,41 @@ $LS_HOME/bin/logstash-plugin install logstash-filter-greynoise
|
|
19
19
|
|
20
20
|
Or you can build it yourself:
|
21
21
|
```
|
22
|
-
git clone https://github.com/
|
22
|
+
git clone https://github.com/nicksherron/logstash-filter-greynoise.git
|
23
23
|
bundle install
|
24
24
|
gem build logstash-filter-greynoise.gemspec
|
25
|
-
$LS_HOME/bin/logstash-plugin install logstash-filter-greynoise-0.1.
|
25
|
+
$LS_HOME/bin/logstash-plugin install logstash-filter-greynoise-0.1.7.gem
|
26
26
|
```
|
27
27
|
|
28
28
|
### 2. Filter Configuration
|
29
|
-
Add the following inside the filter section of your logstash configuration:
|
30
29
|
|
31
30
|
```sh
|
32
31
|
filter {
|
33
32
|
greynoise {
|
34
|
-
ip
|
35
|
-
|
36
|
-
|
33
|
+
ip => "ip_value" # string (required, reference to ip address field)
|
34
|
+
full_context => true # bool (optional, whether to use context lookup, default false)
|
35
|
+
key => "your_greynoise_key" # string (required)
|
36
|
+
target => "greynoise" # string (optional, default = greynoise)
|
37
|
+
hit_cache_size => 100 # number (optional, default = 0)
|
38
|
+
hit_cache_ttl => 6 # number (optional, default = 60)
|
37
39
|
}
|
38
40
|
}
|
39
41
|
```
|
42
|
+
The GreyNoise Logstash filter plugin can be used in two different modes:
|
43
|
+
|
44
|
+
##### Quick IP Enrichment
|
45
|
+
In this mode documents (default) are enriched with a bool field `seen` which is true if GreyNoise has seen this IP or false otherwise.
|
46
|
+
|
47
|
+
This mode is faster than doing full enrichment and should be used for better performance on high-volume/-throughput event streams.
|
48
|
+
If you have a data pipeline with multiple enrichment points, you can use the boolean field to later enrich the document with IP information from GreyNoise's context endpoint.
|
49
|
+
|
50
|
+
##### Full IP Enrichment
|
51
|
+
|
52
|
+
In this mode (`full_context => true`), documents are enriched with full context from GreyNoise API if the IP has been observed by GreyNoise.
|
53
|
+
|
54
|
+
This mode is slower than doing quick IP enrichment and might be reasonable for low-volume/-throughput event streams.
|
55
|
+
|
56
|
+
**NOTE**: Beware that the full context document from GreyNoise API can be large and as such the cache can grow quickly in size. Set the cache size accordingly to prevent high memory consumption by the cache.
|
40
57
|
|
41
58
|
Print plugin version:
|
42
59
|
|
@@ -79,3 +96,5 @@ Programming is not a required skill. Whatever you've seen about open source and
|
|
79
96
|
It is more important to the community that you are able to contribute.
|
80
97
|
|
81
98
|
For more information about contributing, see the [CONTRIBUTING](https://github.com/elasticsearch/logstash/blob/master/CONTRIBUTING.md) file.
|
99
|
+
|
100
|
+
See also Logstash [Elastic's filter plugin development guide](https://www.elastic.co/guide/en/logstash/current/filter-new-plugin.html).
|
@@ -1,59 +1,142 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require
|
2
|
+
require 'logstash/filters/base'
|
3
3
|
require "json"
|
4
4
|
require "logstash/namespace"
|
5
|
-
require
|
5
|
+
require "ipaddr"
|
6
|
+
require "lru_redux"
|
7
|
+
require 'net/http'
|
8
|
+
require 'uri'
|
6
9
|
|
10
|
+
VERSION = "1.0.0"
|
11
|
+
|
12
|
+
class InvalidAPIKey < StandardError
|
13
|
+
end
|
7
14
|
|
8
15
|
# This filter will replace the contents of the default
|
9
16
|
# message field with whatever you specify in the configuration.
|
10
17
|
#
|
11
|
-
# It is only intended to be used as an .
|
12
18
|
class LogStash::Filters::Greynoise < LogStash::Filters::Base
|
13
19
|
|
14
20
|
# Setting the config_name here is required. This is how you
|
15
21
|
# configure this filter from your Logstash config.
|
16
22
|
#
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
23
|
+
# filter {
|
24
|
+
# greynoise {
|
25
|
+
# ip => "ip"
|
26
|
+
# }
|
27
|
+
# }
|
22
28
|
|
23
29
|
config_name "greynoise"
|
24
30
|
|
25
|
-
#
|
26
|
-
|
31
|
+
# ip address to use for greynoise query
|
27
32
|
config :ip, :validate => :string, :required => true
|
28
|
-
|
33
|
+
|
34
|
+
# whether or not to use full context endpoint
|
35
|
+
config :full_context, :validate => :boolean, :default => false
|
36
|
+
|
37
|
+
# greynoise enterprise api key
|
38
|
+
config :key, :validate => :string, :required => true
|
39
|
+
|
40
|
+
# target top level key of hash response
|
29
41
|
config :target, :validate => :string, :default => "greynoise"
|
30
42
|
|
43
|
+
# tag if ip address supplied is invalid
|
44
|
+
config :tag_on_failure, :validate => :string, :default => '_greynoise_filter_invalid_ip'
|
45
|
+
|
46
|
+
# tag if API key not valid or missing
|
47
|
+
config :tag_on_auth_failure, :validate => :string, :default => '_greynoise_filter_invalid_api_key'
|
31
48
|
|
49
|
+
# set the size of cache for successful requests
|
50
|
+
config :hit_cache_size, :validate => :number, :default => 0
|
51
|
+
|
52
|
+
# how long to cache successful requests (in seconds)
|
53
|
+
config :hit_cache_ttl, :validate => :number, :default => 60
|
32
54
|
|
33
55
|
public
|
56
|
+
|
34
57
|
def register
|
35
|
-
|
58
|
+
if @hit_cache_size > 0
|
59
|
+
@hit_cache = LruRedux::TTL::ThreadSafeCache.new(@hit_cache_size, @hit_cache_ttl)
|
60
|
+
end
|
61
|
+
end
|
36
62
|
|
37
|
-
public
|
38
|
-
def filter(event)
|
39
63
|
|
40
|
-
|
41
|
-
url = "https://enterprise.api.greynoise.io/v2/noise/context/" + event.sprintf(ip)
|
42
|
-
uri = URI.parse(URI.encode(url.strip))
|
64
|
+
private
|
43
65
|
|
44
|
-
|
66
|
+
def lookup_ip(target_ip, api_key, context = false)
|
67
|
+
endpoint = "quick/"
|
68
|
+
if context
|
69
|
+
endpoint = "context/"
|
70
|
+
end
|
71
|
+
|
72
|
+
uri = URI.parse("https://api.greynoise.io/v2/noise/" + endpoint + target_ip)
|
73
|
+
request = Net::HTTP::Get.new(uri)
|
74
|
+
request["Key"] = api_key
|
75
|
+
request["User-Agent"] = "logstash-filter-greynoise " + VERSION
|
76
|
+
req_options = {
|
77
|
+
use_ssl: uri.scheme == "https",
|
78
|
+
}
|
79
|
+
response = Net::HTTP.start(uri.hostname, uri.port, req_options) { |http|
|
80
|
+
http.request(request)
|
81
|
+
}
|
82
|
+
|
83
|
+
if response.is_a?(Net::HTTPSuccess)
|
84
|
+
result = JSON.parse(response.body)
|
85
|
+
unless context
|
86
|
+
result["seen"] = result.delete("noise")
|
87
|
+
end
|
88
|
+
result
|
89
|
+
elsif response.is_a?(Net::HTTPUnauthorized)
|
90
|
+
raise InvalidAPIKey.new
|
45
91
|
else
|
46
|
-
|
47
|
-
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
public
|
48
97
|
|
98
|
+
def filter(event)
|
99
|
+
valid = nil
|
100
|
+
begin
|
101
|
+
IPAddr.new(event.sprintf(ip))
|
102
|
+
rescue ArgumentError => e
|
103
|
+
valid = e
|
49
104
|
end
|
50
105
|
|
51
|
-
|
106
|
+
if valid
|
107
|
+
@logger.error("Invalid IP address, skipping", :ip => event.sprintf(ip), :event => event.to_hash)
|
108
|
+
event.tag(@tag_on_failure)
|
109
|
+
return
|
110
|
+
end
|
52
111
|
|
53
|
-
|
54
|
-
|
55
|
-
filter_matched(event)
|
112
|
+
if @hit_cache
|
113
|
+
gn_result = @hit_cache[event.sprintf(ip)]
|
56
114
|
|
57
|
-
|
58
|
-
|
115
|
+
# use cached data
|
116
|
+
if gn_result
|
117
|
+
event.set(@target, gn_result)
|
118
|
+
filter_matched(event)
|
119
|
+
return
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# use GN API, since not found in cache
|
124
|
+
begin
|
125
|
+
gn_result = lookup_ip(event.sprintf(ip), event.sprintf(key), @full_context)
|
126
|
+
unless gn_result.nil?
|
127
|
+
if @hit_cache
|
128
|
+
# store in cache
|
129
|
+
@hit_cache[event.sprintf(ip)] = gn_result
|
130
|
+
end
|
131
|
+
|
132
|
+
event.set(@target, gn_result)
|
133
|
+
# filter_matched should go in the last line of our successful code
|
134
|
+
filter_matched(event)
|
135
|
+
end
|
136
|
+
rescue InvalidAPIKey => _
|
137
|
+
@logger.error("unauthorized - check API key")
|
138
|
+
event.tag(@tag_on_auth_failure)
|
139
|
+
end
|
140
|
+
end
|
59
141
|
|
142
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'logstash-filter-greynoise'
|
3
|
-
s.version = '0.
|
3
|
+
s.version = '1.0.0'
|
4
4
|
s.licenses = ['Apache-2.0']
|
5
|
-
s.summary = 'This greynoise filter takes contents in the
|
5
|
+
s.summary = 'This greynoise filter takes contents in the IP field and returns GreyNoise API data (see https://developer.greynoise.io/ for more info).'
|
6
6
|
s.description = 'This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install logstash-filter-greynoise. This gem is not a stand-alone program'
|
7
|
-
s.homepage = 'https://github.com/
|
7
|
+
s.homepage = 'https://github.com/nicksherron/logstash-filter-greynoise'
|
8
8
|
s.authors = ['nsherron90']
|
9
9
|
s.email = 'nsherron90@gmail.com'
|
10
10
|
s.require_paths = ['lib']
|
@@ -18,8 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.metadata = { "logstash_plugin" => "true", "logstash_group" => "filter" }
|
19
19
|
|
20
20
|
# Gem dependencies
|
21
|
-
s.add_runtime_dependency 'logstash-core-plugin-api', '~> 2.0'
|
22
21
|
s.add_development_dependency 'logstash-devutils'
|
23
|
-
s.add_runtime_dependency
|
24
|
-
|
22
|
+
s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
|
23
|
+
s.add_runtime_dependency 'lru_redux', "~> 1.1.0"
|
25
24
|
end
|
@@ -3,33 +3,47 @@ require_relative '../spec_helper'
|
|
3
3
|
require "logstash/filters/greynoise"
|
4
4
|
|
5
5
|
describe LogStash::Filters::Greynoise do
|
6
|
-
|
7
6
|
describe "defaults" do
|
8
|
-
let(:config) do
|
7
|
+
let(:config) do
|
8
|
+
api_key = ENV["GN_API_KEY"]
|
9
|
+
<<-CONFIG
|
9
10
|
filter {
|
10
11
|
greynoise {
|
11
|
-
ip => "ip"
|
12
|
+
ip => "%{ip}"
|
13
|
+
key => "#{api_key}"
|
12
14
|
}
|
13
15
|
}
|
14
|
-
|
15
|
-
|
16
|
+
CONFIG
|
17
|
+
end
|
16
18
|
|
17
19
|
sample("ip" => "8.8.8.8") do
|
18
20
|
insist { subject }.include?("greynoise")
|
19
|
-
|
20
|
-
expected_fields = %w(greynoise.ip greynoise.seen)
|
21
|
+
expected_fields = %w(ip seen code)
|
21
22
|
expected_fields.each do |f|
|
22
23
|
insist { subject.get("greynoise") }.include?(f)
|
23
24
|
end
|
25
|
+
insist { subject.get("greynoise").get("code").equal?("0x05") }
|
26
|
+
end
|
27
|
+
|
28
|
+
sample("ip" => "4.2.1.A") do
|
29
|
+
insist { subject.get("tags") }.include?("_greynoise_filter_invalid_ip")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "invalid_key" do
|
34
|
+
let(:config) do
|
35
|
+
<<-CONFIG
|
36
|
+
filter {
|
37
|
+
greynoise {
|
38
|
+
ip => "%{ip}"
|
39
|
+
key => "BAD_KEY"
|
40
|
+
}
|
41
|
+
}
|
42
|
+
CONFIG
|
24
43
|
end
|
44
|
+
|
45
|
+
sample("ip" => "8.8.8.8") do
|
46
|
+
insist { subject.get("tags") }.include?("_greynoise_filter_invalid_api_key")
|
25
47
|
end
|
26
48
|
end
|
27
49
|
end
|
28
|
-
#
|
29
|
-
#
|
30
|
-
# sample("message" => "some text") do
|
31
|
-
# expect(subject).to include("message")
|
32
|
-
# expect(subject.get('message')).to eq('Hello World')
|
33
|
-
# end
|
34
|
-
# end
|
35
|
-
# end
|
metadata
CHANGED
@@ -1,57 +1,63 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-filter-greynoise
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- nsherron90
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-12-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name: logstash-core-plugin-api
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
16
15
|
requirements:
|
17
|
-
- - "
|
16
|
+
- - ">="
|
18
17
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
20
|
-
|
18
|
+
version: '0'
|
19
|
+
name: logstash-devutils
|
20
|
+
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name: logstash-devutils
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
30
29
|
requirements:
|
31
30
|
- - ">="
|
32
31
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
34
|
-
|
32
|
+
version: '1.60'
|
33
|
+
- - "<="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '2.99'
|
36
|
+
name: logstash-core-plugin-api
|
37
|
+
type: :runtime
|
35
38
|
prerelease: false
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
37
40
|
requirements:
|
38
41
|
- - ">="
|
39
42
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
43
|
+
version: '1.60'
|
44
|
+
- - "<="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '2.99'
|
41
47
|
- !ruby/object:Gem::Dependency
|
42
|
-
name: faraday
|
43
48
|
requirement: !ruby/object:Gem::Requirement
|
44
49
|
requirements:
|
45
50
|
- - "~>"
|
46
51
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
52
|
+
version: 1.1.0
|
53
|
+
name: lru_redux
|
48
54
|
type: :runtime
|
49
55
|
prerelease: false
|
50
56
|
version_requirements: !ruby/object:Gem::Requirement
|
51
57
|
requirements:
|
52
58
|
- - "~>"
|
53
59
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
60
|
+
version: 1.1.0
|
55
61
|
description: This gem is a Logstash plugin required to be installed on top of the
|
56
62
|
Logstash core pipeline using $LS_HOME/bin/logstash-plugin install logstash-filter-greynoise.
|
57
63
|
This gem is not a stand-alone program
|
@@ -70,13 +76,13 @@ files:
|
|
70
76
|
- logstash-filter-greynoise.gemspec
|
71
77
|
- spec/filters/greynoise_spec.rb
|
72
78
|
- spec/spec_helper.rb
|
73
|
-
homepage: https://github.com/
|
79
|
+
homepage: https://github.com/nicksherron/logstash-filter-greynoise
|
74
80
|
licenses:
|
75
81
|
- Apache-2.0
|
76
82
|
metadata:
|
77
83
|
logstash_plugin: 'true'
|
78
84
|
logstash_group: filter
|
79
|
-
post_install_message:
|
85
|
+
post_install_message:
|
80
86
|
rdoc_options: []
|
81
87
|
require_paths:
|
82
88
|
- lib
|
@@ -91,11 +97,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
97
|
- !ruby/object:Gem::Version
|
92
98
|
version: '0'
|
93
99
|
requirements: []
|
94
|
-
rubygems_version: 3.0.
|
95
|
-
signing_key:
|
100
|
+
rubygems_version: 3.0.6
|
101
|
+
signing_key:
|
96
102
|
specification_version: 4
|
97
|
-
summary: This greynoise filter takes contents in the
|
98
|
-
|
103
|
+
summary: This greynoise filter takes contents in the IP field and returns GreyNoise
|
104
|
+
API data (see https://developer.greynoise.io/ for more info).
|
99
105
|
test_files:
|
100
106
|
- spec/filters/greynoise_spec.rb
|
101
107
|
- spec/spec_helper.rb
|