clarenceb-hiera-eyaml 2.0.1

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.
Files changed (36) hide show
  1. data/.gitignore +8 -0
  2. data/.travis.yml +10 -0
  3. data/Gemfile +17 -0
  4. data/Gemfile.lock +52 -0
  5. data/LICENSE.txt +21 -0
  6. data/PLUGINS.md +4 -0
  7. data/README.md +322 -0
  8. data/Rakefile +1 -0
  9. data/bin/eyaml +13 -0
  10. data/hiera-eyaml.gemspec +22 -0
  11. data/lib/hiera/backend/eyaml/CLI.rb +60 -0
  12. data/lib/hiera/backend/eyaml/commands.rb +21 -0
  13. data/lib/hiera/backend/eyaml/encryptor.rb +79 -0
  14. data/lib/hiera/backend/eyaml/encryptors/pkcs7.rb +107 -0
  15. data/lib/hiera/backend/eyaml/options.rb +35 -0
  16. data/lib/hiera/backend/eyaml/parser/encrypted_tokens.rb +138 -0
  17. data/lib/hiera/backend/eyaml/parser/parser.rb +82 -0
  18. data/lib/hiera/backend/eyaml/parser/token.rb +49 -0
  19. data/lib/hiera/backend/eyaml/plugins.rb +70 -0
  20. data/lib/hiera/backend/eyaml/subcommand.rb +126 -0
  21. data/lib/hiera/backend/eyaml/subcommands/createkeys.rb +29 -0
  22. data/lib/hiera/backend/eyaml/subcommands/decrypt.rb +81 -0
  23. data/lib/hiera/backend/eyaml/subcommands/edit.rb +105 -0
  24. data/lib/hiera/backend/eyaml/subcommands/encrypt.rb +100 -0
  25. data/lib/hiera/backend/eyaml/subcommands/help.rb +51 -0
  26. data/lib/hiera/backend/eyaml/subcommands/recrypt.rb +56 -0
  27. data/lib/hiera/backend/eyaml/subcommands/unknown_command.rb +48 -0
  28. data/lib/hiera/backend/eyaml/subcommands/version.rb +47 -0
  29. data/lib/hiera/backend/eyaml/utils.rb +172 -0
  30. data/lib/hiera/backend/eyaml.rb +48 -0
  31. data/lib/hiera/backend/eyaml_backend.rb +125 -0
  32. data/sublime_text/README.md +16 -0
  33. data/sublime_text/eyaml.sublime-package +0 -0
  34. data/sublime_text/eyaml.syntax_definition.json +288 -0
  35. data/tools/regem.sh +9 -0
  36. metadata +114 -0
