nexpose_thycotic 0.0.4 → 0.1.0

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
  SHA1:
3
- metadata.gz: a26c9a2b129625a04a2c7f9a1faef0c3c66899bd
4
- data.tar.gz: 9a421deb85297f7cb43172884d7da17e0999ca5e
3
+ metadata.gz: 72d72a2f1294229464df29985e8c3a0145f18d60
4
+ data.tar.gz: 13e76a6eaeb8f69879b2a808251f9ad2a4bb2716
5
5
  SHA512:
6
- metadata.gz: 758dffd4689c3bb786c5d85169ad0004f2c98e1d823cf5b567c3970434b24b08fe73e37c1641408850490c5a2a59a171fd0fdb19abcfe541d31132062dd45303
7
- data.tar.gz: 479bb7191e92a2642a52511761eb4c9dd58957969ed98f1d4a7bce9c866017bcd21d76db1473ff077284187c797d419f0453c8a11606ab70b7dfb6bd8590e752
6
+ metadata.gz: 0b6f9d979b6064ca1eadfa76d0026461057701110fcddb172bde51429412a97589e4de7b4e1c96bab5b0f5e760337620f955f4d95b7294d1b5d03fbc6d606bef
7
+ data.tar.gz: c9f6ab9b22517feb3035b214492ab2cd7fffa1b0a3b5a4e2c4ef1823ed9e7e41113fa20d7cf25f2db5600346acc2b94b93a4f6911fbef3579d2bc1ca0349e635
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nexpose_thycotic (0.0.4)
4
+ nexpose_thycotic (0.0.5)
5
5
  nexpose (~> 0.8.0)
6
6
  rubyntlm
7
7
  savon
@@ -25,8 +25,6 @@ GEM
25
25
  rex (~> 2.0.4, >= 2.0.4)
26
26
  nokogiri (1.6.6.2)
27
27
  mini_portile (~> 0.6.0)
28
- nokogiri (1.6.6.2-x86-mingw32)
29
- mini_portile (~> 0.6.0)
30
28
  nori (2.6.0)
31
29
  rack (1.6.1)
32
30
  rake (10.4.2)
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # NexposeThycotic
2
2
 
3
- Nexpose Thycotic Gem allows users to import credentials from Thycotic SecretServer into their Nexpose instance.
3
+ Nexpose Thycotic Gem allows users to import credentials from Thycotic SecretServer into their Nexpose instance.
4
+
5
+ For assistance with using the gem, documentation, or issues, please email the Rapid7 support team at support@rapid7.com, including description of issues and log files.
4
6
 
5
7
  ## Installation
6
8
 
@@ -18,6 +20,51 @@ Or install it yourself as:
18
20
 
19
21
  ## Usage
20
22
 
