jerakia 1.2.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,17 +1,15 @@
1
1
  class Jerakia::Datasource
2
- module File
3
- class Json
2
+ class File
3
+ module Json
4
4
  EXTENSION = 'json'.freeze
5
5
 
6
- class << self
7
- require 'json'
8
- def convert(data)
9
- return {} if data.empty?
10
- begin
11
- JSON.load(data)
12
- rescue JSON::ParserError => e
13
- raise Jerakia::FileParseError, "Could not parse JSON content, #{e.message}"
14
- end
6
+ require 'json'
7
+ def convert(data)
8
+ return {} if data.empty?
9
+ begin
10
+ JSON.load(data)
11
+ rescue JSON::ParserError => e
12
+ raise Jerakia::FileParseError, "Could not parse JSON content, #{e.message}"
15
13
  end
16
14
  end
17
15
  end
@@ -1,18 +1,16 @@
1
1
  class Jerakia::Datasource
2
- module File
3
- class Yaml
4
- EXTENSION = 'yaml'.freeze
5
-
6
- class << self
7
- require 'yaml'
8
- def convert(data)
9
- return {} if data.empty?
10
- begin
11
- YAML.load(data)
12
- rescue Psych::SyntaxError => e
13
- raise Jerakia::FileParseError, "Error parsing YAML document: #{e.message}"
14
- end
15
- end
2
+ class File
3
+ module Yaml
4
+ EXTENSION = 'yaml'.freeze
5
+ require 'yaml'
6
+ def convert(data)
7
+ return {} if data.empty?
8
+ begin
9
+ YAML.load(data)
10
+ rescue Psych::SyntaxError => e
11
+ raise Jerakia::FileParseError, "Error parsing YAML document: #{e.message}"
12
+ end
13
+
16
14
  end
17
15
  end
18
16
  end
@@ -1,34 +1,28 @@
1
1
  class Jerakia
2
2
  module Dsl
3
3
  class Lookup
4
- attr_reader :policy
5
4
  attr_reader :request
5
+ attr_reader :scope_object
6
6
  attr_accessor :lookup
7
7
 
8
- def initialize(name, policy, opts = {})
9
- @policy = policy
10
- @request = policy.clone_request
11
- scope = policy.scope
12
- @lookup = Jerakia::Lookup.new(name, opts, @request, scope)
8
+ def initialize(name, request, scope, opts = {})
9
+ @request = request
10
+ @scope_object = scope
11
+ @lookup = Jerakia::Lookup.new(name, opts, request, scope)
13
12
  end
14
13
 
15
- def self.evaluate(name, policy, opts, &block)
16
- lookup_block = new(name, policy, opts)
14
+ def self.evaluate(name, request, scope, opts, &block)
15
+ lookup_block = new(name, request, scope, opts)
17
16
  lookup_block.instance_eval &block
18
- policy.submit_lookup(lookup_block.lookup)
17
+ return lookup_block.lookup
19
18
  end
20
19
 
21
20
  # define the data source for the lookup
22
21
  # @api: public
23
22
  def datasource(name, opts = {})
24
- datasource = Jerakia::Datasource.new(name, lookup, opts)
25
- lookup.datasource = datasource
26
- end
27
-
28
- # give access to the lookup scope object
29
- # @api: public
30
- def scope
31
- lookup.scope
23
+ lookup.datasource = { :name => name, :opts => opts }
24
+ #datasource = Jerakia::Datasource.new(name, lookup, opts)
25
+ #lookup.datasource = datasource
32
26
  end
33
27
 
34
28
  # pass through exposed functions from the main lookup object
@@ -37,6 +31,10 @@ class Jerakia
37
31
  lookup.confine(*args)
38
32
  end
39
33
 
34
+ def scope
35
+ @scope_object.value
36
+ end
37
+
40
38
  def exclude(*args)
41
39
  lookup.exclude(*args)
42
40
  end
@@ -3,14 +3,14 @@ require 'jerakia/cache/file'
3
3
  class Jerakia
4
4
  module Dsl
5
5
  class Policy
6
- def self.evaluate_file(filename, request)
7
- policy = new(request)
6
+ def self.evaluate_file(filename)
7
+ policy = new
8
8
  policy.evaluate_file(filename)
9
9
  policy.instance
10
10
  end
11
11
 
12
- def self.evaluate(request, &block)
13
- policy = new(request)
12
+ def self.evaluate(&block)
13
+ policy = new
14
14
  policy.instance_eval &block
15
15
  policy.instance
16
16
  end
@@ -18,8 +18,7 @@ class Jerakia
18
18
  attr_accessor :request
19
19
  attr_reader :instance
20
20
 
21
- def initialize(req)
22
- @request = req
21
+ def initialize()
23
22
  end
24
23
 
25
24
  def evaluate_file(filename)
@@ -33,7 +32,7 @@ class Jerakia
33
32
  end
34
33
 
