hiera-eyaml 1.3.4 → 1.3.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6991fc6c0e02d5a2fe5d177c48f37c41c8e665b9
4
+ data.tar.gz: aea670bff868758d3e43529b4df32ad50a7c6d0d
5
+ SHA512:
6
+ metadata.gz: 87057907d1ffc81c21fb4fafb040f08f3d1a69cf0af00332dcc6ed642144a0e33be9eb5087b2d5ca5fc90f637c2ef3035447d64dde2c3a7062e288e7fcfe0078
7
+ data.tar.gz: c67e419ec77d72dde2ad5ee6f0aeb51d20696db797db5512b3aec212befe92884d1e61868da5f423ac35c9ac66822d816110b77def803cdc8d718e9b78fdfaa4
@@ -8,25 +8,34 @@ GEM
8
8
  builder (3.2.2)
9
9
  childprocess (0.3.9)
10
10
  ffi (~> 1.0, >= 1.0.11)
11
- cucumber (1.3.5)
11
+ cucumber (1.3.9)
12
12
  builder (>= 2.1.2)
13
13
  diff-lcs (>= 1.1.3)
14
- gherkin (~> 2.12.0)
15
- multi_json (~> 1.7.5)
14
+ gherkin (~> 2.12)
15
+ multi_json (>= 1.7.5, < 2.0)
16
16
  multi_test (>= 0.0.2)
17
- diff-lcs (1.2.4)
18
- ffi (1.9.0)
19
- gherkin (2.12.0)
17
+ diff-lcs (1.2.5)
18
+ facter (1.7.3)
19
+ ffi (1.9.3)
20
+ gherkin (2.12.2)
20
21
  multi_json (~> 1.3)
21
- hiera-eyaml (1.2.0)
22
+ hiera (1.2.1)
23
+ json_pure
24
+ hiera-eyaml (1.3.4)
22
25
  highline (>= 1.6.19)
23
26
  trollop (>= 2.0)
24
- hiera-eyaml-plaintext (0.1)
25
- hiera-eyaml (>= 1.2.0)
26
- highline (1.6.19)
27
- multi_json (1.7.8)
27
+ hiera-eyaml-plaintext (0.4)
28
+ hiera-eyaml (>= 1.3.1)
29
+ highline (1.6.20)
30
+ json_pure (1.8.1)
31
+ multi_json (1.8.2)
28
32
  multi_test (0.0.2)
29
- rspec-expectations (2.14.0)
33
+ puppet (3.3.1)
34
+ facter (~> 1.6)
35
+ hiera (~> 1.0)
36
+ rgen (~> 0.6.5)
37
+ rgen (0.6.6)
38
+ rspec-expectations (2.14.4)
30
39
  diff-lcs (>= 1.1.3, < 2.0)
31
40
  trollop (2.0)
32
41
 
@@ -37,4 +46,5 @@ DEPENDENCIES
37
46
  aruba
38
47
  hiera-eyaml-plaintext
39
48
  highline
49
+ puppet
40
50
  trollop
data/README.md CHANGED
@@ -32,6 +32,14 @@ Setup
32
32
  ### Installing hiera-eyaml
33
33
 
34
34
  $ gem install hiera-eyaml
35
+
36
+ #### Installing from behind a corporate/application proxy
37
+ $ export HTTP_PROXY=http://yourcorporateproxy:3128/
38
+ $ export HTTPS_PROXY=http://yourcorporateproxy:3128/
39
+
40
+ then run your install
41
+
42
+ $ gem install hiera-eyaml
35
43
 
36
44
  ### Generate keys
37
45
 
@@ -41,6 +49,26 @@ The first step is to create a pair of keys:
41
49
 
42
50
  This creates a public and private key with default names in the default location. (./keys)
43
51
 
