def load(self): botocore_session = BotocoreSession(profile=self._profile) config = botocore_session.get_scoped_config() source_profile = config.get('source_profile') mfa_serial = config.get('mfa_serial') if source_profile and mfa_serial: fetcher = SourceProfileMfaCredentialsFetcher( profile=source_profile, mfa_serial=mfa_serial, mfa_prompter=self._mfa_prompter, cache=cache, ) refresher = fetcher.fetch_credentials role_arn = config.get('role_arn') if role_arn: external_id = config.get('external_id') duration_seconds = config.get('duration_seconds') refresher = create_assume_role_refresher( refresher=refresher, role_arn=role_arn, external_id=external_id, session_name=config.get('role_session_name') or self._profile, duration_seconds=duration_seconds, ) return DeferredRefreshableCredentials( method=self.METHOD, refresh_using=refresher, )
def load(cls, profile: str) -> Optional["AwsConfig"]: session = Session(profile=profile) try: c = session.get_scoped_config() except ProfileNotFound: raise MissingAwsConfigException(session.profile) def get(key: str, func: Callable = None): val = c.get(key) if val is None or val == "None": return None else: return func(val) if func else val try: return cls( profile=profile, azure_tenant_id=c["awsad-azure_tenant_id"], azure_app_id=c["awsad-azure_app_id"], azure_app_title=c["awsad-azure_app_title"], aws_default_role_arn=get("awsad-aws_default_role_arn"), aws_session_duration=get("awsad-aws_session_duration", int), aws_access_key_id=get("aws_access_key_id"), aws_secret_access_key=get("aws_secret_access_key"), aws_session_token=get("aws_session_token"), aws_expiration_time=get("awsad-aws_expiration_time", datetime.datetime.fromisoformat)) except KeyError: raise MissingAwsConfigException(session.profile)
def write_session(app_meta, profile, dest_profile, mfacode): pr_max = max(len(profile), len(dest_profile)) print( esc(34) + "Selected *start* profile:", esc(47) + esc(30), profile.ljust(pr_max), esc(0)) print( esc(34) + " *dest* profile:", esc(42) + esc(30), dest_profile.ljust(pr_max), esc(0)) if dest_profile == profile: die("Cannot continue with 'start == destination' ! ") #info(f"back {backup}") aws_session = Session(profile=profile) creds, backup = load_creds(dest_profile) # useful stuff in botocore: scopeConfig = aws_session.get_scoped_config() # debug(scopeConfig) mfa_serial = scopeConfig.get('mfa_serial') if not mfa_serial: die(f"No 'mfa_serial' in profile [{profile}]") else: print(f"MFA device = {mfa_serial}") opts = {"TokenCode": mfacode, "SerialNumber": mfa_serial} resp = sts_session_token(aws_session, opts) r_creds = resp['Credentials'] info(f"Downloaded new temp/creds. ID = {r_creds['AccessKeyId']}") #info(resp) creds.set_new_attrs(backup, **attribs_from_raw(r_creds, app_meta)) edits = creds.save() print( esc(32) + "Wrote edits to file(s) :", esc(42) + esc(30) + str([str(fn.path) for fn in edits]) + esc(0))
def credential_process(profile, start_url, region, account_id, role_name, force_refresh, verbose): """Helper for AWS SDKs that don't yet support AWS SSO. This is not a command you use directly. In a ~/.aws/config profile set up for AWS SSO, you can add the line credential_process = aws-sso-util credential-process --profile NAME with the profile name set to the profile the line is in. This line is automatically added by aws-sso-util configure commands. """ if verbose or os.environ.get("AWS_SSO_CREDENTIAL_PROCESS_DEBUG", "").lower() in ["1", "true"]: logging.basicConfig(level=logging.DEBUG, filename=LOG_FILE, filemode="w") else: logging.disable(logging.CRITICAL) LOGGER.info("Starting credential process at {}".format( datetime.datetime.now().isoformat())) if role_name is None and os.environ.get("AWS_SSO_ROLE_NAME"): LOGGER.debug("Using role from env: {}".format( os.environ["AWS_SSO_ROLE_NAME"])) role_name = os.environ["AWS_SSO_ROLE_NAME"] if account_id is None and os.environ.get("AWS_SSO_ACCOUNT_ID"): LOGGER.debug("Using acccount from env: {}".format( os.environ["AWS_SSO_ACCOUNT_ID"])) account_id = os.environ["AWS_SSO_ACCOUNT_ID"] # if role_name and role_name.startswith("arn"): # parts = role_name.split(":") # account_id = parts[4] # role_name = parts[5].split("/", 1)[1] if start_url is None and os.environ.get("AWS_SSO_START_URL"): start_url = os.environ["AWS_SSO_START_URL"] if region is None and os.environ.get("AWS_SSO_REGION"): region = os.environ["AWS_SSO_REGION"] session_kwargs = {} if profile: session_kwargs["profile"] = profile arg_config = { "sso_start_url": start_url, "sso_region": region, "sso_role_name": role_name, "sso_account_id": account_id, } LOGGER.info("CONFIG FROM ARGS: {}".format(json.dumps(arg_config))) try: session = Session(**session_kwargs) if profile: profile_config = session.get_scoped_config() LOGGER.info("CONFIG FROM PROFILE: {}".format( json.dumps(profile_config))) else: profile_config = {} config = get_config(arg_config, profile_config) LOGGER.info("CONFIG: {}".format(json.dumps(config))) if (config.get("sso_interactive_auth") or "").lower() == "true": raise InvalidSSOConfigError( "Interactive auth has been removed. See https://github.com/benkehoe/aws-sso-credential-process/issues/4" ) if not config["sso_account_id"]: raise InvalidSSOConfigError("Missing account id") if not config["sso_role_name"]: raise InvalidSSOConfigError("Missing role") credentials = get_credentials( session=session, start_url=config["sso_start_url"], sso_region=config["sso_region"], account_id=config["sso_account_id"], role_name=config["sso_role_name"], force_refresh=force_refresh, ) output = { "Version": 1, "AccessKeyId": credentials["access_key"], "SecretAccessKey": credentials["secret_key"], "SessionToken": credentials["token"], # as provided the expiration isn"t valid ISO8601 and that causes parsing errors for some SDKs "Expiration": credentials["expiry_time"].replace("UTC", "Z"), } LOGGER.debug("CREDENTIALS: " + json.dumps(output)) print(json.dumps(output, separators=(",", ":"))) except (AuthenticationNeededError, UnauthorizedSSOTokenError) as e: if profile: aws_sso_util_cmd = f"aws-sso-util login --profile {profile}" aws_sso_cmd = f"aws sso login --profile {profile}" else: aws_sso_util_cmd = f"aws-sso-util login {config['sso_start_url']} {config['sso_region']}" aws_sso_cmd = f"aws sso login" print( f"Login required. Use `{aws_sso_util_cmd}` or `{aws_sso_cmd}` and try again.", file=sys.stderr) sys.exit(1) except InvalidSSOConfigError as e: LOGGER.error(e) print(e, file=sys.stderr) sys.exit(2) except AuthDispatchError as e: LOGGER.error(e) print(e, file=sys.stderr) sys.exit(3) except ClientError as e: LOGGER.error(e, exc_info=True) #TODO: print a different message for AccessDeniedException during CreateToken? -> user canceled login # boto_error_matches(e, "CreateToken", "AccessDeniedException") print("ERROR:", e, file=sys.stderr) sys.exit(4) except Exception as e: LOGGER.error(e, exc_info=True) print("ERROR:", e, file=sys.stderr) sys.exit(5)
def main(): parser = argparse.ArgumentParser() parser.add_argument('--profile', help='Extract settings from the given profile') parser.add_argument('--role-name') parser.add_argument('--account-id') parser.add_argument('--start-url') parser.add_argument('--region') interactive_group = parser.add_mutually_exclusive_group() interactive_group.add_argument('--interactive', '-i', action='store_const', const=True, dest='interactive', help='Enable interactive auth') interactive_group.add_argument('--noninteractive', '-n', action='store_const', const=False, dest='interactive', help='Disable interactive auth') parser.add_argument('--force-refresh', action='store_true', help='Do not reuse cached AWS SSO token') parser.add_argument('--debug', action='store_true', help='Write to the debugging log file') parser.add_argument('--version', action='store_true') args = parser.parse_args() if args.version: print(__version__) parser.exit() if args.debug or os.environ.get('AWS_SSO_CREDENTIAL_PROCESS_DEBUG', '').lower() in ['1', 'true']: logging.basicConfig(level=logging.DEBUG, filename=LOG_FILE, filemode='w') else: logging.disable(logging.CRITICAL) LOGGER.info( f'Starting credential process at {datetime.datetime.now().isoformat()}' ) if args.role_name is None and os.environ.get('AWS_SSO_ROLE_NAME'): LOGGER.debug(f"Using role from env: {os.environ['AWS_SSO_ROLE_NAME']}") args.role_name = os.environ['AWS_SSO_ROLE_NAME'] if args.account_id is None and os.environ.get('AWS_SSO_ACCOUNT_ID'): LOGGER.debug( f"Using acccount from env: {os.environ['AWS_SSO_ACCOUNT_ID']}") args.account_id = os.environ['AWS_SSO_ACCOUNT_ID'] # if args.role_name and args.role_name.startswith('arn'): # parts = args.role_name.split(':') # args.account_id = parts[4] # args.role_name = parts[5].split('/', 1)[1] if args.start_url is None and os.environ.get('AWS_SSO_START_URL'): args.start_url = os.environ['AWS_SSO_START_URL'] if args.region is None and os.environ.get('AWS_SSO_REGION'): args.region = os.environ['AWS_SSO_REGION'] if args.interactive is None: if os.environ.get('AWS_SSO_INTERACTIVE_AUTH'): LOGGER.debug( f"Setting interactive auth from env: {os.environ['AWS_SSO_INTERACTIVE_AUTH']}" ) args.interactive = os.environ['AWS_SSO_INTERACTIVE_AUTH'].lower( ) in ['true', '1'] else: args.interactive = False session_kwargs = {} if args.profile: session_kwargs['profile'] = args.profile arg_config = { 'sso_start_url': args.start_url, 'sso_region': args.region, 'sso_role_name': args.role_name, 'sso_account_id': args.account_id, 'sso_interactive_auth': 'true' if args.interactive else 'false', } LOGGER.info(f'CONFIG FROM ARGS: {json.dumps(arg_config)}') try: session = Session(**session_kwargs) if args.profile: profile_config = session.get_scoped_config() LOGGER.info(f'CONFIG FROM PROFILE: {json.dumps(profile_config)}') else: profile_config = {} config = get_config(arg_config, profile_config) LOGGER.info(f'CONFIG: {json.dumps(config)}') interactive = config['sso_interactive_auth'].lower() == 'true' token_loader = get_token_loader( session=session, sso_region=config['sso_region'], interactive=interactive, force_refresh=args.force_refresh, ) if not config['sso_account_id']: #TODO: if interactive, prompt for account raise InvalidSSOConfigError('Missing account id') if not config['sso_role_name']: #TODO: if interactive, prompt for role raise InvalidSSOConfigError('Missing role') credentials = get_credentials(session=session, sso_region=config['sso_region'], start_url=config['sso_start_url'], account_id=config['sso_account_id'], role_name=config['sso_role_name'], token_loader=token_loader) output = { "Version": 1, "AccessKeyId": credentials['access_key'], "SecretAccessKey": credentials['secret_key'], "SessionToken": credentials['token'], "Expiration": credentials['expiry_time'] } LOGGER.debug('CREDENTIALS: ' + json.dumps(output)) print(json.dumps(output, separators=(',', ':'))) except InteractiveAuthDisabledError as e: LOGGER.info('Auth needed but interactive auth disabled') print('Interactive auth disabled, use `aws sso login` and try again', file=sys.stderr) sys.exit(1) except InvalidSSOConfigError as e: LOGGER.error(e) print(e, file=sys.stderr) sys.exit(2) except AuthDispatchError as e: LOGGER.error(e) print(e, file=sys.stderr) sys.exit(3) except ClientError as e: LOGGER.error(e, exc_info=True) #TODO: print a different message for AccessDeniedException during CreateToken? -> user canceled login # boto_error_matches(e, 'CreateToken', 'AccessDeniedException') print('ERROR:', e, file=sys.stderr) sys.exit(4) except Exception as e: LOGGER.error(e, exc_info=True) print('ERROR:', e, file=sys.stderr) sys.exit(5)