AWS Developer Tools Blog
Credential Providers (Credential Management Part 3)
In part 1 of this series, I wrote about how to configure your access credentials with the AWS SDK for Ruby (aws-sdk
gem). In part 2 we learned how to rotate your access credentials using the aws-sdk
gem.
This week we explore credential providers and how they can help you keep your secrets safe and fresh.
Credential Providers
A credential provider is an object that responds to the following methods:
-
#access_key_id
-
#secret_access_key
-
#session_token
-
#refresh
Internally, the aws-sdk
gem uses a chain of credential providers to load credentials from various locations including:
-
AWS.config
-
ENV
(from multiple key prefixes) - EC2 instance metadata service
You can fully customize this behavior by configuring your own custom credential provider, as follows.
AWS.config(:credential_provider => CustomCredentialProvider.new)
Why Use a Custom Credential Provider?
In the previous post in this series we discussed rotating credentials. It can be painful to build logic that restarts processes or applications that are using stale or soon-to-be removed/expired credentials.
If your application uses a custom credential provider, the application does not need to be restarted. The SDK automatically calls #refresh
on the credential provider when it receives a response from AWS that indicates its credentials are expired.
In-house hosted applications and utility scripts can use a custom provider to load credentials from a simple web service that vends current credentials. This web service could even go as far as vending session credentials that auto-expire and are limited to specific AWS operations. This greatly reduces exposure if these credentials are ever leaked.
Build a Custom Credential Provider
Here is a really simple custom credential provider that makes a HTTPS request to https://internal.domain/
and expects a JSON response of credentials.
require 'net/https' class CustomCredentialProvider include AWS::Core::CredentialProviders::Provider private def get_credentials begin http = Net::HTTP.new('internal.domain', 443) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_PEER response = http.request(Net::HTTP::Get.new('/')) # symbolize keys to :access_key_id, :secret_access_key if response.code == '200' JSON.load(response.body).inject({}) {|h,(k,v)| h.merge(k.to_sym => v) } else {} end rescue {} end end end
The include
statement in the example above does much of the heavy lifting. It defines all of the public methods required for a credential provider. It also caches the credentials until #refresh
is called. We only have to define #get_credentials
and return a hash with symbol keys (or an empty hash if we fail).
You can make this example more robust if you:
- Set network timeouts
- Rescue Exceptions raised by HTTP that do not extend StandardError
- Add basic retry logic to handle transient network errors
In the next (and last) post in this series I will explore how the aws-sdk
gem uses the EC2 metadata service.