hiera-crypt 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
data/bin/hiera-crypt CHANGED
@@ -1,3 +1,53 @@
1
- #!/bin/bash
1
+ #!/usr/bin/env ruby
2
2
 
3
- exec gpg --symmetric --cipher-algo AES256 "$@"
3
+ require 'optparse'
4
+ require_relative '../lib/passwordbox'
5
+
6
+ password = nil
7
+ input = '-'
8
+ output = '-'
9
+ mode = :encrypt
10
+
11
+ OptionParser.new do |opts|
12
+ opts.banner = "Usage: #{$0} [options] [FILE]"
13
+ opts.on('-f', '--password-file [FILE]', 'Read password from a file') do |f|
14
+ file_name = File.expand_path(f)
15
+ password = File.open(file_name, 'r').read.chomp
16
+ end
17
+ opts.on('-d', '--decrypt', 'Decrypt instead of encrypting') do |decrypt|
18
+ mode = :decrypt
19
+ end
20
+ opts.on('-o', '--output [FILE]', 'Write result to file instead of stdout') do |f|
21
+ output = f
22
+ end
23
+ end.parse!
24
+
25
+ input = ARGV.first if ARGV.length > 0
26
+
27
+ def read_password(prompt="Password: ")
28
+ `stty -echo`
29
+ STDERR.write(prompt)
30
+ STDERR.flush
31
+ STDIN.readline.chomp
32
+ rescue Interrupt
33
+ exit 1
34
+ ensure
35
+ STDERR.write("\n")
36
+ STDERR.flush
37
+ `stty echo`
38
+ end
39
+
40
+ password = read_password if password.nil?
41
+
42
+ crypto = PasswordBox.new(password)
43
+
44
+ in_file = input == '-' ? STDIN : File.open(input, 'r')
45
+
46
+ if mode == :encrypt
47
+ out = crypto.box(in_file.read, :base64)
48
+ elsif mode == :decrypt
49
+ out = crypto.open(in_file.read, :base64)
50
+ end
51
+
52
+ out_file = output == '-' ? STDOUT : File.open(output, 'w')
53
+ out_file.write(out)
data/hiera-crypt.gemspec CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "hiera-crypt"
7
- spec.version = "0.1"
7
+ spec.version = "0.2"
8
8
  spec.authors = ["Carl Jackson"]
9
9
  spec.email = ["carl@avtok.com"]
10
10
  spec.description = "Encrypted file backend for Hiera"
@@ -19,7 +19,8 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency "hiera", "~> 1.2.1"
22
- spec.add_dependency "gpgme", "~> 2.0.2"
22
+ spec.add_dependency "pbkdf2", "~> 0.1.0"
23
+ spec.add_dependency "rbnacl", "~> 1.1.0"
23
24
 
24
25
  spec.add_development_dependency "bundler", "~> 1.3"
25
26
  spec.add_development_dependency "rake"
@@ -5,13 +5,11 @@ class Hiera
5
5
  class Crypt_backend
6
6
  DEBUG_PREFIX = '[crypt backend]'
7
7
 
8
- def initialize()
8
+ def initialize(cache=nil)
9
9
  unless Hiera::Config.include?(:crypt)
10
10
  raise "Expected :crypt section in hiera.yaml"
11
11
  end
12
12
  conf = Hiera::Config[:crypt]
13
- unless conf.include?(:password) || conf.include?(:password_file)
14
- end
15
13
  password = if conf.include?(:password)
16
14
  conf[:password]
17
15
  elsif conf.include?(:password_file)
@@ -22,10 +20,10 @@ class Hiera
22
20
  raise "Expected either a :password or :password_file"
23
21
  end
24
22
 
25
- @cache = {}
23
+ @cache = cache || Filecache.new
26
24
 
27
- require 'gpgme'
28
- @crypto = GPGME::Crypto.new(:password => password)
25
+ require 'passwordbox'
26
+ @crypto = PasswordBox.new(password)
29
27
  debug("Loaded!")
30
28
  end
31
29
 
@@ -40,11 +38,13 @@ class Hiera
40
38
  Backend.datasources(scope, order_override) do |source|
41
39
  debug("Looking for data source #{source}")
42
40
 
43
- file = File.join(Backend.datadir(:crypt, scope), source, "#{key}.gpg")
41
+ file = File.join(Backend.datadir(:crypt, scope), source, "#{key}.crypt")
44
42
  debug("Examining file #{file}")
45
43
  next unless File.exist?(file)
