From dfabe34c5c0642d0b9c725009a7cdf25ee07b389 Mon Sep 17 00:00:00 2001 From: Hector Castro <43639+hectcastro@users.noreply.github.com> Date: Thu, 17 Dec 2020 07:07:51 -0500 Subject: [PATCH] Remove AWS STS specific code from release tooling (#1279) We now depend on AWS credential configuration via the standard AWS_PROFILE environment variable. --- .gitignore | 1 + deployment/auth.py | 84 --------------------------------- deployment/cac-stack.py | 42 ++++------------- deployment/packer/cac_packer.py | 19 +++----- 4 files changed, 17 insertions(+), 129 deletions(-) delete mode 100755 deployment/auth.py diff --git a/.gitignore b/.gitignore index 327430b54..ebc292212 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ develop-eggs lib lib64 __pycache__ +.venv # Installer logs pip-log.txt diff --git a/deployment/auth.py b/deployment/auth.py deleted file mode 100755 index 7f488e612..000000000 --- a/deployment/auth.py +++ /dev/null @@ -1,84 +0,0 @@ -"""Helper for setting up AWS MFA auth""" -import os - -import boto - - -class AuthException(Exception): - pass - - -BOTO_CONFIG_TEMPLATE = """ -[default] -aws_access_key_id = {aws_access_key_id} -aws_secret_access_key = {aws_secret_access_key} -aws_security_token = {aws_security_token} -""" - - -def get_creds(aws_access_key_id, aws_secret_access_key, aws_role_arn): - """Helper method that returns a new AWS config with temp credentials - - Args: - aws_access_key_id (str): AWS access key id (public) - aws_secret_access_key (str): AWS secret key (private) - """ - aws_config = {'aws_access_key_id': aws_access_key_id, - "aws_secret_access_key": aws_secret_access_key} - iam_conn = boto.connect_iam(**aws_config) - sts_conn = boto.connect_sts(**aws_config) - username = input('Please provide AWS username: ') - mfa_devices = (iam_conn.get_all_mfa_devices(username) - ['list_mfa_devices_response'] - ['list_mfa_devices_result'] - ['mfa_devices']) - - if len(mfa_devices) > 1: - raise AuthException('Unable to handle a user with multiple MFA devices') - - if len(mfa_devices) == 0: - raise AuthException('Must have MFA device to get temporary credentials') - - mfa_serial_number = mfa_devices[0]['serial_number'] - mfa_token = input('Please enter your 6 digit MFA token: ') - - assumed_role = sts_conn.assume_role( - role_arn=aws_role_arn, - role_session_name='AssumeRoleSessionWithMFA', - mfa_serial_number=mfa_serial_number, - mfa_token=mfa_token - ) - - return dict(aws_access_key_id=assumed_role.credentials.access_key, - aws_secret_access_key=assumed_role.credentials.secret_key, - aws_security_token=assumed_role.credentials.session_token) - - -def write_creds(creds, force): - """Write temporary credentials to file to be used by boto or other CLI tools - - Args: - creds (Dict): Dictionary containing AWS credentials - force (bool): whether or not to overwrite existing credentials files - """ - aws_dir = os.path.expanduser('~/.aws') - if not os.path.exists(aws_dir): - os.mkdir(aws_dir) - - boto_config_path = os.path.join(aws_dir, 'credentials') - if os.path.isfile(boto_config_path) and not force: - raise AuthException('Must use --force option to replace existing boto config') - - with open(boto_config_path, 'w') as fh: - boto_config = BOTO_CONFIG_TEMPLATE.format( - aws_access_key_id=creds['aws_access_key_id'], - aws_secret_access_key=creds['aws_secret_access_key'], - aws_security_token=creds['aws_security_token']) - fh.write(boto_config) - - -def delete_creds(): - """Delete the credentials file if there is one""" - cred_file = os.path.expanduser('~/.aws/credentials') - if os.path.isfile(cred_file): - os.remove(cred_file) diff --git a/deployment/cac-stack.py b/deployment/cac-stack.py index 1b539b03c..b57ff9143 100755 --- a/deployment/cac-stack.py +++ b/deployment/cac-stack.py @@ -3,46 +3,36 @@ """Commands for building AMIs and setting up CAC TripPlanner stacks on AWS""" import argparse import os -import sys from cloudformation.stacks import build_stacks from cloudformation.template_utils import get_config from packer.cac_packer import run_packer -from auth import get_creds, write_creds, delete_creds file_dir = os.path.dirname(os.path.realpath(__file__)) -def launch_stacks(cac_config, creds, stack_type, stack_color, **kwargs): +def launch_stacks(cac_config, stack_type, stack_color, **kwargs): """Launches the specified stacks Args: cac_config (Dict): Dictionary of AWS parameter values - creds (Dict): Dictionary containing AWS credentials stack_type (str): Type of environment (dev, prod, test) stack_color (str): Color of environment (blue, green) """ - # Launching the stack is a lot easier with the credentials file in place. Otherwise, - # there are several places where credentials need to be passed into boto directly. - # Write it out temporarily, and remove it afterwards. - write_creds(creds, True) - try: - build_stacks(cac_config, stack_type, stack_color) - finally: - delete_creds() + build_stacks(cac_config, stack_type, stack_color) -def create_ami(machine_type, aws_region, creds, aws_config, **kwargs): +def create_ami(machine_type, aws_region, aws_profile, **kwargs): """Creates the specified AMI(s) Args: machine_type (str): Optional type of AMI to build (all are built if unspecified) aws_region (str): AWS region id - creds (Dict): Dictionary containing AWS credentials + aws_profile (str): AWS profile name """ - run_packer(machine_type, aws_region, creds, aws_config) + run_packer(machine_type, aws_region, aws_profile) def main(): @@ -50,23 +40,13 @@ def main(): common_parser = argparse.ArgumentParser(add_help=False) common_parser.add_argument('--aws-region', default='us-east-1', help='AWS region -- defaults to us-east-1') - common_parser.add_argument('--aws-access-key-id', required=True, - help='AWS Access Key ID') - common_parser.add_argument('--aws-secret-access-key', required=True, - help='AWS Secret Access Key') - common_parser.add_argument('--aws-role-arn', required=True, - help='AWS ARN for assumed role') + common_parser.add_argument('--aws-profile', default=os.environ.get('AWS_PROFILE', 'gophillygo'), + help='AWS profile') common_parser.add_argument('--cac-config-path', default=os.path.join(file_dir, 'default.yaml'), help='Path to CAC stack config') common_parser.add_argument('--cac-profile', default='default', help='CAC stack profile to use for launching stacks') - if os.path.isfile(os.path.expanduser('~/.aws/credentials')): - # There is a bug in Packer that makes it so the only way to successfully build - # an AMI using MFA tokens is to not have the ~/.aws/credentials file, and instead - # specify the parameters via environment variables. - print("Please delete your ~/.aws/credentials and try again") - sys.exit(1) parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(title='CAC TripPlanner Stack Commands') @@ -93,14 +73,10 @@ def main(): help='Optional machine type. One of "app", "bastion", "otp"') cac_ami.set_defaults(func=create_ami) - # Parse, obtain temporary MFA credentials, and run + # Parse and run args = parser.parse_args() - creds = get_creds(args.aws_access_key_id, args.aws_secret_access_key, - args.aws_role_arn) - aws_config = {'aws_access_key_id': args.aws_access_key_id, - "aws_secret_access_key": args.aws_secret_access_key} cac_config = get_config(args.cac_config_path, args.cac_profile) - args.func(cac_config=cac_config, creds=creds, aws_config=aws_config, **vars(args)) + args.func(cac_config=cac_config, **vars(args)) if __name__ == '__main__': main() diff --git a/deployment/packer/cac_packer.py b/deployment/packer/cac_packer.py index d09445ada..96bc24a91 100644 --- a/deployment/packer/cac_packer.py +++ b/deployment/packer/cac_packer.py @@ -13,7 +13,7 @@ class CacStackException(Exception): pass -def get_ubuntu_ami(region, creds): +def get_ubuntu_ami(region): """Gets AMI ID for current release in region Args: @@ -42,13 +42,13 @@ def ami_filter(ami): return amis[0]['id'] -def run_packer(machine_type, region, creds, aws_config): +def run_packer(machine_type, aws_region, aws_profile): """Runs packer command to build the desired AMI(s) Args: machine_type (str): Optional machine type string for passing in as the `-only` param - region (str): AWS region id - creds (Dict): Dictionary containing AWS credentials + aws_region (str): AWS region id + aws_profile (str): AWS profile name """ # Remove examples subdirectory from all Azavea roles @@ -60,16 +60,11 @@ def run_packer(machine_type, region, creds, aws_config): print(('Removing {}'.format(examples_path))) shutil.rmtree(examples_path) - env = os.environ.copy() - env['AWS_ACCESS_KEY_ID'] = creds['aws_access_key_id'] - env['AWS_SECRET_ACCESS_KEY'] = creds['aws_secret_access_key'] - env['AWS_SESSION_TOKEN'] = creds['aws_security_token'] - - aws_ubuntu_ami = get_ubuntu_ami(region, aws_config) + aws_ubuntu_ami = get_ubuntu_ami(aws_region) packer_template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'cac.json') packer_command = ['packer', 'build', - '-var', 'aws_region={}'.format(region), + '-var', 'aws_region={}'.format(aws_region), '-var', 'ubuntu_ami={}'.format(aws_ubuntu_ami)] # Create the specified machine type, or all of them if one is not specified @@ -80,4 +75,4 @@ def run_packer(machine_type, region, creds, aws_config): packer_command.append(packer_template_path) print('Running Packer Command: {}'.format(' '.join(packer_command))) - subprocess.check_call(packer_command, env=env) + subprocess.check_call(packer_command, env=os.environ.copy().update({'AWS_PROFILE': aws_profile}))