52
+ #### Storing the keys securely when using Puppet
53
+
54
+ Since the point of using this module is to securely store sensitive information, it's important to store these keys securely.
55
+ If using Hiera with Puppet, Your puppetmaster will need to access these keys to perform decryption when the puppet agent runs on a remote node.
56
+ So for this reason, a suggested location might be to store them in:
57
+
58
+ /etc/puppet/secure/keys
59
+
60
+ (Using a secure/keys/ subfolder is so that you can still store other secure puppet files in the secure/ folder that might not be related to this module.)
61
+
62
+ The permissions for this folder should allow the puppet user (normally 'puppet') execute access to the keys directory, read only access to the keys themselves and restrict everyone else:
63
+
64
+ $ chown -R puppet:puppet /etc/puppet/secure/keys
65
+ $ chmod -R 0500 /etc/puppet/secure/keys
66
+ $ chmod 0400 /etc/puppet/secure/keys/*.pem
67
+ $ ls -lha /etc/puppet/secure/keys
68
+ -r-------- 1 puppet puppet 1.7K Sep 24 16:24 private_key.pkcs7.pem
69
+ -r-------- 1 puppet puppet 1.1K Sep 24 16:24 public_key.pkcs7.pem
70
+
71
+
44
72
  ### Encryption
45
73
 
46
74
  To encrypt something, you only need the public_key, so distribute that to people creating hiera properties
@@ -49,9 +77,10 @@ To encrypt something, you only need the public_key, so distribute that to people
49
77
  $ eyaml -e -s 'hello there' # Encrypt a string
50
78
  $ eyaml -e -p # Encrypt a password (prompt for it)
51
79
 
52
- Use the -l parameter to pass in a label for the encrypted value
80
+ Use the -l parameter to pass in a label for the encrypted value,
81
+
82
+ $ eyaml -e -l 'some_easy_to_use_label' -s 'yourSecretString' --pkcs7-private-key /etc/puppet/secure/keys/private_key.pkcs7.pem --pkcs7-public-key /etc/puppet/secure/keys/public_key.pkcs7.pem
53
83
 
54
- $ eyaml -e -l 'my-secret-key' -s 'very secret stuffs'
55
84
 
56
85
  ### Decryption
57
86
 
@@ -108,13 +137,18 @@ To use eyaml with hiera and puppet, first configure hiera.yaml to use the eyaml
108
137
  :datadir: '/etc/puppet/hieradata'
109
138
 
110
139
  # If using the pkcs7 encryptor (default)
111
- :pkcs7_private_key: /path/to/private_key_file.pem
112
- :pkcs7_public_key: /path/to/public_key_file.pem
140
+ :pkcs7_private_key: /path/to/private_key.pkcs7.pem
141
+ :pkcs7_public_key: /path/to/public_key.pkcs7.pem
113
142
 
114
143
  </pre>
115
144
 
116
145
  Then, edit your hiera yaml files (renaming them with the .eyaml extension), and insert your encrypted values:
117
146
 
147
+
148
+ *Important Note:*
149
+ The eYaml backend will not parse internally json formatted yaml files, whereas the regular yaml backend will.
150
+ You'll need to ensure any existing yaml files using json format are converted to syntactically correct yaml format.
151
+
118
152
  <pre>
119
153
  ---
120
154
  plain-property: You can see me
@@ -172,6 +206,14 @@ Notes
172
206
 
173
207
  If you do not specify an encryption method within ENC[] tags, it will be assumed to be PKCS7
174
208
 
209
+ Also remember that after encrypting your sensitive properties, if anyone has access to your git source,
210
+ they will see what the property was in previous commits before you encrypted. It's recommended that you
211
+ roll any passwords when switching from unencrypted to encrypted properties. eg, Developers having write
212
+ access to a DEV branch will be able to read/view the contents of the PRD branch, as per the design of GIT.
213
+
214
+ Github has a great guide on removing sensitive data from repos here:
215
+ https://help.github.com/articles/remove-sensitive-data
216
+
175
217
  Authors
176
218
  =======
177
219
 
@@ -2,7 +2,7 @@ class Hiera
2
2
  module Backend
3
3
  module Eyaml
4
4
 
5
- VERSION = "1.3.4"
5
+ VERSION = "1.3.5"
6
6
 
7
7
  def self.default_encryption_scheme= new_encryption
8
8
  @@default_encryption_scheme = new_encryption
@@ -33,17 +33,19 @@ Options:
33
33
  EOS
34
34
 
35
35
  opt :createkeys, "Create public and private keys for use encrypting properties", :short => 'c'
36
- opt :decrypt, "Decrypt something"
37
- opt :encrypt, "Encrypt something"
38
- opt :edit, "Decrypt, Edit, and Reencrypt", :type => :string
39
- opt :eyaml, "Source input is an eyaml file", :type => :string
36
+ opt :decrypt, "Decrypt something", :short => 'd'
37
+ opt :encrypt, "Encrypt something", :short => 'e'
38
+ opt :edit, "Decrypt, Edit, and Reencrypt", :short => 'i', :type => :string
39
+ opt :eyaml, "Source input is an eyaml file", :short => 'y', :type => :string
40
40
  opt :password, "Source input is a password entered on the terminal", :short => 'p'
41
41
  opt :string, "Source input is a string provided as an argument", :short => 's', :type => :string
42
42
  opt :file, "Source input is a file", :short => 'f', :type => :string
43
- opt :stdin, "Source input it taken from stdin", :short => 'z'
43
+ opt :stdin, "Source input is taken from stdin", :short => :none
44
44
  opt :encrypt_method, "Override default encryption and decryption method (default is PKCS7)", :short => 'n', :default => "pkcs7"
45
- opt :output, "Output format of final result (examples, block, string)", :type => :string, :default => "examples"
45
+ opt :output, "Output format of final result (examples, block, string)", :type => :string, :short => 'o', :default => "examples"
46
46
  opt :label, "Apply a label to the encrypted result", :short => 'l', :type => :string
47
+ opt :debug, "Be more verbose", :short => :none
48
+ opt :quiet, "Be less verbose", :short => :none
47
49
 
48
50
  Hiera::Backend::Eyaml::Plugins.options.each do |name, option|
49
51
  opt name, option[:desc], :type => option[:type], :short => option[:short], :default => option[:default]
@@ -90,6 +92,7 @@ Options:
90
92
 
91
93
  Eyaml.default_encryption_scheme = options[:encrypt_method].upcase if options[:encrypt_method]
92
94
  Eyaml::Options.set options
95
+ Eyaml::Options.debug
93
96
 
94
97
  end
95
98
 
@@ -98,7 +101,8 @@ Options:
98
101
  action = Eyaml::Options[:action]
99
102
  action_class = Module.const_get('Hiera').const_get('Backend').const_get('Eyaml').const_get('Actions').const_get("#{Utils.camelcase action.to_s}Action")
100
103
 
101
- puts action_class.execute
104
+ return_value = action_class.execute
105
+ puts return_value unless return_value.nil?
102
106
 
103
107
  end
104
108
 
@@ -1,5 +1,6 @@
1
1
  require 'hiera/backend/eyaml/utils'
2
2
  require 'hiera/backend/eyaml/options'
3
+ require 'hiera/backend/eyaml/parser/parser'
3
4
 
4
5
  class Hiera
5
6
  module Backend
@@ -8,57 +9,27 @@ class Hiera
8
9
 
9
10
  class DecryptAction
10
11
 
11
- REGEX_ENCRYPTED_BLOCK = />\n(\s*)ENC\[(\w+,)?([a-zA-Z0-9\+\/ =\n]+)\]/
12
- REGEX_ENCRYPTED_STRING = /ENC\[(\w+,)?([a-zA-Z0-9\+\/=]+)\]/
13
-
14
- def self.execute
15
-
16
- output_data = case Eyaml::Options[:source]
17
- when :eyaml
18
- encryptions = []
19
-
20
- # blocks
21
- output = Eyaml::Options[:input_data].gsub( REGEX_ENCRYPTED_BLOCK ) { |match|
22
- indentation = $1
23
- encryption_scheme = parse_encryption_scheme( $2 )
24
- decryptor = Encryptor.find encryption_scheme
25
- ciphertext = $3.gsub(/[ \n]/, '')
26
- plaintext = decryptor.decrypt( decryptor.decode ciphertext )
27
- ">\n" + indentation + "DEC::#{decryptor.tag}[" + plaintext + "]!"
28
- }
29
-
30
- # strings
31
- output.gsub!( REGEX_ENCRYPTED_STRING ) { |match|
32
- encryption_scheme = parse_encryption_scheme( $1 )
33
- decryptor = Encryptor.find encryption_scheme
34
-
35
- plaintext = decryptor.decrypt( decryptor.decode $2 )
36
- "DEC::#{decryptor.tag}[" + plaintext + "]!"
37
- }
38
-
39
- output
40
- else
41
-
42
- output = Eyaml::Options[:input_data].gsub( REGEX_ENCRYPTED_STRING ) { |match|
43
- encryption_scheme = parse_encryption_scheme( $1 )
44
- decryptor = Encryptor.find encryption_scheme
45
- decryptor.decrypt( decryptor.decode $2 )
46
- }
47
-
48
- output
12
+ def self.execute
13
+ parser = Parser::ParserFactory.encrypted_parser
14
+ tokens = parser.parse(Eyaml::Options[:input_data])
15
+ case Eyaml::Options[:source]
16
+ when :eyaml
17
+ decrypted = tokens.map{ |token| token.to_decrypted }
18
+ decrypted.join
19
+ else
20
+ decrypted = tokens.map{ |token|
21
+ case token.class.name
22
+ when /::EncToken$/
23
+ token.plain_text
24
+ else
25
+ token.match
26
+ end
27
+ }
28
+ decrypted.join
49
29
  end
50
30
 
51
- output_data
52
-
53
31
  end
54
32
 
55
- protected
56
-
57
- def self.parse_encryption_scheme regex_result
58
- regex_result = Eyaml.default_encryption_scheme + "," if regex_result.nil?
59
- regex_result.split(",").first
60
- end
61
-
62
33
  end
63
34
 
64
35
  end
@@ -2,6 +2,7 @@ require 'hiera/backend/eyaml/utils'
2
2
  require 'hiera/backend/eyaml/actions/decrypt_action'
3
3
  require 'hiera/backend/eyaml/actions/encrypt_action'
4
4
  require 'hiera/backend/eyaml/options'
5
+ require 'hiera/backend/eyaml/parser/parser'
5
6
 
6
7
  class Hiera
7
8
  module Backend
@@ -11,32 +12,60 @@ class Hiera
11
12
  class EditAction
12
13
 
13
14
  def self.execute
14
-
15
- decrypted_input = DecryptAction.execute
15
+
16
+ encrypted_parser = Parser::ParserFactory.encrypted_parser
17
+ tokens = encrypted_parser.parse Eyaml::Options[:input_data]
18
+ decrypted_input = tokens.each_with_index.to_a.map{|(t,index)| t.to_decrypted :index => index}.join
16
19
  decrypted_file = Utils.write_tempfile decrypted_input
20
+
17
21
  editor = Utils.find_editor
18
22
  system editor, decrypted_file
19
23
  status = $?
20
- raise StandardError, "Editor #{editor} has not exited?" unless status.exited?
21
- raise StandardError, "Editor did not exit successfully (exit code #{status.exitstatus}), aborting" unless status.exitstatus #TODO: The file is left on the disk
22
- raise StandardError, "File was moved by editor" unless File.file? decrypted_file
23
24
 
25
+ raise StandardError, "File was moved by editor" unless File.file? decrypted_file
24
26
  edited_file = File.read decrypted_file
25
27
  Utils.secure_file_delete :file => decrypted_file, :num_bytes => [edited_file.length, decrypted_input.length].max
28
+
29
+ raise StandardError, "Editor #{editor} has not exited?" unless status.exited?
30
+ raise StandardError, "Editor did not exit successfully (exit code #{status.exitstatus}), aborting" unless status.exitstatus
31
+
26
32
  raise StandardError, "Edited file is blank" if edited_file.empty?
27
- raise StandardError, "No changes" if edited_file == decrypted_input
28
33
 
29
- Eyaml::Options[:input_data] = edited_file
30
- Eyaml::Options[:output] = "raw"
34
+ if edited_file == decrypted_input
35
+ Utils.info "No changes detected, exiting"
36
+ else
37
+ decrypted_parser = Parser::ParserFactory.decrypted_parser
38
+ edited_tokens = decrypted_parser.parse(edited_file)
39
+
40
+ # check that the tokens haven't been copy / pasted
41
+ used_ids = edited_tokens.find_all{ |t| t.class.name =~ /::EncToken$/ }.map{ |t| t.id }
42
+ if used_ids.length != used_ids.uniq.length
43
+ raise StandardError, "A duplicate DEC(ID) was found so I don't know how to proceed. This is probably because you copy and pasted a value - if you do this please delete the ID in parentheses"
44
+ end
45
+
46
+ # replace untouched values with the source values
47
+ edited_denoised_tokens = edited_tokens.map{ |token|
48
+ if token.class.name =~ /::EncToken$/ && !token.id.nil?
49
+ old_token = tokens[token.id]
50
+ if old_token.plain_text.eql? token.plain_text
51
+ old_token
52
+ else
53
+ token
54
+ end
55
+ else
56
+ token
57
+ end
58
+ }
31
59
 
32
- encrypted_output = EncryptAction.execute
60
+ encrypted_output = edited_denoised_tokens.map{ |t| t.to_encrypted }.join
33
61
 
34
- filename = Eyaml::Options[:eyaml]
35
- File.open("#{filename}", 'w') { |file|
36
- file.write encrypted_output
37
- }
62
+ filename = Eyaml::Options[:eyaml]
63
+ File.open("#{filename}", 'w') { |file|
64
+ file.write encrypted_output
65
+ }
66
+ end
38
67
 
39
- true
68
+ nil
40
69
  end
41
70
 
42
71
  end
@@ -1,87 +1,40 @@
1
1
  require 'hiera/backend/eyaml/options'
2
+ require 'hiera/backend/eyaml/parser/parser'
3
+ require 'hiera/backend/eyaml/parser/encrypted_tokens'
2
4
 
3
5
  class Hiera
4
6
  module Backend
5
7
  module Eyaml
6
8
  module Actions
7
-
8
9
  class EncryptAction
9
10
 
10
- REGEX_DECRYPTED_BLOCK = />\n(\s*)DEC(::\w+)?\[(.+)\]\!/
11
- REGEX_DECRYPTED_STRING = /DEC(::\w+)?\[(.+)\]\!/
12
-
13
11
  def self.execute
14
-
15
12
  case Eyaml::Options[:source]
16
- when :eyaml
17
- encryptions = []
18
-
19
- # blocks
20
- output = Eyaml::Options[:input_data].gsub( REGEX_DECRYPTED_BLOCK ) { |match|
21
- indentation = $1
22
- encryption_scheme = parse_encryption_scheme( $2 )
23
- encryptor = Encryptor.find encryption_scheme
24
- ciphertext = encryptor.encode( encryptor.encrypt($3) ).gsub(/\n/, "\n" + indentation)
25
- ">\n" + indentation + "ENC[#{encryptor.tag},#{ciphertext}]"
26
- }
27
-
28
- # strings
29
- output.gsub( REGEX_DECRYPTED_STRING ) { |match|
30
- encryption_scheme = parse_encryption_scheme( $1 )
31
- encryptor = Encryptor.find encryption_scheme
32
- ciphertext = encryptor.encode( encryptor.encrypt($2) ).gsub(/\n/, "")
33
- "ENC[#{encryptor.tag},#{ciphertext}]"
34
- }
35
-
36
- else
37
- encryptor = Encryptor.find
38
- ciphertext = encryptor.encode( encryptor.encrypt(Eyaml::Options[:input_data]) )
39
- self.format :data => "ENC[#{encryptor.tag},#{ciphertext}]", :structure => Eyaml::Options[:output], :label => Eyaml::Options[:label]
40
- end
41
-
42
- end
43
-
44
- protected
45
-
46
- def self.parse_encryption_scheme regex_result
47
- regex_result = "::" + Eyaml.default_encryption_scheme if regex_result.nil?
48
- regex_result.split("::").last
49
- end
50
-
51
- def self.format_string data, label
52
- data_as_string = data.split("\n").join("")
53
- prefix = label ? "#{label}: " : ''
54
- prefix + data_as_string
55
- end
56
-
57
- def self.format_block data, label
58
- data_as_block = data.split("\n").join("\n ")
59
- prefix = label ? "#{label}: >\n" : ''
60
- prefix + " #{data_as_block}"
61
- end
62
-
63
- def self.format args
64
- data = args[:data]
65
- structure = args[:structure]
66
- label = args[:label]
67
-
68
- case structure
69
- when "examples"
70
- self.format_string(data, label || 'string') + "\n\n" +
71
- "OR\n\n" +
72
- self.format_block(data, label || 'block')
73
- when "block"
74
- self.format_block data, label
75
- when "string"
76
- self.format_string data, label
13
+ when :eyaml
14
+ parser = Parser::ParserFactory.decrypted_parser
15
+ tokens = parser.parse(Eyaml::Options[:input_data])
16
+ encrypted = tokens.map{ |token| token.to_encrypted }
17
+ encrypted.join
77
18
  else
78
- data.to_s
79
- end
80
-
19
+ encryptor = Encryptor.find
20
+ ciphertext = encryptor.encode( encryptor.encrypt(Eyaml::Options[:input_data]) )
21
+ token = Parser::EncToken.new(:block, Eyaml::Options[:input_data], encryptor, ciphertext, nil, ' ')
22
+ case Eyaml::Options[:output]
23
+ when "block"
24
+ token.to_encrypted :label => Eyaml::Options[:label], :use_chevron => !Eyaml::Options[:label].nil?, :format => :block
25
+ when "string"
26
+ token.to_encrypted :label => Eyaml::Options[:label], :format => :string
27
+ when "examples"
28
+ string = token.to_encrypted :label => Eyaml::Options[:label] || 'string', :format => :string
29
+ block = token.to_encrypted :label => Eyaml::Options[:label] || 'block', :format => :block
30
+ "#{string}\n\nOR\n\n#{block}"
31
+ else
32
+ token.to_encrypted :format => :string
33
+ end
81
34
  end
35
+ end
82
36
 
83
37
  end
84
-
85
38
  end
86
39
  end
87
40
  end
@@ -77,7 +77,7 @@ class Hiera
77
77
  if self.hiera?
78
78
  Hiera.debug format_message msg
79
79
  else
80
- STDERR.puts format_message msg
80
+ Utils::debug format_message msg
81
81
  end
82
82
  end
83
83
 
@@ -85,7 +85,7 @@ class Hiera
85
85
  if self.hiera?
86
86
  Hiera.warn format_message msg
87
87
  else
88
- STDERR.puts format_message msg
88
+ Utils::info format_message msg
89
89
  end
90
90
  end
91
91
 
@@ -21,9 +21,12 @@ class Hiera
21
21
  end
22
22
 
23
23
  def self.debug
24
+ Utils::debug "Dump of eyaml tool options dict:"
25
+ Utils::debug "--------------------------------"
24
26
  @@options.each do |k, v|
25
- puts "#{k.class.name}:#{k} = #{v.class.name}:#{v}"
27
+ Utils::debug "#{k.class.name}:#{k} = #{v.class.name}:#{v}"
26
28
  end
29
+ Utils::debug ""
27
30
  end
28
31
 
29
32
  end
@@ -0,0 +1,135 @@
1
+ require 'hiera/backend/eyaml/parser/token'
2
+ require 'hiera/backend/eyaml/utils'
3
+ require 'hiera/backend/eyaml/encryptor'
4
+ require 'hiera/backend/eyaml'
5
+
6
+
7
+ class Hiera
8
+ module Backend
9
+ module Eyaml
10
+ module Parser
11
+ class EncToken < Token
12
+ attr_reader :format, :cipher, :encryptor, :indentation, :plain_text, :id
13
+ def self.encrypted_value(format, encryption_scheme, cipher, match, indentation = '')
14
+ decryptor = Encryptor.find encryption_scheme
15
+ plain_text = decryptor.decrypt( decryptor.decode cipher )
16
+ EncToken.new(format, plain_text, decryptor, cipher, match, indentation)
17
+ end
18
+ def self.decrypted_value(format, plain_text, encryption_scheme, match, id, indentation = '')
19
+ encryptor = Encryptor.find encryption_scheme
20
+ cipher = encryptor.encode( encryptor.encrypt plain_text )
21
+ id_number = id.nil? ? nil : id.gsub(/\(|\)/, "").to_i
22
+ EncToken.new(format, plain_text, encryptor, cipher, match, indentation, id_number)
23
+ end
24
+
25
+ def initialize(format, plain_text, encryptor, cipher, match = '', indentation = '', id = nil)
26
+ @format = format
27
+ @plain_text = plain_text
28
+ @encryptor = encryptor
29
+ @cipher = cipher
30
+ @indentation = indentation
31
+ @id = id
32
+ super(match)
33
+ end
34
+
35
+ def to_encrypted(args={})
36
+ label = args[:label]
37
+ label_string = label.nil? ? '' : "#{label}: "
38
+ format = args[:format].nil? ? @format : args[:format]
39
+ case format
40
+ when :block
41
+ ciphertext = @cipher.gsub(/\n/, "\n" + @indentation)
42
+ chevron = (args[:use_chevron].nil? || args[:use_chevron]) ? ">\n" : ''
43
+ "#{label_string}#{chevron}" + @indentation + "ENC[#{@encryptor.tag},#{ciphertext}]"
44
+ when :string
45
+ ciphertext = @cipher.gsub(/\n/, "")
46
+ "#{label_string}ENC[#{@encryptor.tag},#{ciphertext}]"
47
+ else
48
+ raise "#{@format} is not a valid format"
49
+ end
50
+ end
51
+
52
+ def to_decrypted(args={})
53
+ label = args[:label]
54
+ label_string = label.nil? ? '' : "#{label}: "
55
+ format = args[:format].nil? ? @format : args[:format]
56
+ index = args[:index].nil? ? '' : "(#{args[:index]})"
57
+ case format
58
+ when :block
59
+ chevron = (args[:use_chevron].nil? || args[:use_chevron]) ? ">\n" : ''
60
+ "#{label_string}#{chevron}" + indentation + "DEC#{index}::#{@encryptor.tag}[" + @plain_text + "]!"
61
+ when :string
62
+ "#{label_string}DEC#{index}::#{@encryptor.tag}[" + @plain_text + "]!"
63
+ else
64
+ raise "#{@format} is not a valid format"
65
+ end
66
+ end
67
+
68
+ def to_plain_text
69
+ @plain_text
70
+ end
71
+
72
+ end
73
+
74
+ class EncTokenType < TokenType
75
+ def create_enc_token(match, type, enc_comma, cipher, indentation = '')
76
+ encryption_scheme = enc_comma.nil? ? Eyaml.default_encryption_scheme : enc_comma.split(",").first
77
+ EncToken.encrypted_value(type, encryption_scheme, cipher, match, indentation)
78
+ end
79
+ end
80
+
81
+ class EncHieraTokenType < EncTokenType
82
+ def initialize
83
+ @regex = /ENC\[(\w+,)?([a-zA-Z0-9\+\/ =\n]+?)\]/
84
+ @string_token_type = EncStringTokenType.new()
85
+ end
86
+ def create_token(string)
87
+ @string_token_type.create_token(string.gsub(/[ \n]/, ''))
88
+ end
89
+ end
90
+
91
+ class EncStringTokenType < EncTokenType
92
+ def initialize
93
+ @regex = /ENC\[(\w+,)?([a-zA-Z0-9\+\/=]+?)\]/
94
+ end
95
+ def create_token(string)
96
+ md = @regex.match(string)
97
+ self.create_enc_token(string, :string, md[1], md[2])
98
+ end
99
+ end
100
+
101
+ class EncBlockTokenType < EncTokenType
102
+ def initialize
103
+ @regex = />\n(\s*)ENC\[(\w+,)?([a-zA-Z0-9\+\/ =\n]+?)\]/
104
+ end
105
+ def create_token(string)
106
+ md = @regex.match(string)
107
+ self.create_enc_token(string, :block, md[2], md[3], md[1])
108
+ end
109
+ end
110
+
111
+ class DecStringTokenType < TokenType
112
+ def initialize
113
+ @regex = /DEC(\(\d+\))?::(\w+)\[(.+?)\]\!/
114
+ end
115
+ def create_token(string)
116
+ md = @regex.match(string)
117
+ EncToken.decrypted_value(:string, md[3], md[2], string, md[1])
118
+ end
119
+ end
120
+
121
+ class DecBlockTokenType < TokenType
122
+ def initialize
123
+ @regex = />\n(\s*)DEC(\(\d+\))?::(\w+)\[(.+?)\]\!/
124
+ end
125
+ def create_token(string)
126
+ md = @regex.match(string)
127
+ EncToken.decrypted_value(:block, md[4], md[3], string, md[2], md[1])
128
+ EncToken.decrypted_value(:block, md[4], md[3], string, md[2], md[1])
129
+ end
130
+ end
131
+
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,82 @@
1
+ require 'strscan'
2
+ require 'hiera/backend/eyaml/parser/token'
3
+ require 'hiera/backend/eyaml/parser/encrypted_tokens'
4
+
5
+ class Hiera
6
+ module Backend
7
+ module Eyaml
8
+ module Parser
9
+ class ParserFactory
10
+ def self.encrypted_parser
11
+ enc_string = EncStringTokenType.new()
12
+ enc_block = EncBlockTokenType.new()
13
+ Parser.new([enc_string, enc_block])
14
+ end
15
+
16
+ def self.decrypted_parser
17
+ dec_string = DecStringTokenType.new()
18
+ dec_block = DecBlockTokenType.new()
19
+ Parser.new([dec_string, dec_block])
20
+ end
21
+
22
+ def self.hiera_backend_parser
23
+ enc_hiera = EncHieraTokenType.new()
24
+ Parser.new([enc_hiera])
25
+ end
26
+ end
27
+
28
+ class Parser
29
+ attr_reader :token_types
30
+
31
+ def initialize(token_types)
32
+ @token_types = token_types
33
+ end
34
+
35
+ def parse text
36
+ parse_scanner(StringScanner.new(text)).reverse
37
+ end
38
+
39
+ def parse_scanner s
40
+ if s.eos?
41
+ []
42
+ else
43
+ # Check if the scanner currently matches a regex
44
+ current_match = @token_types.find { |token_type|
45
+ s.match?(token_type.regex)
46
+ }
47
+
48
+ token =
49
+ if current_match.nil?
50
+ # No regex matches here. Find the earliest match.
51
+ next_match_indexes = @token_types.map { |token_type|
52
+ next_match = s.check_until(token_type.regex)
53
+ if next_match.nil?
54
+ nil
55
+ else
56
+ next_match.length - s.matched.length
57
+ end
58
+ }.reject { |i| i.nil? }
59
+ non_match_size =
60
+ if next_match_indexes.length == 0
61
+ s.rest_size
62
+ else
63
+ next_match_indexes.min
64
+ end
65
+ non_match = s.peek(non_match_size)
66
+ # advance scanner
67
+ s.pos = s.pos + non_match_size
68
+ NonMatchToken.new(non_match)
69
+ else
70
+ # A regex matches so create a token and do a recursive call with the advanced scanner
71
+ current_match.create_token s.scan(current_match.regex)
72
+ end
73
+
74
+ self.parse_scanner(s) << token
75
+ end
76
+ end
77
+
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,49 @@
1
+ class Hiera
2
+ module Backend
3
+ module Eyaml
4
+ module Parser
5
+ class TokenType
6
+ attr_reader :regex
7
+ @regex
8
+ def create_token string
9
+ raise 'Abstract method called'
10
+ end
11
+ end
12
+
13
+ class Token
14
+ attr_reader :match
15
+ def initialize(match)
16
+ @match = match
17
+ end
18
+ def to_encrypted(args={})
19
+ raise 'Abstract method called'
20
+ end
21
+ def to_decrypted(args={})
22
+ raise 'Abstract method called'
23
+ end
24
+ def to_plain_text
25
+ raise 'Abstract method called'
26
+ end
27
+ def to_s
28
+ "#{self.class.name}:#{@match}"
29
+ end
30
+ end
31
+
32
+ class NonMatchToken < Token
33
+ def initialize(non_match)
34
+ super(non_match)
35
+ end
36
+ def to_encrypted(args={})
37
+ @match
38
+ end
39
+ def to_decrypted(args={})
40
+ @match
41
+ end
42
+ def to_plain_text
43
+ @match
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -40,7 +40,7 @@ class Hiera
40
40
  if Gem::VERSION >= "1.8.0"
41
41
  file = spec.matches_for_glob("**/eyaml_init.rb").first