46
44
 
47
- plaintext = decrypt(file)
45
+ plaintext = @cache.read(file, String) do |data|
46
+ @crypto.open(data, :base64)
47
+ end
48
48
 
49
49
  return plaintext if resolution_type == :priority
50
50
 
@@ -55,21 +55,6 @@ class Hiera
55
55
  end
56
56
 
57
57
  private
58
- def decrypt(file)
59
- stat = File.stat(f = File.new(file))
60
- info = {:inode => stat.ino, :mtime => stat.mtime, :size => stat.size}
61
- @cache.delete(file) if @cache[file] && @cache[file][:info] != info
62
-
63
- debug("Using cached value for #{file}") if @cache.include?(file)
64
-
65
- @cache[file] ||= {
66
- :contents => @crypto.decrypt(f).to_s,
67
- :info => info
68
- }
69
-
70
- @cache[file][:contents]
71
- end
72
-
73
58
  def debug(msg)
74
59
  Hiera.debug("#{DEBUG_PREFIX} #{msg}")
75
60
  end
@@ -0,0 +1,70 @@
1
+ require 'rbnacl'
2
+ require 'pbkdf2'
3
+ require 'forwardable'
4
+
5
+ # A SecretBox that (like RandomNonceBox) automatically generates a suitable
6
+ # nonce, but also which uses PBKDF2 to derive a password of the right length.
7
+ class PasswordBox < Crypto::SecretBox
8
+ DEFAULT_PBKDF2_ITERS = 5000
9
+
10
+ # Create a new PasswordBox
11
+ #
12
+ # @param password [String] A password of any length
13
+ def initialize(password)
14
+ @password = password
15
+ end
16
+
17
+ # Encrypts the message using a salted key derived from the given password, and
18
+ # a random nonce.
19
+ #
20
+ # @param message [String] The message to encrypt
21
+ # @param encoding [Symbol] Encoding for the returned ciphertext
22
+ #
23
+ # @return [String] The encrypted message
24
+ def box(message, encoding = :raw)
25
+ nonce = generate_nonce
26
+ salt, iters, @key = generate_key
27
+ ciphertext = super(nonce, message)
28
+ Crypto::Encoder[encoding].encode(nonce + salt + iters + ciphertext)
29
+ end
30
+ alias encrypt box
31
+
32
+ # Decrypts the message. Extracts both the encryption nonce and the salt from
33
+ # the message.
34
+ #
35
+ # @param enciphered_message [String] The message to decrypt
36
+ # @param encoding [Symbol] Encoding for the given ciphertext
37
+ #
38
+ # @raise [CryptoError] If the message has been tampered with.
39
+ #
40
+ # @return [String] The plaintext of the message
41
+ def open(enciphered_message, encoding = :raw)
42
+ decoded = Crypto::Encoder[encoding].decode(enciphered_message)
43
+ nonce, salt, iters, ciphertext = extract(decoded)
44
+ @key = generate_key(salt, iters).last
45
+ super(nonce, ciphertext)
46
+ end
47
+ alias decrypt open
48
+
49
+ private
50
+ def generate_nonce
51
+ Crypto::Random.random_bytes(nonce_bytes)
52
+ end
53
+ def generate_key(salt=nil, iters=DEFAULT_PBKDF2_ITERS)
54
+ salt ||= generate_nonce
55
+ key = PBKDF2.new(
56
+ :password => @password,
57
+ :salt => salt,
58
+ :iterations => iters,
59
+ :hash_function => :sha256,
60
+ :key_length => key_bytes
61
+ )
62
+ [salt, [iters].pack("N"), key.bin_string]
63
+ end
64
+ def extract(bytes)
65
+ nonce = bytes.slice!(0, nonce_bytes)
66
+ salt = bytes.slice!(0, nonce_bytes)
67
+ iters = bytes.slice!(0, 4).unpack("N").first
68
+ [nonce, salt, iters, bytes]
69
+ end
70
+ end
@@ -0,0 +1 @@
1
+ Eu5V2p42KnCo2kkWH4VPmXxdpkBUussPJQwX5WzW4sMD/OEIzTjO9GadWin2ZPc/AAATiJoMMuEM1xFefXzu9SdUyfpr0TAhsm4AjV6AysFt7WJifs85wLOl1AI1SDihZKdh9/i9EuQ6odCpmI4O2MTIA61aw+g/v0eAyF3Sz7eWRALpGNz1PF1lNs0xlilDMq1P+Yk9/QtdESzJe+VrnW4TvsV1dEemSa91ctzhonnaxy06zgbljKMEE8UD+j8UUqwHigOzyRvk+iA4tvnnYcR10jJD1KJZYkLvQr8i01MdYXV8OHQNutnoqxQrzAM9H+vi3p3BMDdsvXPswyO0Ym3bdfeXiTno5kMWL/SIw+Y7kmsT9fN5uU2935POO/lBkESZ2pw0AsJSw/P4C2SF5lEpPaSzHVDzMPru1+bg+vUYufDqgLhT1TDRkDs0maIXuS4MmH/PqpTL3/5JbsUT/CLICEdSrWSCclvNw8mA21+Nqx7e9smEc2gPK05C9vGx6o27/+7IsdxTsUmp6SaWIwaPQFbEfRZARScZfouCq6swost9VFw1TnCYZHcf0EfdqVqdGoMP3UcxboQBrKKM0Y3qC/Z2O1pKlpFFAVYj6ZFM9D/hnHRVI+2wB3dTx2FtGpEoBXg2mYK33uOI5uazmSy3Vz9q8a24EgTF5dwduJ2zlXmm6yidu96MxL3w0k3BuB4wbAH5cCW/E+Kkj7T/9c39tcIfKekrgPejna9ONnkWRnkfF2Abh/YiCa5F6omWVc+dBBxpWRyFjLj4/ywkcqi+O7gU40JYIyb8A32z8EIfHk/FeH0zTBbjctBxu9R3zWRxkWy5JzkgHYGuS1oB3IZ8Ki7YZVHDTWmk3Lrm5cP6k2G1SKkLD5G+CuUpSREiC0CSFqmnRTM7WCqYojlB72QZbT8qQffidXjj1dzx2u9Puui0pz4bUOnNNDUaoNbVigJn+H+HpLxrx5CBFByaN/jdKsavayPVxgiSQe0=
@@ -0,0 +1 @@
1
+ MTSajTxKc8nGJ+ZjwEM7uaCoOp8ZSIesT4HhRmcn29FWoyMioPrsz+aylaHdyCxwAAATiOuxY8cDs9uHEZmxmxnX4uMjNFhlQ2j2b9InSonhKeb6MAgE3uUH3bcCkwJ87tjlKeqjzAihD3cnFzcCU8V66gFQsUFa0uTM3olAgYvCe/tvWx4VEe4eO4/igBrcaBQ3JevA06TzRZPV9Ge2PEjr8EocZB2BcWCtjjzeOD/Plk60QaC59nlW3s1T6ZFyFMH/ufmv1MEqXH2dK7RKECRrbCsnp4DGO2SQMMfJoVF3pcGObtalJbf8Q1vM0RVQYZD4q0Yg2EjrXf2vqN12ufnzY9p8uP2P+efwD0xzCCk3BrYd7GbRXwnVbARC3e7mRoVlJfM9GUot1kqZb7rNdBFSD/RVTSoCbUjdqieN8E510SHJzQ121gGm/OVzHIorBZ5FoBgEBrQDzAq9gpNuKEj5c6LQHaXw37ix5DxkMpM3LOnzWb3OALNb2vx28BdIo3Q+uBg/hVDhfDJygDiZJqSHYcamegZLsquqpSyT65x/BDazkqkNKIl33jSnbNWpAyB5kkl+KpnoY0bBWvIdbgoqe4iolJPOEqQlzUeXUzUhh28vn9N/F8tIt7xQPhIHwsa5hmp6zJy7YZ0V2PNfbRcvfmeO4iYHZVGb9wmz9Pwe7DoC8ub80YCz9092NIya/Y3WcMJRF0mZCCe44elT1NgBe6wx9fz52gh9d/dmQPs73QrSoS5bahYh3BucqVs0xsp1YJ4VqGtf0oNpwNaEGVp0bwtFHq0Tdtg2OegGPiNUMiVjTMVLUqb5C6f8NwLDG4JwtqZQAUMYODgWRI1UTNRSEKQcLpzrG1OEwf3MucYOVPed82xmlekGgF5GXBZG/9qbuwBHGBjfYbf30+OUqV2PEIWxPQSN4p0GfPp1pFa/m4cM7WrxYROey3bfcLnvk//qMjE4tHxmaldJd4y1qwrr6CGTzl/UAuMfCss=
@@ -0,0 +1 @@
1
+ L531Q4wbmLbrjSHbvUKBs+3cGB+E6lYeTMVCLgKXoMW//E2+6vfjycj+62hKYg6wAAATiIY3gSySt20vPOZxypNd5QIYloZ1JisvMlxE1nLUT6rk9SMF+eW/CFl6IskduAVCh/+H4thRK3uFewGkFUvbRTzcyXcPpKPaezm+SfGDI5gyU0CNv2dAeSZGAQl9YzNiiTkUucbmu4lg5w4mXR4cM5+WdKh0Z9bpt4oBJvXz7RCez35IBQYCZv/iO/YjZ1XZTe4jbfJNWdXzKkQhXj8aANn4kGEUZeUB2L7HMGPMnDEDjCtG/yzgpHhrusM+uiXYfI65+Q5G2zWGUEEwmBkmAKGMeAfgP3MBFUNTH58yxxmZmcxRpE35vqIpGGlsh8QDJwNDr02jhKn1Ebopo77nPy3LXB1oIbAq1bEknTcMWJ9v4yvVwD8knxwKv4b0R4PpoA/vnAtMQGS8jjgS8JkTrh0mxrmHVI4aqXad3UFQ6VEt3OTxlewLYxSxPdv2g5IQhQLCt21nOtDeU/SGAHCsSW4E5zqAVH5sZbekYYES70Z4PiRXqRWYfNhIiiv+asH+Ed8ikvTpqHpvCS9vccADJG1YmN/m55CGt9nCkvMMTMSHrAE8huCAMWaMgOshJ2F5NNwnylxpcLrLsraNTLGP4YyRJYQGsBZdL8SdrR4HteP3yMYUY3KHp+0yCSjGXWMso5QNJVnTX/VAi4yoQh07mocJOOdS0ah93nsv2Z4zmYTeOHzoBaviG2ID6/kD/UHlNawCbb1su1vSg+tfp7EEzF+BZU06LyWXlgZ8Eux1MkmwN/sU8t40rxew5cVIhcE6gxxCyQenumWhCRJJuAT0ukZ0wJjVOdX6ApkDoVRX6t+ZSuLR5/egLIzu0pLdpJFOidSas8j+6Mib89+KWU1ZsNOJ/cyGtO095QEumX+meWIKqKLFBzz8abOLQc7Ces4nOH/EusjmgsJgjawgI997unuMTRhtYpFmoTSSt7Kt54483bIWLK93xYBVspLQAQHou94TvlNnbifsj25/eobwx0nNWbq8lsjbtEhmz3q+dR3s6p0j3tzP61uLaSqqSEDlkYmI2DIa8NsoivOJB6/YsVOCdiMDiXlZc5hSW5OpDl8E7BionNT0MYSvk2uMEJGSXoZYPKfDXsNj0OamZaCI86XKf+9PRKyfvorP0R17KudU0eE19Ac7EfyBrYiDvz5E6wGHg/2Ba6vUM6wudEEf8vPmO0eA5d8AUfy35R43nwo0tpaWoCO2hykVCbm9lbLc2cmtWFv1xY9kttv3qbLc2kPPzIUVL8MoVPaD5fzr+Rr7RUrAGFdANjKzeM5xnjKy64lCGAAKFo0Xs9EBExESoiTBR5RjcaOOpBGhL1+IpQRs5+U2EqUj9WrBZ0DApDlfGB8ygbRxdIJYlnWaUqvAHz7axCiwsFVu9V/UCAjwQ5Q9CHHXVXXXmHB3LaG7Egrr0Xx0hms4Md7tksynHOKDOFb1IjRAmbuyJStA187J/DhCdeINFULMmMn606b6ouO5lCVcphb5WTg7kOZpIaXnYRDCpT4I8PjIHXT/5RgawMQ=
metadata CHANGED
@@ -1,18 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hiera-crypt
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.2'
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Carl Jackson
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-07-14 00:00:00.000000000 Z
12
+ date: 2013-08-11 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: hiera
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
19
  - - ~>
