heirloom 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
data/.rvmrc CHANGED
@@ -1 +1 @@
1
- rvm use ruby-1.9.3-p194@heirloom --create
1
+ rvm use ruby-1.9.3-p327@heirloom --create
data/CHANGELOG CHANGED
@@ -1,3 +1,10 @@
1
+ ## 0.10.0:
2
+
3
+ * Confirm tar and gpg executables are present when necessary
4
+ * Switch to gpg for encryption
5
+ * S3 key names will have gpg appended when encrypted
6
+ * Switch rvm to 1.9.3-p327
7
+
1
8
  ## 0.9.0:
2
9
 
3
10
  * Updated tempfile logic to fix GC issues.
data/README.md CHANGED
@@ -22,7 +22,7 @@ Install the gem
22
22
  gem install heirloom --no-ri --no-rdoc
23
23
  ```
24
24
 
25
- To get started copy the sample below into ~/.heirloom.yml and update the specified fields.
25
+ To get started, copy the sample below to ~/.heirloom.yml and update the specified fields.
26
26
 
27
27
  ```
28
28
  aws:
@@ -31,16 +31,6 @@ aws:
31
31
  metadata_region: us-west-1
32
32
  ```
33
33
 
34
- Proxy Support
35
- -------------
36
-
37
- Heirloom supports accessing AWS API endpoint throught a proxy. This can be set via the http_proxy and https_proxy variables.
38
-
39
- ```
40
- export http_proxy=http://1.2.3.4:80
41
- export http_proxys=http://1.2.3.4:80
42
- ```
43
-
44
34
  Documentation
45
35
  -------------
46
36
 
data/lib/heirloom.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require "heirloom/utils"
2
+
1
3
  require "heirloom/acl"
2
4
  require "heirloom/archive"
3
5
  require "heirloom/aws"
@@ -11,20 +11,19 @@ module Heirloom
11
11
  end
12
12
 
13
13
  def allow_read_access_from_accounts(args)
14
- bucket = args[:bucket]
15
- key_name = args[:key_name]
14
+ bucket = args[:bucket]
15
+ key_name = args[:key_name]
16
16
  key_folder = args[:key_folder]
17
- accounts = args[:accounts]
17
+ accounts = args[:accounts]
18
18
 
19
- key = "#{key_folder}/#{key_name}.tar.gz"
19
+ key = "#{key_folder}/#{key_name}"
20
20
 
21
21
  current_acls = s3.get_bucket_acl bucket
22
+ name = current_acls['Owner']['Name']
23
+ id = current_acls['Owner']['ID']
22
24
 
23
- name = current_acls['Owner']['Name']
24
- id = current_acls['Owner']['ID']
25
-
26
- grants = build_bucket_grants :id => id,
27
- :name => name,
25
+ grants = build_bucket_grants :id => id,
26
+ :name => name,
28
27
  :accounts => accounts
29
28
 
30
29
  accounts.each do |a|
@@ -17,13 +17,15 @@ module Heirloom
17
17
 
18
18
  @logger.info "Authorizing #{@accounts.join(', ')}."
19
19
 
20
+ key_name = reader.key_name
21
+
20
22
  regions.each do |region|
21
23
  bucket = reader.get_bucket :region => region
22
24
 
23
25
  s3_acl = ACL::S3.new :config => @config,
24
26
  :region => region
25
27
 
26
- s3_acl.allow_read_access_from_accounts :key_name => @id,
28
+ s3_acl.allow_read_access_from_accounts :key_name => key_name,
27
29
  :key_folder => @name,
28
30
  :accounts => @accounts,
29
31
  :bucket => bucket
@@ -18,15 +18,15 @@ module Heirloom
18
18
  regions.each do |region|
19
19
  bucket = reader.get_bucket :region => region
20
20
 
21
- key = "#{@id}.tar.gz"
21
+ key_name = reader.key_name
22
22
 
23
23
  if bucket
24
- @logger.debug "Destroying 's3://#{bucket}/#{@name}/#{key}'."
24
+ @logger.debug "Destroying 's3://#{bucket}/#{@name}/#{key_name}'."
25
25
 
26
26
  s3_destroyer = Destroyer::S3.new :config => @config,
27
27
  :region => region
28
28
 
29
- s3_destroyer.destroy_file :key_name => key,
29
+ s3_destroyer.destroy_file :key_name => key_name,
30
30
  :bucket => bucket,
31
31
  :key_folder => @name
32
32
  end
@@ -12,8 +12,8 @@ module Heirloom
12
12
  def download(args)
13
13
  @region = args[:region]
14
14
  @bucket_prefix = args[:bucket_prefix]
15
+ @secret = args[:secret]
15
16
  extract = args[:extract]
16
- secret = args[:secret]
17
17
  output = args[:output] ||= './'
18
18
 
19
19
  @logger.info "Downloading s3://#{bucket}/#{key} from #{@region}."
@@ -28,7 +28,7 @@ module Heirloom
28
28
  return false unless raw_archive
29
29
 
30
30
  archive = cipher_data.decrypt_data :data => raw_archive,
31
- :secret => secret
31
+ :secret => @secret
32
32
 
33
33
  return false unless archive
34
34
 
@@ -44,12 +44,12 @@ module Heirloom
44
44
 
45
45
  private
46
46
 
47
- def file
47
+ def file
48
48
  "#{@id}.tar.gz"
49
49
  end
50
50
 
51
51
  def key
52
- "#{@name}/#{file}"
52
+ @secret ? "#{@name}/#{@id}.tar.gz.gpg" : "#{@name}/#{@id}.tar.gz"
53
53
  end
54
54
 
55
55
  def bucket
@@ -4,9 +4,9 @@ module Heirloom
4
4
 
5
5
  def initialize(args)
6
6
  @config = args[:config]
7
- @name = args[:name]
7
+ @name = args[:name]
8
+ @id = args[:id]
8
9
  @domain = "heirloom_#{@name}"
9
- @id = args[:id]
10
10
  @logger = @config.logger
11
11
  end
12
12
 
@@ -66,21 +66,31 @@ module Heirloom
66
66
  end
67
67
  end
68
68
 
69
+ def key_name
70
+ encrypted? ? "#{@id}.tar.gz.gpg" : "#{@id}.tar.gz"
71
+ end
72
+
69
73
  private
70
74
 
75
+ def encrypted?
76
+ show['encrypted'] == 'true'
77
+ end
78
+
71
79
  def domain_exists?
72
80
  sdb.domain_exists? @domain
73
81
  end
74
82
 
75
83
  def get_url(args)
84
+ region = args[:region]
85
+
76
86
  return nil unless exists?
77
- @logger.debug "Looking for #{args[:region]} endpoint for #{@id}"
78
- url = "#{args[:region]}-s3-url"
87
+ @logger.debug "Looking for #{region} endpoint for #{@id}"
88
+ url = "#{region}-s3-url"
79
89
  if show[url]
80
90
  @logger.debug "Found #{url} for #{@id}."
81
91
  show[url]
82
92
  else
83
- @logger.debug "#{args[:region]} endpoint for #{@id} not found."
93
+ @logger.debug "#{region} endpoint for #{@id} not found."
84
94
  nil
85
95
  end
86
96
  end
@@ -4,16 +4,19 @@ module Heirloom
4
4
 
5
5
  def initialize(args)
6
6
  @config = args[:config]
7
- @name = args[:name]
8
- @id = args[:id]
7
+ @name = args[:name]
8
+ @id = args[:id]
9
9
  @logger = @config.logger
10
10
  end
11
11
 
12
12
  def upload(args)
13
- heirloom_file = args[:file]
14
- bucket_prefix = args[:bucket_prefix]
15
- regions = args[:regions]
13
+ heirloom_file = args[:file]
14
+ bucket_prefix = args[:bucket_prefix]
15
+ regions = args[:regions]
16
16
  public_readable = args[:public_readable]
17
+ secret = args[:secret]
18
+
19
+ key_name = secret ? "#{@id}.tar.gz.gpg" : "#{@id}.tar.gz"
17
20
 
18
21
  regions.each do |region|
19
22
  bucket = "#{bucket_prefix}-#{region}"
@@ -26,13 +29,14 @@ module Heirloom
26
29
  :file => heirloom_file,
27
30
  :id => @id,
28
31
  :key_folder => @name,
29
- :key_name => "#{@id}.tar.gz",
32
+ :key_name => key_name,
30
33
  :name => @name,
31
34
  :public_readable => public_readable
32
35
 
33
- s3_uploader.add_endpoint_attributes :bucket => bucket,
34
- :id => @id,
35
- :name => @name
36
+ s3_uploader.add_endpoint_attributes :bucket => bucket,
37
+ :id => @id,
38
+ :name => @name,
39
+ :key_name => key_name
36
40
  end
37
41
  @logger.info "Upload complete."
38
42
  end
@@ -1,3 +1,5 @@
1
+ require 'tempfile'
2
+
1
3
  module Heirloom
2
4
  class Writer
3
5
 
@@ -1,2 +1,4 @@
1
+ require 'heirloom/cipher/shared'
2
+
1
3
  require 'heirloom/cipher/data'
2
4
  require 'heirloom/cipher/file'
@@ -1,36 +1,54 @@
1
- require 'openssl'
1
+ require 'tempfile'
2
2
 
3
3
  module Heirloom
4
4
  module Cipher
5
5
  class Data
6
6
 
7
+ include Heirloom::Cipher::Shared
8
+
7
9
  def initialize(args)
8
10
  @config = args[:config]
9
11
  @logger = @config.logger
10
12
  end
11
13
 
12
14
  def decrypt_data(args)
13
- data = args[:data]
14
- secret = args[:secret]
15
-
16
- return data unless args[:secret]
17
-
18
- @logger.info "Secret provided. Decrypting Heirloom."
19
-
20
- @aes = OpenSSL::Cipher::AES256.new(:CBC)
21
- @aes.decrypt
22
- @aes.key = Digest::SHA256.hexdigest secret
23
- @aes.iv = data.slice!(0,16)
24
- begin
25
- @aes.update(data) + @aes.final
26
- rescue OpenSSL::Cipher::CipherError => e
27
- if e.message == 'wrong final block length'
28
- @logger.error 'This Heirloom does not appear to be encrypted.'
29
- end
30
- @logger.error "Unable to decrypt Heirloom: '#{e.message}'"
15
+ @data = args[:data]
16
+ @secret = args[:secret]
17
+
18
+ return @data unless args[:secret]
19
+ return false unless gpg_in_path?
20
+
21
+ @encrypted_file = Tempfile.new('archive.tar.gz.gpg')
22
+ @decrypted_file = Tempfile.new('archive.tar.gz')
23
+
24
+ ::File.open(@encrypted_file, 'w') { |f| f.write @data }
25
+
26
+ decrypt
27
+ end
28
+
29
+ private
30
+
31
+ def decrypt
32
+ @logger.info "Secret provided. Decrypting with: '#{command}'"
33
+ output = `#{command(@secret)}`
34
+ @logger.debug "Decryption output: '#{output}'"
35
+
36
+ if $?.success?
37
+ @decrypted_file.read
38
+ else
39
+ @logger.error "Decryption failed with output: '#{output}'"
31
40
  false
