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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d47cd7afd1f7d7a6173276637585c6ef77f34c017a6c4d3217dfb60bb348b913
4
- data.tar.gz: a18bdcb399a28ffa3849bb0d4c1937c042b39a22b7f482b0f0e997d381d70618
3
+ metadata.gz: 9a8f0682f3292b7e21b6aeb2200b45ad16d1e71b6f60f6c74db54e6a1a7d229d
4
+ data.tar.gz: 248adab329c505788c4a394a839fcaf0a63ca72b76c20a8b4407839206acf191
5
5
  SHA512:
6
- metadata.gz: d8a6d98c4e3f987c99d821e310f720e3b2fde984ddd2d7087f212b9dd26351456afe17cdce768b55f9b79b04aa2e2c93a52770a316e7790146078304a2f78e37
7
- data.tar.gz: 3a72cc450f862dcb0632b05458d670bf46e1f9332ad88ad0449bd2e31a215343d8683fe963b3e18e9e0739f1282a9fd710ea6b00a2c466e4f4ea13028d764846
6
+ metadata.gz: f10678522e0686ffd212fa53275da708daa1900bbb4e13e982e91e491f08c2815d39f3d78160a1d9150960108d4377bf9aa4e59463fd7afe4614ac0adfb6056b
7
+ data.tar.gz: 8d07a3c74c739141c08ce930eed271b3c0ea70aa52d4023afbcf0ee9c1970f1df2485a5a365a94958836db8aabfac63b329fbb1334122505ff668656602c7caf
@@ -6,3 +6,10 @@
6
6
  - Added user-agent
7
7
  ## 0.1.3
8
8
  - Removed unused dependencies
9
+ ## 0.1.4
10
+ - Updated key check for len
11
+ ## 0.1.5
12
+ - Fixed nil key error
13
+ ## 0.1.6
14
+ - Added LRU cache options and filter for invalid ips
15
+
@@ -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
- * nsherron90 - nsherron90@gmail.com
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
@@ -1,2 +1,19 @@
1
1
  # logstash-filter-greynoise
2
- Example filter plugin. This should help bootstrap your effort to write your own filter plugin!
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 REST Filter
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 Greynoise filter adds information about IP addresses from logstash events via the Greynoise API.
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/nsherron90/logstash-filter-greynoise.git
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.3.gem
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 => "ip_value" # string (required, reference to ip address field)
35
- key => "your_greynoise_key" # string (optional, no default)
36
- target => "greynoise" # string (optional, default = greynoise)
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 "logstash/filters/base"
2
+ require 'logstash/filters/base'
3
3
  require "json"
4
4
  require "logstash/namespace"
5
- require 'faraday'
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
- # filter {
18
- # greynoise {
19
- # ip => "ip"
20
- # }
21
- # }
23
+ # filter {
24
+ # greynoise {
25
+ # ip => "ip"
26
+ # }
27
+ # }
22
28
 
23
29
  config_name "greynoise"
24
30
 
25
- # Replace the message with this value.
26
-
31
+ # ip address to use for greynoise query
27
32
  config :ip, :validate => :string, :required => true
28
- config :key, :validate => :string, :required => false
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
- end # def register
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
- if @key
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
- response = Faraday.get(uri, nil, 'User-Agent' => 'logstash-filter-greynoise', Key: event.sprintf(key))
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
- url = "https://api.greynoise.io/v1/query/ip"
47
- response = Faraday.post url, { :ip => event.sprintf(ip) }, 'User-Agent' => 'logstash-filter-greynoise'
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
- result = JSON.parse(response.body)
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
- event.set(@target, result)
54
- # filter_matched should go in the last line of our successful code
55
- filter_matched(event)
112
+ if @hit_cache
113
+ gn_result = @hit_cache[event.sprintf(ip)]
56
114
 
57
- end # def filter
58
- end # class LogStash::Filters::Greynoise
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.1.3'
3
+ s.version = '1.0.0'
4
4
  s.licenses = ['Apache-2.0']
5
- s.summary = 'This greynoise filter takes contents in the ip field and returns greynoise api data (see https://greynoise.io/ for more info).'
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/nsherron90/logstash-filter-greynoise'
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 'faraday', '~> 0.9.2'
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 <<-CONFIG
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
- CONFIG
15
- # end
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.1.3
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: 2019-04-29 00:00:00.000000000 Z
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: '2.0'
20
- type: :runtime
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: '2.0'
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: '0'
34
- type: :development
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: '0'
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: 0.9.2
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: 0.9.2
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/nsherron90/logstash-filter-greynoise
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.3
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 ip field and returns greynoise
98
- api data (see https://greynoise.io/ for more info).
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