summaryrefslogtreecommitdiff
path: root/lib/chef/secret_fetcher
diff options
context:
space:
mode:
authorMarc A. Paradise <marc.paradise@gmail.com>2021-07-15 17:18:43 -0400
committerMarc A. Paradise <marc.paradise@gmail.com>2021-07-15 17:18:43 -0400
commit10b6ff915c6fe24ad0708f0df8fa7514d9bca933 (patch)
treeecfd5b99aacfaa4cd27ab9daa878de6f6e2cb459 /lib/chef/secret_fetcher
parent6d44ea1df5914f067c346c2a9de8512407988a6a (diff)
downloadchef-10b6ff915c6fe24ad0708f0df8fa7514d9bca933.tar.gz
Allow versioned secrets via DSL
Versioning is commonly supported across most major secrets services. This change allows the DSL to support fetching a specific secret version. Implementations are expected to default to fetching the most recent version when no version is provided. Usage: secret(name: 'secret1', version: 'version1', service: :example) Signed-off-by: Marc A. Paradise <marc.paradise@gmail.com>
Diffstat (limited to 'lib/chef/secret_fetcher')
-rw-r--r--lib/chef/secret_fetcher/aws_secrets_manager.rb26
-rw-r--r--lib/chef/secret_fetcher/base.rb15
-rw-r--r--lib/chef/secret_fetcher/example.rb2
3 files changed, 18 insertions, 25 deletions
diff --git a/lib/chef/secret_fetcher/aws_secrets_manager.rb b/lib/chef/secret_fetcher/aws_secrets_manager.rb
index f5508cf59b..a04d90dcdf 100644
--- a/lib/chef/secret_fetcher/aws_secrets_manager.rb
+++ b/lib/chef/secret_fetcher/aws_secrets_manager.rb
@@ -24,6 +24,9 @@ class Chef
# A fetcher that fetches a secret from AWS Secrets Manager
# In this initial iteration it defaults to authentication via instance profile.
# It is possible to pass options that configure it to use alternative credentials.
+ # This implementation supports fetching with version.
+ #
+ # NOTE: This does not yet support automatic retries, which the AWS client does by default.
#
# For configuration options see https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/SecretsManager/Client.html#initialize-instance_method
#
@@ -33,31 +36,18 @@ class Chef
# Usage Example:
#
# fetcher = SecretFetcher.for_service(:aws_secrets_manager, { region: "us-east-1" })
- # fetcher.fetch("secretkey1")
+ # fetcher.fetch("secretkey1", "v1")
class SecretFetcher
class AWSSecretsManager < Base
- DEFAULT_AWS_OPTS = {} # rubocop: disable Style/MutableConstant
- def validate!
- # Note that we are not doing any validation of required configuration here, we will
- # rely on the API client to do that for us, since it will work with the merge of
- # the config we provide, env-based config, and/or an appropriate profile in ~/.aws
-
- # Instantiating the client is an opportunity for an API provider to do validation,
- # so we'll do that first here.
- client
- end
-
# @param identifier [String] the secret_id
+ # @param version [String] the secret version. Not usd at this time
# @return Aws::SecretsManager::Types::GetSecretValueResponse
- def do_fetch(identifier)
- result = client.get_secret_value(secret_id: identifier)
+ def do_fetch(identifier, version)
+ client = Aws::SecretsManager::Client.new()
+ result = client.get_secret_value(secret_id: identifier, version_stage: version)
# These fields are mutually exclusive
result.secret_string || result.secret_binary
end
-
- def client
- @client ||= Aws::SecretsManager::Client.new(DEFAULT_AWS_OPTS.merge(config))
- end
end
end
end
diff --git a/lib/chef/secret_fetcher/base.rb b/lib/chef/secret_fetcher/base.rb
index a80ecea0fb..bfc3a44e4c 100644
--- a/lib/chef/secret_fetcher/base.rb
+++ b/lib/chef/secret_fetcher/base.rb
@@ -37,14 +37,14 @@ class Chef
# Fetch the named secret by invoking implementation-specific [Chef::SecretFetcher::Base#do_fetch]
#
# @param name [Object] the name or identifier of the secret.
+ # @param version [Object] Optional version of the secret to fetch.
# @note - the name parameter will probably see a narrowing of type as we learn more about different integrations.
- # @return [Object] the result of the secret fetch
+ # @return [Object] the fetched secret
# @raise [Chef::Exceptions::Secret::MissingSecretName] when secret name is not provided
# @raise [Chef::Exceptions::Secret::FetchFailed] when the underlying attempt to fetch the secret fails.
- def fetch(name)
- raise Chef::Exceptions::Secret::MissingSecretName.new if name.nil? || name.to_s == ""
-
- do_fetch(name)
+ def fetch(name, version = nil)
+ raise Chef::Exceptions::Secret::MissingSecretName.new if name.to_s == ""
+ do_fetch(name, version)
end
# Validate that the instance is correctly configured.
@@ -57,12 +57,15 @@ class Chef
# @param identifier [Object] Unique identifier of the secret to be retrieved.
# When invoked via DSL, this is pre-verified to be not nil/not empty string.
# The expected data type and form can vary by implementation.
+ # @param version [Object] Optional version of the secret to be retrieved. If not
+ # provided, implementations are expected to fetch the most recent version of the
+ # secret by default.
#
# @return [Object] The secret as returned from the implementation. The data type
# will vary implementation.
#
# @raise [Chef::Exceptions::Secret::FetchFailed] if the secret could not be fetched
- def do_fetch(identifier); raise NotImplementedError.new; end
+ def do_fetch(identifier, version); raise NotImplementedError.new; end
end
end
end
diff --git a/lib/chef/secret_fetcher/example.rb b/lib/chef/secret_fetcher/example.rb
index 7a0e671929..28b806bf31 100644
--- a/lib/chef/secret_fetcher/example.rb
+++ b/lib/chef/secret_fetcher/example.rb
@@ -36,7 +36,7 @@ class Chef
end
end
- def do_fetch(identifier)
+ def do_fetch(identifier, version)
raise Chef::Exceptions::Secret::FetchFailed.new("Secret #{identifier}) not found.") unless config.key?(identifier)
config[identifier]