42
42
  else
43
- file = Gem.searcher.matching_files(spec, "eyaml_init.rb").first
43
+ file = Gem.searcher.matching_files(spec, "**/eyaml_init.rb").first
44
44
  end
45
45
 
46
46
  next unless file
@@ -72,7 +72,7 @@ class Hiera
72
72
  unless File.directory? key_dir
73
73
  begin
74
74
  FileUtils.mkdir_p key_dir
75
- puts "Created key directory: #{key_dir}"
75
+ Utils::info "Created key directory: #{key_dir}"
76
76
  rescue
77
77
  raise StandardError, "Cannot create key directory: #{key_dir}"
78
78
  end
@@ -80,6 +80,14 @@ class Hiera
80
80
 
81
81
  end
82
82
 
83
+ def self.info message
84
+ STDERR.puts message unless Eyaml::Options[:quiet]
85
+ end
86
+
87
+ def self.debug message
88
+ STDERR.puts message if Eyaml::Options[:debug]
89
+ end
90
+
83
91
  end
84
92
  end
85
93
  end
@@ -1,6 +1,7 @@
1
1
  require 'hiera/backend/eyaml/encryptor'
2
2
  require 'hiera/backend/eyaml/actions/decrypt_action'
3
3
  require 'hiera/backend/eyaml/utils'