21
- Edit the nx_thycotic.rb file in the /bin folder with a text editor. Add a proper username/password for both nexpose
22
- and SecretServer, followed by the URL for SecretServer webservice.
23
- Add the Site # to be managed by this integration, save and run on schedule.
23
+ Edit the nexpose_thycotic.config file in the */lib/nexpose\_thycotic/config* folder with a text editor.
24
+ Nexpose and Thycotic configuration options can be set in this file, or as Environment Variables.
25
+
26
+ - Add a proper username/password for both Nexpose and SecretServer.
27
+ - Add the URL for SecretServer webservice and URL for Nexpose, with optional port (defaults to 3780).
28
+ - The logging level can also be modified.
29
+ - Add the Site ID(s) to be managed by this integration, save and run on schedule.
30
+
31
+ Run the following command from inside the bin folder:
32
+
33
+ nexpose_thycotic
34
+
35
+ ## Encryption Settings
36
+
37
+ The usernames and passwords within the configuration files are automatically encrypted when the integration runs. The key and IV files used during encryption/decryption are saved within the config folder by default.
38
+
39
+ #### Setting Custom Locations for Encryption Files
40
+
41
+ To set custom locations for the key and IV files, update the following values within the encryption.config file:
42
+
43
+ - key_filename - The absolute path to where the key file will be created.
44
+ - iv_file - The absolute path to where the IV file will be created.
45
+
46
+ To set a custom path after the integration has already executed, the files must be moved to the new location manually.
47
+
48
+ #### Encrypting the Configuration without running the Integration
49
+ The Nexpose Thycotic integration can encrypt its configuration file without running the gem. This allows users to secure their login information for future use e.g for use in a cron-schedule.
50
+
51
+ The command to do so is:
52
+ ```
53
+ nexpose_thycotic -e
54
+ ```
55
+ or
56
+ ```
57
+ nexpose_thycotic --encrypt_config
58
+ ```
59
+
60
+ ## License
61
+
62
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
63
+
64
+ ## Changelog
65
+
66
+ ### 0.0.7
67
+ - User now has the option to configure the gem using a configuration file as well as with environment variables. Nexpose and Thycotic options have been added to the configuration file.
68
+ - Added an encryption configuration file. Usernames and passwords within the configuration file are now encrypted when the application runs.
69
+ - Command line options have been added to the gem. Several are common to all Nexpose gem integrations. Call the gem with '-h' or '--help' to view these options.
70
+ - *Breaking change*: Environment variables **NEXPOSE_PASS** and **THYCOTIC_PASS** have been renamed to **NEXPOSE_PASSWORD** and **THYCOTIC_PASSWORD** respectively.
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ # Please refer to the configuration documentation for instructions on how to run this gem
3
+ # Do NOT run this gem without proper pre-configuration.
4
+ require 'nexpose_thycotic'
5
+ require 'nexpose_thycotic/utilities/config_parser.rb'
6
+ require 'nexpose_thycotic/utilities/gem_options'
7
+
8
+ CONFIG_PATH = File.join(File.dirname(__FILE__),
9
+ '../lib/nexpose_thycotic/config/nexpose_thycotic.config')
10
+ config_path = File.expand_path(CONFIG_PATH)
11
+
12
+ # Setup CLI Options
13
+ GemOptions.create_parser
14
+ .with_banner_and_options('nexpose_thycotic')
15
+ .with_configuration_encryption([config_path])
16
+ .with_help_and_version('Nexpose Thycotic', NexposeThycotic::VERSION)
17
+ .parse
18
+
19
+ settings = ConfigParser.get_config(config_path)
20
+
21
+ thycotic_options = NexposeThycotic.set_variables(settings[:thycotic_options])
22
+ nexpose_options = NexposeThycotic.set_variables(settings[:nexpose_options])
23
+ options = settings[:options]
24
+
25
+ vault_options = { url: thycotic_options[:thycotic_url],
26
+ username: thycotic_options[:thycotic_username],
27
+ password: thycotic_options[:thycotic_password] }
28
+
29
+ nexpose_options = { nexpose_ip: nexpose_options[:nexpose_url],
30
+ nexpose_username: nexpose_options[:nexpose_username],
31
+ nexpose_password: nexpose_options[:nexpose_password],
32
+ nexpose_port: nexpose_options[:nexpose_port],
33
+ sites: options[:sites],
34
+ logging_enabled: options[:logging_enabled],
35
+ log_level: options[:log_level],
36
+ log_console: options[:log_console] }
37
+ NexposeThycotic.update_credentials(vault_options, nexpose_options)
38
+
@@ -1,39 +1,68 @@
1
1
  require "nexpose_thycotic/version"
2
2
  require 'nexpose_thycotic/operations'
3
- require 'nexpose_thycotic/nx_logger'
3
+ require 'nexpose_thycotic/utilities/nx_logger'
4
4
  require 'nexpose'
5
5
 
6
- module NexposeThycotic
6
+ module NexposeThycotic
7
7
  def self.update_credentials(vault_options, nexpose_options = nil)
8
- NxLogger.log_message('Starting integration.', 'info')
8
+ log = NexposeThycotic::NxLogger.instance
9
+ log.setup_statistics_collection(NexposeThycotic::VENDOR,
10
+ NexposeThycotic::PRODUCT,
11
+ NexposeThycotic::VERSION)
12
+ log.setup_logging(true, nexpose_options[:log_level], nexpose_options[:log_console])
13
+ log.log_message('Starting integration.')
14
+
9
15
  ss = ThycoticOperations.new(vault_options[:url])
10
- NxLogger.log_message("Logging to Thycotic at #{vault_options[:url]}", 'info')
16
+ log.log_message("Logging to Thycotic at #{vault_options[:url]}")
11
17
  token = ss.authenticate(vault_options[:username], vault_options[:password])
12
- @nx = NexposeOperations.new(nexpose_options[:nexpose_ip], nexpose_options[:nexpose_username], nexpose_options[:nexpose_password])
13
- NxLogger.log_message('Processing sites', 'info')
18
+
19
+ @nx = NexposeOperations.new(nexpose_options[:nexpose_ip],
20
+ nexpose_options[:nexpose_username],
21
+ nexpose_options[:nexpose_password],
22
+ nexpose_options[:nexpose_port])
23
+ log.log_message('Processing sites')
14
24
 
