nexpose_cyberark 0.0.4-java → 0.0.5-java

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,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 31488ff05e81489acc3af0ac332479ca64cc3331
4
- data.tar.gz: dd1b49654a42402cf6bd5e20a3033fc5645804e4
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MDIzMGQ4MWQxMmJhYTEwYWUyNjI2OGNjMWE5ZDllMmViZjhhMTg1Yg==
5
+ data.tar.gz: !binary |-
6
+ ZDBiNTUzMWNkOGI4OTIyOWQ4Njk2YzRmZGVjOWVkMThhNGQzNzUzNg==
5
7
  SHA512:
6
- metadata.gz: 5b8c5b52afc4cd3d4a245adad43da70438bc66edfae16bed1f71f5e068ad2b0b74e9de6ce9715ebdc380cd332b98bbbf050a923bf4f6a36899641faace656a9f
7
- data.tar.gz: 30e9aea26b744e948f38c2612f0c4cfc4797de9e8cc0f2e28a8adc3765b24c5c12e11c75a6dbff67012a2a8941ea78d7cd7fb7f6dda4fdcd7cf2471c0bbf1f7b
8
+ metadata.gz: !binary |-
9
+ NTlkNTI2MTdlOWUyZDdmZjg3NWE3YjQ2OThhZDVlZjA0M2ViMDNiMmVhODQz
10
+ M2ZkYWU3NWI2M2JiMmZmNmQ4YzFjNDc1ZDQyNDkyYjM4N2Y3M2I0YTZlOGUz
11
+ M2ExYmFiZDJlMTQ0YTQ4MjAzMzViZjEzNmNmYzE3NzU3OGM1YmY=
12
+ data.tar.gz: !binary |-
13
+ ZTA1NmVmOTc4Y2RhZWVlZTcxODQ1MzA3ODE3MDBjNjc4NzUwODM0N2Q5ZmY1
14
+ NjhjOWEyMjA5ODBmZWU4ZjM2ZDMxMmE1ZjdiY2VkNWE3NzZjNDllZmU1NjVl
15
+ MDY2NDczZDYxNjYxNDM2MTljYjk4MDM2ZDczOTQwYzU1YjU3ZDA=
data/bin/nx_cyberark.rb CHANGED
@@ -3,43 +3,40 @@
3
3
  # Please refer to the configuration documentation for instructions on how to run this gem
4
4
  # Do NOT run this gem without proper pre-configuration.
5
5
  require 'nexpose_cyberark'
6
+ require 'nexpose_cyberark/version'
7
+ require 'nexpose_cyberark/nx_logger'
8
+ require 'yaml'
6
9
 
7
- # ---- CyberArk Configuration ---- #
8
- # Vault Options
9
- # App ID
10
- app_id = ''
11
- # Safe
12
- safe = ''
13
- # Folder
14
- folder = ''
15
- # Objects should have the same name as their counterparts in Nexpose
16
- # IE: 'serverx01.mydomain.com' in both Cyberark and Nexpose.
17
- # Individual IP's will be queried and should also correspond to Object names.
18
- # For ranges, this integration will use the first IP as the credential for all the range.
19
- # Finally, PolicyIDs in Cyberark should have 'unix' or 'windows' as part of their name for proper credential
20
- # type assignment in Nexpose (ssh / cifs)
21
- # ---- End CyberArk Configuration ---- #
10
+ CONFIG_PATH = File.join(File.dirname(__FILE__), '../lib/nexpose_cyberark/config/nexpose_cyberark.config')
22
11
 
23
- # ---- Nexpose Configuration ---- #
24
- # Nexpose username
25
- nxuser = 'nxadmin'
26
- # Nexpose password
27
- nxpasswd = 'nxadmin'
28
- # Nexpose IP Address
29
- nxip = 'localhost'
30
- # Sites to process credentials, separated by commas, ie: [3, 4, 6]
31
- sites = [ 1 ]
32
- # Start scans?
33
- # This setting will start scans on those sites, wait for the site to complete and then remove the credentials
34
- # If you prefer to let the scans run on schedule, set this to false, otherwise set to true.
35
- start_scans = false
36
- # ---- End Nexpose Configuration ---- #
12
+ # Obtain Nexpose settings from Environment Variables.
13
+ configuration_settings = begin
14
+ YAML.load_file(CONFIG_PATH)
15
+ rescue ArgumentError => e
16
+ raise "Could not parse YAML #{CONFIG_PATH} : #{e.message}"
17
+ end
18
+
19
+ raise 'Must configure nexpose settings before starting' if ENV['NEXPOSE_URL'].nil? || ENV['NEXPOSE_USERNAME'].nil? || ENV['NEXPOSE_PASSWORD'].nil?
20
+ configuration_settings[:nexpose_address] = ENV['NEXPOSE_URL']
21
+ configuration_settings[:nexpose_port] = ENV['NEXPOSE_PORT']
22
+ configuration_settings[:nexpose_username] = ENV['NEXPOSE_USERNAME']
23
+ configuration_settings[:nexpose_password] = ENV['NEXPOSE_PASSWORD']
37
24
 
38
25
 
39
26
 
40
27
  # --- DO NOT EDIT BELOW THIS LINE --- #
