bcome 1.1.4 → 1.3.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: cdf4b817e9c7afb1b0b5508a811e2e74a71a6b97
4
- data.tar.gz: 881ae215c6873cc81b5214f2876cd13833ce1ae1
3
+ metadata.gz: 5175174eb843f0f210c02ca12ded004f75d77368
4
+ data.tar.gz: ffd09a8ba7edc0df044a17b55db02d3176e0feee
5
5
  SHA512:
6
- metadata.gz: c67f27ca6e33d761311f9bf8c226e774b10516b9f14d2bade52e8d04e2e9df35b48e0e8ad53c1199b4fa18381e8e7a3fe51ddf9e52d05035aab15024be5c4472
7
- data.tar.gz: c610e971283f6cc178d746a4eb1ceca8b619ac66d4a76bd1353eb2c999ab5da48c4075d7d02f7da4197ac253ab928a02c31a5e4f9cb6ce0a0032017fc73df5ec
6
+ metadata.gz: 855151285002aa2270d025f97ff85c8beead1f3b7aabcad797bcd8eb03885c38f296a2ad1f1e01c2b1be6631a3d9ba724f57fae6046e6132f8ec8e95ae3e9ee9
7
+ data.tar.gz: 09500dc2f2ac59b5a1c70252c07ca0bbc665f32eac78a52fc1ded53d3e9d79ba09d0367ac56921eab5d56d9ddd300665924640733d3e0d436150badbec7f55db
@@ -1,3 +1,3 @@
1
1
  module Bcome
2
- VERSION = '1.1.4'.freeze
2
+ VERSION = '1.3.0'.freeze
3
3
  end
@@ -8,7 +8,7 @@ module Bcome::Command
8
8
  command
9
9
  end
10
10
 
11
- attr_reader :stdout, :stderr
11
+ attr_reader :stdout, :stderr, :process_status
12
12
 
13
13
  def initialize(command, success_exit_codes = [0])
14
14
  @command = command
@@ -1,5 +1,8 @@
1
1
  module Bcome::Driver
2
2
  class Ec2 < Bcome::Driver::Base
3
+
4
+ PATH_TO_FOG_CREDENTIALS = "#{ENV['HOME']}/.fog".freeze
5
+
3
6
  def initialize(*params)
4
7
  super
5
8
  raise Bcome::Exception::Ec2DriverMissingProvisioningRegion, params.inspect unless provisioning_region
@@ -22,6 +25,10 @@ module Bcome::Driver
22
25
  fog_client.servers.all({})
23
26
  end
24
27
 
28
+ def raw_fog_credentials
29
+ @raw_fog_credentials ||= YAML.load_file(PATH_TO_FOG_CREDENTIALS)[credentials_key]
30
+ end
31
+
25
32
  def credentials_key
26
33
  @params[:credentials_key]
27
34
  end