18
20
  - !ruby/object:Gem::Version
@@ -20,27 +22,47 @@ dependencies:
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
27
  - - ~>
25
28
  - !ruby/object:Gem::Version
26
29
  version: 1.2.1
27
30
  - !ruby/object:Gem::Dependency
28
- name: gpgme
31
+ name: pbkdf2
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
35
  - - ~>
32
36
  - !ruby/object:Gem::Version
33
- version: 2.0.2
37
+ version: 0.1.0
34
38
  type: :runtime
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
43
  - - ~>
39
44
  - !ruby/object:Gem::Version
40
- version: 2.0.2
45
+ version: 0.1.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: rbnacl
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.1.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.1.0
41
62
  - !ruby/object:Gem::Dependency
42
63
  name: bundler
43
64
  requirement: !ruby/object:Gem::Requirement
65
+ none: false
44
66
  requirements:
45
67
  - - ~>
46
68
  - !ruby/object:Gem::Version
@@ -48,6 +70,7 @@ dependencies:
48
70
  type: :development
49
71
  prerelease: false
50
72
  version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
51
74
  requirements:
52
75
  - - ~>
53
76
  - !ruby/object:Gem::Version
@@ -55,15 +78,17 @@ dependencies:
55
78
  - !ruby/object:Gem::Dependency
