def connect(self): """ Connect to an AWS API via boto3 low-level client and set ``self.conn`` to the `boto3.client <https://boto3.readthed ocs.org/en/latest/reference/core/boto3.html#boto3.client>`_ object (a ``botocore.client.*`` instance). If ``self.conn`` is not None, do nothing. This connects to the API name given by ``self.api_name``. :returns: None """ if self.conn is not None: return default_config = Config(retries={'mode': 'adaptive'}) kwargs = dict(self._boto3_connection_kwargs) kwargs['config'] = default_config if self._max_retries_config is not None: kwargs['config'] = default_config.merge(self._max_retries_config) self.conn = boto3.client(self.api_name, **kwargs) logger.info("Connected to %s in region %s", self.api_name, self.conn._client_config.region_name)
class AWSClient: def __init__(self, aws_default_region, aws_role_arn, aws_role_session_name, aws_role_session_duration, aws_role_policy, aws_access_key_id, aws_secret_access_key, verify_certificate, timeout, retries): self.aws_default_region = aws_default_region self.aws_role_arn = aws_role_arn self.aws_role_session_name = aws_role_session_name self.aws_role_session_duration = aws_role_session_duration self.aws_role_policy = aws_role_policy self.aws_access_key_id = aws_access_key_id self.aws_secret_access_key = aws_secret_access_key self.verify_certificate = verify_certificate proxies = handle_proxy(proxy_param_name='proxy', checkbox_default_value=False) (read_timeout, connect_timeout) = AWSClient.get_timeout(timeout) if int(retries) > 10: retries = 10 self.config = Config(connect_timeout=connect_timeout, read_timeout=read_timeout, retries=dict(max_attempts=int(retries)), proxies=proxies) def update_config(self): command_config = {} retries = demisto.getArg( 'retries' ) # Supports retries and timeout parameters on the command execution level if retries is not None: command_config['retries'] = dict(max_attempts=int(retries)) timeout = demisto.getArg('timeout') if timeout is not None: (read_timeout, connect_timeout) = AWSClient.get_timeout(timeout) command_config['read_timeout'] = read_timeout command_config['connect_timeout'] = connect_timeout if retries or timeout: demisto.debug( 'Merging client config settings: {}'.format(command_config)) self.config = self.config.merge(Config(**command_config)) def aws_session(self, service, region=None, role_arn=None, role_session_name=None, role_session_duration=None, role_policy=None): kwargs = {} self.update_config() if role_arn and role_session_name is not None: kwargs.update({ 'RoleArn': role_arn, 'RoleSessionName': role_session_name, }) elif self.aws_role_arn and self.aws_role_session_name is not None: kwargs.update({ 'RoleArn': self.aws_role_arn, 'RoleSessionName': self.aws_role_session_name, }) if role_session_duration is not None: kwargs.update({'DurationSeconds': int(role_session_duration)}) elif self.aws_role_session_duration is not None: kwargs.update( {'DurationSeconds': int(self.aws_role_session_duration)}) if role_policy is not None: kwargs.update({'Policy': role_policy}) elif self.aws_role_policy is not None: kwargs.update({'Policy': self.aws_role_policy}) if kwargs and not self.aws_access_key_id: # login with Role ARN if not self.aws_access_key_id: sts_client = boto3.client('sts', config=self.config, verify=self.verify_certificate, region_name=self.aws_default_region) sts_response = sts_client.assume_role(**kwargs) client = boto3.client( service_name=service, region_name=region if region else self.aws_default_region, aws_access_key_id=sts_response['Credentials'] ['AccessKeyId'], aws_secret_access_key=sts_response['Credentials'] ['SecretAccessKey'], aws_session_token=sts_response['Credentials'] ['SessionToken'], verify=self.verify_certificate, config=self.config) elif self.aws_access_key_id and self.aws_role_arn: # login with Access Key ID and Role ARN sts_client = boto3.client( service_name='sts', aws_access_key_id=self.aws_access_key_id, aws_secret_access_key=self.aws_secret_access_key, verify=self.verify_certificate, config=self.config) kwargs.update({ 'RoleArn': self.aws_role_arn, 'RoleSessionName': self.aws_role_session_name, }) sts_response = sts_client.assume_role(**kwargs) client = boto3.client( service_name=service, region_name=self.aws_default_region, aws_access_key_id=sts_response['Credentials']['AccessKeyId'], aws_secret_access_key=sts_response['Credentials'] ['SecretAccessKey'], aws_session_token=sts_response['Credentials']['SessionToken'], verify=self.verify_certificate, config=self.config) elif self.aws_access_key_id and not self.aws_role_arn: # login with access key id client = boto3.client( service_name=service, region_name=region if region else self.aws_default_region, aws_access_key_id=self.aws_access_key_id, aws_secret_access_key=self.aws_secret_access_key, verify=self.verify_certificate, config=self.config) else: # login with default permissions, permissions pulled from the ec2 metadata client = boto3.client( service_name=service, region_name=region if region else self.aws_default_region) return client @staticmethod def get_timeout(timeout): if not timeout: timeout = "60,10" # default values try: timeout_vals = timeout.split(',') read_timeout = int(timeout_vals[0]) except ValueError: raise DemistoException( "You can specify just the read timeout (for example 60) or also the connect " "timeout followed after a comma (for example 60,10). If a connect timeout is not " "specified, a default of 10 second will be used.") connect_timeout = 10 if len(timeout_vals) == 1 else int( timeout_vals[1]) return read_timeout, connect_timeout
def boto3_cached_conn(service, service_type='client', future_expiration_minutes=15, account_number=None, assume_role=None, session_name='cloudaux', region='us-east-1', return_credentials=False, external_id=None, arn_partition='aws', read_only=False, retry_max_attempts=10, config=None, sts_client_kwargs=None, client_kwargs=None): """ Used to obtain a boto3 client or resource connection. For cross account, provide both account_number and assume_role. :usage: # Same Account: client = boto3_cached_conn('iam') resource = boto3_cached_conn('iam', service_type='resource') # Cross Account Client: client = boto3_cached_conn('iam', account_number='000000000000', assume_role='role_name') # Cross Account Resource: resource = boto3_cached_conn('iam', service_type='resource', account_number='000000000000', assume_role='role_name') :param service: AWS service (i.e. 'iam', 'ec2', 'kms') :param service_type: 'client' or 'resource' :param future_expiration_minutes: Connections will expire from the cache when their expiration is within this many minutes of the present time. [Default 15] :param account_number: Required if assume_role is provided. :param assume_role: Name of the role to assume into for account described by account_number. :param session_name: Session name to attach to requests. [Default 'cloudaux'] :param region: Region name for connection. [Default us-east-1] :param return_credentials: Indicates if the STS credentials should be returned with the client [Default False] :param external_id: Optional external id to pass to sts:AssumeRole. See https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html :param arn_partition: Optional parameter to specify other aws partitions such as aws-us-gov for aws govcloud :param read_only: Optional parameter to specify the built in ReadOnlyAccess AWS policy :param retry_max_attempts: An integer representing the maximum number of retry attempts that will be made on a single request :param config: Optional botocore.client.Config :param sts_client_kwargs: Optional arguments to pass during STS client creation :return: boto3 client or resource connection """ key = (account_number, assume_role, session_name, external_id, region, service_type, service, arn_partition, read_only) client_config = Config(retries=dict(max_attempts=retry_max_attempts)) if not client_kwargs: client_kwargs = {} if config: client_config = client_config.merge(config) if key in CACHE: retval = _get_cached_creds(key, service, service_type, region, future_expiration_minutes, return_credentials, client_config, client_kwargs) if retval: return retval role = None if assume_role: sts_client_kwargs = sts_client_kwargs or {} sts = boto3.session.Session().client('sts', **sts_client_kwargs) # prevent malformed ARN if not all([account_number, assume_role]): raise ValueError( "Account number and role to assume are both required") arn = 'arn:{partition}:iam::{0}:role/{1}'.format( account_number, assume_role, partition=arn_partition) assume_role_kwargs = {'RoleArn': arn, 'RoleSessionName': session_name} if read_only: assume_role_kwargs['PolicyArns'] = [ { 'arn': 'arn:aws:iam::aws:policy/ReadOnlyAccess' }, ] if external_id: assume_role_kwargs['ExternalId'] = external_id role = sts.assume_role(**assume_role_kwargs) if service_type == 'client': conn = _client(service, region, role, client_config, client_kwargs) elif service_type == 'resource': conn = _resource(service, region, role, client_config, client_kwargs) if role: CACHE[key] = role if return_credentials: return conn, role['Credentials'] return conn