41
28
 
42
- vault_options = { :app_id => app_id, :safe => safe, :folder => folder }
43
- nexpose_options = { :nxip => nxip, :nxuser => nxuser, :nxpassword => nxpasswd, :sites => sites }
29
+ vault_options = { :app_id => configuration_settings[:ca_options][:app_id], :safe => configuration_settings[:ca_options][:safe], :folder => configuration_settings[:ca_options][:folder] }
30
+ nexpose_options = { :nxip => configuration_settings[:nexpose_address],
31
+ :nxport => configuration_settings[:nexpose_port],
32
+ :nxuser => configuration_settings[:nexpose_username],
33
+ :nxpassword => configuration_settings[:nexpose_password],
34
+ :sites => configuration_settings[:ca_options][:sites],
35
+ :windows_policy_ids => configuration_settings[:ca_options][:windows_policy_ids],
36
+ :unix_policy_ids => configuration_settings[:ca_options][:unix_policy_ids],
37
+ :logging => configuration_settings[:ca_options][:logging],
38
+ :log_level => configuration_settings[:ca_options][:log_level]}
44
39
  NexposeCyberark::Vault.update_credentials(vault_options, nexpose_options)
45
- NexposeCyberark::Vault.start_scans(nexpose_options) if start_scans
40
+ NexposeCyberark::Vault.start_scans(nexpose_options) if configuration_settings[:ca_options][:start_scans].casecmp('y') == 0
41
+ log = NexposeCyberark::NxLogger.instance
42
+ log.log_message('Importing credentials complete. Exiting...')
@@ -1,49 +1,195 @@
1
- require "nexpose_cyberark/version"
2
1
  Dir[File.dirname(__FILE__)+'/nexpose_cyberark/lib/java/*.jar'].each { |jar| require jar }
3
- require "nexpose_cyberark/password_ops"
4
- require "nexpose_cyberark/nexpose_ops"
2
+ require 'nexpose_cyberark/password_ops'
3
+ require 'nexpose_cyberark/nexpose_ops'
4
+ require 'nexpose_cyberark/version'
5
+ require 'waitutil'
6
+ require 'Resolv'
7
+
5
8
  module NexposeCyberark
6
9
  module Vault
10
+
7
11
  def self.update_credentials(vault_options, nexpose_options = nil)
8
- @nx = Ops::Nexpose.new(nexpose_options[:nxip], nexpose_options[:nxuser], nexpose_options[:nxpassword])
12
+ #Setup logger
13
+ @log = NexposeCyberark::NxLogger.instance
14
+ @log.setup_logging(nexpose_options[:logging] || true, nexpose_options[:log_level] || 'info')
15
+ @log.setup_statistics_collection(NexposeCyberark::VENDOR, NexposeCyberark::PRODUCT_NAME, NexposeCyberark::VERSION)
16
+
17
+ @nx = Ops::Nexpose.new(nexpose_options[:nxip], nexpose_options[:nxport], nexpose_options[:nxuser], nexpose_options[:nxpassword])
18
+ @log.log_message('Connection to the Nexpose console complete!')
19
+
9
20
  # Parse sites from config
10
21
  nexpose_options[:sites].each do |site_id|
11
- # Get ips for each site
12
- site_ips = @nx.get_ips_from_site(site_id)
22
+ # Get included scan targets
23
+ site_scan_target_addresses = @nx.get_site_scan_target_addresses(site_id)
24
+
25
+
26
+ # We now have all Nexpose scan targets. Defined scan targets can be IP/Host where
27
+ # devices that have been scanned before will be an IP. Get the scan targets & subtract
28
+ # the defined devices to produce a list of new devices to be scanned.
29
+
30
+ # Nexpose site credentials expect the credential to match the scan target which may not be the IP.
31
+ site_devices = @nx.get_site_devices(site_id)
32
+
33
+ all_asset_details = []
34
+ site_devices.each { |device|
35
+ # Start by getting all the details we have on current devices
36
+ asset = @nx.load_asset(device.id)
37
+ asset_details= {}
38
+ asset_details[:address] = device.address
39
+ asset_details[:host_names] = asset.host_names
40
+ asset_details[:os_name] = [asset.os_name]
41
+
42
+
43
+ # Next subtract the defined assets from the defined scan targets to get devices never scanned before.
44
+ # Also monitor the deleted identifier. We need to remember this to sync the credential to the site for scanning.
45
+ scan_target_idq = site_scan_target_addresses.delete(device.address)
46
+ asset_details[:scan_target_idq] = scan_target_idq unless scan_target_idq.nil?
47
+ asset.host_names.each do |host_name|
48
+ scan_target_idq = site_scan_target_addresses.delete(host_name)
49
+ asset_details[:scan_target_idq] = scan_target_idq unless scan_target_idq.nil?
50
+ end unless asset.host_names.nil?
51
+ # Handle the case that a device may have been scanned before but is no longer in the scan target list.
52
+ # in this case we do not add it to the list of assets to import credentials for (think old sites with many inactive devices)
53
+ # asset_details[:scan_target_idq] = asset_details[:address] if asset_details[:scan_target_idq].nil?
54
+ if asset_details[:scan_target_idq].nil?
55
+ @log.log_warn_message("Found a site device not in the scan target list. Not fetching credentials for address <#{asset_details[:address]}>")
56
+ else
57
+ all_asset_details << asset_details
58
+ end
59
+ }
60
+
61
+ #Start by trying to get credentials for defined assets.
62
+ @log.log_message('Starting to query CyberArk for defined asset credentials...')
13
63
  site_credentials = []
