CloudyScripts 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Matthias Jung
1
+ Copyright (c) 2010 Matthias Jung
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
4
 
data/README.rdoc ADDED
@@ -0,0 +1,22 @@
1
+ # =About
2
+ # CloudyScripts is a library that implements tasks that support common
3
+ # usecases on Cloud Computing Infrastructures (such as Amazon EC2 or Rackspace).
4
+ # It aims to facilitate the implementation of usecases that are not directly
5
+ # available via the providers' API (e.g. like encrypting storage,
6
+ # migrating instances betweem accounts, activating HTTPS). The scripts typically
7
+ # use the provider APIs plus remote access to command-line tools installed on
8
+ # the instances themselves.
9
+ #
10
+ # =Installation and Usage
11
+ # ==Installation
12
+ # <tt>gem install CloudyScripts</tt>
13
+ #
14
+ # ==Usage
15
+ # All scripts are available under /lib/scripts/<provider>
16
+ # They are initialized with a set of parameters and return a well-define
17
+ # set of return values.
18
+ #
19
+ # =Scripts
20
+ # Here are the scripts implemented so far:
21
+ # * #DmEncrypt (encrypt Amazon EBS Storage using dm-encrypt)
22
+ #
data/Rakefile CHANGED
@@ -12,9 +12,9 @@ require 'rake/testtask'
12
12
 
13
13
  spec = Gem::Specification.new do |s|
14
14
  s.name = 'CloudyScripts'
15
- s.version = '0.0.2'
15
+ s.version = '0.0.3'
16
16
  s.has_rdoc = true
17
- s.extra_rdoc_files = ['README', 'LICENSE']
17
+ s.extra_rdoc_files = ['README.rdoc', 'LICENSE']
18
18
  s.summary = 'Scripts to facilitate programming for infrastructure clouds.'
19
19
  s.description = s.summary
20
20
  s.homepage = "http://elastic-security.com"
@@ -22,7 +22,7 @@ spec = Gem::Specification.new do |s|
22
22
  s.author = 'Matthias Jung'
23
23
  s.email = 'matthias.jung@gmail.com'
24
24
  # s.executables = ['your_executable_here']
25
- s.files = %w(LICENSE README Rakefile) + Dir.glob("{bin,lib,spec}/**/*")
25
+ s.files = %w(LICENSE README.rdoc Rakefile) + Dir.glob("{bin,lib,spec}/**/*")
26
26
  s.require_path = "lib"
27
27
  s.bindir = "bin"
28
28
  s.has_rdoc = true
data/lib/cloudyscripts.rb CHANGED
@@ -2,6 +2,25 @@ require 'rubygems'
2
2
  require 'net/ssh'
3
3
  require 'AWS'
4
4
 
5
+ # =About
6
+ # CloudyScripts is a library that implements tasks that support common
7
+ # usecases on Cloud Computing Infrastructures (such as Amazon EC2 or Rackspace).
8
+ # It aims to facilitate the implementation of usecases that are not directly
9
+ # available via the providers' API (e.g. like encrypting storage,
10
+ # migrating instances betweem accounts, activating HTTPS). The scripts typically
11
+ # use the provider APIs plus remote access to command-line tools installed on
12
+ # the instances themselves.
5
13
  #
6
- #
7
- #
14
+ # =Installation and Usage
15
+ # ===Installation
16
+ # <tt>gem install CloudyScripts</tt>
17
+ #
18
+ # ===Usage
19
+ # All scripts are available under /lib/scripts/<em>provider</em>>
20
+ #
21
+ # ===Scripts
22
+ # Here are the scripts implemented so far:
23
+ # * #Scripts::EC2::DmEncrypt (encrypt Amazon EBS Storage using dm-encrypt)
24
+ #
25
+ class CloudyScripts
26
+ end
@@ -1,16 +1,23 @@
1
- require 'lib/ssh_api'
1
+ require 'help/remote_command_handler'
2
+
3
+ # This class implements helper methods for Dm Encryption
4
+ # (see #Scripts::EC2::DmEncrypt)
2
5
 