15
25
  nexpose_options[:sites].each do |site_id|
16
- NxLogger.log_message("Processing site #{site_id}", 'debug')
17
- ips = @nx.get_ips_from_site(site_id)
18
- site_credentials = []
19
- ips.each do |ip|
20
- ip_from = ip.instance_of?(Nexpose::HostName) ? ip.host : ip.from
21
- NxLogger.log_message("Getting credentials for #{ip_from}", 'debug')
22
- secret_summary = ss.get_secret_id(token, ip_from)
23
- unless secret_summary.nil?
24
- NxLogger.log_message("Found credentials for #{ip_from}", 'debug')
25
- # Gets OS
26
- asset_data = {}
27
- asset_data[:ip] = ip_from
28
- res = ss.get_secret_simple(token, secret_summary[:secret_id])
29
- asset_data[:username] = res[:username]
30
- asset_data[:password] = res[:password]
31
- credential = Nexpose::Credential.for_service(ss.check_type(secret_summary[:secret_type]), asset_data[:username], asset_data[:password], nil, ip.from )
32
- site_credentials.push(credential)
26
+ log.log_debug_message("Processing site #{site_id}")
27
+ ips = @nx.get_ips_from_site(site_id)
28
+ site_credentials = []
29
+
30
+ ips.each do |ip|
31
+ ip_from = ip.instance_of?(Nexpose::HostName) ? ip.host : ip.from
32
+ log.log_debug_message("Getting credentials for #{ip_from}")
33
+ secret_summary = ss.get_secret_id(token, ip_from)
34
+ unless secret_summary.nil?
35
+ log.log_debug_message("Found credentials for #{ip_from}")
36
+ # Gets OS
37
+ asset_data = {}
38
+ asset_data[:ip] = ip_from
39
+ res = ss.get_secret_simple(token, secret_summary[:secret_id])
40
+ asset_data[:username] = res[:username]
41
+ asset_data[:password] = res[:password]
42
+ credential = Nexpose::Credential.for_service(ss.check_type(secret_summary[:secret_type]),
43
+ asset_data[:username],
44
+ asset_data[:password],
45
+ nil,
46
+ ip.from)
47
+ site_credentials.push(credential)
48
+ end
33
49
  end
50
+ @nx.save_site(site_id, site_credentials)
51
+ log.log_message("Finished processing #{site_id}")
34
52
  end
35
- @nx.save_site(site_id, site_credentials)
36
- NxLogger.log_message("Finished processing #{site_id}", 'info')
53
+ end
54
+
55
+ def self.set_variables(options)
56
+ settings = {}
57
+ options.each_key do |key|
58
+ value = ENV[key.to_s.upcase]
59
+ value ||= options[key]
60
+ if value.nil?
61
+ log = NexposeThycotic::NxLogger.instance
62
+ log.info("No configuration value found for #{key}")
63
+ end
64
+ settings[key] = value
37
65
  end
66
+ settings
38
67
  end
39
- end
68
+ end
@@ -0,0 +1,21 @@
1
+ #
2
+ # Symmetric Encryption for Ruby
3
+ #
4
+ ---
5
+ production:
6
+ # Since the encryption key must NOT be stored along with the
7
+ # source code, only store the key encryption key here.
8
+ private_rsa_key:
9
+
10
+ # List Symmetric Key Ciphers in the order of current / newest first
11
+ ciphers:
12
+ -
13
+ # Name of the file containing the encrypted key and iv.
14
+ key_filename: <absolute/path/to/filename>.key
15
+ iv_filename: <absolute/path/to/filename>.iv
16
+
17
+ cipher: aes-256-cbc
18
+ encoding: base64strict
19
+ version: 1
20
+ always_add_header: true
21
+
@@ -0,0 +1,34 @@
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
+ :options:
7
+ # (M) Enables logging to the log directory.
8
+ :logging_enabled: true
9
+ # (M) Sets the log level threshold for output.
10
+ :log_level: info
11
+ # (M) Enables logging output to console
12
+ :log_console: true
13
+ # Filters to specific sites one per line, leave empty to generate for all sites.
14
+ :sites:
15
+ - '1'
16
+ :nexpose_options:
17
+ # (M) Nexpose IP address
18
+ :nexpose_url: 127.0.0.1
19
+ # (M) Nexpose username
20
+ :nexpose_username: nxadmin
21
+ # (M) Nexpose password
22
+ :nexpose_password: nxadmin
23
+ # (M) The port Nexpose listens on. Default is 3780
24
+ :nexpose_port: 3780
25
+ :thycotic_options:
26
+ # (M) Thycotic Instance IP address with WSDL
27
+ :thycotic_url: https://127.0.0.1/SecretServer/webservices/sswebservice.asmx?wsdl
28
+ # (M) The Thycotic username
29
+ :thycotic_username: user
30
+ # (M) The password for the above user
31
+ :thycotic_password: password
32
+ :encryption_options:
33
+ # (M) Path to the encryption.config file
34
+ :directory: ../../config/encryption.config
@@ -7,7 +7,9 @@ module NexposeThycotic
7
7
  def initialize(ip, user, pass, port = 3780)
