anamo 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +16 -0
- data/README.md +98 -0
- data/bin/anamo +47 -0
- data/bin/anamo_cli +5 -0
- data/lib/anamo/api.rb +93 -0
- data/lib/anamo/fstree/thor.rb +226 -0
- data/lib/anamo/groups/thor.rb +52 -0
- data/lib/anamo/node/thor.rb +107 -0
- data/lib/anamo/pkgver/thor.rb +111 -0
- data/lib/anamo/ports/thor.rb +100 -0
- data/lib/anamo/runner.rb +136 -0
- data/lib/anamo/thor.rb +31 -0
- data/lib/anamo/users/thor.rb +42 -0
- data/lib/anamo/version.rb +3 -0
- metadata +144 -0
checksums.yaml
ADDED
@@ -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
|
+
|
data/README.md
ADDED
@@ -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
|
+
```
|
data/bin/anamo
ADDED
@@ -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
|
data/bin/anamo_cli
ADDED
data/lib/anamo/api.rb
ADDED
@@ -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
|
data/lib/anamo/runner.rb
ADDED
@@ -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
|
data/lib/anamo/thor.rb
ADDED
@@ -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
|
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: []
|