56
79
  name: rake
57
80
  requirement: !ruby/object:Gem::Requirement
81
+ none: false
58
82
  requirements:
59
- - - '>='
83
+ - - ! '>='
60
84
  - !ruby/object:Gem::Version
61
85
  version: '0'
62
86
  type: :development
63
87
  prerelease: false
64
88
  version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
65
90
  requirements:
66
- - - '>='
91
+ - - ! '>='
67
92
  - !ruby/object:Gem::Version
68
93
  version: '0'
69
94
  description: Encrypted file backend for Hiera
@@ -82,11 +107,12 @@ files:
82
107
  - bin/hiera-crypt
83
108
  - hiera-crypt.gemspec
84
109
  - lib/hiera/backend/crypt_backend.rb
110
+ - lib/passwordbox.rb
85
111
  - test/data/data.txt
86
112
  - test/data/data2.txt
87
- - test/data/one/data.txt.gpg
88
- - test/data/two/backup.txt.gpg
89
- - test/data/two/data.txt.gpg
113
+ - test/data/one/data.txt.crypt
114
+ - test/data/two/backup.txt.crypt
115
+ - test/data/two/data.txt.crypt
90
116
  - test/everything
91
117
  - test/hiera-file.yaml
92
118
  - test/hiera-inline.yaml