8
8
  @nsc = Connection.new(ip, user, pass, port)
9
9
  @nsc.login
10
+ NexposeThycotic::NxLogger.instance.on_connect(ip, port, @nsc.session_id, "{}")
10
11
  end
12
+
11
13
  def get_ips_from_site(site_id)
12
14
  site = Site.load(@nsc, site_id)
13
15
  site.assets
@@ -40,7 +42,9 @@ module NexposeThycotic
40
42
  attr_accessor :client
41
43
  def initialize(url = nil)
42
44
  # log: true, log_level: :info
43
- @client = Savon.client(wsdl: url)
45
+ @client = Savon.client(
46
+ wsdl: url,
47
+ ssl_verify_mode: :none)
44
48
  end
45
49
 
46
50
  def operations
@@ -0,0 +1,141 @@
1
+ require 'erb'
2
+ require 'yaml'
3
+ require 'fileutils'
4
+ require 'symmetric-encryption'
5
+
6
+ class ConfigParser
7
+ ENCRYPTED_FORMAT = '<%%= SymmetricEncryption.try_decrypt "%s" %%>'
8
+ PLACEHOLDER = '<absolute/path/to/filename>'
9
+ # The environment to use, defined within the encryption config
10
+ STANZA = 'production'
11
+ # The line width of the YAML file before line-wrapping occurs
12
+ WIDTH = 120
13
+
14
+ # Encrypts a configuration file and returns the unencrypted hash.
15
+ def self.get_config(config_path, enc_path=nil)
16
+ # Try to load a path from the provided config
17
+ custom_enc_path = get_enc_directory(config_path)
18
+ enc_path = custom_enc_path unless custom_enc_path.nil?
19
+
20
+ enc_path = File.expand_path(enc_path, __FILE__)
21
+ config_path = File.expand_path(config_path)
22
+
23
+
24
+ generate_keys(enc_path, config_path)
25
+ encrypt_config(enc_path, config_path)
26
+ decrypt_config(enc_path, config_path)
27
+ end
28
+
29
+ # Writes the YAML to file with custom formatting options
30
+ def self.save_config(config_details, config_path)
31
+ yaml = config_details.to_yaml(line_width: WIDTH)
32
+ File.open(config_path, 'w') {|f| f.write yaml }
33
+ end
34
+
35
+ def self.encrypt_field(value)
36
+ encrypted_value = SymmetricEncryption.encrypt value
37
+ ENCRYPTED_FORMAT % encrypted_value
38
+ end
39
+
40
+ # Retrieves the custom directory of the encryption config
41
+ def self.get_enc_directory(config_path)
42
+ settings = YAML.load_file(config_path)
43
+ return nil if settings[:encryption_options].nil?
44
+
45
+ enc_dir = settings[:encryption_options][:directory]
46
+ return nil if (enc_dir.nil? || enc_dir == '')
47
+
48
+ File.expand_path(enc_dir, __FILE__)
49
+ end
50
+
51
+ # Generates the RSA key, associated files and directories.
52
+ def self.generate_keys(enc_path, config_path)
53
+ settings = YAML.load_file(enc_path)
54
+ key = settings[STANZA]['private_rsa_key']
55
+
56
+ # Recognise an existing key
57
+ return unless (key.nil? || key == '')
58
+
59
+ # Generate a new RSA key and store the details
60
+ new_rsa_key = SymmetricEncryption::KeyEncryptionKey.generate
61
+ settings[STANZA]['private_rsa_key'] = new_rsa_key
62
+ save_config(settings, enc_path)
63
+
64
+ # Populate the placeholder values within the config
65
+ populate_ciphers(enc_path, config_path)
66
+
67
+ # Need to create a folder (specified by the user) to store the key files
68
+ dir = File.dirname(settings[STANZA]['ciphers'].first['key_filename'])
69
+
70
+ begin
71
+ unless File.directory?(dir) || PLACEHOLDER.include?(dir)
72
+ puts "Creating folder: #{dir}"
73
+ FileUtils::mkdir_p dir
74
+ end
75
+ rescue Exception => e
76
+ msg = "Unable to create the folders used to store encryption details.\n"\
77
+ 'Please ensure the user has permissions to create folders in the ' \
78
+ "path specified in the encryption config: #{enc_path}\n"
79
+ handle_error(msg, e)
80
+ end
81
+
82
+ SymmetricEncryption.generate_symmetric_key_files(enc_path, STANZA)
83
+ end
84
+
85
+ # Replace placeholder values for the key and iv file paths,
86
+ # placing them in the config folder by default.
87
+ def self.populate_ciphers(enc_path, config_path)
88
+ settings = YAML.load_file(enc_path)
89
+ ciphers = settings[STANZA]['ciphers'].first
90
+ config_folder = File.dirname(config_path)
91
+ config_name = File.basename(config_path, File.extname(config_path))
92
+
93
+ %w(key iv).each do |file|
94
+ label = "#{file}_filename"
95
+ file_path = ciphers[label]
96
+ next unless file_path.include? PLACEHOLDER
97
+
98
+ filename = ".#{config_name}.#{file}"
99
+ ciphers[label] = File.join(config_folder, filename)
100
+ end
101
+
102
+ save_config(settings, enc_path)
103
+ end
104
+
105
+ def self.encrypt_config(enc_path, config_path)
106
+ SymmetricEncryption.load!(enc_path, STANZA)
107
+
108
+ # Read the config in as an array of strings
109
+ f = File.open(config_path)
110
+ config_lines = f.readlines
111
+ f.close
112
+
113
+ # Define the regex that can find relevant fields
114
+ regex = /^(?<label>\s*:?\w*(passw|pwd|user|usr)\w*:?\s)(?<value>.*)$/
115
+
116
+ # Line by line, write the line to file, encrypting sensitive fields
117
+ File.open(config_path, 'w+') do |f|
118
+ config_lines.each do |l|
119
+ matches = l.match(regex)
120
+
121
+ # Encrypt fields with username/password labels that are in plaintext
122
+ unless matches.nil? || matches['value'].include?('SymmetricEncryption')
123
+ l = "#{matches['label']}#{encrypt_field(matches['value'])}"
124
+ end
125
+
126
+ f.puts l
127
+ end
128
+ end
129
+ end
130
+
131
+ # Returns a hash containing the decrypted details from a config file.
132
+ def self.decrypt_config(enc_path, config_path)
133
+ SymmetricEncryption.load!(enc_path, STANZA)
134
+ return YAML.load(ERB.new(File.new(config_path).read).result)
135
+ end
136
+
137
+ def self.handle_error(message, error)
138
+ puts message
139
+ raise error
140
+ end
141
+ end
@@ -0,0 +1,91 @@
1
+ require 'optparse'
2
+
3
+ class GemOptions
4
+
5
+ @parser
6
+
7
+ def self.create_parser
8
+ @parser = OptionParser.new
9
+ self
10
+ end
11
+
12
+ # How the gem is used e.g 'nexpose ticketing jira [options]'
13
+ def self.with_banner(gem_usage_string)
14
+ @parser.banner = "Usage: #{gem_usage_string} [options]"
15
+ @parser.separator ''
16
+ self
17
+ end
18
+
19
+ # Header for options list
20
+ def self.with_options
21
+ @parser.separator 'Options:'
22
+ self
23
+ end
24
+
25
+ # Creates banner and options
26
+ def self.with_banner_and_options(gem_usage_string)
27
+ with_banner(gem_usage_string)
28
+ with_options
29
+ self
30
+ end
31
+
32
+ # For setting encryption switch. Can be set to work with two configurations
33
+ # Config_paths is an array
34
+ def self.with_configuration_encryption(config_paths, enc_path = nil)
35
+ @parser.on('-e',
36
+ '--encrypt_config',
37
+ 'Encrypt the configuration file(s) without running the gem') do |e|
38
+ ConfigParser.get_config(config_paths.first, enc_path) unless enc_path.nil?
39
+ ConfigParser.get_config(config_paths.last)
40
+ puts "\nConfiguration File(s) Encrypted"
41
+ exit
42
+ end
43
+ self
44
+ end
45
+
46
+ def self.with_help
47
+ @parser.on_tail('-h', '--help', 'Show this message') do |h|
48
+ puts @parser
49
+ exit
50
+ end
51
+ self
52
+ end
53
+
54
+ def self.with_version(gem, version)
55
+ @parser.on_tail('--version', 'Version Information') do |v|
56
+ puts "#{gem} #{version}"
57
+ exit
58
+ end
59
+ self
60
+ end
61
+
62
+ def self.with_help_and_version(gem, version)
63
+ with_help
64
+ with_version(gem, version)
65
+ self
66
+ end
67
+
68
+ # Method to allow integrations to create own options, with both short and long
69
+ # switches and description.
70
+ # Handler is the block to run when option is called.
71
+ def self.with_other_option(short_switch, long_switch, description, &handler)
72
+ @parser.on("-#{short_switch}", "--#{long_switch}", description) do |opt|
73
+ handler.call
74
+ end
75
+ end
76
+
77
+ # Method to allow integrations to create own options, with only one size of
78
+ # switch and description.
79
+ # '-' for short switches and '--' for long switches is required.
80
+ # Handler is the block to run when option is called.
81
+ def self.with_single_switch_option(identifier, switch, description, &handler)
82
+ @parser.on("#{identifier}#{switch}", description) do |opt|
83
+ handler.call
84
+ end
85
+ end
86
+
87
+ # Parses the options to make them available
88
+ def self.parse
89
+ @parser.parse!
90
+ end
91
+ end
@@ -0,0 +1,166 @@
1
+ require 'fileutils'
2
+ require 'json'
3
+ require 'net/http'
4
+ require 'singleton'
5
+
6
+ module NexposeThycotic
7
+ class NxLogger
8
+ include Singleton
9
+ LOG_PATH = "../logs/rapid7_%s.log"
10
+ KEY_FORMAT = "external.integration.%s"
11
+ PRODUCT_FORMAT = "%s_%s"
12
+
13
+ DEFAULT_LOG = 'integration'
14
+ PRODUCT_RANGE = 4..30
15
+ KEY_RANGE = 3..15
16
+
17
+ ENDPOINT = '/data/external/statistic/'
18
+
19
+ def initialize()
20
+ create_calls
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
+ begin
27
+ @statistic_key = get_statistic_key vendor
28
+ @product = get_product product_name, gem_version
29
+ rescue => e
30
+ #Continue
31
+ end
32
+ end
33
+
34
+ def setup_logging(enabled, log_level = 'info', stdout=false)
35
+ @stdout = stdout
36
+
37
+ log_message('Logging disabled.') unless enabled || @log.nil?
38
+ @enabled = enabled
39
+ return unless @enabled
40
+
41
+ @logger_file = get_log_path @product
42
+
43
+ require 'logger'
44
+ directory = File.dirname(@logger_file)
45
+ FileUtils.mkdir_p(directory) unless File.directory?(directory)
46
+ io = IO.for_fd(IO.sysopen(@logger_file, 'a'), 'a')
47
+ io.autoclose = false
48
+ io.sync = true
49
+ @log = Logger.new(io, 'weekly')
50
+ @log.level = if log_level.to_s.casecmp('info') == 0
51
+ Logger::INFO
52
+ else
53
+ Logger::DEBUG
54
+ end
55
+ log_message("Logging enabled at level <#{log_level}>")
56
+ end
57
+
58
+ def create_calls
59
+ levels = [:info, :debug, :error, :warn]
60
+ levels.each do |level|
61
+ method_name =
62
+ define_singleton_method("log_#{level.to_s}_message") do |message|
63
+ puts message if @stdout
64
+ @log.send(level, message) unless !@enabled || @log.nil?
65
+ end
66
+ end
67
+ end
68
+
69
+ def log_message(message)
70
+ log_info_message message
71
+ end
72
+
73
+ def log_stat_message(message)
74
+ end
75
+
76
+ def get_log_path(product)
77
+ product.downcase! unless product.nil?
78
+ File.join(File.dirname(__FILE__), LOG_PATH % (product || DEFAULT_LOG))
79
+ end
80
+
81
+ def get_statistic_key(vendor)
82
+ if vendor.nil? || vendor.length < KEY_RANGE.min
83
+ log_stat_message("Vendor length is below minimum of <#{KEY_RANGE}>")
84
+ return nil
85
+ end
86
+
87
+ vendor.gsub!('-', '_')
88
+ vendor.slice! vendor.rindex('_') until vendor.count('_') <= 1
89
+
90
+ vendor.delete! "^A-Za-z0-9\_"
91
+
92
+ KEY_FORMAT % vendor[0...KEY_RANGE.max].downcase
93
+ end
94
+
95
+ def get_product(product, version)
96
+ return nil if ((product.nil? || product.empty?) ||
97
+ (version.nil? || version.empty?))
98
+
99
+ product.gsub!('-', '_')
100
+ product.slice! product.rindex('_') until product.count('_') <= 1
101
+
102
+ product.delete! "^A-Za-z0-9\_"
103
+ version.delete! "^A-Za-z0-9\.\-"
104
+
105
+ product = (PRODUCT_FORMAT % [product, version])[0...PRODUCT_RANGE.max]
106
+
107
+ product.slice! product.rindex(/[A-Z0-9]/i)+1..-1
108
+
109
+ if product.length < PRODUCT_RANGE.min
110
+ log_stat_message("Product length below minimum <#{PRODUCT_RANGE.min}>.")
111
+ return nil
112
+ end
113
+ product.downcase
114
+ end
115
+
116
+ def generate_payload(statistic_value='')
117
+ product_name, separator, version = @product.to_s.rpartition('_')
118
+ payload_value = {'version' => version}.to_json
119
+
120
+ payload = {'statistic-key' => @statistic_key.to_s,
121
+ 'statistic-value' => payload_value,
122
+ 'product' => product_name}
123
+ JSON.generate(payload)
124
+ end
125
+
126
+ def send(nexpose_address, nexpose_port, session_id, payload)
127
+ header = {'Content-Type' => 'application/json',
128
+ 'nexposeCCSessionID' => session_id,
129
+ 'Cookie' => "nexposeCCSessionID=#{session_id}"}
130
+ req = Net::HTTP::Put.new(ENDPOINT, header)
131
+ req.body = payload
132
+ http_instance = Net::HTTP.new(nexpose_address, nexpose_port)
133
+ http_instance.use_ssl = true
134
+ http_instance.verify_mode = OpenSSL::SSL::VERIFY_NONE
135
+ response = http_instance.start { |http| http.request(req) }
136
+ log_stat_message "Received code #{response.code} from Nexpose console."
137
+ log_stat_message "Received message #{response.msg} from Nexpose console."
138
+ log_stat_message 'Finished sending statistics data to Nexpose.'
139
+
140
+ response.code
141
+ end
142
+
143
+ def on_connect(nexpose_address, nexpose_port, session_id, value)
144
+ log_stat_message 'Sending statistics data to Nexpose'
145
+
146
+ if @product.nil? || @statistic_key.nil?
147
+ log_stat_message('Invalid product name and/or statistics key.')
148
+ log_stat_message('Statistics collection not enabled.')
149
+ return
150
+ end
151
+
152
+ begin
153
+ payload = generate_payload value
154
+ send(nexpose_address, nexpose_port, session_id, payload)
155
+ rescue => e
156
+ #Let the program continue
157
+ end
158
+ end
159
+
160
+ #Used by net library for debugging
161
+ def <<(value)
162
+ log_debug_message(value)
163
+ end
164
+
165
+ end
166
+ end
@@ -1,5 +1,7 @@
1
1
  module NexposeThycotic