14
- # Get password for each IP
15
- site_ips.each do |asset|
16
- host = ''
17
- host = asset.host if asset.is_a?(HostName)
18
- host = asset.from if asset.is_a?(IPRange)
19
- range_scenario = false
20
- if asset.is_a?(IPRange) then range_scenario = true unless asset.from.nil? end
21
- vault_options[:object] = host
22
- asset_data = PasswordOps::get_password(vault_options)
23
- host = nil if range_scenario
24
- unless asset_data[:password].nil?
25
- asset_data[:os] == 'cifs' ? user = 'Administrator' : user = 'root'
26
- credential = Credential.for_service(asset_data[:os], user, asset_data, nil, host)
64
+ credential_data = nil
65
+ all_asset_details.each do |asset_details|
66
+ #The address stored in the vault could be any of the values we have.. so try and find it!
67
+ vault_options[:address] = asset_details[:address]
68
+ vault_options[:nexpose_os] = asset_details[:os_name]
69
+ credential_data = PasswordOps::get_password(nexpose_options, vault_options)
70
+
71
+ if credential_data.empty?
72
+ @log.log_debug_message("Failed to fetch credential for IP <#{asset_details[:address]}>")
73
+ # No credential for the IP. Let's check if Nexpose has any hostnames and if these match credentials
74
+ # within CyberArk. If they do not then try and resolve one from the IP.
75
+ if asset_details[:host_names].nil?
76
+ asset_details[:host_names] = resolve_address(asset_details[:address])
77
+ end
78
+
79
+ @log.log_debug_message("Trying to fetch credentials for resolved addresses <#{asset_details[:address]}> instead...")
80
+
81
+ asset_details[:host_names].each do |hostname|
82
+ vault_options[:address] = hostname
83
+ vault_options[:nexpose_os] = asset_details[:os_name]
84
+ credential_data = PasswordOps::get_password(nexpose_options, vault_options)
85
+ break unless credential_data.empty?
86
+ end
87
+ end
88
+
89
+ if credential_data.empty?
90
+ @log.log_error_message("Failed to get credential for asset with IP <#{asset_details[:address]}> and names <#{asset_details[:host_names]}>. Please check configuration!!!")
91
+ else
92
+ credential = @nx.credential_for_service(asset_details[:scan_target_idq], nil, "Automated import for Nexpose scan target <#{asset_details[:scan_target_idq]}> and CyberArk credential <#{vault_options[:address]}>", asset_details[:scan_target_idq], nil, credential_data[:service])
93
+ credential.user_name = credential_data[:user]
94
+ credential.password = credential_data[:password]
95
+ # Only Linux SUDO support currently
96
+ credential.permission_elevation_user = credential_data[:p_e_user] unless credential_data[:p_e_user].nil?
97
+ credential.permission_elevation_password = credential_data[:password] unless credential_data[:password].nil?
98
+ credential.permission_elevation_type = credential_data[:p_e_type]
27
99
  site_credentials.push(credential)
28
100
  end
29
101
  end
102
+
103
+ @log.log_message('Starting to query CyberArk for new scan target credentials...')
104
+
105
+ # Now let's deal with assets that have never been scanned.
106
+ # These are more difficult as we only have the scan target address (whatever that may be).
107
+ site_scan_target_addresses.each do |address|
108
+ vault_options[:address] = address
109
+ credential_data = PasswordOps::get_password(nexpose_options, vault_options)
110
+
111
+ if credential_data.empty?
112
+ @log.log_debug_message("Failed to fetch credential for IP <#{address}>")
113
+ other_addresses = resolve_address(address)
114
+ @log.log_debug_message("Trying to fetch credentials for resolved addresses <#{other_addresses}> instead...")
115
+ other_addresses.each do |other_address|
116
+ vault_options[:address] = other_address
117
+ credential_data = PasswordOps::get_password(nexpose_options, vault_options)
118
+ break unless credential_data.empty?
119
+ end
120
+ end
121
+
122
+ if credential_data.empty?
123
+ @log.log_error_message("Failed to get credential for asset with IP <#{address}> and names <#{other_addresses}>. Please check configuration!!!")
124
+ else
125
+ credential = @nx.credential_for_service(address, nil, "Automated import for Nexpose scan target <#{address}> and CyberArk credential <#{vault_options[:address]}>",address, nil, credential_data[:service])
126
+ credential.user_name = credential_data[:user]
127
+ credential.password = credential_data[:password]
128
+ #Only Linux SUDO support currently
129
+ credential.permission_elevation_user = credential_data[:p_e_user] unless credential_data[:p_e_user].nil?
130
+ credential.permission_elevation_password = credential_data[:password] unless credential_data[:password].nil?
131
+ credential.permission_elevation_type = credential_data[:p_e_type]
132
+ site_credentials.push(credential)
133
+ end
134
+ end
135
+ @log.log_message("Saving credentials for site <#{site_id}>. Number of credentials to be saved <#{site_credentials.size}>.")
30
136
  # Save site
