nexpose_cyberark 0.0.4-java → 0.0.5-java

Sign up to get free protection for your applications and to get access to all the features.
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