nexpose_thycotic 0.0.4 → 0.1.0

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