2
- VERSION = "0.0.4"
2
+ VERSION = "0.1.0"
3
+ VENDOR = "Thycotic"
4
+ PRODUCT = "Secret Server"
3
5
  def self.show_version
4
6
  puts VERSION
5
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nexpose_thycotic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
- - Damian Finol
7
+ - Damian Finol, Adam Robinson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-20 00:00:00.000000000 Z
11
+ date: 2017-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -72,20 +72,40 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.8.0
75
+ version: '0.9'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.8.0
83
- description: This gem allows Nexpose users to poll credentials from Thycotic SecretServer
82
+ version: '0.9'
83
+ - !ruby/object:Gem::Dependency
84
+ name: symmetric-encryption
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.9'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 3.9.0
93
+ type: :runtime
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '3.9'
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 3.9.0
103
+ description: This gem allows Nexpose users to pull credentials from Thycotic SecretServer
84
104
  into Nexpose
85
105
  email:
86
- - damian_finol@rapid7.com
106
+ - support@rapid7.com
87
107
  executables:
88
- - nx_thycotic.rb
108
+ - nexpose_thycotic
89
109
  extensions: []
90
110
  extra_rdoc_files: []
91
111
  files:
@@ -94,13 +114,15 @@ files:
94
114
  - LICENSE.txt
