cerberus_client 1.5.1 → 2.0.0

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.
@@ -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