@@ -0,0 +1,99 @@
1
+ module Bcome
2
+ class Encryptor
3
+
4
+ UNENC_SIGNIFIER = "".freeze
5
+ ENC_SIGNIFIER = "enc".freeze
6
+
7
+ include Singleton
8
+
9
+ attr_reader :key
10
+
11
+ def pack
12
+ # Bcome currently works with a single encryption key - the same one - for all files
13
+ # When we attempt an encrypt we'll check first to see if any encrypted files already exists, and
14
+ # we'll try our key on it. If the fails to unpack the file, we abort the encryption attempt.
15
+ prompt_for_key
16
+ if has_files_to_encrypt?
17
+ verify_presented_key if has_encrypted_files?
18
+ toggle_packed_files(all_unencrypted_filenames, :encrypt)
19
+ else
20
+ puts "\nNo unencrypted files to encrypt.\n".warning
21
+ end
22
+ return
23
+ end
24
+
25
+ def prompt_for_key
26
+ message = "Please enter an encryption key (and if your data is already encrypted, you must provide the same key): ".informational
27
+ @key = ::Readline.readline("\n#{message}", true).squeeze('').to_s
28
+ puts "\n"
29
+ end
30
+
31
+ def has_encrypted_files?
32
+ all_encrypted_filenames.any?
33
+ end
34
+
35
+ def has_files_to_encrypt?
36
+ all_unencrypted_filenames.any?
37
+ end
38
+
39
+ def verify_presented_key
40
+ # We attempt a decrypt of any encrypted file in order to verify that a newly presented key
41
+ # matches the key used to previously encrypt. Bcome operates on a one-key-per-implementation basis.
42
+ test_file = all_encrypted_filenames.first
43
+ file_contents = File.read(test_file)
44
+ file_contents.decrypt(@key)
45
+ end
46
+
47
+ def unpack
48
+ prompt_for_key
49
+ toggle_packed_files(all_encrypted_filenames,:decrypt)
50
+ return
51
+ end
52
+
53
+ def toggle_packed_files(filenames, packer_method)
54
+ raise "Missing encryption key. Please set an encryption key" unless @key
55
+ filenames.each do |filename|
56
+ # Get raw
57
+ raw_contents = File.read(filename)
58
+
59
+ if packer_method == :decrypt
60
+ filename =~ /#{path_to_metadata}\/(.+)\.enc/
61
+ opposing_filename = $1
62
+ action = "Unpacking"
63
+ else
64
+ filename =~ /#{path_to_metadata}\/(.*)/
65
+ opposing_filename = "#{$1}.enc"
66
+ action = "Packing"
67
+ end
68
+
69
+ # Write encrypted/decryption action
70
+ enc_decrypt_result = raw_contents.send(packer_method, @key)
71
+ puts "#{action}\s".informational + filename + "\sto\s".informational + "#{path_to_metadata}/" + opposing_filename
72
+ write_file(opposing_filename, enc_decrypt_result)
73
+ end
74
+ puts "\ndone".informational
75
+ end
76
+
77
+ def path_to_metadata
78
+ "bcome/metadata"
79
+ end
80
+
81
+ def write_file(filename, contents)
82
+ filepath = "#{path_to_metadata}/#{filename}"
83
+ File.open("#{filepath}", 'w') { |f| f.write(contents) }
84
+ end
85
+
86
+ def all_unencrypted_filenames
87
+ Dir["#{metadata_path}/*"].reject {|f| f =~ /\.enc/}
88
+ end
89
+
90
+ def all_encrypted_filenames
91
+ Dir["#{metadata_path}/*.enc"]
92
+ end
93
+
94
+ def metadata_path
95
+ "bcome/metadata"
96
+ end
97
+
98
+ end
99
+ end
@@ -0,0 +1,7 @@
1
+ module Bcome::Exception
2
+ class InvalidMetaDataEncryptionKey < ::Bcome::Exception::Base
3
+ def message_prefix
4
+ 'Your metadata encryption key is invalid - your metadata files are encrypted with a different key.'
5
+ end
6
+ end
7
+ end
@@ -113,6 +113,12 @@ module Bcome::WorkspaceMenu
113
113
  meta: {
114
114
  description: 'Print out all metadata related to this node'
115
115
  },