95
115
  - README.md
96
116
  - Rakefile
97
- - bin/nx_thycotic.rb
117
+ - bin/nexpose_thycotic
98
118
  - lib/nexpose_thycotic.rb
99
- - lib/nexpose_thycotic/nx_logger.rb
119
+ - lib/nexpose_thycotic/config/encryption.config
120
+ - lib/nexpose_thycotic/config/nexpose_thycotic.config
100
121
  - lib/nexpose_thycotic/operations.rb
122
+ - lib/nexpose_thycotic/utilities/config_parser.rb
123
+ - lib/nexpose_thycotic/utilities/gem_options.rb
124
+ - lib/nexpose_thycotic/utilities/nx_logger.rb
101
125
  - lib/nexpose_thycotic/version.rb
102
- - nexpose_thycotic-0.0.3.gem
103
- - nexpose_thycotic.gemspec
104
126
  homepage: http://www.rapid7.com/
105
127
  licenses:
106
128
  - MIT
@@ -121,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
143
  version: '0'
122
144
  requirements: []
123
145
  rubyforge_project:
124
- rubygems_version: 2.4.4
146
+ rubygems_version: 2.5.1
125
147
  signing_key:
126
148
  specification_version: 4
127
149
  summary: Nexpose Thycotic Gem Integration
