cerberus_client 1.5.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,13 @@
1
+ module Cerberus
2
+ class CerberusAuthInfo
3
+ attr_reader :iam_principal_arn
4
+ attr_reader :region
5
+ attr_reader :credentials
6
+
7
+ def initialize(iam_principal_arn, region, credentials = nil)
8
+ @iam_principal_arn = iam_principal_arn
9
+ @region = region
10
+ @credentials = credentials
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,138 @@
1
+
2
+
3
+ module Cerberus
4
+
5
+ require_relative('../cerberus_utils/log')
6
+ require_relative('../cerberus_utils/utils')
7
+ require_relative('exception/http_error')
8
+ require_relative('exception/ambiguous_vault_bad_request')
9
+ require_relative('default_credentials_provider_chain')
10
+ require('json')
11
+
12
+ ##
13
+ # Client for interacting with the Cerberus API
14
+ ##
15
+ class CerberusClient
16
+
17
+ # relative path to the Cerberus secrets API
18
+ SECRET_PATH_PREFIX = "/v1/secret/"
19
+ SECRET_MAP_DATA_KEY = CERBERUS_LIST_DATA_KEY = "data"
20
+ CERBERUS_TOKEN_HEADER_KEY = 'X-Vault-Token'
21
+ CERBERUS_ERRORS_KEY = "errors"
22
+ CERBERUS_PERMISSION_DENIED_ERR = "permission denied"
23
+ CERBERUS_LIST_KEYS_KEY = "keys"
24
+ CERBERUS_LIST_PARAM_KEY = "list"
25
+ SLASH = "/"
26
+
27
+ attr_reader :cerberus_base_url
28
+ attr_reader :credentials_provider
29
+
30
+ ##
31
+ # Init with the base URL for cerberus
32
+ ##
33
+ def initialize(cerberus_url_resolver, credentials_provider_chain)
34
+
35
+ require 'net/https'
36
+
37
+ @cerberus_base_url = CerberusUtils::get_url_from_resolver(cerberus_url_resolver)
38
+ @credentials_provider = credentials_provider_chain.get_credentials_provider
39
+
40
+ end # initialize
41
+
42
+ ##
43
+ # Read operation for a specified path.
44
+ ##
45
+ def read(path)
46
+ begin
47
+ response = read_value_from_cerberus(SECRET_PATH_PREFIX + path)
48
+ CerberusUtils::Log.instance.debug("CerberusClient::read(path) HTTP response: #{response.code}, #{response.message}")
49
+ response.body
50
+
51
+ rescue => ex
52
+ CerberusUtils::Log.instance.error("CerberusClient::read(#{path}) unhandled exception trying to read: #{ex.message}")
53
+ raise ex
54
+ end
55
+ end # read
56
+
57
+ ##
58
+ # Returns a list of key names at the specified location. Folders are suffixed with /.
59
+ # The input must be a folder; list on a file will return nil
60
+ ##
61
+ def list(path)
62
+ begin
63
+ response = read_value_from_cerberus(SECRET_PATH_PREFIX + path + "?list=true")
64
+
65
+ CerberusUtils::Log.instance.debug("CerberusClient::list(#{path}) HTTP response: #{response.code}, #{response.message}")
66
+
67
+ json_response_body = JSON.parse(response.body)
68
+ pathList = json_response_body[CERBERUS_LIST_DATA_KEY][CERBERUS_LIST_KEYS_KEY]
69
+ CerberusUtils::Log.instance.debug("CerberusClient::list returning #{pathList.join(", ")} ")
70
+ pathList
71
+
72
+ rescue => ex
73
+
74
+ # check to see if we threw the Http error with a response object
75
+ response = (ex.instance_of?(Cerberus::Exception::HttpError)) ? ex.response : nil
76
+ if(!response.nil? && response.code.to_i == 404)
77
+ return nil
78
+ end
79
+
80
+ CerberusUtils::Log.instance.error("CerberusClient::list(#{path}) unhandled exception trying to read: #{ex.message}")
81
+ raise ex
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ ##
88
+ # Do an http request to Cerberus using the relative URI passed in
89
+ ##
90
+ def read_value_from_cerberus(releative_uri)
91
+
92
+ url = URI(@cerberus_base_url + releative_uri)
93
+ use_ssl, = ! ("#{@cerberus_base_url}".include? "localhost")
94
+
95
+ begin
96
+ headers_map = {CERBERUS_TOKEN_HEADER_KEY => @credentials_provider.get_client_token}
97
+ response = CerberusUtils::Http.new.make_http_call(url, "GET", use_ssl, nil, headers_map)
98
+
99
+ rescue Cerberus::Exception::HttpError => ex
100
+ # Since Vault wants to pass back 400 bad request for both paths we don't have access to
101
+ # and paths that don't actually exist at all, I'm sending back a specific error so that implementing clients
102
+ # can at least understand the situation they find themselves in
103
+ #
104
+ # This client could actually work around this problem by first getting a list of all paths we have access to and
105
+ # determining if the path exists in that list. If not, 404 (which is more appropriate than 400).
106
+ # TODO: implement "list > check for path" work around <-- NOTE: This is a relatively expensive operation
107
+ if(!ex.response.nil? && (ex.response.code.to_i == 400))
108
+ raise Exception::AmbiguousVaultBadRequest.new
109
+ else
110
+ raise ex
111
+ end
112
+
113
+ end
114
+
115
+ response
116
+
117
+ end # read_value_from_cerberus
118
+
119
+ ##
120
+ # Parse out the permission errors json
121
+ ##
122
+ def has_permission_errors?(json_errors_msg)
123
+ begin
124
+ json = JSON.parse(json_errors_msg)
125
+ json[CERBERUS_ERRORS_KEY].each { |err|
126
+ if(err == CERBERUS_PERMISSION_DENIED_ERR)
127
+ return true
128
+ end
129
+ }
130
+ rescue => ex
131
+ CerberusUtils::Log.instance.warn(
132
+ "CerberusClient::hasPermissionErrors? called and exception thrown parsing #{json_errors_msg}: #{ex.message}")
133
+ return false
134
+ end
135
+ end
136
+
137
+ end
138
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Cerberus
4
4
 
