fluent-plugin-shodan 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c5065dc8260c9015eebe3ab7e1095d050fdb64b3fa0d0168c414342e31f7960d
4
+ data.tar.gz: b26a7a5b53c56cc6469433eb5867749ec26c7298958294dcf7c6c5b2f8a335e0
5
+ SHA512:
6
+ metadata.gz: 3244f0dd4e8c9d916a83220e2a534631f3c413a15b68ce985dc93a2632691a75b4147c838a2a871136a58c0e753b701304faf2cf86e6b18bd421302162836ac7
7
+ data.tar.gz: 14b471034818688f951fdb36d0102dccd63bd8f3a6437e6ee8e44de9d8a0d799190241250aaacd7aa631e5a4ab467f36d11ad4e59e2615cbfccfb33e92db174a
data/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ srilumpa <marcandre.doll@gmail.com>
data/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # fluent-plugin-shodan
2
+
3
+ [![Unit tests](https://github.com/srilumpa/fluent-plugin-shodan/actions/workflows/ruby.yml/badge.svg)](https://github.com/srilumpa/fluent-plugin-shodan/actions/workflows/ruby.yml)[![Ruby Gem](https://github.com/srilumpa/fluent-plugin-shodan/actions/workflows/rubygem-push.yml/badge.svg)](https://github.com/srilumpa/fluent-plugin-shodan/actions/workflows/rubygem-push.yml)[![GPR](https://github.com/srilumpa/fluent-plugin-shodan/actions/workflows/gpr-push.yml/badge.svg)](https://github.com/srilumpa/fluent-plugin-shodan/actions/workflows/gpr-push.yml)
4
+
5
+ The [Shodan](https://www.shodan.io/) plugin offers [Fluentd](https://fluentd.org/) capacities to gather data from shodan and send them to whatever system you want (on the condition Fluentd has an [output plugin](https://docs.fluentd.org/output) fitting your needs).
6
+
7
+ The Shodan plugin can adress three ways of gathering data
8
+
9
+ - by querying the [Search API](https://developer.shodan.io/api)
10
+ - by consuming the [Stream API](https://developer.shodan.io/api/stream) (WIP)
11
+ - or by consuming the [Alert API](https://developer.shodan.io/api/stream) (WIP)
12
+
13
+ The outputed "logs" follow the Shodan [Banner specification](https://datapedia.shodan.io/).
14
+
15
+ A valid API key will be necessary for this plugin to work. The Shodan Search plugin will work with a _Free_ account with limited functionnalities, but the Shodans Stream and the Shodan Alert plugins will need a subscription plan to work.
16
+
17
+ This plugin relies on the
18
+
19
+ ## Installation
20
+
21
+ ### RubyGems
22
+
23
+ ```
24
+ $ gem install fluent-plugin-shodan
25
+ ```
26
+
27
+ ### Bundler
28
+
29
+ Add following line to your Gemfile:
30
+
31
+ ```ruby
32
+ gem "fluent-plugin-shodan"
33
+ ```
34
+
35
+ And then execute:
36
+
37
+ ```
38
+ $ bundle
39
+ ```
40
+
41
+ ## Shodan Search
42
+
43
+ ### Example configuration
44
+
45
+ ```
46
+ <source>
47
+ @type shodan_search
48
+ interval 15m
49
+ tag shodan.ssh
50
+ query ssh
51
+ api_key 1234567890AZERTYUIOP
52
+ </source>
53
+ ```
54
+
55
+ ### How it works
56
+
57
+ When Fluentd is started with `in_shodan_search`, it will create a Shodan client and passes to it the API key. It will then query the Shodan API to get the account information to check if the API key is valid. If it is not, an error will be logged and the plugin will stop.
58
+
59
+ Once the client is ready, a timer will be set to query the Shodan API a the interval set up in the configuration. One line of "log" will be generated per element contained in the `matches` array from the query result. An other query will be submitted to gather data fro mthe next page if
60
+
61
+ - the amount of read entries is lesser than the total available entries
62
+ - the current read page is not greater than the `max_pages` parameter
63
+
64
+ ### Plugin helpers
65
+
66
+ * [timer](https://docs.fluentd.org/v/1.0/plugin-helper-overview/api-plugin-helper-timer)
67
+ * See also: [Input Plugin Overview](https://docs.fluentd.org/v/1.0/input#overview)
68
+
69
+ ### Configuration
70
+
71
+ #### api_key (string) (required)
72
+
73
+ The API key to connect to the Shodan API.
74
+
75
+ #### interval (time) (optional)
76
+
77
+ The interval time between running queries.
78
+
79
+ Default value: `3600`.
80
+
81
+ #### tag (string) (optional)
82
+
83
+ The tag to apply to each shodan entries.
84
+
85
+ #### query (string) (required)
86
+
87
+ The Shodan query to execute.
88
+
89
+ #### max_pages (integer) (optional)
90
+
91
+ The maximum amount of pages to crawl. A 0 or negative value means to crawl all pages. Note that if you have a free account, querying a page other than the first one will result in a `HTTP 401` response.
92
+
93
+ Default value: `1`.
94
+
95
+ ## Shodan Stream
96
+
97
+ WIP
98
+
99
+ ## Shodan Alert
100
+
101
+ WIP
102
+
103
+ ## Testing
104
+
105
+ ### Unit tests
106
+
107
+ 1. Clone this repository
108
+ 2. Install all dependencies with `bundle install`
109
+ 3. Set the `SHODAN_TEST_API_KEY` environment variable with your API key
110
+ 4. Run `rake` or `rake test`
111
+
112
+ ### Live tests
113
+
114
+ On a system where fluentd is installed
115
+
116
+ 1. Clone this repository
117
+ 2. Build the gem with `gem build fluent-plugin-shodan.gemspec`
118
+ 3. Install the built gem with `fluent-gem install fluent-plugin-shodan-<version>.gem`
119
+ 4. Follow the [debugging guide from FluentD](https://docs.fluentd.org/plugin-development#debugging-plugins)
120
+
121
+ ## Copyright
122
+
123
+ * Copyright(c) 2022 Marc-André Doll
124
+ * License
125
+ * Apache License, Version 2.0
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,65 @@
1
+ require 'fluent/plugin/input'
2
+ require 'shodanz'
3
+
4
+ module Fluent::Plugin
5
+ class ShodanSearch < Input
6
+ Fluent::Plugin.register_input('shodan_search', self)
7
+
8
+ helpers :timer
9
+
10
+ desc "The API key to connect to the Shodan API."
11
+ config_param :api_key, :string, secret: true
12
+ desc "The interval time between running queries."
13
+ config_param :interval, :time, default: 3600
14
+ desc "The tag to apply to each shodan entries."
15
+ config_param :tag, :string, default: nil
16
+ desc "The Shodan query to execute."
17
+ config_param :query, :string
18
+ desc "The maximum amount of pages to crawl. A 0 or negative value means to crawl all pages."
19
+ config_param :max_pages, :integer, default: 1
20
+
21
+ def configure(conf)
22
+ super
23
+
24
+ @client = Shodanz.client.new(key: @api_key)
25
+ begin
26
+ log.info "Shodan client properly registered", client_info: @client.info
27
+ rescue RuntimeError => exception
28
+ raise Fluent::ConfigError.new "Invalid Shodan API key"
29
+ end
30
+ end
31
+
32
+ def multi_workers_ready?
33
+ false
34
+ end
35
+
36
+ def start
37
+ super
38
+
39
+ timer_execute("shodan_#{self.class.name}_#{@tag}".to_sym, @interval, repeat: true, &method(:run))
40
+ end
41
+
42
+ private
43
+
44
+ def run
45
+ log.debug "Starting Shodan search", query: @query, max_pages: @max_pages
46
+ es_time = Fluent::EventTime.now
47
+ current_page = 0
48
+ read_entries = 0
49
+ loop do
50
+ current_page += 1
51
+ result = @client.host_search(@query, page: current_page)
52
+ result['matches'].each do |rec|
53
+ router.emit(@tag, es_time, rec)
54
+ end
55
+ read_entries += result['matches'].length
56
+ break if (@max_pages >= 0 && current_page >= @max_pages) || read_entries >= result['total']
57
+ end
58
+ log.debug "Shodan search ending", query: @query, total_read: read_entries
59
+ rescue RuntimeError => re
60
+ log.error "Unable to execute Shodan query", query: @query, page: current_page, error: re
61
+ rescue => exception
62
+ log.error "Error executing Shodan query", error: exception
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,59 @@
1
+ require 'test/unit'
2
+ require 'fluent/env'
3
+ require 'fluent/test'
4
+ require 'fluent/test/driver/input'
5
+ require 'fluent/test/helpers'
6
+ require "fluent/plugin/in_shodan_search"
7
+
8
+ class ShodanSearchInputTest < Test::Unit::TestCase
9
+ include Fluent::Test::Helpers
10
+
11
+ API_KEY = ENV['SHODAN_TEST_API_KEY']
12
+
13
+ def setup
14
+ Fluent::Test.setup
15
+ end
16
+
17
+ sub_test_case 'Configuration' do
18
+ test 'is invalid without API key' do
19
+ assert_raise Fluent::ConfigError do
20
+ create_driver(%[
21
+ query 8.8.8.8
22
+ ])
23
+ end
24
+ end
25
+ test 'is invalid if the API key is invalid' do
26
+ assert_raise Fluent::ConfigError do
27
+ create_driver(%[
28
+ api_key 1234567890AZERTYUIOP
29
+ query 8.8.8.8
30
+ ])
31
+ end
32
+ end
33
+ test 'is invalid without a query' do
34
+ assert_raise Fluent::ConfigError do
35
+ create_driver(%[
36
+ api_key #{API_KEY}
37
+ ])
38
+ end
39
+ end
40
+ test 'query is set correctly' do
41
+ d = create_driver
42
+ assert_equal '8.8.8.8', d.instance.query
43
+ end
44
+ end
45
+
46
+ sub_test_case 'Plugin emission' do
47
+ end
48
+
49
+ private
50
+
51
+ CONFIG = %[
52
+ api_key #{API_KEY}
53
+ query 8.8.8.8
54
+ ]
55
+
56
+ def create_driver(conf = CONFIG)
57
+ Fluent::Test::Driver::Input.new(Fluent::Plugin::ShodanSearch).configure(conf)
58
+ end
59
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-shodan
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - srilumpa
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-01-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fluentd
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.12.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 0.12.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2'
33
+ - !ruby/object:Gem::Dependency
34
+ name: shodanz
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '2.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: io-console
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: bundler
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '2'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rake
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '13'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '13'
89
+ - !ruby/object:Gem::Dependency
90
+ name: test-unit
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '3.5'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.5'
103
+ description:
104
+ email: marcandre.doll@gmail.com
105
+ executables: []
106
+ extensions: []
107
+ extra_rdoc_files:
108
+ - README.md
109
+ files:
110
+ - AUTHORS
111
+ - README.md
112
+ - VERSION
113
+ - lib/fluent/plugin/in_shodan_search.rb
114
+ - test/test_in_shodan_search.rb
115
+ homepage: https://github.com/srilumpa/fluent-plugin-shodan
116
+ licenses:
117
+ - Apache-2.0
118
+ metadata: {}
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ requirements: []
134
+ rubygems_version: 3.0.3.1
135
+ signing_key:
136
+ specification_version: 3
137
+ summary: Fluentd plugin to extract data from Shodan
138
+ test_files:
139
+ - test/test_in_shodan_search.rb