Ejemplo n.º 1
0
    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,
            )
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
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))
Ejemplo n.º 4
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)