5
- require_relative('../cerberus_client/log')
5
+ require_relative('../cerberus_utils/log')
6
6
 
7
7
  ##
8
8
  # Object to hold the Cerberus client token credentials and check for expiration and refresh
@@ -20,8 +20,8 @@ module Cerberus
20
20
  @createTime = Time.now
21
21
  @cacheLifetimeSec = cacheLifetimeSec
22
22
  @policies = policiesArray
23
- CerberusClient::Log.instance.debug("AwsCredentials cache lifetime set to #{@cacheLifetimeSec} seconds")
24
- CerberusClient::Log.instance.debug("AwsCredentials policies: #{@policies.join(", ")}")
23
+ CerberusUtils::Log.instance.debug("AwsCredentials cache lifetime set to #{@cacheLifetimeSec} seconds")
24
+ CerberusUtils::Log.instance.debug("AwsCredentials policies: #{@policies.join(", ")}")
25
25
  @authToken = authToken
26
26
  end
27
27
 
@@ -1,8 +1,8 @@
1
1
  require_relative('../cerberus_client')
2
- require_relative('../cerberus_client/log')
2
+ require_relative('../cerberus_utils/log')
3
+ require_relative('../cerberus_utils/utils')
3
4
  require_relative('exception/no_value_error')
4
5
  require_relative('exception/no_valid_providers')
5
- require_relative('aws_role_credentials_provider')
6
6
  require_relative('aws_principal_credentials_provider')
7
7
  require_relative('env_credentials_provider')
8
8
 
@@ -13,24 +13,25 @@ module Cerberus
13
13
  ##
14
14
  class DefaultCredentialsProviderChain
15
15
 
16
- def initialize(urlResolver, instanceMdSvcBaseUrl = nil)
17
- vaultBaseUrl = CerberusClient.getUrlFromResolver(urlResolver)
16
+ # AWS metadata instance URL
17
+ AWS_EC2_METADATA_URL = "http://169.254.169.254/latest/meta-data"
18
+
19
+ def initialize(url_resolver, region = nil, instance_metadata_url = AWS_EC2_METADATA_URL)
18
20
 
19
21
  # return default array of providers