@@ -94,34 +120,35 @@ files:
94
120
  homepage: https://github.com/zenazn/hiera-crypt
95
121
  licenses:
96
122
  - MIT
97
- metadata: {}
98
123
  post_install_message:
99
124
  rdoc_options: []
100
125
  require_paths:
101
126
  - lib
102
127
  required_ruby_version: !ruby/object:Gem::Requirement
128
+ none: false
103
129
  requirements:
104
- - - '>='
130
+ - - ! '>='
105
131
  - !ruby/object:Gem::Version
106
132
  version: '0'
107
133
  required_rubygems_version: !ruby/object:Gem::Requirement
134
+ none: false
108
135
  requirements:
109
- - - '>='
136
+ - - ! '>='
110
137
  - !ruby/object:Gem::Version
111
138
  version: '0'
112
139
  requirements: []
113
140
  rubyforge_project:
114
- rubygems_version: 2.0.0
141
+ rubygems_version: 1.8.23
115
142
  signing_key:
116
- specification_version: 4
143
+ specification_version: 3
117
144
  summary: A data backend for Hiera that returns the decrypted contents of files. Useful
118
145
  for secrets.
119
146
  test_files:
120
147
  - test/data/data.txt
121
148
  - test/data/data2.txt
122
- - test/data/one/data.txt.gpg
123
- - test/data/two/backup.txt.gpg
124
- - test/data/two/data.txt.gpg
149
+ - test/data/one/data.txt.crypt
150
+ - test/data/two/backup.txt.crypt
151
+ - test/data/two/data.txt.crypt
125
152
  - test/everything
126
153
  - test/hiera-file.yaml
127
154
  - test/hiera-inline.yaml
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: f4f9ddadfa2194dd22b7bb21f285ad2aaa1c8f53
4
- data.tar.gz: 5c8f3883285be5be667491484cd41a02d8137d08
5
- SHA512:
6
- metadata.gz: da6f2ad40a25a9b6098eeed5a4cfbe8393b7ad7ed9d3d882ccf72454d993b9eb6e60a917aae1ee146f19aa0ccc00262e095be71afb6084f29aee9905601ba3b0
7
- data.tar.gz: 95becd1d2756194762e70c1e7f8924a1d808402e419138d9d874eb37a011f11f11b6f46e81bf7ef651e0bf9bdd08710814235cd855707ecf48a7d6c82ff859d0
Binary file
Binary file
Binary file