32
41
  end
33
42
  end
43
+
44
+ def command(secret='XXXXXXXX')
45
+ "gpg --batch --yes --cipher-algo AES256 --passphrase #{secret} --output #{@decrypted_file.path} #{@encrypted_file.path} 2>&1"
46
+ end
47
+
48
+ def logger
49
+ @logger
50
+ end
51
+
34
52
  end
35
53
  end
36
54
  end
@@ -1,4 +1,3 @@
1
- require 'openssl'
2
1
  require 'tempfile'
3
2
  require 'fileutils'
4
3
 
@@ -6,45 +5,47 @@ module Heirloom
6
5
  module Cipher
7
6
  class File
8
7
 
8
+ include Heirloom::Cipher::Shared
9
+
9
10
  def initialize(args)
10
11
  @config = args[:config]
11
- @aes = OpenSSL::Cipher::AES256.new(:CBC)
12
+ @logger = @config.logger
12
13
  end
13
14
 
14
15
  def encrypt_file(args)
15
16
  @file = args[:file]
17
+ @secret = args[:secret]
16
18
  @encrypted_file = Tempfile.new('archive.tar.gz.enc')
17
- secret = args[:secret]
18
- iv = @aes.random_iv
19
-
20
- @aes.encrypt
21
- @aes.iv = iv
22
- @aes.key = Digest::SHA256.hexdigest secret
23
-
24
- # Need to refactor to be less complex
25
- # Additionally tests to do fully cover logic
26
- ::File.open(@encrypted_file,'w') do |enc|
27
- enc << iv
28
- ::File.open(@file) do |f|
29
- loop do
30
- r = f.read(4096)
31
- break unless r
32
- enc << @aes.update(r)
33
- end
34
- end
35
- enc << @aes.final
36
- end
19
+
20
+ return false unless gpg_in_path?
21
+ return false unless encrypt
37
22
 