@@ -0,0 +1,107 @@
1
+ require 'openssl'
2
+ require 'hiera/backend/eyaml/encryptor'
3
+ require 'hiera/backend/eyaml/utils'
4
+ require 'hiera/backend/eyaml/options'
5
+
6
+ class Hiera
7
+ module Backend
8
+ module Eyaml
9
+ module Encryptors
10
+
11
+ class Pkcs7 < Encryptor
12
+
13
+ self.options = {
14
+ :private_key => { :desc => "Path to private key",
15
+ :type => :string,
16
+ :default => "./keys/private_key.pkcs7.pem" },
17
+ :public_key => { :desc => "Path to public key",
18
+ :type => :string,
19
+ :default => "./keys/public_key.pkcs7.pem" }
20
+ }
21
+
22
+ self.tag = "PKCS7"
23
+
24
+ def self.encrypt plaintext
25
+
26
+ public_key = self.option :public_key
27
+ raise StandardError, "pkcs7_public_key is not defined" unless public_key
28
+
29
+ public_key_pem = File.read public_key
30
+ public_key_x509 = OpenSSL::X509::Certificate.new( public_key_pem )
31
+
32
+ cipher = OpenSSL::Cipher::AES.new(256, :CBC)
33
+ OpenSSL::PKCS7::encrypt([public_key_x509], plaintext, cipher, OpenSSL::PKCS7::BINARY).to_der
34
+
35
+ end
36
+
37
+ def self.decrypt ciphertext
38
+
39
+ public_key = self.option :public_key
40
+ private_key = self.option :private_key
41
+ raise StandardError, "pkcs7_public_key is not defined" unless public_key
42
+ raise StandardError, "pkcs7_private_key is not defined" unless private_key
43
+
44
+ private_key_pem = File.read private_key
45
+ private_key_rsa = OpenSSL::PKey::RSA.new( private_key_pem )
46
+
47
+ public_key_pem = File.read public_key
48
+ public_key_x509 = OpenSSL::X509::Certificate.new( public_key_pem )
49
+
50
+ pkcs7 = OpenSSL::PKCS7.new( ciphertext )
51
+ pkcs7.decrypt(private_key_rsa, public_key_x509)
52
+
53
+ end
54
+
55
+ def self.create_keys
56
+
57
+ # Try to do equivalent of:
58
+ # openssl req -x509 -nodes -days 100000 -newkey rsa:2048 -keyout privatekey.pem -out publickey.pem -subj '/'
59
+
60
+ public_key = self.option :public_key
61
+ private_key = self.option :private_key
62
+
63
+ key = OpenSSL::PKey::RSA.new(2048)
64
+ Utils.ensure_key_dir_exists private_key
65
+ Utils.write_important_file :filename => private_key, :content => key.to_pem, :mode => 0600
66
+
67
+ name = OpenSSL::X509::Name.parse("/DC=org/DC=example/CN=eyaml")
68
+ cert = OpenSSL::X509::Certificate.new()
69
+ cert.serial = 0
70
+ cert.version = 2
71
+ cert.subject = name
72
+ cert.issuer = cert.subject
73
+ cert.not_before = Time.now
74
+ cert.not_after = if 1.size == 8 # 64bit
75
+ Time.now + 50 * 365 * 24 * 60 * 60
76
+ else # 32bit
77
+ Time.at(0x7fffffff)
78
+ end
79
+ cert.public_key = key.public_key
80
+
81
+ ef = OpenSSL::X509::ExtensionFactory.new
82
+ ef.subject_certificate = cert
83
+ ef.issuer_certificate = cert
84
+ cert.extensions = [
85
+ ef.create_extension("basicConstraints","CA:TRUE", true),
86
+ ef.create_extension("subjectKeyIdentifier", "hash"),
87
+ ]
88
+ cert.add_extension ef.create_extension("authorityKeyIdentifier",
89
+ "keyid:always,issuer:always")
90
+
91
+ cert.sign key, OpenSSL::Digest::SHA1.new
92
+
93
+ Utils.ensure_key_dir_exists public_key
94
+ Utils.write_important_file :filename => public_key, :content => cert.to_pem
95
+ puts "Keys created OK"
96
+
97
+ end
98
+
99
+ end
100
+
101
+ end
102
+
103
+ end
104
+
105
+ end
106
+
107
+ end
@@ -0,0 +1,35 @@
1
+ class Hiera
2
+ module Backend
3
+ module Eyaml
4
+ class Options
5
+
6
+ def self.[]= key, value
7
+ @@options ||= {}
8
+ @@options[ key.to_sym ] = value
9
+ end
10
+
11
+ def self.[] key
12
+ @@options ||= {}
13
+ @@options[ key.to_sym ]
14
+ end
15
+
16
+ def self.set hash
17
+ @@options = {}
18
+ hash.each do |k, v|
19
+ @@options[ k.to_sym ] = v
20
+ end
21
+ end
22
+
23
+ def self.debug
24
+ Utils::debug "Dump of eyaml tool options dict:"
25
+ Utils::debug "--------------------------------"
26
+ @@options.each do |k, v|
27
+ Utils::debug sprintf "%18s %-18s = %-18s %-18s", "(#{k.class.name})", k.to_s, v.to_s, "(#{v.class.name})"
28
+ end
29
+ Utils::debug "--------------------------------"
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,138 @@
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
+ # strip any white space
42
+ @cipher = @cipher.gsub(/ /m, "")
43
+ # normalize indentation
44
+ ciphertext = @cipher.gsub(/\n/, "\n" + @indentation)
45
+ chevron = (args[:use_chevron].nil? || args[:use_chevron]) ? ">\n" : ''
46
+ "#{label_string}#{chevron}" + @indentation + "ENC[#{@encryptor.tag},#{ciphertext}]"
47
+ when :string
48
+ ciphertext = @cipher.gsub(/\n/, "")
49
+ "#{label_string}ENC[#{@encryptor.tag},#{ciphertext}]"
50
+ else
51
+ raise "#{@format} is not a valid format"
52
+ end
53
+ end
54
+
55
+ def to_decrypted(args={})
56
+ label = args[:label]
57
+ label_string = label.nil? ? '' : "#{label}: "
58
+ format = args[:format].nil? ? @format : args[:format]
59
+ index = args[:index].nil? ? '' : "(#{args[:index]})"
60
+ case format
61
+ when :block
62
+ chevron = (args[:use_chevron].nil? || args[:use_chevron]) ? ">\n" : ''
63
+ "#{label_string}#{chevron}" + indentation + "DEC#{index}::#{@encryptor.tag}[" + @plain_text + "]!"
64
+ when :string
65
+ "#{label_string}DEC#{index}::#{@encryptor.tag}[" + @plain_text + "]!"
66
+ else
67
+ raise "#{@format} is not a valid format"
68
+ end
69
+ end
70
+
71
+ def to_plain_text
72
+ @plain_text
73
+ end
74
+
75
+ end
76
+
77
+ class EncTokenType < TokenType
78
+ def create_enc_token(match, type, enc_comma, cipher, indentation = '')
79
+ encryption_scheme = enc_comma.nil? ? Eyaml.default_encryption_scheme : enc_comma.split(",").first
80
+ EncToken.encrypted_value(type, encryption_scheme, cipher, match, indentation)
81
+ end
82
+ end
83
+
84
+ class EncHieraTokenType < EncTokenType
85
+ def initialize
86
+ @regex = /ENC\[(\w+,)?([a-zA-Z0-9\+\/ =\n]+?)\]/
87
+ @string_token_type = EncStringTokenType.new()
88
+ end
89
+ def create_token(string)
90
+ @string_token_type.create_token(string.gsub(/[ \n]/, ''))
91
+ end
92
+ end
93
+
94
+ class EncStringTokenType < EncTokenType
95
+ def initialize
96
+ @regex = /ENC\[(\w+,)?([a-zA-Z0-9\+\/=]+?)\]/
97
+ end
98
+ def create_token(string)
99
+ md = @regex.match(string)
100
+ self.create_enc_token(string, :string, md[1], md[2])
101
+ end
102
+ end
103
+
104
+ class EncBlockTokenType < EncTokenType
105
+ def initialize
106
+ @regex = />\n(\s*)ENC\[(\w+,)?([a-zA-Z0-9\+\/ =\n]+?)\]/
107
+ end
108
+ def create_token(string)
109
+ md = @regex.match(string)
110
+ self.create_enc_token(string, :block, md[2], md[3], md[1])
111
+ end
112
+ end
113
+
114
+ class DecStringTokenType < TokenType
115
+ def initialize
116
+ @regex = /DEC(\(\d+\))?::(\w+)\[(.+?)\]\!/
117
+ end
118
+ def create_token(string)
119
+ md = @regex.match(string)
120
+ EncToken.decrypted_value(:string, md[3], md[2], string, md[1])
121
+ end
122
+ end
123
+
124
+ class DecBlockTokenType < TokenType
125
+ def initialize
126
+ @regex = />\n(\s*)DEC(\(\d+\))?::(\w+)\[(.+?)\]\!/m
127
+ end
128
+ def create_token(string)
129
+ md = @regex.match(string)
130
+ EncToken.decrypted_value(:block, md[4], md[3], string, md[2], md[1])
131
+ EncToken.decrypted_value(:block, md[4], md[3], string, md[2], md[1])
132
+ end
133
+ end
134
+
135
+ end
136
+ end
137
+ end
138
+ 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
@@ -0,0 +1,70 @@
1
+ require 'rubygems'
2
+
3
+ class Hiera
4
+ module Backend
5
+ module Eyaml
6
+ class Plugins
7
+
8
+ @@plugins = []
9
+ @@commands = []
10
+ @@options = []
11
+
12
+ def self.register_options args
13
+ options = args[ :options ]
14
+ plugin = args[ :plugin ]
15
+ options.each do |name, option_hash|
16
+ new_option = {:name => "#{plugin}_#{name}"}
17
+ new_option.merge! option_hash
18
+ @@options << new_option
19
+ end
20
+ end
21
+
22
+ def self.options
23
+ @@options
24
+ end
25
+
26
+ def self.find
27
+
28
+ this_version = Gem::Version.create(Hiera::Backend::Eyaml::VERSION)
29
+ index = Gem::VERSION >= "1.8.0" ? Gem::Specification : Gem.source_index
30
+
31
+ [index].flatten.each do |source|
32
+ specs = Gem::VERSION >= "1.6.0" ? source.latest_specs(true) : source.latest_specs
33
+
34
+ specs.each do |spec|
35
+ next if @@plugins.include? spec
36
+
37
+ dependency = spec.dependencies.find { |d| d.name == "hiera-eyaml" }
38
+ next if dependency && !dependency.requirement.satisfied_by?( this_version )
39
+
40
+ file = nil
41
+ if Gem::VERSION >= "1.8.0"
42
+ file = spec.matches_for_glob("**/eyaml_init.rb").first
43
+ else
44
+ file = Gem.searcher.matching_files(spec, "**/eyaml_init.rb").first
45
+ end
46
+
47
+ next unless file
48
+
49
+ @@plugins << spec
50
+ load file
51
+ end
52
+
53
+ end
54
+
55
+ @@plugins
56
+
57
+ end
58
+
59
+ def self.plugins
60
+ @@plugins
61
+ end
62
+
63
+ def self.commands
64
+ @@commands
65
+ end
66
+
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,126 @@
1
+ require 'base64'
2
+ # require 'hiera/backend/eyaml/subcommands/unknown_command'
3
+
4
+ class Hiera
5
+ module Backend
6
+ module Eyaml
7
+
8
+ class Subcommand
9
+
10
+ class << self
11
+ attr_accessor :global_options, :options, :helptext
12
+ end
13
+
14
+ @@global_options = [
15
+ {:name => :encrypt_method,
16
+ :description => "Override default encryption and decryption method (default is PKCS7)",
17
+ :short => 'n',
18
+ :default => "pkcs7"},
19
+ {:name => :version,
20
+ :description => "Show version information"},
21
+ {:name => :verbose,
22
+ :description => "Be more verbose",
23
+ :short => 'v'},
24
+ {:name => :quiet,
25
+ :description => "Be less verbose",
26
+ :short => 'q'},
27
+ {:name => :help,
28
+ :description => "Information on how to use this command",
29
+ :short => 'h'}
30
+ ]
31
+
32
+ def self.all_options
33
+ options = @@global_options.dup
34
+ options += self.options if self.options
35
+ options += Plugins.options
36
+ options
37
+ end
38
+
39
+ def self.attach_option opt
40
+ self.suboptions += opt
41
+ end
42
+
43
+ def self.find commandname = "unknown_command"
44
+ begin
45
+ require "hiera/backend/eyaml/subcommands/#{commandname.downcase}"
46
+ rescue Exception => e
47
+ require "hiera/backend/eyaml/subcommands/unknown_command"
48
+ return Hiera::Backend::Eyaml::Subcommands::UnknownCommand
49
+ end
50
+ command_module = Module.const_get('Hiera').const_get('Backend').const_get('Eyaml').const_get('Subcommands')
51
+ command_class = Utils.find_closest_class :parent_class => command_module, :class_name => commandname
52
+ command_class || Hiera::Backend::Eyaml::Subcommands::UnknownCommand
53
+ end
54
+
55
+ def self.parse
56
+
57
+ me = self
58
+
59
+ options = Trollop::options do
60
+
61
+ version "Hiera-eyaml version " + Hiera::Backend::Eyaml::VERSION.to_s
62
+ banner ["eyaml #{me.prettyname}: #{me.description}", me.helptext, "Options:"].compact.join("\n\n")
63
+
64
+ me.all_options.each do |available_option|
65
+
66
+ skeleton = {:description => "",
67
+ :short => :none}
68
+
69
+ skeleton.merge! available_option
70
+ opt skeleton[:name],
71
+ skeleton[:desc] || skeleton[:description], #legacy plugins
72
+ :short => skeleton[:short],
73
+ :default => skeleton[:default],
74
+ :type => skeleton[:type]
75
+
76
+ end
77
+
78
+ stop_on Eyaml.subcommands
79
+
80
+ end
81
+
82
+ if options[:verbose]
83
+ Hiera::Backend::Eyaml.verbosity_level += 1
84
+ end
85
+
86
+ if options[:quiet]
87
+ Hiera::Backend::Eyaml.verbosity_level = 0
88
+ end
89
+
90
+ if options[:encrypt_method]
91
+ Hiera::Backend::Eyaml.default_encryption_scheme = options[:encrypt_method]
92
+ end
93
+
94
+ options
95
+
96
+ end
97
+
98
+ def self.validate args
99
+ args
100
+ end
101
+
102
+ def self.description
103
+ "no description"
104
+ end
105
+
106
+ def self.helptext
107
+ "Usage: eyaml #{self.prettyname} [options]"
108
+ end
109
+
110
+ def self.execute
111
+ raise StandardError, "This command is not implemented yet (#{self.to_s.split('::').last})"
112
+ end
113
+
114
+ def self.prettyname
115
+ Utils.snakecase self.to_s.split('::').last
116
+ end
117
+
118
+ def self.hidden?
119
+ false
120
+ end
121
+
122
+ end
123
+
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,29 @@
1
+ require 'hiera/backend/eyaml/subcommand'
2
+
3
+ class Hiera
4
+ module Backend
5
+ module Eyaml
6
+ module Subcommands
7
+
8
+ class Createkeys < Subcommand
9
+
10
+ def self.options
11
+ []
12
+ end
13
+
14
+ def self.description
15
+ "create a set of keys with which to encrypt/decrypt eyaml data"
16
+ end
17
+
18
+ def self.execute
19
+ encryptor = Encryptor.find Eyaml.default_encryption_scheme
20
+ encryptor.create_keys
21
+ nil
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end