data/bin/nx_thycotic.rb DELETED
@@ -1,31 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # Please refer to the configuration documentation for instructions on how to run this gem
3
- # Do NOT run this gem without proper pre-configuration.
4
- require 'nexpose_thycotic'
5
-
6
- # Sites to collect credentials on, separated by commas.
7
- sites = [ 7 ]
8
- # Thycotic SecretServer URL.
9
- # Set an Environment variable called THYCOTIC_URL to your sswebservice install
10
- # IE: http://127.0.0.1/SecretServer/webservices/sswebservice.asmx?wsdl
11
- thycotic_url = ENV['THYCOTIC_URL']
12
- # Thycotic username.
13
- thycotic_username = ENV['THYCOTIC_USER']
14
- # Thycotic password
15
- thycotic_password = ENV['THYCOTIC_PASS']
16
-
17
-
18
- ### NEXPOSE CONFIGURATION ###
19
- # Nexpose IP Address
20
- nexpose_ip = ENV['NEXPOSE_URL']
21
- # Nexpose username
22
- nexpose_username = ENV['NEXPOSE_USER']
23
- # Nexpose password
24
- nexpose_password = ENV['NEXPOSE_PASS']
25
-
26
-
27
-
28
- ### DO NOT EDIT AFTER THIS LINE ###
29
- vault_options = { url: thycotic_url, username: thycotic_username, password: thycotic_password }
30
- nexpose_options = { nexpose_ip: nexpose_ip, nexpose_username: nexpose_username, nexpose_password: nexpose_password, sites: sites }
31
- NexposeThycotic.update_credentials(vault_options, nexpose_options)
@@ -1,19 +0,0 @@
1
- module NexposeThycotic
2
- module NxLogger
3
- require 'logger'
4
- LOGGER_FILE = File.join(File.dirname(__FILE__), '/log/nx_thycotic.log')
5
- directory = File.dirname(LOGGER_FILE)
6
- FileUtils.mkdir_p(directory) unless File.directory?(directory)
7
- @log = Logger.new(LOGGER_FILE, 'monthly')
8
- @log.level = Logger::INFO
9
-
10
- def self.log_message(message, level)
11
- case level
12
- when 'info' then @log.info(message)
13
- when 'debug' then @log.debug(message)
14
- when 'error' then @log.error(message)
15
- when 'warn' then @log.warn(message)
16
- end
17
- end
18
- end
19
- end
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_thycotic/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "nexpose_thycotic"
8
- spec.version = NexposeThycotic::VERSION
9
- spec.authors = ["Damian Finol"]
10
- spec.email = ["damian_finol@rapid7.com"]
11
- spec.summary = %q{Nexpose Thycotic Gem Integration}
12
- spec.description = %q{This gem allows Nexpose users to poll credentials from Thycotic SecretServer into 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
-
22
- spec.add_development_dependency "bundler", "~> 1.5"
23
- spec.add_development_dependency "rake"
24
- spec.add_runtime_dependency "savon"
25
- spec.add_runtime_dependency "rubyntlm"
26
- spec.add_runtime_dependency 'nexpose', '~> 0.8.0'
27
- end