Skip to content
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

Harcoded ARNS Bad: There are more partitions than standard. #53936

Closed
wants to merge 11 commits into from
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Pipfile.lock
/tests/cachedir/
/tests/unit/templates/roots/
/var/
.direnv
.envrc

# setuptools stuff
*.egg-info
Expand All @@ -54,6 +56,7 @@ htmlcov/
/*.iml
*.sublime-project
*.sublime-workspace
.vscode

# ignore ctags file
tags
Expand Down
48 changes: 10 additions & 38 deletions salt/modules/boto_iam.py
Original file line number Diff line number Diff line change
Expand Up @@ -1283,42 +1283,11 @@ def build_policy(region=None, key=None, keyid=None, profile=None):


def get_account_id(region=None, key=None, keyid=None, profile=None):
'''
Get a the AWS account id associated with the used credentials.

CLI Example:
return __salt__['boto_sts.get_account_id'](region=region, key=key, keyid=keyid, profile=profile)

.. code-block:: bash

salt myminion boto_iam.get_account_id
'''
cache_key = 'boto_iam.account_id'
if cache_key not in __context__:
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
try:
ret = conn.get_user()
# The get_user call returns an user ARN:
# arn:aws:iam::027050522557:user/salt-test
arn = ret['get_user_response']['get_user_result']['user']['arn']
account_id = arn.split(':')[4]
except boto.exception.BotoServerError:
# If call failed, then let's try to get the ARN from the metadata
timeout = boto.config.getfloat(
'Boto', 'metadata_service_timeout', 1.0
)
attempts = boto.config.getint(
'Boto', 'metadata_service_num_attempts', 1
)
identity = boto.utils.get_instance_identity(
timeout=timeout, num_retries=attempts
)
try:
account_id = identity['document']['accountId']
except KeyError:
log.error('Failed to get account id from instance_identity in'
' boto_iam.get_account_id.')
__context__[cache_key] = account_id
return __context__[cache_key]
def get_partition(region=None, key=None, keyid=None, profile=None):
return __salt__['boto_sts.get_partition'](region=region, key=key, keyid=keyid, profile=profile)


def get_all_roles(path_prefix=None, region=None, key=None, keyid=None,
Expand Down Expand Up @@ -1669,13 +1638,16 @@ def export_roles(path_prefix='/', region=None, key=None, keyid=None, profile=Non


def _get_policy_arn(name, region=None, key=None, keyid=None, profile=None):
if name.startswith('arn:aws:iam:'):
return name

account_id = get_account_id(
region=region, key=key, keyid=keyid, profile=profile
)
return 'arn:aws:iam::{0}:policy/{1}'.format(account_id, name)
partition = get_partition(
region=region, key=key, keyid=keyid, profile=profile
)
if name.startswith('arn:{0}:iam'.format(partition)):
return name

return 'arn:{0}:iam::{1}:policy/{2}'.format(partition, account_id, name)


def policy_exists(policy_name,
Expand Down
15 changes: 10 additions & 5 deletions salt/modules/boto_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,17 +187,22 @@ def function_exists(FunctionName, region=None, key=None,


def _get_role_arn(name, region=None, key=None, keyid=None, profile=None):
if name.startswith('arn:aws:iam:'):
return name

account_id = __salt__['boto_iam.get_account_id'](
account_id = __salt__['boto_sts.get_account_id'](
region=region, key=key, keyid=keyid, profile=profile
)
partition = __salt__['boto_sts.get_partition'](
region=region, key=key, keyid=keyid, profile=profile
)

if name.startswith('arn:{0}:iam'.format(partition)):
return name

if profile and 'region' in profile:
region = profile['region']
if region is None:
region = 'us-east-1'
return 'arn:aws:iam::{0}:role/{1}'.format(account_id, name)

return 'arn:{0}:iam::{1}:role/{2}'.format(partition, account_id, name)


def _filedata(infile):
Expand Down
190 changes: 190 additions & 0 deletions salt/modules/boto_sts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# -*- coding: utf-8 -*-
'''
Connection module for Amazon STS

'''
# keep lint from choking on _get_conn and _cache_id
# pylint: disable=E0602

# Import Python libs
from __future__ import absolute_import
import logging
import hashlib

# Import Salt libs
import salt.utils.compat
from salt.ext import six
from salt.utils.versions import LooseVersion as _LooseVersion


# Support 2017 to 2018+ transition of salt release
try:
# 2018+ Salt Release
from salt.utils.stringutils import to_bytes as salt_utils_stringutils_to_bytes
except ImportError:
from salt.utils import to_bytes as salt_utils_stringutils_to_bytes
log = logging.getLogger(__name__)

# Import third party libs

# pylint: disable=import-error
try:
# pylint: disable=unused-import
import boto
import boto3
# pylint: enable=unused-import
from botocore import __version__ as found_botocore_version
logging.getLogger('boto').setLevel(logging.CRITICAL)
logging.getLogger('boto3').setLevel(logging.CRITICAL)
HAS_BOTO = True
except ImportError:
HAS_BOTO = False
# pylint: enable=import-error


def __virtual__():
'''
Only load if boto libraries exist and if boto libraries are greater than
a given version.
'''
required_boto_version = '2.8.0'
required_boto3_version = '1.2.5'
required_botocore_version = '1.5.2'
if not HAS_BOTO:
return (False, 'The boto_lambda module could not be loaded: '
'boto libraries not found')
elif _LooseVersion(boto.__version__) < _LooseVersion(required_boto_version):
return (False, 'The boto_lambda module could not be loaded: '
'boto version {0} or later must be installed.'.format(required_boto_version))
elif _LooseVersion(boto3.__version__) < _LooseVersion(required_boto3_version):
return (False, 'The boto_lambda module could not be loaded: '
'boto version {0} or later must be installed.'.format(required_boto3_version))
elif _LooseVersion(found_botocore_version) < _LooseVersion(required_botocore_version):
return (False, 'The boto_apigateway module could not be loaded: '
'botocore version {0} or later must be installed.'.format(required_botocore_version))
else:
return True


def __init__(opts):
salt.utils.compat.pack_dunder(__name__)
if HAS_BOTO:
__utils__['boto3.assign_funcs'](__name__, 'sts')


def get_account_id(region=None, key=None, keyid=None, profile=None):
'''
Get a the AWS account id associated with the used credentials.

CLI Example:

.. code-block:: bash

salt myminion boto_sts.get_account_id
'''

cxkey, _aregion, _akey, _akeyid = _hash_profile('sts', region, key,
keyid, profile)
cxkey = cxkey + ':account_id'

cache_key = cxkey
if cache_key not in __context__:
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
try:
ret = conn.get_caller_identity()
# The get_user call returns an user ARN:
# arn:aws:iam::027050522557:user/salt-test
arn = ret['Arn']
account_id = arn.split(':')[4]
except boto.exception.BotoServerError as err:
log.info("Falling back to the metadata server - this is probably wrong")
log.debug(err)
# If call failed, then let's try to get the ARN from the metadata
timeout = boto.config.getfloat(
'Boto', 'metadata_service_timeout', 1.0
)
attempts = boto.config.getint(
'Boto', 'metadata_service_num_attempts', 1
)
identity = boto.utils.get_instance_identity(
timeout=timeout, num_retries=attempts
)
try:
account_id = identity['document']['accountId']
except KeyError:
log.error('Failed to get account id from instance_identity in'
' boto_sts.get_caller_identity.')
__context__[cache_key] = account_id
return __context__[cache_key]


def get_partition(region=None, key=None, keyid=None, profile=None):
'''
Get a the AWS partition associated with the used credentials.

CLI Example:

.. code-block:: bash

salt myminion boto_sts.get_partition
'''
cxkey, _aregion, _akey, _akeyid = _hash_profile('sts', region, key,
keyid, profile)
cxkey = cxkey + ':partition'

cache_key = cxkey
if cache_key not in __context__:
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
try:
ret = conn.get_caller_identity()
# The get_user call returns an user ARN:
# arn:aws:iam::027050522557:user/salt-test
arn = ret['Arn']
partition = arn.split(':')[1]
except boto.exception.BotoServerError as err:
log.info("Falling back to the metadata server - this is probably wrong")
log.debug(err)
# If call failed, then let's try to get the ARN from the metadata
timeout = boto.config.getfloat(
'Boto', 'metadata_service_timeout', 1.0
)
attempts = boto.config.getint(
'Boto', 'metadata_service_num_attempts', 1
)
identity = boto.utils.get_instance_metadata(
timeout=timeout, num_retries=attempts
)
try:
partition = identity['iam']['info']['InstanceProfileArn'].split(':')[1]
except KeyError:
log.error('Failed to get partition from metadata in'
' boto_sts.partition.')
__context__[cache_key] = partition
return __context__[cache_key]


def _hash_profile(service, region=None, key=None, keyid=None, profile=None):
'''
Generate a bunch of hashed values for injecting stuff into the __context__ dunder

'''
if profile:
key = profile.get('key', None)
keyid = profile.get('keyid', None)
region = profile.get('region', None)

if not region:
region = 'us-east-1'
msg = 'Assuming default region {0}'.format(region)
log.info(msg)

label = 'boto_{0}:'.format(service)
if keyid:
hash_string = region + keyid + key
if six.PY3:
hash_string = salt_utils_stringutils_to_bytes(hash_string)
cxkey = label + hashlib.md5(hash_string).hexdigest()
else:
cxkey = label + region

return (cxkey, region, key, keyid)
33 changes: 23 additions & 10 deletions salt/states/boto_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,13 +312,22 @@ def function_present(name, FunctionName, Runtime, Role, Handler, ZipFile=None,


def _get_role_arn(name, region=None, key=None, keyid=None, profile=None):
if name.startswith('arn:aws:iam:'):
return name

account_id = __salt__['boto_iam.get_account_id'](
account_id = __salt__['boto_sts.get_account_id'](
region=region, key=key, keyid=keyid, profile=profile
)
return 'arn:aws:iam::{0}:role/{1}'.format(account_id, name)
partition = __salt__['boto_sts.get_partition'](
region=region, key=key, keyid=keyid, profile=profile
)

if name.startswith('arn:{0}:iam'.format(partition)):
return name

if profile and 'region' in profile:
region = profile['region']
if region is None:
region = 'us-east-1'

return 'arn:{0}:iam::{1}:role/{2}'.format(partition, account_id, name)


def _resolve_vpcconfig(conf, region=None, key=None, keyid=None, profile=None):
Expand Down Expand Up @@ -735,17 +744,21 @@ def alias_absent(name, FunctionName, Name, region=None, key=None, keyid=None, pr


def _get_function_arn(name, region=None, key=None, keyid=None, profile=None):
if name.startswith('arn:aws:lambda:'):
return name

account_id = __salt__['boto_iam.get_account_id'](
account_id = __salt__['boto_sts.get_account_id'](
region=region, key=key, keyid=keyid, profile=profile
)
partition = __salt__['boto_sts.get_partition'](
region=region, key=key, keyid=keyid, profile=profile
)

if name.startswith('arn:{0}:lambda'.format(partition)):
return name

if profile and 'region' in profile:
region = profile['region']
if region is None:
region = 'us-east-1'
return 'arn:aws:lambda:{0}:{1}:function:{2}'.format(region, account_id, name)
return 'arn:{0}:lambda:{1}:{2}:function:{3}'.format(partition, region, account_id, name)


def event_source_mapping_present(name, EventSourceArn, FunctionName,
Expand Down