116
+ pack_metadata: {
117
+ description: 'Encrypt your metadata files',
118
+ },
119
+ unpack_metadata: {
120
+ description: 'Decrypt and expose your encrypted metadata files',
121
+ },
116
122
  registry: {
117
123
  description: 'List all user defined commands present in your registry, and available to this namespace',
118
124
  console_only: false
@@ -41,7 +41,7 @@ module Bcome::Node::Attributes
41
41
  def recurse_hash_data_for_instance_var(instance_var_name, parent_key)
42
42
  instance_data = instance_variable_defined?(instance_var_name) ? instance_variable_get(instance_var_name) : {}
43
43
  instance_data ||= {}
44
- instance_data = parent.send(parent_key).merge(instance_data) if has_parent?
44
+ instance_data = parent.send(parent_key).deep_merge(instance_data) if has_parent?
45
45
  instance_data
46
46
  end
47
47
  end
@@ -50,7 +50,7 @@ module Bcome::Node
50
50
  end
51
51
 
52
52
  def enabled_menu_items
53
- [:ls, :lsa, :workon, :enable, :disable, :enable!, :disable!, :run, :tree, :ping, :put, :rsync, :cd, :meta, :registry, :interactive, :execute_script]
53
+ [:ls, :lsa, :workon, :enable, :disable, :enable!, :disable!, :run, :tree, :ping, :put, :rsync, :cd, :meta, :pack_metadata, :unpack_metadata, :registry, :interactive, :execute_script]
54
54
  end
55
55
 
56
56
  def has_proxy?
@@ -91,6 +91,14 @@ module Bcome::Node
91
91
  end
92
92
  results
93
93
  end
94
+
95
+ def pack_metadata
96
+ ::Bcome::Encryptor.instance.pack
97
+ end
98
+
99
+ def unpack_metadata
100
+ ::Bcome::Encryptor.instance.unpack
101
+ end
94
102
 
95
103
  def validate_attributes
96
104
  validate_identifier
@@ -6,6 +6,8 @@ module Bcome::Node
6
6
 
7
7
  CONFIG_PATH = 'bcome'.freeze
8
8
  DEFAULT_CONFIG_NAME = 'networks.yml'.freeze
9
+ SERVER_OVERRIDE_CONFIG_NAME = 'machines-data.yml'.freeze
10
+
9
11
  INVENTORY_KEY = 'inventory'.freeze
10
12
  COLLECTION_KEY = 'collection'.freeze
11
13
  SUBSELECT_KEY = 'inventory-subselect'.freeze
@@ -24,6 +26,10 @@ module Bcome::Node
24
26
  "#{CONFIG_PATH}/#{config_file_name}"
25
27
  end
26
28
 
29
+ def machines_data_path
30
+ "#{CONFIG_PATH}/#{SERVER_OVERRIDE_CONFIG_NAME}"
31
+ end
32
+
27
33
  def config_file_name
28
34
  @config_file_name ||= ENV['CONF'] ? ENV['CONF'] : DEFAULT_CONFIG_NAME
29
35
  end
@@ -46,7 +52,7 @@ module Bcome::Node
46
52
  raise Bcome::Exception::InvalidNetworkConfig, 'missing config type' unless config[:type]
47
53
 
48
54
  klass = klass_for_view_type[config[:type]]
49
-
55
+
50
56
  raise Bcome::Exception::InvalidNetworkConfig, "invalid config type #{config[:type]}" unless klass
51
57
 
52
58
  node = klass.new(views: config, parent: parent)
@@ -84,6 +90,14 @@ module Bcome::Node
84
90
  @estate_config ||= reformat_config(load_estate_config)
85
91
  end
86
92
 
93
+ def machines_data
94
+ @machines_data ||= load_machines_data
95
+ end
96
+
97
+ def machines_data_for_namespace(namespace)
98
+ machines_data[namespace] ? machines_data[namespace] : {}
99
+ end
100
+
87
101
  def rewrite_estate_config(data)
88
102
  File.open(config_path, 'w') do |file|
89
103
  file.write data.to_yaml
@@ -94,12 +108,20 @@ module Bcome::Node
94
108
  config = YAML.load_file(config_path).deep_symbolize_keys
95
109
  return config
96
110
  rescue ArgumentError, Psych::SyntaxError => e
97
- raise Bcome::Exception::InvalidNetworkConfig, 'Invalid yaml in config' + e.message
111
+ raise Bcome::Exception::InvalidNetworkConfig, 'Invalid yaml in network config' + e.message
98
112
  rescue Errno::ENOENT
99
113
  raise Bcome::Exception::DeprecationWarning if is_running_deprecated_configs?
100
114
  raise Bcome::Exception::MissingNetworkConfig, config_path
101
115
  end
102
116
 
117
+ def load_machines_data
118
+ return {} unless File.exist?(machines_data_path)
119
+ config = YAML.load_file(machines_data_path).deep_symbolize_keys
120
+ return config
121
+ rescue ArgumentError, Psych::SyntaxError => e
122
+ raise Bcome::Exception::InvalidNetworkConfig, 'Invalid yaml in machines data config' + e.message
123
+ end
124
+
103
125
  def is_running_deprecated_configs?
104
126
  File.exist?("bcome/config/platform.yml")
105
127
  end
@@ -25,9 +25,11 @@ module Bcome::Node::Meta
25
25
  @data
26
26
  end
27
27
 
28
- def fetch(key)
28
+ def fetch(key, default = nil)
29
29
  if @data.key?(key)
30
30
  @data[key]
31
+ elsif default
32
+ return default
31
33
  else
32
34
  raise Bcome::Exception::CantFindKeyInMetadata, key unless @data.key?(key)
33
35
  end
@@ -8,6 +8,10 @@ module Bcome::Node
8
8
  @all_metadata_filenames = Dir["#{META_DATA_FILE_PATH_PREFIX}/*"]
9
9
  end
10
10
 
11
+ def decryption_key
12
+ @decryption_key
13
+ end
14
+
11
15
  def data
12
16
  @data ||= do_load
13
17
  end
@@ -16,11 +20,29 @@ module Bcome::Node
16
20
  data[namespace.to_sym] ? data[namespace.to_sym] : {}
17
21
  end
18
22
 
23
+ def prompt_for_decryption_key
24
+ message = "Please enter your metadata encryption key: ".informational
25
+ @decryption_key = ::Readline.readline("\n#{message}", true).squeeze('').to_s
26
+ end
27
+
28
+ def load_file_data_for(filepath)
29
+ if filepath =~ /.enc/ # encrypted file contents
30
+ prompt_for_decryption_key unless decryption_key
31
+ encrypted_contents = File.read(filepath)
32
+ decrypted_contents = encrypted_contents.decrypt(decryption_key)
33
+ return YAML.load(decrypted_contents)
34
+ else # unencrypted
35
+ return YAML.load_file(filepath)
36
+ end
37
+ end
38
+
19
39
  def do_load
20
40
  all_meta_data = {}
21
- @all_metadata_filenames.each do |filename|
41
+ @all_metadata_filenames.each do |filepath|
42
+ next if filepath =~ /-unenc/ # we only read from the encrypted, packed files.
43
+
22
44
  begin
23
- filedata = YAML.load_file(filename)
45
+ filedata = load_file_data_for(filepath)
24
46
  all_meta_data.deep_merge!(filedata)
25
47
  rescue Psych::SyntaxError => e
26
48
  raise Bcome::Exception::InvalidMetaDataConfig, "Error: #{e.message}"
@@ -10,6 +10,17 @@ module Bcome::Node::Server
10
10
  @bootstrap = false
11
11
  end
12
12
 
13
+ # override a server namespace's parameters. This enables features such as specific SSH parameters for a specific server, e.g. my use case was a
14
+ # single debian box within an ubuntu network, where I needed to access the machine bootstrapping mode with the 'admin' rather 'ubuntu' username.
15
+ def set_view_attributes
16
+ super
17
+ overridden_attributes = ::Bcome::Node::Factory.instance.machines_data_for_namespace(namespace.to_sym)
18
+ overridden_attributes.each do |override_key, override_value|
19
+ instance_variable_name = "@#{override_key}"
20
+ instance_variable_set(instance_variable_name, override_value)
21
+ end
22
+ end
23
+
13
24
  def bootstrap?
14
25
  @bootstrap ? true : false
15
26
  end
@@ -98,6 +109,10 @@ module Bcome::Node::Server
98
109
  base_items
99
110
  end
100
111
 
112
+ def local_port_forward(start_port, end_port)
113
+ ssh_driver.local_port_forward(start_port, end_port)
114
+ end
115
+
101
116
  def open_ssh_connection
102
117
  ssh_driver.ssh_connection
103
118
  end
@@ -79,6 +79,27 @@ module Bcome::Ssh
79
79
  end
80
80
  end
81
81
 
82
+ def local_port_forward(start_port, end_port)
83
+ if has_proxy?
84
+
85
+ if bootstrap?
86
+ tunnel_command = "ssh -N -L #{start_port}:#{@context_node.internal_ip_address}:#{end_port} -i #{@bootstrap_settings.ssh_key_path} #{bastion_host_user}@#{@proxy_data.host}"
87
+ else
88
+ tunnel_command = "ssh -N -L #{start_port}:#{@context_node.internal_ip_address}:#{end_port} #{bastion_host_user}@#{@proxy_data.host}"
89
+ end
90
+ else
91
+ if bootstrap?
92
+ tunnel_command = "ssh -i #{@bootstrap_settings.ssh_key_path} -N -L #{start_port}:#{@context_node.public_ip_address}:#{end_port}"
93
+ else
94
+ tunnel_command = "ssh -N -L #{start_port}:#{@context_node.public_ip_address}:#{end_port}"
95
+ end
96
+ end
97
+
98
+ tunnel = ::Bcome::Ssh::Tunnel::LocalPortForward.new(tunnel_command)
99
+ tunnel.open!
100
+ return tunnel
101
+ end
102
+
82
103
  def bootstrap_ssh_command
83
104
  if has_proxy?
84
105
  "ssh -i #{@bootstrap_settings.ssh_key_path} -t #{bastion_host_user}@#{@proxy_data.host} ssh -t #{user}@#{@context_node.internal_ip_address}"
@@ -0,0 +1,20 @@
1
+ module Bcome::Ssh::Tunnel
2
+ class LocalPortForward
3
+
4
+ def initialize(tunnel_command)
5
+ @tunnel_command = tunnel_command
6
+ @process_pid = nil
7
+ end
8
+
9
+ def open!
10
+ puts "Opening tunnel: #{@tunnel_command}".informational
11
+ @process_pid = spawn(@tunnel_command)
12
+ end
13
+
14
+ def close!
15
+ puts "Closing tunnel with process pid ##{@process_pid}: #{@tunnel_command}".informational
16
+ ::Process.kill("HUP", @process_pid)
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,40 @@
1
+ require 'openssl'
2
+
3
+ # Adapted from https://stackoverflow.com/questions/39033577/opensslcipherciphererror-wrong-final-block-length
4
+
5
+ class String
6
+
7
+ ALGORITHM = 'AES-256-ECB'
8
+
9
+ def encrypt(key)
10
+ begin
11
+ cipher = OpenSSL::Cipher.new(ALGORITHM)
12
+ cipher.encrypt()
13
+ cipher.key = key.as_256_bit_key
14
+ crypt = cipher.update(self) + cipher.final()
15
+ crypt_string = (Base64.encode64(crypt))
16
+ return crypt_string
17
+ rescue Exception => e
18
+ puts "Failed to encrypt: #{e.message}"
19
+ end
20
+ end
21
+
22
+ def decrypt(key)
23
+ begin
24
+ cipher = OpenSSL::Cipher.new(ALGORITHM)
25
+ cipher.decrypt()
26
+ cipher.key = key.as_256_bit_key
27
+ tempkey = Base64.decode64(self)
28
+ crypt = cipher.update(tempkey)
29
+ crypt << cipher.final()
30
+ return crypt
31
+ rescue Exception => e
32
+ raise ::Bcome::Exception::InvalidMetaDataEncryptionKey.new
33
+ end
34
+ end
35
+
36
+ def as_256_bit_key
37
+ ::Digest::SHA256.digest self
38
+ end
39
+
40
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bcome
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Guillaume Roderick (Webzakimbo)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-03 00:00:00.000000000 Z
11
+ date: 2018-06-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -163,6 +163,7 @@ files:
163
163
  - lib/objects/driver/bucket.rb
164
164
  - lib/objects/driver/ec2.rb
165
165
  - lib/objects/driver/static.rb
166
+ - lib/objects/encryptor.rb
166
167
  - lib/objects/exception/argument_error_invoking_method_from_command_line.rb
167
168
  - lib/objects/exception/base.rb
168
169
  - lib/objects/exception/can_only_subselect_on_inventory.rb
@@ -186,6 +187,7 @@ files:
186
187
  - lib/objects/exception/invalid_machines_cache_config.rb
187
188
  - lib/objects/exception/invalid_matcher_query.rb
188
189
  - lib/objects/exception/invalid_meta_data_config.rb
190
+ - lib/objects/exception/invalid_metadata_encryption_key.rb
189
191
  - lib/objects/exception/invalid_network_config.rb
190
192
  - lib/objects/exception/invalid_network_driver_type.rb
191
193
  - lib/objects/exception/invalid_proxy_config.rb
@@ -264,9 +266,11 @@ files:
264
266
  - lib/objects/ssh/driver.rb
265
267
  - lib/objects/ssh/proxy_data.rb
266
268
  - lib/objects/ssh/script_exec.rb
269
+ - lib/objects/ssh/tunnel/local_port_forward.rb
267
270
  - lib/objects/system/local.rb
268
271
  - lib/objects/workspace.rb
269
272
  - patches/irb.rb
273
+ - patches/string-encrypt.rb
270
274
  - patches/string.rb
271
275
  - patches/string_stylesheet.rb
272
276
  homepage: https://github.com/webzakimbo/bcome-kontrol