3
6
  class DmCryptHelper
4
7
 
8
+ # Passes an remote command handler object
9
+ # (see #Help::RemoteCommandHandler)
5
10
  def set_ssh(ssh_session)
6
11
  @ssh_session = ssh_session
7
12
  end
8
13
 
14
+ # Installs the dm-crypt tools (if not yet done)
9
15
  def install()
10
16
  #TODO: dm-crypt seems to be installed automatically
11
17
  true
12
18
  end
13
19
 
20
+ # Checks if the dm-crypt tool is installed (true/false)
14
21
  def tools_installed?()
15
22
  @ssh_session.exec! "which dmsetup" do |ch, stream, data|
16
23
  if stream == :stderr
@@ -26,9 +33,15 @@ class DmCryptHelper
26
33
  true
27
34
  end
28
35
 
36
+ # Encrypts the device and mounting it using dm-crypt tools.
37
+ # Params
38
+ # * name: name of the virtual volume
39
+ # * password: paraphrase to be used for encryption
40
+ # * device: device to be encrypted
41
+ # * path: path to which the encrypted device is mounted
29
42
  def encrypt_storage(name, password, device, path)
30
43
  # first: check if a file in /dev/mapper exists
31
- if SshApi.file_exists?(@ssh_session, "/dev/mapper/dm-#{name}")
44
+ if RemoteCommandHandler.file_exists?(@ssh_session, "/dev/mapper/dm-#{name}")
32
45
  mapper_exists = true
33
46
  else
34
47
  mapper_exists = false
@@ -95,7 +108,7 @@ class DmCryptHelper
95
108
  exec_string = "vgcreate vg-#{name} /dev/mapper/dm-#{name}"
96
109
  puts "vg_exists == false; execute #{exec_string}"
97
110
  @ssh_session.exec! exec_string do |ch, stream, data|
98
- if stream == :stderr && !data.blank?
111
+ if stream == :stderr && data != nil
99
112
  err = "Failed during creation of volume group"
100
113
  puts "#{err}: #{data}"
101
114
  raise Exception.new(err)
@@ -105,7 +118,7 @@ class DmCryptHelper
105
118
  exec_string = "lvcreate -n lv-#{name} -l100%FREE vg-#{name}"
106
119
  puts "execute #{exec_string}"
107
120
  @ssh_session.exec! exec_string do |ch, stream, data|
108
- if stream == :stderr && !data.blank?
121
+ if stream == :stderr && data != nil
109
122
  err = "Failed during creation of logical volume"
110
123
  puts "#{err}: #{data}"
111
124
  raise Exception.new(err)
@@ -114,13 +127,13 @@ class DmCryptHelper
114
127
  exec_string = "mkfs -t ext3 /dev/vg-#{name}/lv-#{name}"
115
128
  puts "execute #{exec_string}"
116
129
  @ssh_session.exec! exec_string #do |ch, stream, data|
117
- #if stream == :stderr && !data.blank?
130
+ #if stream == :stderr && data != nil
118
131
  #err = "Failed during creation of file-system"
119
132
  #puts "#{err}: #{data}"
120
133
  #raise Exception.new(err)
121
134
  #end
122
135
  #end
123
- if !SshApi.file_exists?(@ssh_session,"/dev/vg-#{name}/lv-#{name}")
136
+ if !RemoteCommandHandler.file_exists?(@ssh_session,"/dev/vg-#{name}/lv-#{name}")
124
137
  err = "Missing file: /dev/vg-#{name}/lv-#{name}"
125
138
  raise Exception.new(err)
126
139
  end
@@ -128,7 +141,7 @@ class DmCryptHelper
128
141
  exec_string = "/sbin/vgchange -a y vg-#{name}"
129
142
  puts "vg_exists == true; execute #{exec_string}"
130
143
  @ssh_session.exec! exec_string do |ch, stream, data| #TODO: the right size instead L2G!