38
23
  replace_file
39
24
  end
40
25
 
41
26
  private
42
27
 
28
+ def encrypt
29
+ @logger.info "Encrypting with: '#{command}'"
30
+ output = `#{command(@secret)}`
31
+ @logger.debug "Encryption output: '#{output}'"
32
+ @logger.error "Encryption failed with output: '#{output}'" unless $?.success?
33
+ $?.success?
34
+ end
35
+
36
+ def command(secret="XXXXXXXX")
37
+ "gpg --batch --yes -c --cipher-algo AES256 --passphrase #{secret} --output #{@encrypted_file.path} #{@file} 2>&1"
38
+ end
39
+
43
40
  def replace_file
44
41
  FileUtils.mv @encrypted_file.path, @file
45
42
  @encrypted_file.close!
46
43
  end
47
44
 
45
+ def logger
46
+ @logger
47
+ end
48
+
48
49
  end
49
50
  end
50
51
  end
@@ -0,0 +1,19 @@
1
+ require 'tempfile'
2
+
3
+ module Heirloom
4
+ module Cipher
5
+ module Shared
6
+
7
+ include Heirloom::Utils::File
8
+
9
+ def gpg_in_path?
10
+ unless which('gpg')
11
+ @logger.error "gpg not found in path."
12
+ return false
13
+ end
14
+ true
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -62,7 +62,8 @@ module Heirloom
62
62
  @archive.upload :bucket_prefix => @bucket_prefix,