20
22
  @providers = [Cerberus::EnvCredentialsProvider.new,
21
- Cerberus::AwsRoleCredentialsProvider.new(vaultBaseUrl, instanceMdSvcBaseUrl),
22
- Cerberus::AwsPrincipalCredentialsProvider.new(vaultBaseUrl)]
23
+ Cerberus::AwsPrincipalCredentialsProvider.new(url_resolver, region, instance_metadata_url)]
23
24
  end
24
25
 
25
26
  ##
26
27
  # Return the first provider in the default hierarchy that has a valid token
27
28
  ##
28
- def getCredentialsProvider
29
+ def get_credentials_provider
29
30
  @providers.each { |p|
30
31
  begin
31
32
  # if token is assigned, that's the provider we want.
32
33
  # providers must throw NoValueError so that we can fall to the next provider if necessary
33
- CerberusClient.getCredentialsFromProvider(p)
34
+ CerberusUtils::get_credentials_from_provider(p)
34
35
  return p
35
36
 
36
37
  rescue Cerberus::Exception::NoValueError
@@ -39,7 +40,7 @@ module Cerberus
39
40
  }
40
41
 
41
42
  # we should have found and returned a valid provider above, else there's a problem
42
- CerberusClient::Log.instance.error(" could not find a valid provider")
43
+ CerberusUtils::Log.instance.error("Could not find a valid provider")
43
44
  raise Cerberus::Exception::NoValidProviders.new
44
45
  end
45
46
  end
@@ -4,20 +4,20 @@ module Cerberus
4
4
  require_relative('exception/no_value_error')
5
5
 
6
6
  ##
7
- # The default Vault URL resolver - looks for #{CERBERUS_VAULT_URL_ENV_KEY} in env vars
7
+ # The default Cerberus URL resolver - looks for #{CERBERUS_URL_ENV_KEY} in env vars
8
8
  ##
9
9
  class DefaultUrlResolver
10
10
 
11
- CERBERUS_VAULT_URL_ENV_KEY = "CERBERUS_ADDR"
11
+ CERBERUS_URL_ENV_KEY = "CERBERUS_ADDR"
12
12
 
13
13
  ##
14
- # Look for the vault url in the env var
14
+ # Look for the cerberus url in the env var
15
15
  ##
16
- def getUrl
17
- urlOption = ENV[CERBERUS_VAULT_URL_ENV_KEY]
16
+ def get_url
17
+ url_option = ENV[CERBERUS_URL_ENV_KEY]
18
18
 
19
- if(urlOption != nil)
20
- return urlOption
19
+ if(url_option != nil)
20
+ return url_option
21
21
  else
22
22
  raise Cerberus::Exception::NoValueError
23
23
  end
@@ -5,18 +5,18 @@ module Cerberus
5
5
  require_relative('exception/no_value_error')
6
6
 
7
7
  ##
8
- # The Environment variable credentials provider - looks for #{CERBERUS_VAULT_TOKEN_ENV_KEY} in env vars
8
+ # The Environment variable credentials provider - looks for #{CERBERUS_TOKEN_ENV_KEY} in env vars
9
9
  ##
10
10
  class EnvCredentialsProvider
11
11
 
12
- CERBERUS_VAULT_TOKEN_ENV_KEY = "CERBERUS_TOKEN"
12
+ CERBERUS_TOKEN_ENV_KEY = "CERBERUS_TOKEN"
13
13
 
14
14
  ##
15
- # Look for the vault token in the env var
15
+ # Look for the cerberus token in the env var
16
16
  ##
17
- def getClientToken
17
+ def get_client_token
18
18
 
19
- tokenOption = ENV[CERBERUS_VAULT_TOKEN_ENV_KEY]
19
+ tokenOption = ENV[CERBERUS_TOKEN_ENV_KEY]
20
20
 
21
21
  if(tokenOption != nil)
22
22
  return tokenOption
@@ -1,5 +1,5 @@
1
- require_relative('cerberus_client/version')
2
- require_relative('cerberus/vault_client')
1
+ require_relative('cerberus_utils/version')
2
+ require_relative('cerberus/cerberus_client')
3
3
  require_relative('cerberus/default_url_resolver')