131
- if stream == :stderr && !data.blank?
144
+ if stream == :stderr && data != nil
132
145
  err = "Failed during re-activation of volume group"
133
146
  puts "#{err}: #{data}"
134
147
  raise Exception.new(err)
@@ -137,10 +150,12 @@ class DmCryptHelper
137
150
  end
138
151
  end
139
152
 
153
+ # Check if the storage is encrypted (not yet implemented).
140
154
  def test_storage_encryption(password, mount_point, path)
141
155
  raise Exception.new("not yet implemented")
142
156
  end
143
157
 
158
+ # Undo encryption for the volume specified by name and path
144
159
  def undo_encryption(name, path)
145
160
  exec_string = "umount #{path}"
146
161
  puts "going to execute #{exec_string}"
@@ -1,11 +1,13 @@
1
1
  require 'rubygems'
2
2
  require 'net/ssh'
3
3
 
4
+ # Provides methods to be executed via ssh to remote instances.
4
5
  class RemoteCommandHandler
5
6
  def initialize
6
7
  @crypto = DmCryptHelper.new #TODO: instantiate helpers for different tools
7
8
  end
8
9
 
10
+ # Check if the path/file specified exists
9
11
  def self.file_exists?(ssh_session, path)
10
12
  result = true
11
13
  ssh_session.exec!("ls #{path}") do |ch, stream, data|
@@ -15,28 +17,37 @@ class RemoteCommandHandler
15
17
  end
16
18
  result
17
19
  end
18
-
20
+
21
+ # Connect to the machine as root using a keyfile.
22
+ # Params:
23
+ # * ip: ip address of the machine to connect to
24
+ # * keyfile: path of the keyfile to be used for authentication
19
25
  def connect(ip, keyfile)
20
26
  @ssh_session = Net::SSH.start(ip, 'root', :keys => [keyfile])
21
27
  @crypto.set_ssh(@ssh_session)
22
28
  end
23
29
 
30
+ # Disconnect the current handler
24
31
  def disconnect
25
32
  @ssh_session.close
26
33
  end
27
34
 
35
+ # Installs the software package specified.
28
36
  def install(software_package)
29
37
  @crypto.install()
30
38
  end
31
39
 
40
+ # Checks if the software package specified is installed.
32
41
  def tools_installed?(software_package)
33
42
  @crypto.tools_installed?
34
43
  end
35
44
 
45
+ # Encrypt the storage (using the crypto-helper used, e.g. #Help::DmCryptHelper)
36
46
  def encrypt_storage(name, password, device, path)
37
47
  @crypto.encrypt_storage(name, password, device, path)
38
48
  end
39
49
 
50
+ # Check if the storage is encrypted (using the crypto-helper used, e.g. #Help::DmCryptHelper)
40
51
  def storage_encrypted?(password, device, path)
41
52
  drive_mounted?(path) #TODO: must at least also check the name
42
53
  end
@@ -79,6 +90,7 @@ class RemoteCommandHandler
79
90
  drive_mounted
80
91
  end
81
92
 
93
+ # Activates the encrypted volume, i.e. mounts it if not yet done.
82
94
  def activate_encrypted_volume(name, path)
83
95
  drive_mounted = drive_mounted?(path)
84
96
  puts "drive #{path} mounted? #{drive_mounted}"
@@ -87,7 +99,7 @@ class RemoteCommandHandler
87
99
  exec_string = "mount /dev/vg-#{name}/lv-#{name} #{path}"
88
100
  puts "drive not mounted; execute: #{exec_string}"
89
101
  @ssh_session.exec! "mount /dev/vg-#{name}/lv-#{name} #{path}" do |ch, stream, data|
90
- if stream == :stderr && !data.blank?
102
+ if stream == :stderr && data != nil
91
103
  err = "Failed during mounting encrypted device"
92
104
  puts "#{err}: #{data}"
93
105
  puts "mount /dev/vg-#{name}/lv-#{name} #{path}"
