smart-proxy-probing 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/smart-proxy-probing/actions/use_probe.rb +79 -0
- data/lib/smart-proxy-probing/actions.rb +7 -0
- data/lib/smart-proxy-probing/neighbour_cache.rb +34 -0
- data/lib/smart-proxy-probing/plugin.rb +6 -5
- data/lib/smart-proxy-probing/probes/abstract.rb +29 -0
- data/lib/smart-proxy-probing/probes/icmp.rb +18 -0
- data/lib/smart-proxy-probing/probes/nmap.rb +95 -0
- data/lib/smart-proxy-probing/probes/tcp.rb +19 -0
- data/lib/smart-proxy-probing/probes/udp.rb +19 -0
- data/lib/smart-proxy-probing/probes.rb +9 -0
- data/lib/smart-proxy-probing/version.rb +1 -1
- data/lib/smart-proxy-probing.rb +15 -1
- metadata +27 -5
- data/bundler.plugins.d/probing.rb +0 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bd8c5984e72b14774fe2b627c2a07e753a0b0b88bd5f2c4836a66398e3c7b32b
|
|
4
|
+
data.tar.gz: 71953520137a94e049ec1f9ad236b44accb7e0e434e1f7fee55638c14ab513a6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4d7830b23cb0d0ef2c0e3e995c5a0c23061cad916b6e479dda40e7356e966fbd99df946b0782c4b2884c556d32eee36775f1773d39ab31ac728de63b1adb61f8
|
|
7
|
+
data.tar.gz: 8530255a7c8b79b5f8a038a7cfa4c32619322b8ff0510f894499f3456ec27fb377aa84901cdab8d95556e23193ef0aa6f2bfc3cfcdef80132c395232d095c913
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require 'smart_proxy_dynflow/runner'
|
|
2
|
+
require 'smart_proxy_dynflow/runner/command_runner'
|
|
3
|
+
|
|
4
|
+
module Proxy::Probing
|
|
5
|
+
module Actions
|
|
6
|
+
class CommandRunner < Proxy::Dynflow::Runner::CommandRunner
|
|
7
|
+
def initialize(*command)
|
|
8
|
+
super
|
|
9
|
+
@command = command
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def start
|
|
13
|
+
initialize_command(*@command)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class UseProbe < Proxy::Dynflow::Action::Runner
|
|
18
|
+
def initiate_runner
|
|
19
|
+
if input.fetch('options', {})['subnet_discovery']
|
|
20
|
+
input['local_addresses'] = get_local_addrs
|
|
21
|
+
input['targets'] = input['local_addresses'].keys
|
|
22
|
+
end
|
|
23
|
+
output[:targets] = input[:targets]
|
|
24
|
+
output[:local_addresses] = input[:local_addresses] if input.key? :local_addresses
|
|
25
|
+
CommandRunner.new(*probe.command)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def finish_run(update)
|
|
29
|
+
super
|
|
30
|
+
output[:facts] = process_output(output[:result])
|
|
31
|
+
output.delete(:result)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def process_output(output)
|
|
37
|
+
stdout, stderr = output.partition { |out| out[:output_type] == 'stdout' }
|
|
38
|
+
if stderr.any?
|
|
39
|
+
raise stderr.map { |out| out[:output] }.join('')
|
|
40
|
+
else
|
|
41
|
+
output = stdout.map { |out| out[:output] }.join('')
|
|
42
|
+
probe.parse_result(output)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def probe
|
|
47
|
+
@probe ||= probe_class.new(input[:targets],
|
|
48
|
+
input[:ports],
|
|
49
|
+
input[:options])
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def probe_class
|
|
53
|
+
case input[:scan_type].downcase
|
|
54
|
+
when 'tcp'
|
|
55
|
+
Proxy::Probing::Probes::TCP
|
|
56
|
+
when 'udp'
|
|
57
|
+
Proxy::Probing::Probes::UDP
|
|
58
|
+
when 'icmp'
|
|
59
|
+
Proxy::Probing::Probes::ICMP
|
|
60
|
+
else
|
|
61
|
+
raise "Unknown scan_type '#{input[:scan_type]}'"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def get_local_addrs
|
|
66
|
+
locals = Socket.getifaddrs.select { |ifaddr| ifaddr.addr && ifaddr.addr.ipv4_private? }
|
|
67
|
+
locals.reduce({}) do |acc, ifaddr|
|
|
68
|
+
# TODO: This is ugly
|
|
69
|
+
cidr = 32 - ifaddr.netmask.ip_address.to_i.to_s(2).count('1')
|
|
70
|
+
|
|
71
|
+
acc.merge("#{ifaddr.addr.ip_address}/#{cidr}" => { :addr => ifaddr.addr.ip_address,
|
|
72
|
+
:netmask => ifaddr.netmask.ip_address,
|
|
73
|
+
:cidr => cidr })
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Proxy::Probing
|
|
2
|
+
class NeighbourCache
|
|
3
|
+
def initialize
|
|
4
|
+
@cache = []
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def ips_for_mac(mac)
|
|
8
|
+
@cache.select { |record| record['lladdr'] == mac }.map { |record| record[:ip] }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def mac_for_ip(ip)
|
|
12
|
+
@cache.select { |record| record[:ip] == ip }.map { |record| record['lladdr'] }.first
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def cache!
|
|
16
|
+
@cache = `ip neigh show`.lines.map do |line|
|
|
17
|
+
fields = line.chomp.split(/\s+/)
|
|
18
|
+
hash = { :ip => fields.shift }
|
|
19
|
+
fields.each_slice(2) do |k, v|
|
|
20
|
+
if v
|
|
21
|
+
hash[k] = v
|
|
22
|
+
else
|
|
23
|
+
hash[:state] = k
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
hash
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def clean!
|
|
31
|
+
@cache = {}
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -8,12 +8,13 @@ module Proxy
|
|
|
8
8
|
require 'smart_proxy_dynflow'
|
|
9
9
|
require 'smart-proxy-probing/version'
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
require 'smart_proxy_dynflow_core'
|
|
13
|
-
require 'foreman-probing-core'
|
|
14
|
-
rescue LoadError; end
|
|
15
|
-
end
|
|
11
|
+
require 'dynflow'
|
|
16
12
|
|
|
13
|
+
require 'smart_proxy_dynflow/runner'
|
|
14
|
+
require 'smart-proxy-probing/actions'
|
|
15
|
+
require 'smart-proxy-probing/neighbour_cache'
|
|
16
|
+
require 'smart-proxy-probing/probes'
|
|
17
|
+
end
|
|
17
18
|
end
|
|
18
19
|
end
|
|
19
20
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Proxy::Probing
|
|
2
|
+
module Probes
|
|
3
|
+
class Abstract
|
|
4
|
+
class << self
|
|
5
|
+
def scan_type
|
|
6
|
+
raise NotImplementedError
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def humanized_scan_type
|
|
10
|
+
raise NotImplementedError
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def initialize(hosts, ports = [], options = {})
|
|
15
|
+
@hosts = Array(hosts)
|
|
16
|
+
@ports = Array(ports)
|
|
17
|
+
@options = options
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def command
|
|
21
|
+
raise NotImplementedError
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def parse_result(string)
|
|
25
|
+
raise NotImplementedError
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require 'ipaddr'
|
|
2
|
+
require 'open3'
|
|
3
|
+
require 'nmap/xml'
|
|
4
|
+
|
|
5
|
+
module Proxy::Probing
|
|
6
|
+
module Probes
|
|
7
|
+
class Nmap < Abstract
|
|
8
|
+
def command
|
|
9
|
+
sudo_prefix = self.class.scan_type == 'udp' ? ['sudo'] : []
|
|
10
|
+
[sudo_prefix, 'nmap', nmap_ipv6_flag, nmap_arguments, nmap_flags, hosts, with_ports(@ports)].flatten
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# By default just return whatever is passed in
|
|
14
|
+
# Can be used as an extension point
|
|
15
|
+
def parse_result(xml)
|
|
16
|
+
xml_to_hash(xml)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def hosts
|
|
22
|
+
@hosts.map(&:to_s)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def nmap_flags
|
|
26
|
+
raise NotImplementedError
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def nmap_arguments
|
|
30
|
+
%w(-oX -)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def nmap_ipv6_flag
|
|
34
|
+
IPAddr.new(@hosts.first).ipv6? ? '-6' : ''
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def xml_to_hash(output)
|
|
38
|
+
xml = ::Nmap::XML.parse(output)
|
|
39
|
+
xml.hosts.map do |host|
|
|
40
|
+
ports = host.ports.reduce({}) do |acc, port|
|
|
41
|
+
service = %w(confidence extra_info fingerprint hostname protocol product version).reduce({}) do |in_acc, key|
|
|
42
|
+
in_acc.merge(key => port.service.public_send(key.to_sym))
|
|
43
|
+
end
|
|
44
|
+
service[:ssl] = port.service.ssl?
|
|
45
|
+
service[:method] = port.service.fingerprint_method
|
|
46
|
+
record = {
|
|
47
|
+
port.number =>
|
|
48
|
+
{
|
|
49
|
+
:service => { port.service.name => service },
|
|
50
|
+
:state => port.state,
|
|
51
|
+
:reason => port.reason,
|
|
52
|
+
:scripts => port.scripts
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
acc.merge(port.protocol => acc.fetch(port.protocol, {}).merge(record))
|
|
56
|
+
end
|
|
57
|
+
{
|
|
58
|
+
:_type => :foreman_probing,
|
|
59
|
+
:addresses => factify_addresses(lookup_addresses(host.addresses.map(&:to_h))),
|
|
60
|
+
:hostnames => host.hostnames.map(&:to_h),
|
|
61
|
+
:ports => ports,
|
|
62
|
+
:status => host.status.to_h
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def with_ports(ports)
|
|
68
|
+
return '' if ports.empty? || nmap_flags.include?("-sn")
|
|
69
|
+
"-p #{ports.join(',')}"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def factify_addresses(addresses)
|
|
73
|
+
addresses.reduce({}) do |acc, address|
|
|
74
|
+
type = address.delete(:type)
|
|
75
|
+
addr = address.delete(:addr)
|
|
76
|
+
acc.update(type => { addr => address })
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def lookup_addresses(addresses)
|
|
81
|
+
@neighbour_cache ||= Proxy::Probing::NeighbourCache.new.tap(&:cache!)
|
|
82
|
+
ips = addresses.map { |address| address[:addr] }
|
|
83
|
+
macs = ips.map do |ip|
|
|
84
|
+
@neighbour_cache.mac_for_ip(ip)
|
|
85
|
+
end.compact
|
|
86
|
+
new_ips = macs.map do |mac|
|
|
87
|
+
@neighbour_cache.ips_for_mac(mac).select { |ip| !ips.include? ip }.map do |ip|
|
|
88
|
+
{ :type => IPAddr.new(ip).ipv4? ? 'ipv4' : 'ipv6', :addr => ip, :vendor => nil }
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
addresses + new_ips.flatten + macs.flatten.map { |mac| { :type => 'hwaddr', :addr => mac, :vendor => nil } }
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Proxy::Probing
|
|
2
|
+
module Probes
|
|
3
|
+
class TCP < Nmap
|
|
4
|
+
def self.scan_type
|
|
5
|
+
'tcp'
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.humanized_scan_type
|
|
9
|
+
_('TCP')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def nmap_flags
|
|
13
|
+
# Use TCP connect scan with service detection
|
|
14
|
+
%w(-sT -sV -T4 -A)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Proxy::Probing
|
|
2
|
+
module Probes
|
|
3
|
+
class UDP < Nmap
|
|
4
|
+
def self.scan_type
|
|
5
|
+
'udp'
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.humanized_scan_type
|
|
9
|
+
_('UDP')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def nmap_flags
|
|
13
|
+
# Use UDP scan with service detection
|
|
14
|
+
%w(-sU -sV)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
module Proxy::Probing
|
|
2
|
+
module Probes
|
|
3
|
+
require 'smart-proxy-probing/probes/abstract'
|
|
4
|
+
require 'smart-proxy-probing/probes/nmap'
|
|
5
|
+
require 'smart-proxy-probing/probes/tcp'
|
|
6
|
+
require 'smart-proxy-probing/probes/udp'
|
|
7
|
+
require 'smart-proxy-probing/probes/icmp'
|
|
8
|
+
end
|
|
9
|
+
end
|
data/lib/smart-proxy-probing.rb
CHANGED
|
@@ -2,9 +2,23 @@ require 'smart_proxy_dynflow'
|
|
|
2
2
|
|
|
3
3
|
module Proxy
|
|
4
4
|
module Probing
|
|
5
|
-
|
|
6
5
|
require 'smart-proxy-probing/version'
|
|
7
6
|
require 'smart-proxy-probing/plugin'
|
|
8
7
|
|
|
8
|
+
class << self
|
|
9
|
+
def use_nmap?
|
|
10
|
+
# TODO:
|
|
11
|
+
# Settings.nmap_enabled &&
|
|
12
|
+
nmap_available?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def nmap_available?
|
|
16
|
+
return @nmap_available unless @nmap_available.nil?
|
|
17
|
+
`nmap`
|
|
18
|
+
@nmap_available = true
|
|
19
|
+
rescue Errno::ENOENT
|
|
20
|
+
@nmap_available = false
|
|
21
|
+
end
|
|
22
|
+
end
|
|
9
23
|
end
|
|
10
24
|
end
|
metadata
CHANGED
|
@@ -1,29 +1,43 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: smart-proxy-probing
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Adam Ruzicka
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-10-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: ruby-nmap
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0.9'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0.9'
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: smart_proxy_dynflow
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
|
16
30
|
requirements:
|
|
17
31
|
- - "~>"
|
|
18
32
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '0.
|
|
33
|
+
version: '0.5'
|
|
20
34
|
type: :runtime
|
|
21
35
|
prerelease: false
|
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
37
|
requirements:
|
|
24
38
|
- - "~>"
|
|
25
39
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '0.
|
|
40
|
+
version: '0.5'
|
|
27
41
|
description: " Gem to allow probing through smart-proxy\n"
|
|
28
42
|
email:
|
|
29
43
|
- aruzicka@redhat.com
|
|
@@ -33,9 +47,17 @@ extra_rdoc_files:
|
|
|
33
47
|
- LICENSE
|
|
34
48
|
files:
|
|
35
49
|
- LICENSE
|
|
36
|
-
- bundler.plugins.d/probing.rb
|
|
37
50
|
- lib/smart-proxy-probing.rb
|
|
51
|
+
- lib/smart-proxy-probing/actions.rb
|
|
52
|
+
- lib/smart-proxy-probing/actions/use_probe.rb
|
|
53
|
+
- lib/smart-proxy-probing/neighbour_cache.rb
|
|
38
54
|
- lib/smart-proxy-probing/plugin.rb
|
|
55
|
+
- lib/smart-proxy-probing/probes.rb
|
|
56
|
+
- lib/smart-proxy-probing/probes/abstract.rb
|
|
57
|
+
- lib/smart-proxy-probing/probes/icmp.rb
|
|
58
|
+
- lib/smart-proxy-probing/probes/nmap.rb
|
|
59
|
+
- lib/smart-proxy-probing/probes/tcp.rb
|
|
60
|
+
- lib/smart-proxy-probing/probes/udp.rb
|
|
39
61
|
- lib/smart-proxy-probing/version.rb
|
|
40
62
|
- settings.d/probing.yml.example
|
|
41
63
|
homepage: https://github.com/adamruzicka/smart-proxy-probing
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
gem 'smart-proxy-probing'
|