4
4
  require_relative('cerberus/default_credentials_provider_chain')
5
5
  require_relative('cerberus/assumed_role_credentials_provider_chain')
@@ -7,56 +7,46 @@ require_relative('cerberus/assumed_role_credentials_provider_chain')
7
7
  module CerberusClient
8
8
 
9
9
  ##
10
- # Get the vault client using the default vaultUrlResolver and default credentialsProviderChain
10
+ # Get the cerberus client using the default cerberus_url_resolver and default credentialsProviderChain
11
11
  ##
12
- def self.getDefaultVaultClient()
13
- vaultUrlResolver = Cerberus::DefaultUrlResolver.new
14
- return Cerberus::VaultClient.new(vaultUrlResolver,
15
- Cerberus::DefaultCredentialsProviderChain.new(vaultUrlResolver))
12
+ def self.get_default_cerberus_client()
13
+ cerberus_url_resolver = Cerberus::DefaultUrlResolver.new
14
+ return Cerberus::CerberusClient.new(
15
+ cerberus_url_resolver,
16
+ Cerberus::DefaultCredentialsProviderChain.new(cerberus_url_resolver))
16
17
  end
17
18
 
18
19
  ##
19
- # Get the vault client using the provided vaultUrlResolver and the credentialsProviderChain
20
+ # Get the cerberus client using the provided cerberus_url_resolver and the credentialsProviderChain
20
21
  ##
21
- def self.getVaultClientWithUrlResolver(vaultUrlResolver)
22
- return Cerberus::VaultClient.new(vaultUrlResolver, Cerberus::DefaultCredentialsProviderChain.new(vaultUrlResolver))
22
+ def self.get_cerberus_client_with_url_resolver(cerberus_url_resolver)
23
+ return Cerberus::CerberusClient.new(cerberus_url_resolver, Cerberus::DefaultCredentialsProviderChain.new(cerberus_url_resolver))
23
24
  end
24
25
 
25
26
  ##
26
- # Get the vault client using the provided vaultUrlResolver and the credentialsProviderChain
27
+ # Get the cerberus client using the provided cerberus_url_resolver and the credentialsProviderChain
27
28
  ##
28
- def self.getVaultClient(vaultUrlResolver, credentialsProviderChain)
29
- return Cerberus::VaultClient.new(vaultUrlResolver, credentialsProviderChain)
29
+ def self.get_cerberus_client_with_metadata_url(ec2_metadata_service_url)
30
+ cerberus_url_resolver = Cerberus::DefaultUrlResolver.new
31
+ return Cerberus::CerberusClient.new(
32
+ cerberus_url_resolver,
33
+ Cerberus::DefaultCredentialsProviderChain.new(cerberus_url_resolver, nil, ec2_metadata_service_url))
30
34
  end
31
35
 
32
- def self.getVaultClientForAssumedRole(vaultUrlResolver, roleName, roleRegion, roleAccountId)
33
- return Cerberus::VaultClient.new(vaultUrlResolver, Cerberus::AssumedRoleCredentialsProviderChain.new(vaultUrlResolver,
34
- nil,
35
- roleName,
36
- roleRegion,
37
- roleAccountId))
38
- end
39
-
40
-
41
36
  ##
42
- # Get credentials from implementing credentialProvider
37
+ # Get the cerberus client using the provided cerberus_url_resolver and the credentialsProviderChain
43
38
  ##
44
- def self.getCredentialsFromProvider(credentialProvider)
45
- return credentialProvider.getClientToken
39
+ def self.get_cerberus_client(cerberus_url_resolver, credentialsProviderChain)
40
+ return Cerberus::CerberusClient.new(cerberus_url_resolver, credentialsProviderChain)
46
41
  end
47
42
 
48
- ##
49
- # Get credentials provider from chain implementing get getCredentialsProvider
50
- ##
51
- def self.getCredentialsProviderFromChain(credentialProviderChain)
52
- return credentialProviderChain.getCredentialsProvider
53
- end
54
-
55
- ##
56
- # Get url from implementing url resolver
57
- ##
58
- def self.getUrlFromResolver(vaultUrlResolver)
59
- return vaultUrlResolver.getUrl
43
+ def self.get_cerberus_client_for_assumed_role(cerberus_url_resolver, iam_principal_arn, region)
44
+ return Cerberus::CerberusClient.new(
45
+ cerberus_url_resolver,
46
+ Cerberus::AssumedRoleCredentialsProviderChain.new(
47
+ cerberus_url_resolver,
48
+ iam_principal_arn,
49
+ region))
60
50
  end
