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.
- checksums.yaml +4 -4
- data/README.md +10 -6
- data/cerberus_client.gemspec +5 -5
- data/lib/cerberus/assumed_role_credentials_provider_chain.rb +8 -8
- data/lib/cerberus/aws_assumed_role_credentials_provider.rb +144 -0
- data/lib/cerberus/aws_principal_credentials_provider.rb +96 -73
- data/lib/cerberus/cerberus_auth_info.rb +13 -0
- data/lib/cerberus/cerberus_client.rb +138 -0
- data/lib/cerberus/cerberus_client_token.rb +3 -3
- data/lib/cerberus/default_credentials_provider_chain.rb +10 -9
- data/lib/cerberus/default_url_resolver.rb +7 -7
- data/lib/cerberus/env_credentials_provider.rb +5 -5
- data/lib/cerberus_client.rb +27 -37
- data/lib/{cerberus_client → cerberus_utils}/default_logger.rb +1 -1
- data/lib/{cerberus_client → cerberus_utils}/http.rb +10 -9
- data/lib/{cerberus_client → cerberus_utils}/log.rb +1 -1
- data/lib/cerberus_utils/utils.rb +24 -0
- data/lib/cerberus_utils/version.rb +3 -0
- metadata +13 -11
- data/lib/cerberus/aws_role_credentials_provider.rb +0 -239
- data/lib/cerberus/aws_role_info.rb +0 -15
- data/lib/cerberus/vault_client.rb +0 -207
- data/lib/cerberus_client/version.rb +0 -3
@@ -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('../
|
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
|
-
|
24
|
-
|
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('../
|
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
|
-
|
17
|
-
|
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::
|
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
|
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
|
-
|
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
|
-
|
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
|
7
|
+
# The default Cerberus URL resolver - looks for #{CERBERUS_URL_ENV_KEY} in env vars
|
8
8
|
##
|
9
9
|
class DefaultUrlResolver
|
10
10
|
|
11
|
-
|
11
|
+
CERBERUS_URL_ENV_KEY = "CERBERUS_ADDR"
|
12
12
|
|
13
13
|
##
|
14
|
-
# Look for the
|
14
|
+
# Look for the cerberus url in the env var
|
15
15
|
##
|
16
|
-
def
|
17
|
-
|
16
|
+
def get_url
|
17
|
+
url_option = ENV[CERBERUS_URL_ENV_KEY]
|
18
18
|
|
19
|
-
if(
|
20
|
-
return
|
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 #{
|
8
|
+
# The Environment variable credentials provider - looks for #{CERBERUS_TOKEN_ENV_KEY} in env vars
|
9
9
|
##
|
10
10
|
class EnvCredentialsProvider
|
11
11
|
|
12
|
-
|
12
|
+
CERBERUS_TOKEN_ENV_KEY = "CERBERUS_TOKEN"
|
13
13
|
|
14
14
|
##
|
15
|
-
# Look for the
|
15
|
+
# Look for the cerberus token in the env var
|
16
16
|
##
|
17
|
-
def
|
17
|
+
def get_client_token
|
18
18
|
|
19
|
-
tokenOption = ENV[
|
19
|
+
tokenOption = ENV[CERBERUS_TOKEN_ENV_KEY]
|
20
20
|
|
21
21
|
if(tokenOption != nil)
|
22
22
|
return tokenOption
|
data/lib/cerberus_client.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require_relative('
|
2
|
-
require_relative('cerberus/
|
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
|
10
|
+
# Get the cerberus client using the default cerberus_url_resolver and default credentialsProviderChain
|
11
11
|
##
|
12
|
-
def self.
|
13
|
-
|
14
|
-
return Cerberus::
|
15
|
-
|
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
|
20
|
+
# Get the cerberus client using the provided cerberus_url_resolver and the credentialsProviderChain
|
20
21
|
##
|
21
|
-
def self.
|
22
|
-
|
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
|
27
|
+
# Get the cerberus client using the provided cerberus_url_resolver and the credentialsProviderChain
|
27
28
|
##
|
28
|
-
def self.
|
29
|
-
|
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
|
37
|
+
# Get the cerberus client using the provided cerberus_url_resolver and the credentialsProviderChain
|
43
38
|
##
|
44
|
-
def self.
|
45
|
-
return
|
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
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
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
|
-
#
|
19
|
-
#
|
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
|
22
|
+
def make_http_call(uri, method, use_ssl, json_data = nil, headers_map = nil)
|
23
23
|
|
24
24
|
begin
|
25
|
-
|
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(
|
39
|
+
if(json_data != nil); request.body = "#{json_data}"; request['Content-Type'] = "application/json"; end
|
40
40
|
|
41
|
-
if(
|
42
|
-
request['X-Cerberus-Client'] = "CerberusRubyClient/#{
|
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
|
-
|
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
|
@@ -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
|