63
63
  :regions => @regions,
64
64
  :public_readable => @opts[:public],
65
- :file => @file.path
65
+ :file => @file.path,
66
+ :secret => secret
66
67
 
67
68
  @file.close!
68
69
  end
@@ -2,6 +2,8 @@ module Heirloom
2
2
 
3
3
  class Directory
4
4
 
5
+ include Heirloom::Utils::File
6
+
5
7
  def initialize(args)
6
8
  @config = args[:config]
7
9
  @exclude = args[:exclude]
@@ -25,6 +27,7 @@ module Heirloom
25
27
  private
26
28
 
27
29
  def build_archive
30
+ return false unless tar_in_path?
28
31
  command = "cd #{@path} && tar czf #{@file} #{files_to_pack}"
29
32
  @logger.info "Archiving with: `#{command}`"
30
33
  output = `#{command}`
@@ -39,8 +42,16 @@ module Heirloom
39
42
  :secret => @secret
40
43
  end
41
44
 
45
+ def tar_in_path?
46
+ unless which('tar')
47
+ @logger.error "tar not found in path."
48
+ return false
49
+ end
50
+ true
51
+ end
52
+
42
53
  def files_to_pack
43
- (Dir.entries(@path) - ['.', '..'] - @exclude).map do |file|
54
+ @files_to_pack ||= (Dir.entries(@path) - ['.', '..'] - @exclude).map do |file|
44
55
  "'#{file}'"
45
56
  end.join(' ')
46
57
  end