31
137
  @nx.save_site(site_id, site_credentials)
32
138
  end
33
139
  end
140
+
34
141
  def self.start_scans(nexpose_options = nil)
35
- @nx = Ops::Nexpose.new(nexpose_options[:nxip], nexpose_options[:nxuser], nexpose_options[:nxpassword])
142
+ @nx = Ops::Nexpose.new(nexpose_options[:nxip], nexpose_options[:nxport], nexpose_options[:nxuser], nexpose_options[:nxpassword])
143
+ all_site_scan_details = []
36
144
  nexpose_options[:sites].each do |site_id|
37
- puts "Starting scan #{site_id}"
38
145
  scan = @nx.start_scan(site_id)
39
- begin
40
- sleep(30)
41
- status = @nx.scan_status(scan.id)
42
- puts "Waiting for scan #{scan.id} to finish"
43
- end while status == Scan::Status::RUNNING
44
- puts "Deleting creds for #{site_id}"
45
- @nx.delete_site_credentials(site_id)
146
+ if scan.nil?
147
+ @log.log_error_message("Failed to start scan for site <#{site_id}>!")
148
+ next
149
+ end
150
+ @log.log_message("Started scan for site <#{site_id}>. Scan ID is <#{scan.id}>.")
151
+ site_scan_details = {}
152
+ site_scan_details[:site_id] = site_id
153
+ site_scan_details[:scan_id] = scan.id
154
+ all_site_scan_details << site_scan_details
155
+ end
156
+
157
+ WaitUtil.wait_for_condition('wait_for_all_scans_to_finish', :timeout_sec => 10800, :delay_sec => 60) do
158
+ @completed = false
159
+ all_site_scan_details.delete_if do |site_scan_details|
160
+ status = @nx.scan_status(site_scan_details[:scan_id])
161
+ @log.log_debug_message("Scan status for scan ID <#{site_scan_details[:scan_id]}> is <#{status}>.")
162
+ if status == Scan::Status::RUNNING
163
+ @log.log_debug_message("Waiting for scan <#{site_scan_details[:scan_id]}> for site <#{site_scan_details[:site_id]}> to finish.")
164
+ false
165
+ else
166
+ @log.log_message("Scan <#{site_scan_details[:scan_id]}> for site <#{site_scan_details[:scan_id]}> finished. Removing credentials")
167
+ @nx.delete_site_credentials(site_scan_details[:site_id])
168
+ true
169
+ end
170
+ end
171
+ @completed = true if all_site_scan_details.empty?
172
+ end
173
+ end
174
+
175
+ def self.resolve_address(address)
176
+ is_ip = !!((address =~ Resolv::IPv4::Regex) || (address =~ Resolv::IPv6::Regex))
177
+ resolved_addresses = []
178
+ @log.log_debug_message("Resolving address <#{address}>.")
179
+ begin
180
+ if is_ip
181
+ resolved_addresses = Resolv.getnames address
182
+ @log.log_debug_message("Address <#{address}> is an IP. Resolved names are <#{resolved_addresses}>.")
183
+ else
184
+ resolved_addresses = Resolv.getaddress address
185
+ @log.log_debug_message("Address <#{address}> is a name. Resolved IPs are <#{resolved_addresses}>.")
186
+ end
187
+ resolved_addresses = [resolved_addresses] unless resolved_addresses.kind_of?(Array)
188
+ rescue Resolv::ResolvError => e
189
+ @log.log_error_message("Unable to resolve address <#{address}>. Error was <#{e}>")
46
190
  end
191
+ return resolved_addresses
47
192
  end
193
+
48
194
  end
49
195
  end
@@ -0,0 +1,30 @@
1
+ ---
2
+ # This configuration file defines all the particular options necessary to run the service.
3
+ # Fields marked (M) are mandatory.
4
+ #
5
+ # Service options:
6
+ :ca_options:
7
+ # Vault Options
8
+ # App ID
9
+ :app_id: my_app_id
10
+ # Safe
11
+ :safe: my_safe
12
+ # Folder
13
+ :folder: my_folder
14
+ # This setting will start scans on those sites, wait for the site to complete and then remove the credentials
15
+ # If you prefer to let the scans run on schedule, set this to 'N', otherwise set to 'Y'.
16
+ :start_scans: 'Y'
17
+ # CyberArk policy IDs that apply to assets that will require a CIFS connection for authenticated scanning
18
+ :windows_policy_ids:
19
+ - 'WinServerLocal'
20
+ # CyberArk policy IDs that apply to assets that will require an SSH connection for authenticated scanning
21
+ :unix_policy_ids:
22
+ - 'UnixSSH'
23
+ # Nexpose sites to import CyberArk credentials for.
24
+ :sites:
25
+ - '1'
26
+ # - '2'
27
+ # Enable or disable logging ('N' or 'Y'.)
28
+ :logging: 'Y'
29
+ #Support version are 'info' and 'debug'
30
+ :log_level: 'info'
@@ -1,37 +1,100 @@
1
1
  require 'nexpose'