4
+ require 'hiera/backend/eyaml/parser/parser'
4
5
  require 'yaml'
5
6
 
6
7
  class Hiera
@@ -95,11 +96,10 @@ class Hiera
95
96
 
96
97
  Eyaml::Options[:source] = "hiera"
97
98
 
98
- plaintext = value.gsub( /ENC\[([^\]]*)\]/ ) { |match|
99
- Eyaml::Options[:input_data] = deblock match.to_s
100
- Eyaml::Options[:output] = "raw"
101
- Eyaml::Actions::DecryptAction.execute
102
- }
99
+ parser = Eyaml::Parser::ParserFactory.hiera_backend_parser
100
+ tokens = parser.parse(value)
101
+ decrypted = tokens.map{ |token| token.to_plain_text }
102
+ plaintext = decrypted.join
103
103
 
104
104
  plaintext.chomp
105
105
 
metadata CHANGED
@@ -1,46 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hiera-eyaml
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.4
5
- prerelease:
4
+ version: 1.3.5
6
5
  platform: ruby
7
6
  authors:
8
7
  - Tom Poulton
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-09-10 00:00:00.000000000 Z
11
+ date: 2013-11-12 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: trollop
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '2.0'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '2.0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: highline
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: 1.6.19
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: 1.6.19
46
41
  description: Hiera backend for decrypting encrypted yaml properties
