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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 185729a0fc727a5a017e0b3385c50a06fb83b8119f5023a236786ad9a6806a8c
4
- data.tar.gz: 5216379a6a039da619eac04e5935e493b823937afc31de2513e1988244b31a71
3
+ metadata.gz: bd8c5984e72b14774fe2b627c2a07e753a0b0b88bd5f2c4836a66398e3c7b32b
4
+ data.tar.gz: 71953520137a94e049ec1f9ad236b44accb7e0e434e1f7fee55638c14ab513a6
5
5
  SHA512:
6
- metadata.gz: 1180fa725e16714890f57c167481ee0b027508d51cca1fb4551c7407687bf699e6625bb1ea5d0a3c7bc0366c49d0a0c0fed9df2b4a030d2ec77e82f0eecf6c2c
7
- data.tar.gz: 889b2087a16d63200c0de287ee47f10e98b91a3d7e44dc71adb4d1473bc2fca747bb83ed88bef13a2635a6077315d4513ce311b9439d6c85713a7e627932c204
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,7 @@
1
+ require 'dynflow'
2
+
3
+ module Proxy::Probing
4
+ module Actions
5
+ require 'smart-proxy-probing/actions/use_probe'
6
+ end
7
+ 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
- begin
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,18 @@
1
+ module Proxy::Probing
2
+ module Probes
3
+ class ICMP < Nmap
4
+ def self.scan_type
5
+ 'icmp'
6
+ end
7
+
8
+ def self.humanized_scan_type
9
+ 'ICMP'
10
+ end
11
+
12
+ def nmap_flags
13
+ %w(-sn)
14
+ end
15
+ end
16
+ end
17
+ end
18
+
@@ -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
@@ -1,5 +1,5 @@
1
1
  module Proxy
2
2
  module Probing
3
- VERSION = '0.0.3'
3
+ VERSION = '0.0.4'
4
4
  end
5
5
  end
@@ -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.3
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: 1980-01-01 00:00:00.000000000 Z
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.1'
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.1'
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'