2
2
  include Nexpose
3
+
3
4
  module Ops
4
5
  class Nexpose
5
6
  attr_accessor :nsc
6
- def initialize(nxip, nxuser, nxpasword)
7
+ def initialize(nxip, nxport, nxuser, nxpasword)
8
+ @log = NexposeCyberark::NxLogger.instance
9
+ @log.log_message('Connecting to the Nexpose console..')
7
10
  @nsc = Connection.new(nxip, nxuser, nxpasword)
8
11
  @nsc.login
12
+ @log.on_connect(nxip, nxport, @nsc.session_id, '{}')
9
13
  end
10
14
 
11
- def get_ips_from_site(site_id)
15
+ def get_site_scan_targets(site_id)
16
+ @log.log_debug_message("Fetching list of scan targets for site <#{site_id}> from console")
12
17
  site = Site.load(@nsc, site_id)
13
- site.assets
18
+ site.included_addresses
19
+ end
20
+
21
+ def get_site_scan_target_addresses(site_id)
22
+ @log.log_debug_message("Fetching list of scan targets addresses for site <#{site_id}>")
23
+ site_scan_targets = get_site_scan_targets(site_id)
24
+ @log.log_message('Console returned scan targets!')
25
+
26
+ site_scan_target_addresses = []
27
+ #Convert this to a list of only addresses
28
+ site_scan_targets.each do |scan_target|
29
+ host = scan_target.host if scan_target.is_a?(HostName)
30
+ host = scan_target.from if scan_target.is_a?(IPRange)
31
+
32
+ range_scenario = false
33
+ range_scenario = true if scan_target.is_a?(IPRange)
34
+
35
+ if range_scenario
36
+ start_ip = IPAddr.new(scan_target.from)
37
+ end_ip = IPAddr.new(scan_target.to) unless scan_target.to.nil?
38
+ end_ip = IPAddr.new(scan_target.from) if scan_target.to.nil?
39
+ site_scan_target_addresses.concat (start_ip..end_ip).map(&:to_s)
40
+ else
41
+ site_scan_target_addresses << host
42
+ end
43
+ end
44
+ @log.log_debug_message('Processed scan targets. Returning addresses.')
45
+ return site_scan_target_addresses
46
+ end
47
+
48
+ def get_site_devices(site_id)
49
+ @log.log_debug_message("Fetching list of devices for site <#{site_id}>")
50
+ @nsc.list_site_devices(site_id)
51
+ end
52
+
53
+ def load_asset(device_id)
54
+ @log.log_debug_message("Fetching asset details for asset <#{device_id}>")
55
+ Asset.load(@nsc, device_id)
56
+ end
57
+
58
+ def credential_for_service(address, id, description, host, port, service)
59
+ @log.log_debug_message("Generating credential for address <#{address}>")
60
+ SiteCredentials.for_service(address, id, description, host, port, service)
14
61
  end
15
62
 
16
63
  def save_site(site_id, credentials)
64
+ @log.log_debug_message("Saving <#{credentials.size}> credentials for site <#{site_id}>")
17
65
  site = Site.load(@nsc, site_id)
18
- site.credentials = credentials
66
+ site.site_credentials = credentials
19
67
  site.save(@nsc)
20
68
  end
21
69
 
22
70
  def delete_site_credentials(site_id)
71
+ @log.log_debug_message("Deleting existing credentials for site <#{site_id}>")
23
72
  site = Site.load(@nsc, site_id)
24
- site.credentials = []
73
+ site.site_credentials.clear
25
74
  site.save(@nsc)
26
75
  end
27
76
 
77
+ def load_site(site_id)
78
+ @log.log_debug_message("Fetching details for site <#{site_id}>")
79
+ Site.load(@nsc, site_id)
80
+ end
81
+
28
82
  def start_scan(site_id)
29
- site = Site.load(@nsc, site_id)
30
- site.scan(@nsc)
83
+ @log.log_debug_message("Starting for site <#{site_id}>")
84
+ scan_details = nil
85
+ begin
86
+ site = load_site(site_id)
87
+ scan_details = site.scan(@nsc)
88
+ rescue Exception => e
89
+ @log.log_error_message("Failed to start scan for site <#{site_id}>. Error is <#{e}>")
90
+ end
91
+ scan_details
31
92
  end
32
93
 
33
94
  def scan_status(scan_id)
34
- @nsc.scan_status(scan_id)
95
+ status = @nsc.scan_status(scan_id)
96
+ @log.log_debug_message("Scan status for scan ID <#{scan_id}> is <#{status}>.")
97
+ status
35
98
  end
36
99
  end
37
100
  end
