def initial_setup(logger, config, config_path, no_keychain=False): console_input = prompter() profile_name = console_input('Profile name to [%s]: ' % ("default")) if profile_name is None or profile_name == "": profile_name = "default" profile_name = "{}-long-term".format(profile_name) aws_access_key_id = getpass.getpass('aws_access_key_id: ') if aws_access_key_id is None or aws_access_key_id == "": log_error_and_exit(logger, "You must supply aws_access_key_id") aws_secret_access_key = getpass.getpass('aws_secret_access_key: ') if aws_secret_access_key is None or aws_secret_access_key == "": log_error_and_exit(logger, "You must supply aws_secret_access_key") if no_keychain: config.add_section(profile_name) config.set(profile_name, 'aws_access_key_id', aws_access_key_id) config.set(profile_name, 'aws_secret_access_key', aws_secret_access_key) with open(config_path, 'w') as configfile: config.write(configfile) else: keyring.set_password("aws:access_key_id", profile_name, aws_access_key_id) keyring.set_password("aws:secret_access_key", profile_name, aws_secret_access_key)
def get_credentials(short_term_name, lt_key_id, lt_access_key, args, config): if args.token: logger.debug("Received token as argument") mfa_token = '%s' % (args.token) else: console_input = prompter() mfa_token = console_input('Enter AWS MFA code for device [%s] ' '(renewing for %s seconds):' % (args.device, args.duration)) client = boto3.client('sts', aws_access_key_id=lt_key_id, aws_secret_access_key=lt_access_key) if args.assume_role: logger.info("Assuming Role - Profile: %s, Role: %s, Duration: %s", short_term_name, args.assume_role, args.duration) if args.role_session_name is None: log_error_and_exit( logger, "You must specify a role session name " "via --role-session-name") try: response = client.assume_role( RoleArn=args.assume_role, RoleSessionName=args.role_session_name, DurationSeconds=args.duration, SerialNumber=args.device, TokenCode=mfa_token) except ClientError as e: log_error_and_exit( logger, "An error occured while calling " "assume role: {}".format(e)) except ParamValidationError: log_error_and_exit(logger, "Token must be six digits") config.set( short_term_name, 'assumed_role', 'True', ) config.set( short_term_name, 'assumed_role_arn', args.assume_role, ) else: logger.info("Fetching Credentials - Profile: %s, Duration: %s", short_term_name, args.duration) try: response = client.get_session_token(DurationSeconds=args.duration, SerialNumber=args.device, TokenCode=mfa_token) except ClientError as e: log_error_and_exit( logger, "An error occured while calling assume role: {}".format(e)) except ParamValidationError: log_error_and_exit(logger, "Token must be six digits") config.set( short_term_name, 'assumed_role', 'False', ) config.remove_option(short_term_name, 'assumed_role_arn') # aws_session_token and aws_security_token are both added # to support boto and boto3 options = [ ('aws_access_key_id', 'AccessKeyId'), ('aws_secret_access_key', 'SecretAccessKey'), ('aws_session_token', 'SessionToken'), ('aws_security_token', 'SessionToken'), ] for option, value in options: config.set(short_term_name, option, response['Credentials'][value]) # Save expiration individiually, so it can be manipulated config.set( short_term_name, 'expiration', response['Credentials']['Expiration'].strftime('%Y-%m-%d %H:%M:%S')) with open(AWS_CREDS_PATH, 'w') as configfile: config.write(configfile) logger.info("Success! Your credentials will expire in %s seconds at: %s" % (args.duration, response['Credentials']['Expiration'])) sys.exit(0)
def main(): parser = argparse.ArgumentParser() parser.add_argument('--device', required=False, metavar='arn:aws:iam::123456788990:mfa/dudeman', help="The MFA Device ARN. This value can also be " "provided via the environment variable 'MFA_DEVICE' or" " the ~/.aws/credentials variable 'aws_mfa_device'.") parser.add_argument('--duration', type=int, help="The duration, in seconds, that the temporary " "credentials should remain valid. Minimum value: " "900 (15 minutes). Maximum: 129600 (36 hours). " "Defaults to 43200 (12 hours), or 3600 (one " "hour) when using '--assume-role'. This value " "can also be provided via the environment " "variable 'MFA_STS_DURATION'. ") parser.add_argument('--profile', help="If using profiles, specify the name here. The " "default profile name is 'default'. The value can " "also be provided via the environment variable " "'AWS_PROFILE'.", required=False) parser.add_argument('--long-term-suffix', '--long-suffix', help="The suffix appended to the profile name to" "identify the long term credential section", required=False) parser.add_argument('--short-term-suffix', '--short-suffix', help="The suffix appended to the profile name to" "identify the short term credential section", required=False) parser.add_argument('--assume-role', '--assume', metavar='arn:aws:iam::123456788990:role/RoleName', help="The ARN of the AWS IAM Role you would like to " "assume, if specified. This value can also be provided" " via the environment variable 'MFA_ASSUME_ROLE'", required=False) parser.add_argument('--role-session-name', help="Friendly session name required when using " "--assume-role", default=getpass.getuser(), required=False) parser.add_argument('--force', help="Refresh credentials even if currently valid.", action="store_true", required=False) parser.add_argument( '--log-level', help="Set log level", choices=['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET'], required=False, default='DEBUG') parser.add_argument('--setup', help="Setup a new long term credentials section", action="store_true", required=False) parser.add_argument('--token', '--mfa-token', type=str, help="Provide MFA token as an argument", required=False) args = parser.parse_args() level = getattr(logging, args.log_level) setup_logger(level) if not os.path.isfile(AWS_CREDS_PATH): console_input = prompter() create = console_input("Could not locate credentials file at {}, " "would you like to create one? " "[y/n]".format(AWS_CREDS_PATH)) if create.lower() == "y": with open(AWS_CREDS_PATH, 'a'): pass else: log_error_and_exit( logger, 'Could not locate credentials file at ' '%s' % (AWS_CREDS_PATH, )) config = get_config(AWS_CREDS_PATH) if args.setup: initial_setup(logger, config, AWS_CREDS_PATH) return validate(args, config)
def get_credentials(short_term_name, long_term_name, lt_key_id, lt_access_key, args, config): if args.token: logger.debug("Received token as argument") mfa_token = '%s' % (args.token) else: console_input = prompter() mfa_token = console_input('Enter AWS MFA code for device [%s] ' '(renewing for %s seconds):' % (args.device, args.duration)) client = boto3.client('sts', aws_access_key_id=lt_key_id, aws_secret_access_key=lt_access_key) if args.assume_role: logger.info("Assuming Role - Profile: %s, Role: %s, Duration: %s", short_term_name, args.assume_role, args.duration) if args.role_session_name is None: log_error_and_exit( logger, "You must specify a role session name " "via --role-session-name") try: response = client.assume_role( RoleArn=args.assume_role, RoleSessionName=args.role_session_name, DurationSeconds=args.duration, SerialNumber=args.device, TokenCode=mfa_token) except ClientError as e: log_error_and_exit( logger, "An error occured while calling " "assume role: {}".format(e)) except ParamValidationError as e: log_error_and_exit(logger, getattr(e, 'message', repr(e))) config.set( short_term_name, 'assumed_role', 'True', ) config.set( short_term_name, 'assumed_role_arn', args.assume_role, ) else: logger.info("Fetching Credentials - Profile: %s, Duration: %s", short_term_name, args.duration) try: response = client.get_session_token(DurationSeconds=args.duration, SerialNumber=args.device, TokenCode=mfa_token) except ClientError as e: log_error_and_exit( logger, "An error occured while calling assume role: {}".format(e)) except ParamValidationError: log_error_and_exit(logger, getattr(e, 'message', repr(e))) config.set( short_term_name, 'assumed_role', 'False', ) config.remove_option(short_term_name, 'assumed_role_arn') # aws_session_token and aws_security_token are both added # to support boto and boto3 options = [ ('aws_access_key_id', 'AccessKeyId'), ('aws_secret_access_key', 'SecretAccessKey'), ('aws_session_token', 'SessionToken'), ('aws_security_token', 'SessionToken'), ] for option, value in options: config.set(short_term_name, option, response['Credentials'][value]) # Save expiration individiually, so it can be manipulated config.set( short_term_name, 'expiration', response['Credentials']['Expiration'].strftime('%Y-%m-%d %H:%M:%S')) # Copy over other aws config options # List taken from - https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html # Excluding those to do with authentication, which we have already handled copy_options = [ 'region', 's3', 'api_versions', 'ca_bundle', 'cli_follow_urlparam', 'cli_timestamp_format', 'output', 'parameter_validation', 'tcp_keepalive' ] for copy_option in copy_options: if config.has_option(long_term_name, copy_option): config.set(short_term_name, copy_option, config.get(long_term_name, copy_option)) with open(AWS_CREDS_PATH, 'w') as configfile: config.write(configfile) logger.info("Success! Your credentials will expire in %s seconds at: %s" % (args.duration, response['Credentials']['Expiration'])) warn_on_existing_aws_env() sys.exit(0)