@@ -97,10 +109,12 @@ class RemoteCommandHandler
97
109
  end
98
110
  end
99
111
 
112
+ # Unconfigure the storage (using the crypto-helper used, e.g. #Help::DmCryptHelper)
100
113
  def undo_encryption(name, path)
101
114
  @crypto.undo_encryption(name, path)
102
115
  end
103
116
 
117
+ # Unmount the specified path.
104
118
  def umount(path)
105
119
  exec_string = "umount #{path}"
106
120
  puts "going to execute #{exec_string}"
@@ -51,6 +51,7 @@ class ScriptExecutionState
51
51
  s.sub(/.*\:\:/,'')
52
52
  end
53
53
 
54
+ # Standard state reached when an exception occurs.
54
55
  class FailedState < ScriptExecutionState
55
56
  attr_accessor :failure_reason, :from_state
56
57
  def initialize(context, failure_reason, from_state)
@@ -1,23 +1,28 @@
1
1
  require "help/script_execution_state"
2
2
  require "scripts/ec2/ec2_script"
3
+ require "help/remote_command_handler"
4
+ require "help/dm_crypt_helper"
5
+ require "AWS"
3
6
 
4
- # Encrypts an EC2 Storage
7
+ # Script to Encrypt an EC2 Storage (aka Elastic Block Storage)
8
+ #
5
9
  class DmEncrypt < Ec2Script
6
10
  def initialize(input_params)
7
11
  super(input_params)
8
12
  end
9
13
 
10
14
  # Input parameters
11
- # aws_access_key => the Amazon AWS Access Key (see Your Account -> Security Credentials)
12
- # aws_secret_key => the Amazon AWS Secret Key
13
- # ip_address => IP Address of the machine to connect to
14
- # ssh_key_file => Path of the keyfile used to connect to the machine
15
- # device => Path of the device to encrypt
16
- # device_name => Name of the Device to encrypt
17
- # storage_path => Path on which the encrypted device is mounted
18
- # remote_command_handler => object that allows to connect via ssh and execute commands
19
- # ec2_api_handler => object that allows to access the EC2 API
20
- # password => password used for encryption
15
+ # * aws_access_key => the Amazon AWS Access Key (see Your Account -> Security Credentials)
16
+ # * aws_secret_key => the Amazon AWS Secret Key
17
+ # * ip_address => IP Address of the machine to connect to
18
+ # * ssh_key_file => Path of the keyfile used to connect to the machine
19
+ # * device => Path of the device to encrypt
20
+ # * device_name => Name of the Device to encrypt
21
+ # * storage_path => Path on which the encrypted device is mounted
22
+ # * paraphrase => paraphrase used for encryption
23
+ # * remote_command_handler => object that allows to connect via ssh and execute commands (optional)
24
+ # * ec2_api_handler => object that allows to access the EC2 API (optional)
25
+ # * ec2_api_server => server to connect to (option, default is us-east-1.ec2.amazonaws.com)
21
26
  #
22
27
  def initialize(input_params)
23
28
  super(input_params)
@@ -27,6 +32,18 @@ class DmEncrypt < Ec2Script
27
32
  # Executes the script.
28
33
  def start_script
29
34
  begin
35
+ # optional parameters and initialization
36
+ if @input_params[:ec2_api_server] == nil
37
+ @input_params[:ec2_api_server] = "us-east-1.ec2.amazonaws.com"
38
+ end
39
+ if @input_params[:remote_command_handler] == nil
40
+ @input_params[:remote_command_handler] = RemoteCommandHandler.new
41
+ end
42
+ if @input_params[:ec2_api_handler] == nil
43
+ @input_params[:ec2_api_handler] = AWS::EC2::Base.new(:access_key_id => @input_params[:aws_access_key],
44
+ :secret_access_key => @input_params[:aws_secret_key], :server => @input_params[:ec2_api_server])
45
+ end
46
+ # start state machine
30
47
  current_state = DmEncryptState.load_state(@input_params)
31
48
  end_state = current_state.start_state_machine()
