-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add MSK IAM authentication capability and framework for other Kafka S…
…ASL/OAuth mechanisms (#31) * Add MSK IAM authentication capability and framework for other Kafka SASL/OAuth mechanisms * Add AWS_ROLE_SESSION_NAME config to stop errors about changing principals when re-authing * Add support for assuming a specified role
- Loading branch information
Showing
9 changed files
with
197 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import argparse | ||
import importlib | ||
import os | ||
from abc import ABC, abstractmethod | ||
|
||
from typing import TypeVar, Type, Tuple, Optional | ||
|
||
KafkaOauthProviderAbstractType = TypeVar('KafkaOauthProviderAbstractType', bound='KafkaOauthProviderAbstract') | ||
|
||
|
||
class KafkaOauthProviderAbstract(ABC): | ||
@abstractmethod | ||
def consumer_oauth_cb(self, config_str: str) -> Tuple[str, float]: | ||
pass | ||
|
||
@abstractmethod | ||
def producer_oauth_cb(self, config_str: str) -> Tuple[str, float]: | ||
pass | ||
|
||
@abstractmethod | ||
def admin_oauth_cb(self, config_str: str) -> Tuple[str, float]: | ||
pass | ||
|
||
@staticmethod | ||
def add_arguments(parser: argparse.ArgumentParser) -> None: | ||
pass | ||
|
||
@classmethod | ||
@abstractmethod | ||
def construct_with_options(cls: Type[KafkaOauthProviderAbstractType], | ||
opts: argparse.Namespace) -> KafkaOauthProviderAbstractType: | ||
pass | ||
|
||
|
||
def add_kafka_oauth_arg(parser: argparse.ArgumentParser) -> None: | ||
parser.add_argument('--kafka-oauth-provider', | ||
default=os.environ.get('KAFKA_OAUTH_PROVIDER'), | ||
help="A string of form <module_name>.<class_name> indicating an implementation of " | ||
"kafka_oauth.KafkaOauthProviderAbstract that provides OAuth callback functions specified " | ||
"when instantiating Kafka consumers, producers, or admin clients.") | ||
|
||
|
||
def get_kafka_oauth_provider() -> Optional[KafkaOauthProviderAbstract]: | ||
parser = argparse.ArgumentParser() | ||
add_kafka_oauth_arg(parser) | ||
opts, _ = parser.parse_known_args() | ||
|
||
if not opts.kafka_oauth_provider: | ||
return None | ||
|
||
package_module, class_name = opts.kafka_oauth_provider.rsplit('.', 1) | ||
module = importlib.import_module(package_module) | ||
oauth_class: KafkaOauthProviderAbstract = getattr(module, class_name) | ||
oauth_class.add_arguments(parser) | ||
opts, _ = parser.parse_known_args() | ||
return oauth_class.construct_with_options(opts) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import argparse | ||
import datetime | ||
import logging | ||
import os | ||
from typing import Tuple, TypeVar, Type, Optional | ||
|
||
from aws_msk_iam_sasl_signer import MSKAuthTokenProvider | ||
|
||
from . import KafkaOauthProviderAbstract | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
AwsMskOauthCallbackProviderType = TypeVar('AwsMskOauthCallbackProviderType', bound='AwsMskOauthCallbackProvider') | ||
|
||
|
||
class AwsMskOauthCallbackProvider(KafkaOauthProviderAbstract): | ||
def __init__(self, aws_region: str, role_arn: Optional[str] = None): | ||
self.aws_region: str = aws_region | ||
self.role_arn: Optional[str] = role_arn | ||
self._auth_token: str = '' | ||
self._expiry_ts: float = datetime.datetime.now(datetime.timezone.utc).timestamp() | ||
|
||
def consumer_oauth_cb(self, config_str: str) -> Tuple[str, float]: | ||
return self._common_cb() | ||
|
||
def producer_oauth_cb(self, config_str: str) -> Tuple[str, float]: | ||
return self._common_cb() | ||
|
||
def admin_oauth_cb(self, config_str: str) -> Tuple[str, float]: | ||
return self._common_cb() | ||
|
||
def _common_cb(self) -> Tuple[str, float]: | ||
if not self._auth_token or datetime.datetime.now(datetime.timezone.utc).timestamp() > self._expiry_ts: | ||
if self.role_arn: | ||
self._auth_token, expiry_ms = MSKAuthTokenProvider.generate_auth_token_from_role_arn( | ||
self.aws_region, self.role_arn) | ||
else: | ||
self._auth_token, expiry_ms = MSKAuthTokenProvider.generate_auth_token(self.aws_region) | ||
self._expiry_ts = expiry_ms / 1000 | ||
logger.debug('AwsMskOauthCallbackProvider generated an auth token that expires at %s', | ||
datetime.datetime.fromtimestamp(self._expiry_ts, datetime.timezone.utc)) | ||
return self._auth_token, self._expiry_ts | ||
|
||
@staticmethod | ||
def add_arguments(parser: argparse.ArgumentParser) -> None: | ||
parser.add_argument('--msk-cluster-aws-region', default=os.environ.get('MSK_CLUSTER_AWS_REGION'), | ||
help='AWS region name to use for IAM-based authentication to an AWS MSK cluster.') | ||
parser.add_argument('--msk-cluster-access-role-arn', default=os.environ.get('MSK_CLUSTER_ACCESS_ROLE_ARN'), | ||
help='Optional name of an AWS IAM role to assume for authentication to an AWS MSK cluster.') | ||
parser.add_argument('--aws-role-session-name', default=os.environ.get('AWS_ROLE_SESSION_NAME'), | ||
help='A session name for the process to maintain principal-name stability when' | ||
're-authenticating for AWS IAM/SASL') | ||
|
||
@classmethod | ||
def construct_with_options(cls: Type[AwsMskOauthCallbackProviderType], | ||
opts: argparse.Namespace) -> AwsMskOauthCallbackProviderType: | ||
if not opts.msk_cluster_aws_region: | ||
raise Exception('AwsMskOauthCallbackProvider cannot be used without specifying a value for ' | ||
'MSK_CLUSTER_AWS_REGION') | ||
return cls(opts.msk_cluster_aws_region, opts.msk_cluster_access_role_arn) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
avro==1.11.3 | ||
aws-msk-iam-sasl-signer-python==1.0.1 | ||
bitarray==2.9.2 | ||
confluent-kafka==2.3.0 | ||
fastavro==1.9.4 | ||
|