@@ -0,0 +1,150 @@
1
+ require 'fileutils'
2
+ require 'json'
3
+ require 'net/http'
4
+ require 'singleton'
5
+
6
+ module NexposeCyberark
7
+ class NxLogger
8
+ include Singleton
9
+ attr_accessor :options, :statistic_key, :product, :logger_file
10
+ LOG_PATH = "./logs/rapid7_%s.log"
11
+ KEY_FORMAT = "external.integration.%s"
12
+ PRODUCT_FORMAT = "%s_%s"
13
+
14
+ DEFAULT_LOG = 'integration'
15
+ PRODUCT_RANGE = 3..30
16
+ KEY_RANGE = 3..15
17
+
18
+ ENDPOINT = '/data/external/statistic/'
19
+
20
+ def initialize()
21
+ @logger_file = get_log_path product
22
+ setup_logging(true, 'info')
23
+ end
24
+
25
+ def setup_statistics_collection(vendor, product_name, gem_version)
26
+ #Remove illegal characters
27
+ vendor.to_s.gsub!('-', '_')
28
+ product_name.to_s.gsub!('-', '_')
29
+
30
+ begin
31
+ @statistic_key = get_statistic_key vendor
32
+ @product = get_product product_name, gem_version
33
+ rescue => e
34
+ #Continue
35
+ end
36
+ end
37
+
38
+ def setup_logging(enabled, log_level = nil)
39
+ unless enabled || @log.nil?
40
+ log_message('Logging disabled.')
41
+ return
42
+ end
43
+
44
+ @logger_file = get_log_path product
45
+
46
+ require 'logger'
47
+ directory = File.dirname(@logger_file)
48
+ FileUtils.mkdir_p(directory) unless File.directory?(directory)
49
+ io = IO.for_fd(IO.sysopen(@logger_file, 'a'))
50
+ io.autoclose = false
51
+ io.sync = true
52
+ @log = Logger.new(io, 'weekly')
53
+ @log.level = if log_level.casecmp('info') == 0
54
+ Logger::INFO
55
+ else
56
+ Logger::DEBUG
57
+ end
58
+ log_message("Logging enabled at level <#{log_level}>")
59
+ end
60
+
61
+ # Logs an info message
62
+ def log_message(message)
63
+ @log.info(message) unless @log.nil?
64
+ end
65
+
66
+ # Logs a debug message
67
+ def log_debug_message(message)
68
+ @log.debug(message) unless @log.nil?
69
+ end
70
+
71
+ # Logs an error message
72
+ def log_error_message(message)
73
+ @log.error(message) unless @log.nil?
74
+ end
75
+
76
+ # Logs a warn message
77
+ def log_warn_message(message)
78
+ @log.warn(message) unless @log.nil?
79
+ end
80
+
81
+ def log_stat_message(message)
82
+ end
83
+
84
+ def get_log_path(product)
85
+ product.downcase! unless product.nil?
86
+ File.join(File.dirname(__FILE__), LOG_PATH % (product || DEFAULT_LOG))
87
+ end
88
+
89
+ def get_statistic_key(vendor)
90
+ if vendor.nil? || vendor.length < KEY_RANGE.min
91
+ log_stat_message("Vendor length is below minimum of <#{KEY_RANGE}>")
92
+ return nil
93
+ end
94
+
95
+ KEY_FORMAT % vendor[0...KEY_RANGE.max].downcase
96
+ end
97
+
98
+ def get_product(product, version)
99
+ return nil if (product.nil? || version.nil?)
100
+ product = (PRODUCT_FORMAT % [product, version])[0...PRODUCT_RANGE.max]
101
+
102
+ if product.length < PRODUCT_RANGE.min
103
+ log_stat_message("Product length below minimum <#{PRODUCT_RANGE.min}>.")
104
+ return nil
105
+ end
106
+ product.downcase
107
+ end
108
+
109
+ def generate_payload(statistic_value='')
110
+ payload = {'statistic-key' => @statistic_key,
111
+ 'statistic-value' => statistic_value,
112
+ 'product' => @product}
113
+ JSON.generate(payload)
114
+ end
115
+
116
+ def send(nexpose_address, nexpose_port, session_id, payload)
117
+ header = {'Content-Type' => 'application/json',
118
+ 'nexposeCCSessionID' => session_id,
119
+ 'Cookie' => "nexposeCCSessionID=#{session_id}"}
120
+ req = Net::HTTP::Put.new(ENDPOINT, header)
121
+ req.body = payload
122
+ http_instance = Net::HTTP.new(nexpose_address, nexpose_port)
123
+ http_instance.use_ssl = true
124
+ http_instance.verify_mode = OpenSSL::SSL::VERIFY_NONE
125
+ response = http_instance.start { |http| http.request(req) }
126
+ log_stat_message "Received code #{response.code} from Nexpose console."
127
+ log_stat_message "Received message #{response.msg} from Nexpose console."
128
+ log_stat_message 'Finished sending statistics data to Nexpose.'
129
+ response.code
130
+ end
131
+
132
+ def on_connect(nexpose_address, nexpose_port, session_id, value)
133
+ log_stat_message 'Sending statistics data to Nexpose'
134
+
135
+ if @product.nil? || @statistic_key.nil?
136
+ log_stat_message('Invalid product name and/or statistics key.')
137
+ log_stat_message('Statistics collection not enabled.')
138
+ return
139
+ end
140
+
141
+ begin
142
+ payload = generate_payload value
143
+ send(nexpose_address, nexpose_port, session_id, payload)
144
+ rescue => e
145
+ #Let the program continue
146
+ end
147
+ end
148
+
149
+ end
150
+ end
@@ -1,28 +1,47 @@
1
1
  module PasswordOps