32
49
  if end_state.failed?
@@ -40,10 +57,10 @@ class DmEncrypt < Ec2Script
40
57
  puts "exception during encryption: #{e}"
41
58
  puts e.backtrace.join("\n")
42
59
  err = e.to_s
43
- err += " (in #{current_state.end_state.to_s})" unless current_state.blank?
60
+ err += " (in #{current_state.end_state.to_s})" unless current_state == nil
44
61
  @result[:failed] = true
45
62
  @result[:failure_reason] = err
46
- @result[:end_state] = current_state.end_state unless current_state.blank?
63
+ @result[:end_state] = current_state.end_state unless current_state == nil
47
64
  ensure
48
65
  begin
49
66
  @input_params[:remote_command_handler].disconnect
@@ -66,7 +83,6 @@ class DmEncrypt < Ec2Script
66
83
  private
67
84
 
68
85
  # Here begins the state machine implementation
69
-
70
86
  class DmEncryptState < ScriptExecutionState
71
87
 
72
88
  def self.load_state(context)
@@ -135,7 +151,7 @@ class DmEncrypt < Ec2Script
135
151
  end
136
152
  #
137
153
  @context[:remote_command_handler].encrypt_storage(@context[:device_name],
138
- @context[:password], @context[:device], @context[:storage_path])
154
+ @context[:paraphrase], @context[:device], @context[:storage_path])
139
155
  VolumeCreatedState.new(@context)
140
156
  end
141
157
 
@@ -146,6 +162,7 @@ class DmEncrypt < Ec2Script
146
162
 
147
163
  end
148
164
 
165
+ # The encrypted Volume is created. Going to mount it.
149
166
  class VolumeCreatedState < DmEncryptState
150
167
  def enter
151
168
  mount_and_activate()
@@ -159,6 +176,7 @@ class DmEncrypt < Ec2Script
159
176
  end
160
177
  end
161
178
 
179
+ # The encrypted storages is mounted. Cleanup and done.
162
180
  class MountedAndActivatedState < DmEncryptState
163
181
  def enter
164
182
  cleanup()
@@ -1,12 +1,23 @@
1
1
  # Base class for any script on EC2.
2
2
  class Ec2Script
3
- # Input parameters
4
- # aws_access_key => the Amazon AWS Access Key (see Your Account -> Security Credentials)
5
- # aws_secret_key => the Amazon AWS Secret Key
6
- #
3
+ # Initialization. Common Input parameters:
4
+ # * aws_access_key => the Amazon AWS Access Key (see Your Account -> Security Credentials)
5
+ # * aws_secret_key => the Amazon AWS Secret Key
6
+ # Scripts may add specific key/value pairs.
7
7
  def initialize(input_params)
8
8
  @input_params = input_params
9
9
  end
10
10
 
11
+ # Return a hash of results. Common values are:
12
+ # * :done => is true when the script has terminated, otherwise false
13
+ # * :failed => is false when the script succeeded
14
+ # * :failure_reason => returns a failure reason (string)
15
+ # * :end_state => returns the state, in which the script terminated (#Help::ScriptExecutionState)
16
+ # Scripts may add specific key/value pairs.
17
+ # *
18
+ def get_execution_result
19
+ raise Exception.new("must be implemented")
20
+ end
21
+
11
22
  end
12
23
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: CloudyScripts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthias Jung
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-12 00:00:00 +01:00
12
+ date: 2009-12-18 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -39,11 +39,11 @@ executables: []
39
39
  extensions: []
40
40
 
41
41
  extra_rdoc_files:
42
- - README
42
+ - README.rdoc
43
43
  - LICENSE
44
44
  files:
45
45
  - LICENSE
46
- - README
46
+ - README.rdoc
47
47
  - Rakefile
48
48
  - lib/cloudyscripts.rb
49
49
  - lib/help/dm_crypt_helper.rb
data/README DELETED
@@ -1 +0,0 @@
1
- Scripts to facilitate programming for infrastructure clouds