35
34
  def policy(name, opts = {}, &block)
36
- @instance = Jerakia::Policy.new(name, opts, request)
35
+ @instance = Jerakia::Policy.new(name, opts)
37
36
  Jerakia::Dsl::Policyblock.evaluate(instance, &block)
38
37
  end
39
38
  end
@@ -51,7 +50,11 @@ class Jerakia
51
50
  end
52
51
 
53
52
  def lookup(name, opts = {}, &block)
54
- Jerakia::Dsl::Lookup.evaluate(name, policy, opts, &block)
53
+ Jerakia.log.debug("Adding lookup #{name} for policy #{policy}")
54
+ policy.lookups << Proc.new do |request, scope|
55
+ Jerakia.log.debug("Invoking lookup #{name}")
56
+ Jerakia::Dsl::Lookup.evaluate(name, request, scope, opts, &block)
57
+ end
55
58
  end
56
59
  end
57
60
  end
@@ -0,0 +1,60 @@
1
+ class Jerakia
2
+ class Encryption
3
+
4
+ @handler = nil
5
+
6
+ class << self
7
+ def handler
8
+ @handler || @handler = self.new
9
+ end
10
+ end
11
+
12
+ attr_reader :loaded
13
+
14
+ def initialize(provider=nil)
15
+ if provider.nil?
16
+ provider = config["provider"]
17
+ end
18
+
19
+ return nil if provider.nil?
20
+
21
+ begin
22
+ require "jerakia/encryption/#{provider}"
23
+ rescue LoadError => e
24
+ raise Jerakia::Error, "Failed to load encryption provider #{provider}"
25
+ end
26
+
27
+ begin
28
+ eval "extend Jerakia::Encryption::#{provider.capitalize}"
29
+ rescue NameError => e
30
+ raise Jerakia::Error, "Encryption provider #{provider} did not provide class"
31
+ end
32
+ @loaded = true
33
+ end
34
+
35
+ def loaded?
36
+ loaded
37
+ end
38
+
39
+ def features?(feature)
40
+ case feature
41
+ when :encrypt
42
+ respond_to?('encrypt')
43
+ when :decrypt
44
+ respond_to?('decrypt')
45
+ else
46
+ false
47
+ end
48
+ end
49
+
50
+ def self.config
51
+ Jerakia.config[:encryption] || {}
52
+ end
53
+
54
+ def config
55
+ self.class.config
56
+ end
57
+
58
+ end
59
+ end
60
+
@@ -0,0 +1,168 @@
1
+ require 'jerakia/util/http'
2
+ require 'json'
3
+ require 'base64'
4
+
5
+ class Jerakia
6
+ class Encryption
7
+ module Vault
8
+ class AuthenticationError < Jerakia::Error
9
+ end
10
+
11
+ attr_reader :approle_token
12
+ attr_reader :vault_ssl_key
13
+ attr_reader :vault_ssl_cert
14
+
15
+ def signiture
16
+ /^vault:v[0-9]:[^ ]+$/
17
+ end
18
+
19
+ def config
20
+ {
21
+ "vault_keyname" => "jerakia",
22
+ "vault_addr" => "https://127.0.0.1:8200",
23
+ "vault_use_ssl" => true,
24
+ "vault_ssl_verify" => true,
25
+ "vault_token" => ENV['VAULT_TOKEN'] || nil,
26
+ "vault_api_version" => 1
27
+ }.merge(self.class.config)
28
+ end
29
+
30
+
31
+
32
+ def vault_url(endpoint)
33
+ uri = []
34
+ uri << config['vault_addr']
35
+ uri << "v#{config['vault_api_version']}"
36
+ uri << endpoint
37
+ uri.flatten.join("/")
38
+ end
39
+
40
+ def login
41
+ Jerakia.log.debug('Requesting new token from Vault server')
42
+ role_id = config['vault_role_id']
43
+ secret_id = config['vault_secret_id']
44
+
45
+ login_data = { "role_id" => role_id }
46
+ login_data['secret_id'] = secret_id unless secret_id.nil?
47
+
48
+ response = vault_post(login_data, :login, false)
49
+ @approle_token = response['auth']['client_token']
50
+ Jerakia.log.debug("Recieved authentication token from vault server, ttl: #{response['auth']['lease_duration']}")
51
+ end
52
+
53
+ def ssl?
54
+ config['vault_use_ssl']
55
+ end
56
+
57
+ def read_file(file)
58
+ raise Jerakia::EncryptionError, "Cannot read #{file}" unless File.exists?(file)
59
+ File.read(file)
60
+ end
61
+
62
+ def ssl_key
63
+ return nil if config['vault_ssl_key'].nil?
64
+ @vault_ssl_key ||= read_file(config['vault_ssl_key'])
65
+ vault_ssl_key
66
+ end
67
+
68
+ def ssl_cert
69
+ return nil if config['vault_ssl_cert'].nil?
70
+ @vault_ssl_cert ||= read_file(config['vault_ssl_cert'])
71
+ vault_ssl_cert
72
+ end
73
+
74
+
75
+ def token_configured?
76
+ not config['vault_token'].nil?
77
+ end
78
+
79
+ def token
80
+ authenticate
81
+ config['vault_token'] || approle_token
82
+ end
83
+
84
+ def authenticate
85
+ unless token_configured?
86
+ login if approle_token.nil?
87
+ end
88
+ end
89
+
90
+ def endpoint(action)
91
+ {
92
+ :decrypt => "transit/decrypt/#{config['vault_keyname']}",
93
+ :encrypt => "transit/encrypt/#{config['vault_keyname']}",
94
+ :login => "auth/approle/login"
95
+ }[action]
96
+ end
97
+
98
+ def url_path(action)
99
+ vault_url(endpoint(action))
100
+ end
101
+
102
+
103
+
104
+ def parse_response(response)
105
+ body = JSON.load(response.body)
106
+ if response.code_type == Net::HTTPOK
107
+ return body
108
+ else
109
+ if response.code == "403"
110
+ raise Jerakia::Encryption::Vault::AuthenticationError, body
111
+ end
112
+ if body['errors'].is_a?(Array)
113
+ message = body['errors'].join("\n")
114
+ else
115
+ message = "Failed to decrypt entry #{body}"
116
+ end
117
+ raise Jerakia::EncryptionError, "Error decrypting data from Vault: #{message}"
118
+ end
119
+ end
120
+
121
+ def vault_post(data, action, use_token=true, headers={})
122
+ url = url_path(action)
123
+ http_options = {}
124
+
125
+ if ssl?
126
+ http_options = {
127
+ :ssl => true,
128
+ :ssl_verify => config['vault_ssl_verify'],
129
+ :ssl_cert => ssl_cert,
130
+ :ssl_key => ssl_key,
131
+ }
132
+ end
133
+
134
+ Jerakia.log.debug("Connecting to vault at #{url}")
135
+ tries = 0
136
+ begin
137
+ headers['X-Vault-Token'] = token if use_token
138
+ tries += 1
139
+ parse_response Jerakia::Util::Http.post(url, data, headers, http_options)
140
+ rescue Jerakia::Encryption::Vault::AuthenticationError => e
141
+ Jerakia.log.debug("Encountered Jerakia::Encryption::Vault::AuthenticationError, retrying with new token (#{tries})")
142
+
143
+ login
144
+ retry if tries < 2
145
+ raise
146
+ rescue Jerakia::HTTPError => e
147
+ raise Jerakia::EncryptionError, "Error connecting to Vault service: #{e.message}"
148
+ end
149
+ end
150
+
151
+ def decrypt(string)
152
+ response = vault_post({ 'ciphertext' => string}, :decrypt)
153
+ response_data=response['data']
154
+ Base64.decode64(response_data['plaintext'])
155
+ end
156
+
157
+ def encrypt(plain)
158
+ encoded = Base64.encode64(plain)
159
+ response = vault_post({ 'plaintext' => encoded}, :encrypt)
160
+ response_data=response['data']
161
+ response_data['ciphertext']
162
+ end
163
+
164
+
165
+ end
166
+ end
167
+ end
168
+
data/lib/jerakia/error.rb CHANGED
@@ -13,4 +13,14 @@ class Jerakia
13
13
 