2
-
2
+ require 'nexpose'
3
+ include Nexpose
3
4
  def self.cyberark
4
5
  Java::javapasswordsdk
5
6
  end
6
7
 
7
- def self.get_password(vault_options = {}, password_req_sdk = nil, password_sdk = nil )
8
+ def self.get_password(nexpose_options = {}, vault_options = {}, password_req_sdk = nil, password_sdk = nil )
9
+ @log = NexposeCyberark::NxLogger.instance
8
10
  password_req_sdk = cyberark.PSDKPasswordRequest.new if password_req_sdk.nil?
9
11
  asset_data = {}
10
12
  begin
11
13
  password_req_sdk.set_app_id(vault_options[:app_id])
12
14
  password_req_sdk.set_safe(vault_options[:safe])
13
15
  password_req_sdk.set_folder(vault_options[:folder])
14
- password_req_sdk.set_object(vault_options[:object])
16
+ password_req_sdk.set_address(vault_options[:address])
15
17
  password_sdk = cyberark.PasswordSDK if password_sdk.nil?
16
18
  password_result = password_sdk.getPassword(password_req_sdk)
17
- if password_result.get_policy_id.downcase.include? 'unix'
18
- #puts password_result.get_policy_id.downcase.include? 'unix'
19
- asset_data[:os] = 'ssh'
19
+ if nexpose_options[:windows_policy_ids].any?{ |policy| policy.casecmp(password_result.get_policy_id)==0 }
20
+ if nexpose_options[:unix_policy_ids].any?{ |policy| policy.casecmp(password_result.get_policy_id)==0 }
21
+ # Has both Windows and Unix policies. Check Nexpose to see if we have an OS listing
22
+ @log.log_error_message("Asset with credential address <#{vault_options[:address]}> has a conflicting policy configuration! Checking Nexpose fingerprint.. ")
23
+ if vault_options[:nexpose_os].downcase.include? 'windows'
24
+ @log.log_debug_message('Nexpose fingerprinting estimates the system is Windows.')
25
+ asset_data[:service] = Credential::Service::CIFS
26
+ else
27
+ @log.log_debug_message('Nexpose fingerprinting estimates the system is Unix based.')
28
+ asset_data[:service] = Credential::Service::SSH
29
+ asset_data[:p_e_user] = 'root'
30
+ asset_data[:p_e_type] = Nexpose::Credential::ElevationType::SUDO
31
+ end
32
+ end
33
+ @log.log_debug_message('Policy ID indicates the system is Windows based.')
34
+ asset_data[:service] = Credential::Service::CIFS
20
35
  else
21
- asset_data[:os] = 'cifs'
36
+ @log.log_debug_message('Policy ID indicates the system is Unix based.')
37
+ asset_data[:service] = Credential::Service::SSH
38
+ asset_data[:p_e_user] = 'root'
39
+ asset_data[:p_e_type] = Nexpose::Credential::ElevationType::SUDO
22
40
  end
23
41
  asset_data[:password] = password_result.get_content
42
+ asset_data[:user] = password_result.get_user_name
24
43
  rescue Exception => e
25
-
44
+ @log.log_debug_message("Error fetching credential for address <#{vault_options[:address]}>. Error was <#{e}>")
26
45
  end
27
46
  asset_data
28
47
  end
@@ -1,3 +1,5 @@
1
1
  module NexposeCyberark
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
+ VENDOR = "Cyberark"
4
+ PRODUCT_NAME = "nexpose_cyberark"
3
5
  end
metadata CHANGED
@@ -1,93 +1,107 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nexpose_cyberark
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: java
6
6
  authors:
7
7
  - Damian Finol
8
+ - JJ Cassidy
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2015-05-14 00:00:00.000000000 Z
12
+ date: 2015-12-11 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
- - - "~>"
18
+ - - ~>
18
19
  - !ruby/object:Gem::Version
19
20
  version: '1.6'
20
21
  type: :development
21
22
  prerelease: false
22
23
  version_requirements: !ruby/object:Gem::Requirement
23
24
  requirements:
24
- - - "~>"
25
+ - - ~>
25
26
  - !ruby/object:Gem::Version
26
27
  version: '1.6'
27
28
  - !ruby/object:Gem::Dependency
28
29
  name: rake
29
30
  requirement: !ruby/object:Gem::Requirement
30
31
  requirements:
31
- - - ">="
32
+ - - ~>
32
33
  - !ruby/object:Gem::Version
33
- version: '0'
34
+ version: '10.4'
34
35
  type: :development
35
36
  prerelease: false
36
37
  version_requirements: !ruby/object:Gem::Requirement
37
38
  requirements:
38
- - - ">="
39
+ - - ~>
39
40
  - !ruby/object:Gem::Version
40
- version: '0'
41
+ version: '10.4'
41
42
  - !ruby/object:Gem::Dependency
42
43
  name: rspec
43
44
  requirement: !ruby/object:Gem::Requirement
44
45
  requirements:
45
- - - "~>"
46
+ - - ~>
46
47
  - !ruby/object:Gem::Version
47
48
  version: '2.1'
48
49
  type: :development