@@ -68,6 +63,9 @@ files:
68
63
  - lib/hiera/backend/eyaml/encryptor.rb
69
64
  - lib/hiera/backend/eyaml/encryptors/pkcs7.rb
70
65
  - lib/hiera/backend/eyaml/options.rb
66
+ - lib/hiera/backend/eyaml/parser/encrypted_tokens.rb
67
+ - lib/hiera/backend/eyaml/parser/parser.rb
68
+ - lib/hiera/backend/eyaml/parser/token.rb
71
69
  - lib/hiera/backend/eyaml/plugins.rb
72
70
  - lib/hiera/backend/eyaml/utils.rb
73
71
  - lib/hiera/backend/eyaml_backend.rb
@@ -78,26 +76,25 @@ files:
78
76
  homepage: http://github.com/TomPoulton/hiera-eyaml
79
77
  licenses:
80
78
  - MIT
79
+ metadata: {}
81
80
  post_install_message:
82
81
  rdoc_options: []
83
82
  require_paths:
84
83
  - lib
85
84
  required_ruby_version: !ruby/object:Gem::Requirement
86
- none: false
87
85
  requirements:
88
- - - ! '>='
86
+ - - '>='
89
87
  - !ruby/object:Gem::Version
90
88
  version: '0'
91
89
  required_rubygems_version: !ruby/object:Gem::Requirement
92
- none: false
93
90
  requirements:
94
- - - ! '>='
91
+ - - '>='
95
92
  - !ruby/object:Gem::Version
96
93
  version: '0'
97
94
  requirements: []
98
95
  rubyforge_project:
99
- rubygems_version: 1.8.25
96
+ rubygems_version: 2.0.11
100
97
  signing_key:
101
- specification_version: 3
98
+ specification_version: 4
102
99
  summary: OpenSSL Encryption backend for Hiera
103
100
  test_files: []