settings_reader-vault_resolver 0.2.4 → 0.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -1
- data/README.md +7 -1
- data/codecov.yml +12 -0
- data/lib/settings_reader/vault_resolver/cache.rb +4 -0
- data/lib/settings_reader/vault_resolver/configuration.rb +71 -0
- data/lib/settings_reader/vault_resolver/engines/abstract.rb +64 -0
- data/lib/settings_reader/vault_resolver/engines/auth.rb +36 -0
- data/lib/settings_reader/vault_resolver/engines/database.rb +29 -0
- data/lib/settings_reader/vault_resolver/engines/kv2.rb +25 -0
- data/lib/settings_reader/vault_resolver/entry.rb +10 -10
- data/lib/settings_reader/vault_resolver/helpers/vault_authentication.rb +23 -0
- data/lib/settings_reader/vault_resolver/instance.rb +11 -25
- data/lib/settings_reader/vault_resolver/logging.rb +13 -16
- data/lib/settings_reader/vault_resolver/patches/authenticate.rb +1 -1
- data/lib/settings_reader/vault_resolver/refresher.rb +21 -20
- data/lib/settings_reader/vault_resolver/refresher_observer.rb +30 -0
- data/lib/settings_reader/vault_resolver/version.rb +1 -1
- data/lib/settings_reader/vault_resolver.rb +23 -30
- metadata +10 -3
- data/lib/settings_reader/vault_resolver/helpers/k8s_auth.rb +0 -43
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e24591d4c66561cb24e3e12d124d2c4f4ca90236112f6324f709883297e692f
|
4
|
+
data.tar.gz: df5df6353fb0169385697bdb2c6b6e6b1ca082a7cc99e99e166cf29ad38354bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b08d59ab15c02f04f0204e071da10b72aefdd1fe5ef0a70e3902c94487993aadf30afb852a68de9af984f76624dc032d2d4a79e71bce27163782725de15ebff
|
7
|
+
data.tar.gz: a56e1ef3320ba333755c27aa31aea29a51c82c8f6cbdb32cc0f5a287e93636286ee2b4a51ef950318a7029e6ff46bc52199e42832aecfe9661803398f4f9fc74
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,39 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.4.0]
|
4
|
+
### Breaking changes
|
5
|
+
- Reworked authentication helpers interface
|
6
|
+
|
7
|
+
### New features
|
8
|
+
- Retry secret retrieval and renewal
|
9
|
+
- Capture more vault exceptions including connectivity errors
|
10
|
+
- Introduce vault engine adapter concept
|
11
|
+
- Separate kv, database, and auth engine logic
|
12
|
+
|
13
|
+
## [0.3.0]
|
14
|
+
### Breaking changes
|
15
|
+
- Require configuration before use
|
16
|
+
|
17
|
+
### New features
|
18
|
+
- Gem configurations
|
19
|
+
- Require configuration before use
|
20
|
+
- Report renew errors via configuration listeners
|
21
|
+
|
22
|
+
### Fixes
|
23
|
+
- Cleanup logging
|
24
|
+
|
25
|
+
## [0.2.4]
|
26
|
+
### Fixes
|
27
|
+
- Fix refresher task logging
|
28
|
+
|
29
|
+
## [0.2.3]
|
30
|
+
### Fixes
|
31
|
+
- Fix logging setup when gem loaded before rails
|
32
|
+
|
33
|
+
## [0.2.2]
|
34
|
+
### New features
|
35
|
+
- Add logging to gem
|
36
|
+
|
3
37
|
## [0.2.1]
|
4
38
|
### Fixes
|
5
39
|
- Use default k8s auth route without namespace
|
@@ -21,7 +55,12 @@
|
|
21
55
|
- Secrets caching
|
22
56
|
- Automatic secrets lease renewal
|
23
57
|
|
24
|
-
[Unreleased]: https://github.com/matic-insurance/settings_reader-vault_resolver/compare/0.
|
58
|
+
[Unreleased]: https://github.com/matic-insurance/settings_reader-vault_resolver/compare/0.4.0...HEAD
|
59
|
+
[0.4.0]: https://github.com/matic-insurance/settings_reader-vault_resolver/commits/0.4.0
|
60
|
+
[0.3.0]: https://github.com/matic-insurance/settings_reader-vault_resolver/commits/0.3.0
|
61
|
+
[0.2.4]: https://github.com/matic-insurance/settings_reader-vault_resolver/commits/0.2.4
|
62
|
+
[0.2.3]: https://github.com/matic-insurance/settings_reader-vault_resolver/commits/0.2.3
|
63
|
+
[0.2.2]: https://github.com/matic-insurance/settings_reader-vault_resolver/commits/0.2.2
|
25
64
|
[0.2.1]: https://github.com/matic-insurance/settings_reader-vault_resolver/commits/0.2.1
|
26
65
|
[0.2.0]: https://github.com/matic-insurance/settings_reader-vault_resolver/commits/0.2.0
|
27
66
|
[0.1.1]: https://github.com/matic-insurance/settings_reader-vault_resolver/commits/0.1.1
|
data/README.md
CHANGED
@@ -28,7 +28,13 @@ Vault.token = 'MY_SUPER_SECRET_TOKEN'
|
|
28
28
|
|
29
29
|
#Load Settings Reader and configure resolver
|
30
30
|
AppSettings = SettingsReader.load do |config|
|
31
|
-
# ...
|
31
|
+
# ... SettingReader configurations
|
32
|
+
|
33
|
+
# Configure vault resolver
|
34
|
+
SettingsReader::VaultResolver.configure do |vault_resolver_config|
|
35
|
+
vault_resolver_config.logger = Rails.logger
|
36
|
+
# ... VaultResolver configurations
|
37
|
+
end
|
32
38
|
|
33
39
|
# Add vault resolver as one of resolvers
|
34
40
|
config.resolvers << SettingsReader::VaultResolver.resolver
|
data/codecov.yml
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module SettingsReader
|
2
|
+
module VaultResolver
|
3
|
+
# Configurations for vault resolver
|
4
|
+
class Configuration
|
5
|
+
# Logger for gem
|
6
|
+
# Default: Logger.new(STDOUT, level: Logger::ERROR)
|
7
|
+
attr_accessor :logger
|
8
|
+
|
9
|
+
# How many times to retry retrieval of the secret
|
10
|
+
# Default: 2
|
11
|
+
attr_accessor :retrieval_retries
|
12
|
+
|
13
|
+
# How often do we check if secret lease is about to expire
|
14
|
+
# Default: 60seconds
|
15
|
+
attr_accessor :lease_refresh_interval
|
16
|
+
|
17
|
+
# Time before expiration when we try to renew the lease
|
18
|
+
# Default: 300seconds
|
19
|
+
attr_accessor :lease_renew_delay
|
20
|
+
|
21
|
+
# How many times to retry renew of the secret
|
22
|
+
# Default: 4
|
23
|
+
attr_accessor :lease_renew_retries
|
24
|
+
|
25
|
+
# Block to be executed when lease is refreshed
|
26
|
+
# Default: empty proc
|
27
|
+
attr_accessor :lease_renew_success_listener
|
28
|
+
|
29
|
+
# Block to be executed when lease is not refreshed
|
30
|
+
# Default: empty proc
|
31
|
+
attr_accessor :lease_renew_error_listener
|
32
|
+
|
33
|
+
def initialize
|
34
|
+
@logger = Logger.new($stdout, level: Logger::ERROR)
|
35
|
+
@retrieval_retries = 2
|
36
|
+
@lease_refresh_interval = 60
|
37
|
+
@lease_renew_delay = 300
|
38
|
+
@lease_renew_retries = 4
|
39
|
+
@lease_renew_error_listener = proc {}
|
40
|
+
@lease_renew_success_listener = proc {}
|
41
|
+
end
|
42
|
+
|
43
|
+
def setup_lease_refresher(cache, previous_task = nil)
|
44
|
+
previous_task&.shutdown
|
45
|
+
|
46
|
+
timer_task = Concurrent::TimerTask.new(execution_interval: lease_refresh_interval) do
|
47
|
+
SettingsReader::VaultResolver::Refresher.new(cache, self).refresh
|
48
|
+
end
|
49
|
+
timer_task.add_observer(SettingsReader::VaultResolver::RefresherObserver.new(self))
|
50
|
+
timer_task.execute
|
51
|
+
timer_task
|
52
|
+
end
|
53
|
+
|
54
|
+
def vault_engines
|
55
|
+
@vault_engines ||= [
|
56
|
+
SettingsReader::VaultResolver::Engines::KV2.new(self),
|
57
|
+
SettingsReader::VaultResolver::Engines::Database.new(self),
|
58
|
+
SettingsReader::VaultResolver::Engines::Auth.new(self)
|
59
|
+
]
|
60
|
+
end
|
61
|
+
|
62
|
+
def vault_engine_for(address)
|
63
|
+
unless (engine = vault_engines.detect { |e| e.retrieves?(address) })
|
64
|
+
raise SettingsReader::VaultResolver::Error, "Unknown engine for #{address}"
|
65
|
+
end
|
66
|
+
|
67
|
+
engine
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module SettingsReader
|
2
|
+
module VaultResolver
|
3
|
+
module Engines
|
4
|
+
# Abstract interface for Vault Backends
|
5
|
+
class Abstract
|
6
|
+
include Logging
|
7
|
+
|
8
|
+
attr_reader :config
|
9
|
+
|
10
|
+
def initialize(config)
|
11
|
+
@config = config
|
12
|
+
end
|
13
|
+
|
14
|
+
def retrieves?(_address)
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
def get(address)
|
19
|
+
return unless (vault_secret = get_secret_with_retries(address))
|
20
|
+
|
21
|
+
wrap_secret(address, vault_secret)
|
22
|
+
rescue Vault::VaultError => e
|
23
|
+
raise SettingsReader::VaultResolver::Error, e.message
|
24
|
+
end
|
25
|
+
|
26
|
+
def renew(entry)
|
27
|
+
return unless entry.leased?
|
28
|
+
|
29
|
+
new_secret = renew_lease_with_retries(entry)
|
30
|
+
entry.update_renewed(new_secret)
|
31
|
+
true
|
32
|
+
rescue Vault::VaultError => e
|
33
|
+
raise SettingsReader::VaultResolver::Error, e.message
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def get_secret_with_retries(address)
|
39
|
+
Vault.with_retries(Vault::HTTPConnectionError, attempts: config.retrieval_retries) do
|
40
|
+
get_secret(address)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def renew_lease_with_retries(address)
|
45
|
+
Vault.with_retries(Vault::HTTPConnectionError, attempts: config.lease_renew_retries) do
|
46
|
+
renew_lease(address)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_secret(address)
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
53
|
+
|
54
|
+
def renew_lease(entry)
|
55
|
+
raise NotImplementedError
|
56
|
+
end
|
57
|
+
|
58
|
+
def wrap_secret(address, secret)
|
59
|
+
SettingsReader::VaultResolver::Entry.new(address, secret)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module SettingsReader
|
2
|
+
module VaultResolver
|
3
|
+
module Engines
|
4
|
+
# Adapter to retrieve / renew auth tokens
|
5
|
+
class Auth < Abstract
|
6
|
+
MOUNT = 'auth'.freeze
|
7
|
+
K8S_AUTH = 'kubernetes/login'.freeze
|
8
|
+
|
9
|
+
def retrieves?(address)
|
10
|
+
address.mount == MOUNT
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def get_secret(address)
|
16
|
+
return k8s_auth(address) if address.path == K8S_AUTH
|
17
|
+
|
18
|
+
raise SettingsReader::VaultResolver::Error, "Unsupported auth backed for #{address}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def renew_lease(_entry)
|
22
|
+
secret = Vault.client.auth_token.renew_self
|
23
|
+
secret&.auth
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def k8s_auth(address)
|
29
|
+
options = { route: address.options['route'], service_token_path: address.options['service_token_path'] }
|
30
|
+
secret = Vault.auth.kubernetes(address.options['role'], **options)
|
31
|
+
secret&.auth
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module SettingsReader
|
2
|
+
module VaultResolver
|
3
|
+
module Engines
|
4
|
+
# Adapter to retrieve / renew secret from database engine
|
5
|
+
class Database < Abstract
|
6
|
+
MOUNT = 'database'.freeze
|
7
|
+
|
8
|
+
def retrieves?(address)
|
9
|
+
address.mount == MOUNT
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def get_secret(address)
|
15
|
+
debug { "Fetching new database secret at: #{address}" }
|
16
|
+
Vault.logical.read(address.full_path)
|
17
|
+
rescue Vault::HTTPClientError => e
|
18
|
+
return nil if e.message.include?('* unknown role')
|
19
|
+
|
20
|
+
raise e
|
21
|
+
end
|
22
|
+
|
23
|
+
def renew_lease(entry)
|
24
|
+
Vault.sys.renew(entry.lease_id)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module SettingsReader
|
2
|
+
module VaultResolver
|
3
|
+
module Engines
|
4
|
+
# Adapter to retrieve / renew secret from kv2 engine
|
5
|
+
class KV2 < Abstract
|
6
|
+
MOUNT = 'secret'.freeze
|
7
|
+
|
8
|
+
def retrieves?(address)
|
9
|
+
address.mount == MOUNT
|
10
|
+
end
|
11
|
+
|
12
|
+
def renew(_entry)
|
13
|
+
# KV secrets are static. Nothing to do
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def get_secret(address)
|
19
|
+
debug { "Fetching new kv secret at: #{address}" }
|
20
|
+
Vault.kv(address.mount).read(address.path)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -13,7 +13,7 @@ module SettingsReader
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def leased?
|
16
|
-
@secret.
|
16
|
+
@secret.renewable?
|
17
17
|
end
|
18
18
|
|
19
19
|
def expired?
|
@@ -28,26 +28,26 @@ module SettingsReader
|
|
28
28
|
@lease_started + lease_duration - Time.now
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
32
|
-
|
31
|
+
def lease_id
|
32
|
+
@secret.lease_id
|
33
|
+
end
|
33
34
|
|
34
|
-
|
35
|
+
def update_renewed(new_secret)
|
36
|
+
@secret = new_secret
|
35
37
|
@lease_started = Time.now
|
36
|
-
true
|
37
|
-
rescue Vault::HTTPClientError => e
|
38
|
-
raise SettingsReader::VaultResolver::Error, e.message
|
39
38
|
end
|
40
39
|
|
41
40
|
def value_for(attribute)
|
42
|
-
secret.data[attribute.to_sym]
|
41
|
+
return secret.data[attribute.to_sym] if secret.respond_to?(:data) && secret.data.key?(attribute.to_sym)
|
42
|
+
return secret.public_send(attribute) if secret.respond_to?(attribute)
|
43
|
+
|
44
|
+
nil
|
43
45
|
end
|
44
46
|
|
45
47
|
def to_s
|
46
48
|
address.to_s
|
47
49
|
end
|
48
50
|
|
49
|
-
private
|
50
|
-
|
51
51
|
def lease_duration
|
52
52
|
@secret.lease_duration.to_i
|
53
53
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative '../patches/authenticate'
|
2
|
+
|
3
|
+
module SettingsReader
|
4
|
+
module VaultResolver
|
5
|
+
module Helpers
|
6
|
+
# Helps with authentication using different schemes
|
7
|
+
class VaultAuthentication
|
8
|
+
FAKE_RESOLVER_PATH = 'vault/authentication'.freeze
|
9
|
+
|
10
|
+
def authenticate_via_k8s(role, route: nil, service_token_path: nil)
|
11
|
+
params = URI.encode_www_form({ role: role, route: route, service_token_path: service_token_path }.compact)
|
12
|
+
resolver.resolve("vault://auth/kubernetes/login?#{params}#client_token", FAKE_RESOLVER_PATH)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def resolver
|
18
|
+
@resolver = SettingsReader::VaultResolver.resolver
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -9,6 +9,13 @@ module SettingsReader
|
|
9
9
|
IDENTIFIER = 'vault://'.freeze
|
10
10
|
DATABASE_MOUNT = 'database'.freeze
|
11
11
|
|
12
|
+
attr_reader :config
|
13
|
+
|
14
|
+
def initialize(config)
|
15
|
+
@config = config
|
16
|
+
@engines = config.vault_engines
|
17
|
+
end
|
18
|
+
|
12
19
|
def resolvable?(value, _path)
|
13
20
|
return unless value.respond_to?(:start_with?)
|
14
21
|
|
@@ -23,37 +30,16 @@ module SettingsReader
|
|
23
30
|
entry&.value_for(address.attribute)
|
24
31
|
end
|
25
32
|
|
26
|
-
# Resolve KV secret
|
27
|
-
def kv_secret(address)
|
28
|
-
debug { "Fetching new kv secret at: #{address}" }
|
29
|
-
Vault.kv(address.mount).read(address.path)
|
30
|
-
rescue Vault::HTTPClientError => e
|
31
|
-
error { "Error retrieving secret at: #{address}: #{e.message}" }
|
32
|
-
raise SettingsReader::VaultResolver::Error, e.message
|
33
|
-
end
|
34
|
-
|
35
|
-
def database_secret(address)
|
36
|
-
debug { "Fetching new database secret at: #{address}" }
|
37
|
-
Vault.logical.read(address.full_path)
|
38
|
-
rescue Vault::HTTPClientError => e
|
39
|
-
error { "Error retrieving database secret: #{address}: #{e.message}" }
|
40
|
-
return nil if e.message.include?('* unknown role')
|
41
|
-
|
42
|
-
raise SettingsReader::VaultResolver::Error, e.message
|
43
|
-
end
|
44
|
-
|
45
33
|
private
|
46
34
|
|
47
35
|
def fetch_entry(address)
|
48
36
|
cache.fetch(address) do
|
49
37
|
info { "Retrieving new secret at: #{address}" }
|
50
|
-
|
51
|
-
debug { "Retrieved secret at: #{address}" }
|
52
|
-
SettingsReader::VaultResolver::Entry.new(address, secret)
|
53
|
-
else
|
54
|
-
debug { "Secret not retrieved: #{address}" }
|
55
|
-
end
|
38
|
+
config.vault_engine_for(address).get(address)
|
56
39
|
end
|
40
|
+
rescue StandardError => e
|
41
|
+
error { "Error retrieving secret: #{address}: #{e.message}" }
|
42
|
+
raise e
|
57
43
|
end
|
58
44
|
|
59
45
|
def cache
|
@@ -3,37 +3,34 @@ module SettingsReader
|
|
3
3
|
# Methods for centralized logging
|
4
4
|
module Logging
|
5
5
|
def debug(&block)
|
6
|
-
|
7
|
-
"[VaultResolver] #{block.call}"
|
8
|
-
end
|
9
|
-
nil
|
6
|
+
log_message(Logger::DEBUG, &block)
|
10
7
|
end
|
11
8
|
|
12
9
|
def info(&block)
|
13
|
-
|
14
|
-
"[VaultResolver] #{block.call}"
|
15
|
-
end
|
16
|
-
nil
|
10
|
+
log_message(Logger::INFO, &block)
|
17
11
|
end
|
18
12
|
|
19
13
|
def warn(&block)
|
20
|
-
|
21
|
-
"[VaultResolver] #{block.call}"
|
22
|
-
end
|
23
|
-
nil
|
14
|
+
log_message(Logger::WARN, &block)
|
24
15
|
end
|
25
16
|
|
26
17
|
def error(&block)
|
27
|
-
|
18
|
+
log_message(Logger::ERROR, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def log_message(severity, &block)
|
24
|
+
logger&.log(severity) do
|
28
25
|
"[VaultResolver] #{block.call}"
|
26
|
+
rescue StandardError => _e
|
27
|
+
# Ignoring errors in log message
|
29
28
|
end
|
30
29
|
nil
|
31
30
|
end
|
32
31
|
|
33
|
-
private
|
34
|
-
|
35
32
|
def logger
|
36
|
-
|
33
|
+
config.logger
|
37
34
|
end
|
38
35
|
end
|
39
36
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Vault
|
2
2
|
# Monkey patch to support k8s authenticaiton. Taken from https://github.com/hashicorp/vault-ruby/pull/202
|
3
3
|
class Authenticate < Request
|
4
|
-
def kubernetes(role, route
|
4
|
+
def kubernetes(role, route: nil, service_token_path: nil)
|
5
5
|
route ||= '/v1/auth/kubernetes/login'
|
6
6
|
service_token_path ||= '/var/run/secrets/kubernetes.io/serviceaccount/token'
|
7
7
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'concurrent/promise'
|
2
|
+
|
1
3
|
module SettingsReader
|
2
4
|
module VaultResolver
|
3
5
|
# Vault Lease refresher task
|
@@ -7,35 +9,34 @@ module SettingsReader
|
|
7
9
|
DEFAULT_RENEW_DELAY = 200
|
8
10
|
REFRESH_INTERVAL = 60
|
9
11
|
|
10
|
-
|
12
|
+
attr_reader :cache, :config
|
13
|
+
|
14
|
+
def initialize(cache, config)
|
11
15
|
@cache = cache
|
16
|
+
@config = config
|
12
17
|
end
|
13
18
|
|
14
19
|
def refresh
|
15
|
-
info { '
|
16
|
-
|
20
|
+
info { 'Performing Vault leases refresh' }
|
21
|
+
promises = cache.entries.map do |entry|
|
22
|
+
debug { "Checking lease for #{entry}. Leased?: #{entry.leased?}. Expires in: #{entry.expires_in}s" }
|
17
23
|
refresh_entry(entry)
|
18
|
-
end
|
19
|
-
|
24
|
+
end.compact
|
25
|
+
promises.each(&:wait)
|
26
|
+
promises
|
20
27
|
end
|
21
28
|
|
22
29
|
def refresh_entry(entry)
|
23
|
-
|
24
|
-
return unless entry.leased?
|
25
|
-
return unless entry.expires_in < DEFAULT_RENEW_DELAY
|
26
|
-
|
27
|
-
info { "Refreshing lease for #{entry}. Expires in: #{entry.expires_in}" }
|
28
|
-
entry.renew
|
29
|
-
info { "Lease renewed for #{entry}. Expires in: #{entry.expires_in}" }
|
30
|
-
rescue SettingsReader::VaultResolver::Error => e
|
31
|
-
error { "Error refreshing lease for #{entry}: #{e.message}" }
|
32
|
-
# Continue renewal.
|
33
|
-
end
|
30
|
+
return unless entry.expires_in < config.lease_renew_delay
|
34
31
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
32
|
+
Concurrent::Promise.execute do
|
33
|
+
debug { "Refreshing lease for #{entry}. Expires in: #{entry.expires_in}" }
|
34
|
+
config.vault_engine_for(entry.address).renew(entry)
|
35
|
+
info { "Lease renewed for #{entry}. Expires in: #{entry.expires_in}" }
|
36
|
+
entry
|
37
|
+
rescue StandardError => e
|
38
|
+
error { "Error refreshing lease for #{entry}: #{e.message}" }
|
39
|
+
raise SettingsReader::VaultResolver::Error, e.message
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module SettingsReader
|
2
|
+
module VaultResolver
|
3
|
+
# Check lease refresh result and report problems if needed
|
4
|
+
class RefresherObserver
|
5
|
+
attr_reader :config
|
6
|
+
|
7
|
+
def initialize(config)
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
def update(_time, result, error)
|
12
|
+
if result
|
13
|
+
result.map do |promise|
|
14
|
+
promise.on_success(&method(:notify_success)).on_error(&method(:notify_error))
|
15
|
+
end.each(&:wait)
|
16
|
+
else
|
17
|
+
notify_error(error)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def notify_success(result)
|
22
|
+
config.lease_renew_success_listener.call(result)
|
23
|
+
end
|
24
|
+
|
25
|
+
def notify_error(result)
|
26
|
+
config.lease_renew_error_listener.call(result)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -2,13 +2,19 @@ require 'logger'
|
|
2
2
|
require 'concurrent/timer_task'
|
3
3
|
|
4
4
|
require 'settings_reader'
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
require_relative 'vault_resolver/version'
|
6
|
+
require_relative 'vault_resolver/logging'
|
7
|
+
require_relative 'vault_resolver/configuration'
|
8
|
+
require_relative 'vault_resolver/address'
|
9
|
+
require_relative 'vault_resolver/entry'
|
10
|
+
require_relative 'vault_resolver/engines/abstract'
|
11
|
+
require_relative 'vault_resolver/engines/auth'
|
12
|
+
require_relative 'vault_resolver/engines/kv2'
|
13
|
+
require_relative 'vault_resolver/engines/database'
|
14
|
+
require_relative 'vault_resolver/cache'
|
15
|
+
require_relative 'vault_resolver/refresher'
|
16
|
+
require_relative 'vault_resolver/refresher_observer'
|
17
|
+
require_relative 'vault_resolver/instance'
|
12
18
|
|
13
19
|
module SettingsReader
|
14
20
|
# Singleton for lease renewals and secrets cache
|
@@ -16,36 +22,23 @@ module SettingsReader
|
|
16
22
|
class Error < StandardError; end
|
17
23
|
|
18
24
|
class << self
|
19
|
-
|
25
|
+
attr_reader :configuration, :refresher_timer_task
|
20
26
|
end
|
21
27
|
|
22
|
-
def self.
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
@logger = Logger.new($stdout, level: Logger::INFO)
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.logger=(logger)
|
30
|
-
@logger = logger
|
28
|
+
def self.configure(&block)
|
29
|
+
@configuration = SettingsReader::VaultResolver::Configuration.new
|
30
|
+
block&.call(@configuration)
|
31
|
+
@refresher_timer_task = @configuration.setup_lease_refresher(cache, refresher_timer_task)
|
31
32
|
end
|
32
33
|
|
33
|
-
def self.
|
34
|
-
|
35
|
-
self.cache ||= SettingsReader::VaultResolver::Cache.new
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.setup_lease_refresher
|
39
|
-
logger.debug { '[VaultResolver] Setting up lease resolver task' }
|
40
|
-
self.refresher_timer_task ||= SettingsReader::VaultResolver::Refresher.refresh_task(self.cache)
|
41
|
-
self.refresher_timer_task.execute
|
34
|
+
def self.cache
|
35
|
+
@cache ||= SettingsReader::VaultResolver::Cache.new
|
42
36
|
end
|
43
37
|
|
44
38
|
def self.resolver
|
45
|
-
|
46
|
-
end
|
39
|
+
raise Error, 'Gem not configured. Call configure before getting resolver' unless configuration
|
47
40
|
|
48
|
-
|
49
|
-
|
41
|
+
SettingsReader::VaultResolver::Instance.new(configuration)
|
42
|
+
end
|
50
43
|
end
|
51
44
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: settings_reader-vault_resolver
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Volodymyr Mykhailyk
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-04-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -77,16 +77,23 @@ files:
|
|
77
77
|
- Rakefile
|
78
78
|
- bin/console
|
79
79
|
- bin/setup
|
80
|
+
- codecov.yml
|
80
81
|
- docker-compose.yml
|
81
82
|
- lib/settings_reader/vault_resolver.rb
|
82
83
|
- lib/settings_reader/vault_resolver/address.rb
|
83
84
|
- lib/settings_reader/vault_resolver/cache.rb
|
85
|
+
- lib/settings_reader/vault_resolver/configuration.rb
|
86
|
+
- lib/settings_reader/vault_resolver/engines/abstract.rb
|
87
|
+
- lib/settings_reader/vault_resolver/engines/auth.rb
|
88
|
+
- lib/settings_reader/vault_resolver/engines/database.rb
|
89
|
+
- lib/settings_reader/vault_resolver/engines/kv2.rb
|
84
90
|
- lib/settings_reader/vault_resolver/entry.rb
|
85
|
-
- lib/settings_reader/vault_resolver/helpers/
|
91
|
+
- lib/settings_reader/vault_resolver/helpers/vault_authentication.rb
|
86
92
|
- lib/settings_reader/vault_resolver/instance.rb
|
87
93
|
- lib/settings_reader/vault_resolver/logging.rb
|
88
94
|
- lib/settings_reader/vault_resolver/patches/authenticate.rb
|
89
95
|
- lib/settings_reader/vault_resolver/refresher.rb
|
96
|
+
- lib/settings_reader/vault_resolver/refresher_observer.rb
|
90
97
|
- lib/settings_reader/vault_resolver/version.rb
|
91
98
|
- settings_reader-vault_resolver.gemspec
|
92
99
|
homepage: https://github.com/matic-insurance/settings_reader-vault_resolver
|
@@ -1,43 +0,0 @@
|
|
1
|
-
require_relative '../patches/authenticate'
|
2
|
-
|
3
|
-
module SettingsReader
|
4
|
-
module VaultResolver
|
5
|
-
module Helpers
|
6
|
-
# Helps with Vault authentication using kubernetes login
|
7
|
-
class K8sAuth
|
8
|
-
def call(role)
|
9
|
-
secret = Vault.auth.kubernetes(role)
|
10
|
-
|
11
|
-
cache_token_for_renewal(secret)
|
12
|
-
secret
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def cache_token_for_renewal(secret)
|
18
|
-
address = SettingsReader::VaultResolver::Address.new('vault://v1/auth/token/lookup-self')
|
19
|
-
entry = SettingsReader::VaultResolver::AuthEntry.new(address, secret)
|
20
|
-
SettingsReader::VaultResolver.cache.save(entry)
|
21
|
-
entry
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# Helps with auth token lease renewal
|
27
|
-
class AuthEntry < SettingsReader::VaultResolver::Entry
|
28
|
-
def leased?
|
29
|
-
true
|
30
|
-
end
|
31
|
-
|
32
|
-
def lease_duration
|
33
|
-
@secret.auth.lease_duration
|
34
|
-
end
|
35
|
-
|
36
|
-
def renew
|
37
|
-
Vault.client.auth_token.renew_self
|
38
|
-
@lease_started = Time.now
|
39
|
-
true
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|