49
50
  prerelease: false
50
51
  version_requirements: !ruby/object:Gem::Requirement
51
52
  requirements:
52
- - - "~>"
53
+ - - ~>
53
54
  - !ruby/object:Gem::Version
54
55
  version: '2.1'
55
56
  - !ruby/object:Gem::Dependency
56
57
  name: nexpose
57
58
  requirement: !ruby/object:Gem::Requirement
58
59
  requirements:
59
- - - "~>"
60
+ - - ~>
60
61
  - !ruby/object:Gem::Version
61
- version: 0.6.0
62
+ version: '2.1'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '2.1'
70
+ - !ruby/object:Gem::Dependency
71
+ name: waitutil
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: '0.2'
62
77
  type: :runtime
63
78
  prerelease: false
64
79
  version_requirements: !ruby/object:Gem::Requirement
65
80
  requirements:
66
- - - "~>"
81
+ - - ~>
67
82
  - !ruby/object:Gem::Version
68
- version: 0.6.0
83
+ version: '0.2'
69
84
  description: Nexpose Cyberark integration provides credentials for authenticated scans
70
85
  in Nexpose.
71
86
  email:
72
- - damian_finol@rapid7.com
87
+ - integrations_support@rapid7.com
73
88
  executables:
74
89
  - nx_cyberark.rb
75
90
  extensions: []
76
91
  extra_rdoc_files: []
77
92
  files:
78
93
  - Gemfile
79
- - Gemfile.lock
80
94
  - LICENSE.txt
81
95
  - README.md
82
96
  - Rakefile
83
97
  - bin/nx_cyberark.rb
84
98
  - lib/nexpose_cyberark.rb
99
+ - lib/nexpose_cyberark/config/nexpose_cyberark.config
85
100
  - lib/nexpose_cyberark/lib/java/JavaPasswordSDK.jar
86
101
  - lib/nexpose_cyberark/nexpose_ops.rb
102
+ - lib/nexpose_cyberark/nx_logger.rb
87
103
  - lib/nexpose_cyberark/password_ops.rb
88
104
  - lib/nexpose_cyberark/version.rb
89
- - nexpose_cyberark-0.0.3-java.gem
90
- - nexpose_cyberark.gemspec
91
105
  homepage: http://www.rapid7.com/
92
106
  licenses:
93
107
  - MIT
@@ -98,17 +112,17 @@ require_paths:
98
112
  - lib
99
113
  required_ruby_version: !ruby/object:Gem::Requirement
100
114
  requirements:
101
- - - ">="
115
+ - - ! '>='
102
116
  - !ruby/object:Gem::Version
103
117
  version: '0'
104
118
  required_rubygems_version: !ruby/object:Gem::Requirement
105
119
  requirements:
106
- - - ">="
120
+ - - ! '>='
107
121
  - !ruby/object:Gem::Version
108
122
  version: '0'
109
123
  requirements: []
110
124
  rubyforge_project:
111
- rubygems_version: 2.4.4
125
+ rubygems_version: 2.2.2
112
126
  signing_key:
113
127
  specification_version: 4
114
128
  summary: Nexpose Cyberark integration.
data/Gemfile.lock DELETED
@@ -1,30 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- nexpose_cyberark (0.0.2-java)
5
- nexpose (~> 0.6.0)
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- diff-lcs (1.2.5)
11
- librex (0.0.999)
12
- nexpose (0.6.5)
13
- librex (~> 0.0, >= 0.0.68)
14
- rex (~> 1.0, >= 1.0.2)
15
- rex (1.0.2)
16
- rspec (2.99.0)
17
- rspec-core (~> 2.99.0)
18
- rspec-expectations (~> 2.99.0)
19
- rspec-mocks (~> 2.99.0)
20
- rspec-core (2.99.2)
21
- rspec-expectations (2.99.2)
22
- diff-lcs (>= 1.1.3, < 2.0)
23
- rspec-mocks (2.99.3)
24
-
25
- PLATFORMS
26
- java
27
-
28
- DEPENDENCIES
29
- nexpose_cyberark!
30
- rspec (~> 2.1)
Binary file
@@ -1,27 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'nexpose_cyberark/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = 'nexpose_cyberark'
8
- spec.version = NexposeCyberark::VERSION
9
- spec.authors = ['Damian Finol']
10
- spec.email = ['damian_finol@rapid7.com']
11
- spec.summary = %q{Nexpose Cyberark integration.}
12
- spec.description = %q{Nexpose Cyberark integration provides credentials for authenticated scans in Nexpose.}
13
- spec.homepage = 'http://www.rapid7.com/'
14
- spec.license = 'MIT'
15
-
16
- spec.files = Dir['[A-Z]*'] + Dir['lib/**/*'] + Dir['bin/**']
17
- spec.files.reject! { |fn| fn.include? "CVS" }
18
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
- spec.require_paths = ['lib']
21
- spec.platform = 'java'
22
-
23
- spec.add_development_dependency 'bundler', '~> 1.6'
24
- spec.add_development_dependency 'rake'
25
- spec.add_development_dependency 'rspec', '~> 2.1'
26
- spec.add_runtime_dependency('nexpose', '~> 0.6.0')
27
- end