-
Notifications
You must be signed in to change notification settings - Fork 333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactors Drivers for OutputCredentialsProvider #878
Refactors Drivers for OutputCredentialsProvider #878
Conversation
"""Driver for fetching credentials that are saved locally on the filesystem.""" | ||
|
||
def __init__(self, region, service_name): | ||
self._region = region |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think region will ever be used for the 'local' one right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately I need it to pass into the Credentials
constructor, because that class requires the region
to use KMS to decrypt the credentials. I honestly could've passed in a CredentialsFactory
kind of thing that would avoid having to hot-potato around all of these region
, service_name
and so on things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but only the S3
driver would need to do the decryption right? or am I misunderstanding this?
if that's the case - the S3 driver constructor can take different params than the base (super) class and just call the super's init with the right params also. I could be misunderstanding the inheritance model, though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not exactly; the S3 driver (none of the "drivers") actually does not do any decryption on their own. I delegated decryption to this Credentials
class.
I didn't actually use any traditional inheritance. The three "base" classes are actually supposed to behave like interfaces:
- FileDescriptorProvider
- CredentialsCachingDriver
- CredentialsProvidingDriver (The important one)
The implementations of CredentialsProvidingDriver
are supposed to return Credentials
, which can be is_encrypted()
or not. The idea is (was?) to allow credentials to be passed between drivers while still encrypted. The only thing the driver needs to know is whether or not Credentials
are encrypted "at rest" or not.
This is why all of the Drivers need a region
; because they need to pass it to the Credentials
class to be able to decrypt the ciphertext after being read from disk/memory/whatever. The exception is the EphemeralDriver
, which is the only unencrypted-at-rest Driver.
|
||
def __init__(self, config, defaults, region, service_name, prefix=None, aws_account_id=None): | ||
self._config = config | ||
self._region = region |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
subclasses of OutputCredentialsProvider
that use 'boto3' clients and need a region could use the default_config
function to return a botocore config (see: https://github.com/airbnb/streamalert/blob/master/stream_alert/shared/helpers/boto.py#L28)
TBH, I'd advocate for using the default_config
method anywhere we are creating boto3 clients, since it enforces a shorter timeout (which is better for efficiency/cost in lambda).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
So we would do something like
client = boto3.client('zzz', config=default_config(region=self._region))
Instead of
client = boto3.client('zzz', region_name=self._region)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no you could omit region entirely from the call to default_config
(unless it could/would ever be something other than the current region.. which I don't think it would be in this case)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
client = boto3.client('zzz', config=default_config())
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I remembered something when I was confirming whether or not region can be globally configured; what about on the CLI handler? It looks like the region
(among other configurations) is passed in from ... somewhere. How does the CLI handler know which REGION
the code runs in? Is it inferred from the aws2fa.sh
session?
29c52c8
to
849127d
Compare
* Working? Draft of new driver-based credentials storage * Higher quality refactor; needs tests * Kinks ironed out with good tests this time * First maybe working try * Adds lots of tests for the Drivers * Add more tests. Not final; still need to remove deprecated methods * Rename method to reduce confusion * Remove deprecated method load_encrypted_credentials_from_s3 * Removes deprecated method get_local_credentials_temp_dir * Remove deprecated method get_formatted_output_credentials_name * Removes deprecated method kms_decrypt * Removes extraneous imports * Extract globally injected REGION so the handler can be implemented properly * Pylint` * Pylint is my nemesis * Remove extraneous method * Use default_config for boto clients * Code coverage. PR feedback. * Add missing __init__.py file causing poor code coverage * Fixes the tests to get past pylint garbage
* Working? Draft of new driver-based credentials storage * Higher quality refactor; needs tests * Kinks ironed out with good tests this time * First maybe working try * Adds lots of tests for the Drivers * Add more tests. Not final; still need to remove deprecated methods * Rename method to reduce confusion * Remove deprecated method load_encrypted_credentials_from_s3 * Removes deprecated method get_local_credentials_temp_dir * Remove deprecated method get_formatted_output_credentials_name * Removes deprecated method kms_decrypt * Removes extraneous imports * Extract globally injected REGION so the handler can be implemented properly * Pylint` * Pylint is my nemesis * Remove extraneous method * Use default_config for boto clients * Code coverage. PR feedback. * Add missing __init__.py file causing poor code coverage * Fixes the tests to get past pylint garbage
* Working? Draft of new driver-based credentials storage * Higher quality refactor; needs tests * Kinks ironed out with good tests this time * First maybe working try * Adds lots of tests for the Drivers * Add more tests. Not final; still need to remove deprecated methods * Rename method to reduce confusion * Remove deprecated method load_encrypted_credentials_from_s3 * Removes deprecated method get_local_credentials_temp_dir * Remove deprecated method get_formatted_output_credentials_name * Removes deprecated method kms_decrypt * Removes extraneous imports * Extract globally injected REGION so the handler can be implemented properly * Pylint` * Pylint is my nemesis * Remove extraneous method * Use default_config for boto clients * Code coverage. PR feedback. * Add missing __init__.py file causing poor code coverage * Fixes the tests to get past pylint garbage
* Initial commit * Now with tests * Touchups * Remove extraneous static method * DRYs out some code related to S3 secret bucket name * Proof-of-concept of mocking CredentialProvider * Fix some pylints * Removes output_cred_name() to consolidate functionality. Updates all tests * pylint * Refactors Drivers for OutputCredentialsProvider (#878) * Working? Draft of new driver-based credentials storage * Higher quality refactor; needs tests * Kinks ironed out with good tests this time * First maybe working try * Adds lots of tests for the Drivers * Add more tests. Not final; still need to remove deprecated methods * Rename method to reduce confusion * Remove deprecated method load_encrypted_credentials_from_s3 * Removes deprecated method get_local_credentials_temp_dir * Remove deprecated method get_formatted_output_credentials_name * Removes deprecated method kms_decrypt * Removes extraneous imports * Extract globally injected REGION so the handler can be implemented properly * Pylint` * Pylint is my nemesis * Remove extraneous method * Use default_config for boto clients * Code coverage. PR feedback. * Add missing __init__.py file causing poor code coverage * Fixes the tests to get past pylint garbage * Change account_id to a property * Formatting * (fixup) PR Feedback * (fixup) Disentangles streamalert cli helper with stream_alert credentials provider. * (fixup) Deprecate old methods * pylint * Removes deprecated code; adds tests for aws_api_client; DRYs out test code
to: @ryandeivert
cc: @airbnb/streamalert-maintainers
related to:
resolves:
Background
This PR sits on top of #875, in response to this comment. I split this into a separate PR to make it easier to understand.
Changes
I split out the new
OutputCredentialsProvider
guts into this new concept—CredentialsProvidingDriver
. The idea is these drivers each are strategies for fetching Credentials from somewhere.CredentialsProvidingDriver
sI implemented 4 drivers: S3, local files, spooled file, and ephemeral. I tried to design the interface to be plug-and-play-ready for a SSM migration in the future (#240).
New Way
The new algorithm can be found in
setup_drivers()
. The pseudocode works like this:Other stuff
I removed a lot of bloat from OutputDispatcher. These old methods were S3-specific when the OutputDispatcher assumed all of its credentials came from S3. Now that the OutputDispatchers are de-coupled from S3, these methods are no longer relevant.
Testing
unit_test
rule_test