Skip to content

Proxy KMS Client to catch BotoCoreErrors #86

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

Merged
merged 1 commit into from
Oct 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions src/aws_encryption_sdk/key_providers/kms.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
"""Master Key Providers for use with AWS KMS"""
import functools
import logging

import attr
Expand Down Expand Up @@ -128,15 +129,44 @@ def _process_config(self):
if self.config.key_ids:
self.add_master_keys_from_list(self.config.key_ids)

def _wrap_client(self, region_name, method, *args, **kwargs):
"""Proxies all calls to a kms clients methods and removes misbehaving clients
:param str region_name: AWS Region ID (ex: us-east-1)
:param callable method: a method on the KMS client to proxy
:param tuple args: list of arguments to pass to the provided ``method``
:param dict kwargs: dictonary of keyword arguments to pass to the provided ``method``
"""
try:
return method(*args, **kwargs)
except botocore.exceptions.BotoCoreError:
self._regional_clients.pop(region_name)
_LOGGER.error(
'Removing regional client "%s" from cache due to BotoCoreError on %s call', region_name, method.__name__
)
raise

def _register_client(self, client, region_name):
"""Uses functools.partial to wrap all methods on a client with the self._wrap_client method
:param botocore.client.BaseClient client: the client to proxy
:param str region_name: AWS Region ID (ex: us-east-1)
"""
for item in client.meta.method_to_api_mapping:
method = getattr(client, item)
wrapped_method = functools.partial(self._wrap_client, region_name, method)
setattr(client, item, wrapped_method)

def add_regional_client(self, region_name):
"""Adds a regional client for the specified region if it does not already exist.
:param str region_name: AWS Region ID (ex: us-east-1)
"""
if region_name not in self._regional_clients:
self._regional_clients[region_name] = boto3.session.Session(
region_name=region_name, botocore_session=self.config.botocore_session
).client("kms", config=self._user_agent_adding_config)
session = boto3.session.Session(region_name=region_name, botocore_session=self.config.botocore_session)
client = session.client("kms", config=self._user_agent_adding_config)
self._register_client(client, region_name)
self._regional_clients[region_name] = client

def add_regional_clients_from_list(self, region_names):
"""Adds multiple regional clients for the specified regions if they do not already exist.
Expand Down
13 changes: 12 additions & 1 deletion test/integration/test_i_aws_encrytion_sdk_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@
import unittest

import pytest
from botocore.exceptions import BotoCoreError

import aws_encryption_sdk
from aws_encryption_sdk.identifiers import USER_AGENT_SUFFIX, Algorithm
from aws_encryption_sdk.key_providers.kms import KMSMasterKey
from aws_encryption_sdk.key_providers.kms import KMSMasterKey, KMSMasterKeyProvider

from .integration_test_utils import get_cmk_arn, setup_kms_master_key_provider

Expand Down Expand Up @@ -58,6 +59,16 @@ def test_encrypt_verify_user_agent_kms_master_key(caplog):
assert USER_AGENT_SUFFIX in caplog.text


def test_remove_bad_client():
test = KMSMasterKeyProvider()
test.add_regional_client("us-fakey-12")

with pytest.raises(BotoCoreError):
test._regional_clients["us-fakey-12"].list_keys()

assert not test._regional_clients


class TestKMSThickClientIntegration(unittest.TestCase):
def setUp(self):
self.kms_master_key_provider = setup_kms_master_key_provider()
Expand Down