61
51
 
62
52
  end # CerberusClient module
@@ -1,5 +1,5 @@
1
1
 
2
- module CerberusClient
2
+ module CerberusUtils
3
3
 
4
4
  require('logger')
5
5
 
@@ -1,5 +1,5 @@
1
1
 
2
- module CerberusClient
2
+ module CerberusUtils
3
3
 
4
4
  require_relative('../cerberus/exception/http_error')
5
5
  require('net/http')
@@ -15,14 +15,14 @@ module CerberusClient
15
15
  # Generic HTTP handler for Cerberus Client needs
16
16
  # uri: the fully qualified URI object to call
17
17
  # method: the HTTP method to use'
18
- # useSSL: boolean - use HTTPS or not
19
- # jsonData: if not nil, made the request body and 'Content-Type' header is set to "application/json"
18
+ # use_ssl: boolean - use HTTPS or not
19
+ # json_data: if not nil, made the request body and 'Content-Type' header is set to "application/json"
20
20
  # headers: if not nil, should be a HashMap of header Key, Values
21
21
  ##
22
- def doHttp(uri, method, useSSL, jsonData = nil, headersMap = nil)
22
+ def make_http_call(uri, method, use_ssl, json_data = nil, headers_map = nil)
23
23
 
24
24
  begin
25
- CerberusClient::Log.instance.debug("Http::doHttp -> uri: #{uri}, method: #{method}, useSSL: #{useSSL}, jsonData: #{jsonData}")
25
+ CerberusUtils::Log.instance.debug("Http::make_http_call -> uri: #{uri}, method: #{method}, use_ssl: #{use_ssl}, json_data: #{json_data}")
26
26
 
27
27
  http = Net::HTTP.new(uri.host, uri.port)
28
28
 
@@ -36,12 +36,13 @@ module CerberusClient
36
36
  raise NotImplementedError
37
37
  end
38
38
 
39
- if(jsonData != nil); request.body = "#{jsonData}"; request['Content-Type'] = "application/json"; end
39
+ if(json_data != nil); request.body = "#{json_data}"; request['Content-Type'] = "application/json"; end
40
40
 
41
- if(headersMap != nil); headersMap.each{ |headerKey, headerValue| request[headerKey] = headerValue } end
42
- request['X-Cerberus-Client'] = "CerberusRubyClient/#{CerberusClient::VERSION}"
41
+ if(headers_map != nil); headers_map.each{ |headerKey, headerValue| request[headerKey] = headerValue } end
42
+ request['X-Cerberus-Client'] = "CerberusRubyClient/#{CerberusUtils::VERSION}"
43
43
 
44
- http.use_ssl = useSSL
44
+ puts request
45
+ http.use_ssl = use_ssl
45
46
  response = http.request(request)
46
47
 
47
48
  # this is just for convenience handling down the stack... response object inclucded with the exception
@@ -1,4 +1,4 @@
1
- module CerberusClient
1
+ module CerberusUtils
2
2
 
3
3
  require_relative('default_logger')
4
4
  require('singleton')
@@ -0,0 +1,24 @@
1
+ module CerberusUtils
2
+
3
+ ##
4
+ # Get credentials from implementing credential_provider
5
+ ##
6
+ def self.get_credentials_from_provider(credential_provider)
7
+ return credential_provider.get_client_token
8
+ end
9
+
10
+ ##
11
+ # Get credentials provider from chain implementing get get_credentials_provider
12
+ ##
13
+ def self.get_credentials_provider_from_chain(credential_provider_chain)
14
+ return credential_provider_chain.get_credentials_provider
15
+ end
16
+
17
+ ##
18
+ # Get url from implementing url resolver
19
+ ##
20
+ def self.get_url_from_resolver(cerberus_url_resolver)
21
+ return cerberus_url_resolver.get_url
22
+ end
23
+
24
+ end