anamo 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 504309d5a14cbe13c3b572643afb38c3e960b423
4
+ data.tar.gz: 09b8f9233c8d961164c53aedead7a42cb68dad62
5
+ SHA512:
6
+ metadata.gz: 08b1b18913fa75be9774a94604cc3e92d4476e8f0d5b4ef06716f9545af6a00e3b1a3354c07347514c1889ca637069a6da6fb00d756faef073c586ea52766d0f
7
+ data.tar.gz: 74dd5c4e06451d488a49a0534a6cd268c7a28e673c2a73b35ab6cf346782f7ff1bb56c94530a24f995f20a0f76186be471139b72346c68fa38c6f8e866eb6abd
data/LICENSE ADDED
@@ -0,0 +1,16 @@
1
+ 1. General. All code in this repository is the intellectual property of US ProTech, Inc (hereafter “USPT”) and is referred to herein as the "Software." The Software (whether on disk, in read only memory, on any other media or in any other form) is licensed, not sold, to you by USPT for use only under the terms of this License, and USPT reserves all rights not expressly granted to you. The rights granted herein are limited to USPT’s intellectual property rights in the USPT Software and do not include any other patents or intellectual property rights. You own the media on which the USPT Software is recorded but USPT and/or USPT’s licensor(s) retain ownership of the Software itself.
2
+
3
+ 2. Permitted License Uses and Restrictions. This License allows you to install and use one (1) copy of the Software on a single device or computer at a time. This License does not allow the Software to exist on more than one such device or computer at a time, and you may not make the Software available over a network where it could be used by multiple devices or multiple computers at the same time.
4
+
5
+ 3. Except as and only to the extent expressly permitted in this License or by applicable law, you may not copy, distrubute, or make commercially available to any party, for any purpose, the source code or create derivative works of the Software or any part thereof. Any attempt to do so is a violation of the rights of USPT and any of its licensors of the Software which may from time to time exist. If you breach this restriction, you may be subject to prosecution and damages under Title 17 of the United States Code and other applicable state and federal laws.
6
+
7
+ 4. The Software is not intended for any use in which the failure of the Software could lead to death, personal injury, emotional distress, or severe physical or environmental damage.
8
+
9
+ 5. You may not rent, lease, lend or sublicense the Software to any party, for any purpose, without the express, written consent of USPT.
10
+
11
+ 6. This License is effective until terminated. Your rights under this License will terminate automatically without notice from USPT if you fail to comply with any term(s) of this License. Upon the termination of this License, you shall cease all use of the USPT Software and destroy all copies, full or partial, of the USPT Software.
12
+
13
+ 7. Limitation of Liability: TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT SHALL USPT BE LIABLE FOR PERSONAL INJURY, OR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER, INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, LOSS OF DATA, BUSINESS INTERRUPTION OR ANY OTHER COMMERCIAL DAMAGES OR LOSSES, ARISING OUT OF OR RELATED TO YOUR USE OR INABILITY TO USE THE SOFTWARE, HOWEVER CAUSED, REGARDLESS OF THE THEORY OF LIABILITY (CONTRACT, TORT OR OTHERWISE) AND EVEN IF DANGER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY FOR PERSONAL INJURY, OR OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU. In no event shall USPT’s total liability to you for all damages (other than as may be required by applicable law in cases involving personal injury) exceed the amount of twenty dollars ($20.00) in United States currency. The foregoing limitations will apply even if the above stated remedy fails of its essential purpose.
14
+
15
+ 8. Controlling Law and Severability and Choice of Forum. This License will be governed by and construed in accordance with the laws of the State of California, as applied to agreements entered into and to be performed entirely within California between California residents, without giving any effect to the choice of laws provisions of the State of California. Furthermore, you hereby agree that all matters relating to your access to, and use of, all portions and features of the Software, including all legal disputes and complaints (including those involving this License), shall be governed by the laws of the United States and by the laws of the State of California without regard to its conflicts of laws provisions. This License shall not be governed by the United Nations Convention on Contracts for the International Sale of Goods, the application of which is expressly excluded. If for any reason a court of competent jurisdiction finds any provision, or portion thereof, to be unenforceable, the remainder of this License shall continue in full force and effect. You agree that the only courts in which you will bring lawsuits concerning the application or enforcement of this License are courts of competent jurisdiction located in the State of California in the County of Orange and you consent to the exercise of jurisdiction by any such court. You hereby hereby agree to the personal jurisdiction by, and venue in, the state and federal courts in Orange County, California, and waive any legal objection to such jurisdiction and venue. You irrevocably waive any objection on the grounds of venue, forum non-conveniens, fairness, notice, or any similar grounds and irrevocably consent to service of process by mail or in any other manner permitted by applicable law and consent to the jurisdiction of the aforementioned courts. You agree that USPT shall have the option, in the event of a dispute arising out of or relating to this License, to submit any such dispute(s) to arbitration in Orange County, California, pursuant to the rules of the American Arbitration Association. You agree that the decision of the Arbitrator shall be final and binding on you and that judgment upon the award may be entered in any of the aforementioned courts.
16
+
@@ -0,0 +1,98 @@
1
+ The `anamo` gem provides a beacon that collects system telemetry for a client and delivers it to Anamo for analysis.
2
+
3
+ This gem requires **Ruby 2.2+** and is compatible with **RPM-based** and **Debian-based** systems.
4
+
5
+ ## Requirements
6
+
7
+ The Anamo beacon has the following requirements:
8
+
9
+ * RPM-based or Debian-based System
10
+ * Ruby 2.2+
11
+ * Bundler
12
+
13
+ For full effectiveness, `netstat` or `nmap` should be installed.
14
+
15
+ ## Setup
16
+
17
+ To setup the beacon, run the following:
18
+
19
+ ```
20
+ anamo setup
21
+ ```
22
+
23
+ The output should be:
24
+ ```
25
+ [root@localhost bin]# anamo setup
26
+ The Anamo configuration setup tool will walk you through the process
27
+ of defining your configuration for:
28
+
29
+ /etc/anamo.conf.yml
30
+
31
+ What is your Anamo API key?
32
+ [User Note: input your company's API Key here, which you may find at: https://anamo.io/settings/licenses/api-keys]
33
+
34
+ Your configuration file:
35
+
36
+ ---
37
+ api_key: [User-supplied API key]
38
+
39
+ Would you like to save this file now? ("y" to save) y
40
+ Configurated saved at: /etc/anamo.conf.yml
41
+ ```
42
+
43
+ Upon completion, a configuration file will be written to `/etc/anamo.conf.yml`.
44
+
45
+ For provisioned systems, one may also directly write `/etc/anamo.conf.yml`, minimally with:
46
+
47
+ ```
48
+ ---
49
+ api_key: YOUR_API_KEY
50
+ ```
51
+
52
+ A valid API key is required in order to send data to `anamo.io`.
53
+
54
+ ## Usage
55
+
56
+ To start the Anamo beacon:
57
+
58
+ ```
59
+ # anamo start
60
+ ```
61
+
62
+ To check the status of the Anamo beacon:
63
+
64
+ ```
65
+ # anamo status
66
+ ```
67
+
68
+ To stop the Anamo beacon:
69
+
70
+ ```
71
+ # anamo stop
72
+ ```
73
+
74
+ A full set of options:
75
+
76
+ ```
77
+ Usage: anamo <command> <options> -- <application options>
78
+
79
+ * where <command> is one of:
80
+ start start an instance of the application
81
+ stop stop all instances of the application
82
+ restart stop all instances and restart them afterwards
83
+ reload send a SIGHUP to all instances of the application
84
+ run start the application and stay on top
85
+ zap set the application to a stopped state
86
+ status show status (PID) of application instances
87
+
88
+ * and where <options> may contain several of the following:
89
+
90
+ -t, --ontop Stay on top (does not daemonize)
91
+ -s, --shush Silent mode (no output to the terminal)
92
+ -f, --force Force operation
93
+ -n, --no_wait Do not wait for processes to stop
94
+
95
+ Common options:
96
+ -h, --help Show this message
97
+ --version Show version
98
+ ```
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'daemons'
4
+
5
+ # check for "anamo start -- /path/to/config.yml"
6
+ if ARGV[2]
7
+ path = ARGV[2]
8
+ # otherwise use system path
9
+ else
10
+ path = '/etc/anamo.conf.yml'
11
+ end
12
+
13
+ unless ARGV[0] == 'setup'
14
+
15
+ if File.exists? path
16
+
17
+ Daemons.run "#{File.dirname File.dirname __FILE__}/lib/anamo/runner.rb",
18
+ app_name: 'anamo',
19
+ multiple: false,
20
+ monitor: true,
21
+ log_output: true,
22
+ dir: '/var/log/anamo',
23
+ force_kill_waittime: 15,
24
+ dir_mode: :system
25
+
26
+ else
27
+
28
+ $stderr.puts ""
29
+ $stderr.puts "No configuration file defined at `#{path}`. "
30
+ $stderr.puts ""
31
+ $stderr.puts "To configure this file, use:"
32
+ $stderr.puts ""
33
+ $stderr.puts " anamo setup -- #{path}"
34
+ $stderr.puts ""
35
+
36
+ end
37
+
38
+ else
39
+
40
+ require 'anamo/thor'
41
+
42
+ cmd = ::Anamo::Node::Thor.new
43
+ cmd.options = {}
44
+ cmd.set_config_file_path path
45
+ cmd.configure
46
+
47
+ end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'anamo/thor'
4
+
5
+ ::Anamo::Thor.start
@@ -0,0 +1,93 @@
1
+ require "net/http"
2
+ require 'net/http/post/multipart'
3
+ require 'yaml'
4
+
5
+ module Anamo
6
+ class Api
7
+
8
+ def initialize
9
+ @api_base_url = 'https://anamo.io/api'
10
+ @config = YAML.load_file "/etc/anamo.conf.yml"
11
+ @client_key = File.exists?("/etc/anamo.client.key") ? File.open("/etc/anamo.client.key", 'rb') { |f| f.read } : nil
12
+ end
13
+
14
+ def make_url path
15
+ "#{@api_base_url}/#{path}"
16
+ end
17
+
18
+ def make_uri path
19
+ URI.parse make_url path
20
+ end
21
+
22
+ def do_post path, &block
23
+ uri = make_uri path
24
+ http = Net::HTTP.new(uri.host, uri.port)
25
+ http.use_ssl = true
26
+ request = Net::HTTP::Post.new(uri.request_uri)
27
+ request['X-UPTB-Client-Key'] = @client_key if @client_key
28
+ instance_exec request, http, &block
29
+ http.request(request)
30
+ end
31
+
32
+ def do_multipart_post path, files
33
+ uri = make_uri path
34
+ request = Net::HTTP::Post::Multipart.new uri.path, files
35
+ request['X-UPTB-Client-Key'] = @client_key if @client_key
36
+ Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
37
+ http.request(request)
38
+ end
39
+ end
40
+
41
+ def post_fstree data_files
42
+ response = do_multipart_post 'fstree', data_files
43
+ raise 'Send failure' if !response.is_a?(Net::HTTPOK)
44
+ response
45
+ end
46
+
47
+ def post_users data
48
+ response = do_post 'users' do |request|
49
+ request.body = data
50
+ request["Content-Type"] = "text/plain"
51
+ end
52
+ raise 'Send failure' if !response.is_a?(Net::HTTPOK)
53
+ response
54
+ end
55
+
56
+ def post_groups data
57
+ response = do_post 'groups' do |request|
58
+ request.body = data
59
+ request["Content-Type"] = "text/plain"
60
+ end
61
+ raise 'Send failure' if !response.is_a?(Net::HTTPOK)
62
+ response
63
+ end
64
+
65
+ def post_ports data
66
+ response = do_post 'ports' do |request|
67
+ request.body = data
68
+ request["Content-Type"] = "application/json"
69
+ end
70
+ raise 'Send failure' if !response.is_a?(Net::HTTPOK)
71
+ response
72
+ end
73
+
74
+ def post_pkgver data
75
+ response = do_post 'pkgver' do |request|
76
+ request.body = data
77
+ request["Content-Type"] = "application/json"
78
+ end
79
+ raise 'Send failure' if !response.is_a?(Net::HTTPOK)
80
+ response
81
+ end
82
+
83
+ def post_node data
84
+ response = do_post 'node' do |request|
85
+ request.body = data
86
+ request["Content-Type"] = "application/json"
87
+ end
88
+ #raise 'Send failure' if !response.is_a?(Net::HTTPOK)
89
+ response
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,226 @@
1
+ require 'thor'
2
+ require 'benchmark'
3
+ require 'zlib'
4
+ require 'fileutils'
5
+ require 'anamo/api'
6
+
7
+ module Anamo
8
+ module Fstree
9
+
10
+ class Writer
11
+
12
+ def initialize path = '.', prefix = nil, base_path = '/'
13
+ @row_count = 0
14
+ @previous_row = nil
15
+ @handle = nil
16
+ @files_per_file = 1000000
17
+ @path = path
18
+ @prefix = prefix
19
+ @base_path = base_path
20
+ init_handle
21
+ end
22
+
23
+ def init_handle
24
+ @handle = Zlib::GzipWriter.open("#{@path}/#{@prefix ? "#{@prefix}-" : ""}#{(@row_count/@files_per_file).round}.gz")
25
+ @handle.write "[#{@base_path}]\n"
26
+ end
27
+
28
+ def close_handle
29
+ @handle.close if @handle
30
+ end
31
+
32
+ def reinit_handle
33
+ close_handle
34
+ GC.start
35
+ init_handle
36
+ end
37
+
38
+ def add_row row
39
+ reinit_handle if @row_count > 0 && @row_count % @files_per_file == 0
40
+ [0,2,3,4,5].each { |i| row[i] = '' if @previous_row[i] == row[i] } if @previous_row
41
+ @handle.write "#{row.join(',')}\n"
42
+ @row_count = @row_count + 1
43
+ @previous_row = row
44
+ end
45
+
46
+ def close
47
+ @handle.close
48
+ end
49
+
50
+ end
51
+
52
+ class Traverser
53
+
54
+ attr_reader :base_path
55
+
56
+ def initialize base_path = '/', writer = Writer.new, max_depth = 2, exclusions = []
57
+ @max_depth = max_depth
58
+ @base_path = base_path
59
+ @writer = writer
60
+ @exclusions = exclusions
61
+ end
62
+
63
+ def compute
64
+ walk_path @base_path
65
+ @writer.close
66
+ end
67
+
68
+ private
69
+
70
+ def walk_path path, parent_data = {}, depth = 0
71
+
72
+ begin
73
+ return if @exclusions.include? File.realpath(path) # path is in excluded set
74
+ rescue
75
+ return # path does not exist
76
+ end
77
+
78
+ begin
79
+ stat = File.stat(path)
80
+ rescue
81
+ stat = nil
82
+ end
83
+
84
+ # TODO: escape base name!!!!
85
+ data = [depth, File.basename(path)]
86
+
87
+ open_as_directory = false
88
+
89
+ if File.symlink? path
90
+ data << 'sym'
91
+ elsif File.directory? path
92
+ open_as_directory = true
93
+ data << 'd'
94
+ elsif File.file? path
95
+ data << 'f'
96
+ elsif File.socket? path
97
+ data << 'soc'
98
+ elsif File.blockdev? path
99
+ data << 'bd'
100
+ elsif File.chardev? path
101
+ data << 'cd'
102
+ end
103
+
104
+ begin
105
+ data << stat.uid
106
+ rescue
107
+ data << '-'
108
+ end
109
+
110
+ begin
111
+ data << stat.gid
112
+ rescue
113
+ data << '-'
114
+ end
115
+
116
+ begin
117
+ data << stat.mode
118
+ rescue
119
+ data << '-'
120
+ end
121
+
122
+ @writer.add_row data
123
+
124
+ if open_as_directory
125
+ sub_depth = depth + 1
126
+ return if sub_depth > @max_depth
127
+ Dir["#{path}/*"].each do |sub_path|
128
+ walk_path sub_path, data, sub_depth
129
+ end
130
+ end
131
+
132
+ end
133
+
134
+ end
135
+
136
+ class Thor < ::Thor
137
+
138
+ description = "Inspect the file system tree and send the results"
139
+ desc "fstree exec", description
140
+ long_desc description
141
+
142
+ def exec
143
+
144
+ inspect
145
+ send
146
+
147
+ end
148
+
149
+ description = "Inspect the file system tree"
150
+ desc "fstree inspect", description
151
+ long_desc description
152
+
153
+ def inspect
154
+
155
+ FileUtils.rm_rf temp_folder
156
+ FileUtils.mkdir_p temp_folder
157
+
158
+ idx = 0
159
+
160
+ paths.each do |path, depth|
161
+ traverser = Traverser.new(path, Writer.new(temp_folder, idx, path), depth, exclusions)
162
+ traverser.compute
163
+ idx = idx + 1
164
+ end
165
+
166
+ end
167
+
168
+ description = "Send the last version of the file system tree from inspect command to the API"
169
+ desc "fstree send", description
170
+ long_desc description
171
+
172
+ def send
173
+
174
+ files = {}
175
+ Dir["#{temp_folder}/*"].each do |file|
176
+ files[File.basename(file, '.*')] = UploadIO.new(File.new(file), "application/gzip", File.basename(file))
177
+ end
178
+
179
+ ::Anamo::Api.new.post_fstree files
180
+
181
+ end
182
+
183
+ no_commands do
184
+
185
+ def set_paths paths
186
+ @paths = paths
187
+ end
188
+
189
+ def paths
190
+ if defined? @paths
191
+ @paths
192
+ else
193
+ return {
194
+ '/' => 2
195
+ }
196
+ end
197
+ end
198
+
199
+ def set_exclusions paths
200
+ @exclusions = paths
201
+ end
202
+
203
+ def exclusions
204
+ if defined? @exclusions
205
+ @exclusions
206
+ else
207
+ return [
208
+ '/proc',
209
+ '/tmp',
210
+ '/dev',
211
+ '/run'
212
+ ]
213
+ end
214
+ end
215
+
216
+ end
217
+
218
+ private
219
+
220
+ def temp_folder
221
+ "/tmp/anamo/fstree"
222
+ end
223
+
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,52 @@
1
+ require 'etc'
2
+ require 'thor'
3
+ require "net/http"
4
+ require 'anamo/api'
5
+
6
+ module Anamo
7
+ module Groups
8
+
9
+ class Thor < ::Thor
10
+
11
+ description = "Inspect the groups list and send the results"
12
+ desc "groups exec", description
13
+ long_desc description
14
+
15
+ def exec
16
+
17
+ data = inspect false
18
+ response = ::Anamo::Api.new.post_groups data
19
+
20
+ end
21
+
22
+ description = "Inspect the groups list"
23
+ desc "groups inspect", description
24
+ long_desc description
25
+
26
+ def inspect output = true
27
+
28
+ un_to_uid = {};
29
+ gids_from_passwd = {}
30
+ Etc.passwd do |u|
31
+ un_to_uid[u.name] = u.uid
32
+ gids_from_passwd[u.gid] = [] unless gids_from_passwd.has_key? u.gid
33
+ gids_from_passwd[u.gid] << u.uid unless gids_from_passwd[u.gid].include? u.uid
34
+ end
35
+
36
+ ret = CSV.generate do |csv|
37
+ Etc.group do |g|
38
+ uids_in_group = g.mem.map { |un| un_to_uid[un] }
39
+ uids_in_group += gids_from_passwd[g.gid] if gids_from_passwd.has_key? g.gid
40
+ csv << [g.gid, g.name, "[#{uids_in_group.uniq.join(',')}]"]
41
+ end
42
+ end
43
+
44
+ puts ret if output
45
+
46
+ ret
47
+
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,107 @@
1
+ require 'multi_json'
2
+ require 'thor'
3
+ require 'anamo/api'
4
+ require 'socket'
5
+ require 'yaml'
6
+
7
+ module Anamo
8
+ module Node
9
+
10
+ class Thor < ::Thor
11
+
12
+ description = "Inspect the node, send the results and maintain node state"
13
+ desc "users exec", description
14
+ long_desc description
15
+
16
+ def exec
17
+
18
+ data = inspect false
19
+ response = ::Anamo::Api.new.post_node MultiJson.dump data
20
+
21
+ response_data = MultiJson.load response.body
22
+ if response_data.respond_to?(:has_key?) and response_data.has_key?('client_key')
23
+ File.open(key_file, 'w') { |file| file.write response_data['client_key'] }
24
+ File.chmod(0600, key_file)
25
+ end
26
+
27
+ end
28
+
29
+ description = "Inspect the node"
30
+ desc "node inspect", description
31
+ long_desc description
32
+
33
+ def inspect output = true
34
+
35
+ ret = {}
36
+
37
+ config = YAML.load_file "/etc/anamo.conf.yml"
38
+
39
+ ret['api_key'] = config['api_key']
40
+ ret['beacon_modules'] = config['modules']
41
+ ret['node'] = {}
42
+ ret['node']['hostname'] = Socket.gethostname
43
+ ret['node']['ips'] = Socket.ip_address_list.map(){ |entry| entry.ip_address }.uniq
44
+
45
+ puts ret if output
46
+
47
+ ret
48
+
49
+ end
50
+
51
+ description = "Setup configuration on the node"
52
+ desc "node configure", description
53
+ long_desc description
54
+
55
+ def configure
56
+
57
+ configuration = {}
58
+
59
+ say "The Anamo configuration setup tool will walk you through the process\nof defining your configuration for:\n\n", [:green, :bold]
60
+ say " #{config_file_path}\n\n", :green
61
+
62
+ configuration['api_key'] = ''
63
+ until configuration['api_key'].strip.length > 0 do
64
+ configuration['api_key'] = ask "What is your Anamo API key?\n", :bold
65
+ end
66
+
67
+ say "\nYour configuration file:\n\n", [:green, :bold]
68
+
69
+ configuration_yaml = YAML.dump configuration
70
+
71
+ say configuration_yaml
72
+
73
+ if ['yes', 'y'].include? ask("\nWould you like to save this file now? (\"y\" to save)", :bold).downcase
74
+ File.open(config_file_path, 'w') { |file| file.write configuration_yaml }
75
+ File.chmod(0600, config_file_path)
76
+ say "Configurated saved at: #{config_file_path}", [:cyan, :bold]
77
+ else
78
+ say 'Configuration not saved.', [:red, :bold]
79
+ end
80
+
81
+ end
82
+
83
+ no_commands do
84
+
85
+ def set_config_file_path path
86
+ @config_file_path = path
87
+ end
88
+
89
+ def config_file_path
90
+ if defined? @config_file_path
91
+ @config_file_path
92
+ else
93
+ return '/etc/anamo.conf.yml'
94
+ end
95
+ end
96
+
97
+ end
98
+
99
+ private
100
+
101
+ def key_file
102
+ "/etc/anamo.client.key"
103
+ end
104
+
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,111 @@
1
+ require 'thor'
2
+ require 'net/http'
3
+ require 'systemu'
4
+ require 'rubygems'
5
+ require 'multi_json'
6
+ require 'anamo/api'
7
+
8
+ module Anamo
9
+ module Pkgver
10
+
11
+ class Thor < ::Thor
12
+
13
+ description = "Inspect installed packages and their versions and send the results"
14
+ desc "pkgver exec", description
15
+ long_desc description
16
+
17
+ def exec
18
+
19
+ data = inspect false
20
+ ::Anamo::Api.new.post_pkgver MultiJson.dump data
21
+
22
+ end
23
+
24
+ description = "Inspect installed packages and their versions"
25
+ desc "pkgver inspect", description
26
+ long_desc description
27
+
28
+ def inspect output = true
29
+
30
+ rpm_packages = []
31
+ status, stdout, stderr = systemu "rpm -qa --qf '%{NAME} %{VERSION} %{RELEASE} %{ARCH}\n'"
32
+ if status.success?
33
+ stdout.split("\n").each do |package|
34
+ package = package.split(' ')
35
+ rpm_packages << {
36
+ 'n' => package[0],
37
+ 'v' => package[1],
38
+ 'r' => package[2],
39
+ 'a' => package[3]
40
+ }
41
+ end
42
+ else
43
+ rpm_packages = nil
44
+ end
45
+
46
+ deb_packages = []
47
+ status, stdout, strerr = systemu "dpkg-query -W -f='${Package} ${Version} ${Architecture}\n'"
48
+ if status.success?
49
+ stdout.split("\n").each do |package|
50
+ package = package.split(' ')
51
+ deb_packages << {
52
+ 'n' => package[0],
53
+ 'v' => package[1],
54
+ 'a' => package[2]
55
+ }
56
+ end
57
+ else
58
+ deb_packages = nil
59
+ end
60
+
61
+ gem_packages = []
62
+ begin
63
+ Gem::Specification.all = nil # TODO: fix for Debian (can't use all=)
64
+ Gem::Specification.each do |a|
65
+ gem_packages << {
66
+ 'n' => a.name,
67
+ 'v' => a.version.to_s
68
+ }
69
+ end
70
+ Gem::Specification.reset
71
+ rescue
72
+ end
73
+
74
+ npm_packages = nil
75
+ status, stdout, strerr = systemu "npm list --global --json"
76
+ if status.success?
77
+ npm_packages = MultiJson.load(stdout)
78
+ end
79
+
80
+ pip_packages = []
81
+ status, stdout, strerr = systemu "pip list"
82
+ if status.success?
83
+ stdout.split("\n").each do |l|
84
+ match_data = /(.*) \((.*)\)/.match(l)
85
+ pip_packages << {
86
+ 'n' => match_data[1],
87
+ 'v' => match_data[2]
88
+ } if match_data
89
+ end
90
+ else
91
+ pip_packages = nil
92
+ end
93
+
94
+ data = {
95
+ 'gem_packages' => gem_packages
96
+ }
97
+
98
+ data['rpm_packages'] = rpm_packages if rpm_packages
99
+ data['deb_packages'] = deb_packages if deb_packages
100
+ data['npm_packages'] = npm_packages if npm_packages
101
+ data['pip_packages'] = pip_packages if pip_packages
102
+
103
+ puts data if output
104
+
105
+ data
106
+
107
+ end
108
+
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,100 @@
1
+ require 'thor'
2
+ require 'net/http'
3
+ require 'systemu'
4
+ require 'rubygems'
5
+ require 'multi_json'
6
+ require 'anamo/api'
7
+
8
+ module Anamo
9
+ module Ports
10
+
11
+ class Thor < ::Thor
12
+
13
+ description = "Inspect listening ports and send the results"
14
+ desc "ports exec", description
15
+ long_desc description
16
+
17
+ def exec
18
+
19
+ data = inspect false
20
+ ::Anamo::Api.new.post_ports MultiJson.dump data
21
+
22
+ end
23
+
24
+ description = "Inspect listening ports"
25
+ desc "ports inspect", description
26
+ long_desc description
27
+
28
+ def inspect output = true
29
+
30
+ netstat_results = nil
31
+ nmap_results = nil
32
+
33
+ status, stdout, stderr = systemu "netstat -lnutp"
34
+ if status.success?
35
+ netstat_results = []
36
+ stdout.split("\n").drop(2).each do |line|
37
+ cols = line.gsub(/\s+/m, ' ').strip.split(" ")
38
+ cols.delete_at(5) if cols.length == 7
39
+ process = cols[5].match(/^([^\/]*)\/(.*)$/)
40
+ netstat_results << {
41
+ 'proto' => cols[0],
42
+ 'l_addr' => cols[3].split(':')[0],
43
+ 'l_port' => cols[3].split(':')[1],
44
+ 'f_addr' => cols[4].split(':')[0],
45
+ 'f_port' => cols[4].split(':')[1],
46
+ 'p_id' => process[1],
47
+ 'p_name' => process[2]
48
+ }
49
+ end
50
+ end
51
+
52
+ l_addr = "127.0.0.1"
53
+
54
+ # tcp scan
55
+ status, stdout, stderr = systemu "nmap -sS --open -p1-65535 #{l_addr}"
56
+ if status.success?
57
+ nmap_results = [] unless nmap_results
58
+ stdout.split("\n").each do |line|
59
+ cols = line.gsub(/\s+/m, ' ').strip.split(" ")
60
+ next unless cols.length > 0
61
+ port_col = cols[0].match(/([^\/]*)\/tcp/)
62
+ next unless port_col
63
+ nmap_results << {
64
+ 'proto' => 'tcp',
65
+ 'l_addr' => l_addr,
66
+ 'l_port' => port_col[1]
67
+ }
68
+ end
69
+ end
70
+
71
+ status, stdout, stderr = systemu "nmap -sU --open -p1-65535 #{l_addr}"
72
+ if status.success?
73
+ nmap_results = [] unless nmap_results
74
+ stdout.split("\n").each do |line|
75
+ cols = line.gsub(/\s+/m, ' ').strip.split(" ")
76
+ next unless cols.length > 0
77
+ port_col = cols[0].match(/([^\/]*)\/udp/)
78
+ next unless port_col
79
+ nmap_results << {
80
+ 'proto' => 'udp',
81
+ 'l_addr' => l_addr,
82
+ 'l_port' => port_col[1]
83
+ }
84
+ end
85
+ end
86
+
87
+ data = {}
88
+
89
+ data['nmap'] = nmap_results if nmap_results
90
+ data['netstat'] = netstat_results if netstat_results
91
+
92
+ puts data if output
93
+
94
+ data
95
+
96
+ end
97
+
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,136 @@
1
+ require 'anamo/thor'
2
+ require 'yaml'
3
+
4
+ if ARGV[0]
5
+ path = ARGV[0]
6
+ else
7
+ path = '/etc/anamo.conf.yml'
8
+ end
9
+
10
+ config = YAML.load_file(path)
11
+
12
+ unless config.has_key? 'modules'
13
+ config['modules'] = {
14
+ 'node' => {
15
+ 'frequency' => 60
16
+ },
17
+ 'fstree' => {
18
+ 'frequency' => 240,
19
+ 'paths' => {
20
+ '/' => 4
21
+ },
22
+ 'exclude' => [
23
+ '/proc',
24
+ '/tmp',
25
+ '/dev',
26
+ '/run'
27
+ ]
28
+ },
29
+ 'pkgver' => {
30
+ 'frequency' => 60
31
+ },
32
+ 'ports' => {
33
+ 'frequency' => 60
34
+ }
35
+ }
36
+ end
37
+
38
+ minutes_running = 0
39
+
40
+ Dir.chdir(File.dirname File.dirname __FILE__) do
41
+
42
+ # initial node setup
43
+ cmd = ::Anamo::Node::Thor.new
44
+ cmd.options = {}
45
+ cmd.exec
46
+
47
+ loop do
48
+
49
+ if config.has_key?('modules') and config['modules'].has_key?('node')
50
+
51
+ frequency = config['modules']['node'].has_key?('frequency') ? config['modules']['node']['frequency'] : 30
52
+
53
+ if minutes_running % frequency == 0
54
+
55
+ begin
56
+
57
+ cmd = ::Anamo::Node::Thor.new
58
+ cmd.options = {}
59
+ cmd.exec
60
+
61
+ rescue; end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ if config.has_key?('modules') and config['modules'].has_key?('fstree')
68
+
69
+ frequency = config['modules']['fstree'].has_key?('frequency') ? config['modules']['fstree']['frequency'] : 30
70
+
71
+ if minutes_running % frequency == 0
72
+
73
+ begin
74
+
75
+ cmd = ::Anamo::Groups::Thor.new
76
+ cmd.options = {}
77
+ cmd.exec
78
+
79
+ cmd = ::Anamo::Users::Thor.new
80
+ cmd.options = {}
81
+ cmd.exec
82
+
83
+ cmd = ::Anamo::Fstree::Thor.new
84
+ cmd.options = {}
85
+ cmd.set_paths(config['modules']['fstree']['paths']) if config['modules']['fstree'].has_key?('paths')
86
+ cmd.set_exclusions(config['modules']['fstree']['exclude']) if config['modules']['fstree'].has_key?('exclude')
87
+ cmd.exec
88
+
89
+ rescue; end
90
+
91
+ end
92
+
93
+ end
94
+
95
+ if config.has_key?('modules') and config['modules'].has_key?('pkgver')
96
+
97
+ frequency = config['modules']['pkgver'].has_key?('frequency') ? config['modules']['pkgver']['frequency'] : 30
98
+
99
+ if minutes_running % frequency == 0
100
+
101
+ begin
102
+
103
+ cmd = ::Anamo::Pkgver::Thor.new
104
+ cmd.options = {}
105
+ cmd.exec
106
+
107
+ rescue; end
108
+
109
+ end
110
+
111
+ end
112
+
113
+ if config.has_key?('modules') and config['modules'].has_key?('ports')
114
+
115
+ frequency = config['modules']['ports'].has_key?('frequency') ? config['modules']['ports']['frequency'] : 30
116
+
117
+ if minutes_running % frequency == 0
118
+
119
+ begin
120
+
121
+ cmd = ::Anamo::Ports::Thor.new
122
+ cmd.options = {}
123
+ cmd.exec
124
+
125
+ rescue; end
126
+
127
+ end
128
+
129
+ end
130
+
131
+ minutes_running = minutes_running + 1
132
+ sleep 60 # wait one minute
133
+
134
+ end
135
+
136
+ end
@@ -0,0 +1,31 @@
1
+ require 'thor'
2
+ require 'anamo/fstree/thor'
3
+ require 'anamo/groups/thor'
4
+ require 'anamo/node/thor'
5
+ require 'anamo/pkgver/thor'
6
+ require 'anamo/ports/thor'
7
+ require 'anamo/users/thor'
8
+
9
+ module Anamo
10
+ class Thor < ::Thor
11
+
12
+ desc "fstree SUBCOMMAND ...ARGS", "File system tree"
13
+ subcommand "fstree", ::Anamo::Fstree::Thor
14
+
15
+ desc "groups SUBCOMMAND ...ARGS", "Groups"
16
+ subcommand "groups", ::Anamo::Groups::Thor
17
+
18
+ desc "node SUBCOMMAND ...ARGS", "Node"
19
+ subcommand "node", ::Anamo::Node::Thor
20
+
21
+ desc "pkgver SUBCOMMAND ...ARGS", "Package versions"
22
+ subcommand "pkgver", ::Anamo::Pkgver::Thor
23
+
24
+ desc "ports SUBCOMMAND ...ARGS", "Ports"
25
+ subcommand "ports", ::Anamo::Ports::Thor
26
+
27
+ desc "users SUBCOMMAND ...ARGS", "Users"
28
+ subcommand "users", ::Anamo::Users::Thor
29
+
30
+ end
31
+ end
@@ -0,0 +1,42 @@
1
+ require 'etc'
2
+ require 'thor'
3
+ require 'csv'
4
+ require 'anamo/api'
5
+
6
+ module Anamo
7
+ module Users
8
+
9
+ class Thor < ::Thor
10
+
11
+ description = "Inspect the user list and send the results"
12
+ desc "users exec", description
13
+ long_desc description
14
+
15
+ def exec
16
+
17
+ data = inspect false
18
+ ::Anamo::Api.new.post_users data
19
+
20
+ end
21
+
22
+ description = "Inspect the user list"
23
+ desc "users inspect", description
24
+ long_desc description
25
+
26
+ def inspect output = true
27
+
28
+ ret = CSV.generate do |csv|
29
+ Etc.passwd do |u|
30
+ csv << [u.uid, u.gid, u.name, u.gecos, u.dir, u.shell]
31
+ end
32
+ end
33
+
34
+ puts ret if output
35
+
36
+ ret
37
+
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,3 @@
1
+ module Anamo
2
+ VERSION = "0.9.2"
3
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: anamo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.2
5
+ platform: ruby
6
+ authors:
7
+ - US ProTech
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: multipart-post
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: systemu
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: multi_json
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: daemons
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Anamo client beacon
98
+ email:
99
+ - alex@usprotech.com
100
+ executables:
101
+ - anamo
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - LICENSE
106
+ - README.md
107
+ - bin/anamo
108
+ - bin/anamo_cli
109
+ - lib/anamo/api.rb
110
+ - lib/anamo/fstree/thor.rb
111
+ - lib/anamo/groups/thor.rb
112
+ - lib/anamo/node/thor.rb
113
+ - lib/anamo/pkgver/thor.rb
114
+ - lib/anamo/ports/thor.rb
115
+ - lib/anamo/runner.rb
116
+ - lib/anamo/thor.rb
117
+ - lib/anamo/users/thor.rb
118
+ - lib/anamo/version.rb
119
+ homepage: http://anamo.io
120
+ licenses:
121
+ - Nonstandard
122
+ metadata: {}
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 2.5.1
140
+ signing_key:
141
+ specification_version: 4
142
+ summary: This gem provides a beacon that collects system telemetry for a client and
143
+ delivers it to Anamo for analysis.
144
+ test_files: []