14
14
  class FileParseError < Jerakia::Error
15
15
  end
16
+
17
+ class DatasourceArgumentError < Jerakia::Error
18
+ end
19
+
20
+ class HTTPError < Jerakia::Error
21
+ end
22
+
23
+ class EncryptionError < Jerakia::Error
24
+ end
25
+
16
26
  end
@@ -8,15 +8,27 @@ class Jerakia
8
8
  class Launcher
9
9
  attr_reader :request
10
10
  attr_reader :answer
11
+ attr_reader :policies
11
12
 
12
- def initialize(req)
13
- @request = req
13
+ def initialize
14
+ @policies = {}
15
+ policy_files.each do |policy_file|
16
+ policy = Jerakia::Dsl::Policy.evaluate_file(policy_file)
17
+ raise Jerakia::PolicyError, "Policy #{policy.name} declared twice" if @policies[policy.name]
18
+ @policies[policy.name] = policy
19
+ end
14
20
  end
15
21
 
16
- def evaluate(&block)
17
- policy = Jerakia::Dsl::Policy.evaluate(request, &block)
18
- policy.execute
19
- @answer = policy.answer
22
+ def policy_dir
23
+ Jerakia.config.policydir
24
+ end
25
+
26
+ def policy_files
27
+ Dir[File.join(policy_dir, '*.rb')]
28
+ end
29
+
30
+ def self.evaluate(&block)
31
+ Jerakia::Dsl::Policy.evaluate(&block)
20
32
  end
21
33
 
22
34
  def invoke_from_file