Exemplo n.º 1
0
    def ots_svc(self, env: GlobalEnvironment, refresh: bool = False) -> OTSService:
        """
        Returns a hydrated & properly configured One-time-secret service. This service will be configured for the appropriate
        "utility" account by the registry.
        """

        new_env = env

        try:
            config_svc = self.config_svc(env=env, refresh=refresh)
            utility_account_id: Fig = config_svc.get_fig_with_cache(PS_FIGGY_UTILITY_ACCOUNT_ID).value
            log.debug(f"Got utility account id: {utility_account_id}")
            current_account_id: Fig = config_svc.get_fig_with_cache(PS_FIGGY_CURRENT_ACCOUNT_ID).value
            log.debug(f"Got current session alias: {current_account_id}")
            new_role = Role(role=FIGGY_DEFAULT_ROLE_NAME, full_name='figgy-default')
            regions = config_svc.get_fig_with_cache(PS_FIGGY_REGIONS)
            ots_region = json.loads(regions.value)[0]

            new_env = GlobalEnvironment(role=AssumableRole(account_id=utility_account_id,
                                                           run_env=RunEnv(env="utility", account_id=utility_account_id),
                                                           role=new_role,
                                                           provider_name=env.role.provider_name),
                                        region=ots_region)
        except BaseException as e:
            raise InvalidFiggyConfigurationException(f"Unable to initialize one-time-secret service. Are you sure your "
                                                     f"'utility_account_alias' was set to a valid value when you configured "
                                                     f"Figgy Cloud?") from e

        config_svc = self.config_svc(env=new_env, refresh=refresh)
        ots_key: Fig = config_svc.get_fig_with_cache(PS_FIGGY_OTS_KEY_ID)

        return OTSService(self.__ssm(env=new_env, refresh=refresh),
                          self.__kms(env=new_env, refresh=refresh),
                          kms_id=ots_key.value)
Exemplo n.º 2
0
    def get_role(self, prompt: bool, role_override: str = None, is_setup: bool = False) -> Role:
        """
        Returns a string of the user's selected role.

        Lookup the user's default role from the config file (created via the --configure option), an ENV variable, or
        instead prompt the user for the session.

        :param prompt: True/False - if True, users will always be prompted to input their role
        :param role_override: String representation of the role to get, regardless of defaults.
        :return: str: name of the selected role.
        """

        defaults = FiggySetup.stc_get_defaults(self._is_setup_command, profile=self._profile)
        if defaults is not None and not prompt:

            if role_override:
                if role_override in [role.role.role for role in defaults.assumable_roles] or is_setup:
                    return Role(role=role_override)
                else:
                    self._utils.error_exit(f"Invalid role override provided of: {role_override}. "
                                           f"You do not have permissions to assume this role. Contact your system "
                                           f"administrator to receive permissions then rerun `{CLI_NAME} "
                                           f"--{configure.name}`.")

            return defaults.role
        else:
            roles = self.__setup().get_assumable_roles()
            role_names = list(set([x.role.role for x in roles]))
            return Input.select_role(role_names)
Exemplo n.º 3
0
    def select_role(valid_roles: List[str]) -> Role:
        input_role = None
        while input_role not in valid_roles:
            input_role = prompt(
                f'What type of user are you? Options are: {valid_roles}: \n -> ',
                completer=WordCompleter(valid_roles))
            if input_role not in valid_roles:
                print(
                    f"{input_role} is not a valid user type. Please select from: {valid_roles}"
                )

        return Role(role=input_role)
Exemplo n.º 4
0
 def unconfigured():
     return CLIDefaults(role=Role(role="unconfigured"),
                        colors_enabled=False,
                        user=None,
                        run_env=RunEnv(env="unconfigured"),
                        provider=Provider.UNSELECTED,
                        session_duration=DEFAULT_SESSION_DURATION,
                        region="us-east-1",
                        mfa_enabled=False,
                        mfa_serial=None,
                        provider_config=None,
                        report_errors=False,
                        auto_mfa=False,
                        user_id=str(uuid.uuid4()),
                        service_ns="/app",
                        usage_tracking=False,
                        extras={},
                        enabled_regions=["us-east-1"])
Exemplo n.º 5
0
 def from_profile(profile):
     return CLIDefaults(role=Role(role=profile),
                        colors_enabled=Utils.not_windows(),
                        user=profile,
                        run_env=RunEnv(env=profile),
                        provider=Provider.PROFILE,
                        session_duration=DEFAULT_SESSION_DURATION,
                        region="us-east-1",
                        mfa_enabled=False,
                        mfa_serial=None,
                        provider_config=None,
                        report_errors=False,
                        auto_mfa=False,
                        user_id=str(uuid.uuid4()),
                        service_ns=os.environ.get(APP_NS_OVERRIDE)
                        or "/app",
                        usage_tracking=True,
                        extras={},
                        enabled_regions=["us-east-1"])
Exemplo n.º 6
0
 def sandbox(user: str, role: str, colors: bool):
     return CLIDefaults(role=Role(role=role),
                        colors_enabled=colors,
                        user=user,
                        run_env=RunEnv(env="unconfigured"),
                        provider=Provider.AWS_BASTION,
                        session_duration=SANDBOX_SESSION_DURATION,
                        region=FIGGY_SANDBOX_REGION,
                        mfa_enabled=False,
                        mfa_serial=None,
                        provider_config=BastionProviderConfig(
                            profile_name=FIGGY_SANDBOX_PROFILE),
                        report_errors=False,
                        auto_mfa=False,
                        user_id=str(uuid.uuid4()),
                        service_ns="/app",
                        usage_tracking=True,
                        extras={DISABLE_KEYRING: True},
                        enabled_regions=["us-east-1"])
Exemplo n.º 7
0
    def __lookup_roles(self) -> List[AssumableRole]:
        assertion = self.get_saml_assertion(prompt=False)
        root = ET.fromstring(assertion)
        prefix_map = {"saml2": "urn:oasis:names:tc:SAML:2.0:assertion"}
        role_attribute = root.find(
            ".//saml2:Attribute[@Name='https://aws.amazon.com/SAML/Attributes/Role']",
            prefix_map)

        # SAML arns should look something like this:
        # arn:aws:iam::106481321259:saml-provider/okta,arn:aws:iam::106481321259:role/figgy-dev-data
        # One exception is the `figgy-default` role.
        pattern = r'^arn:aws:iam::([0-9]+):saml-provider/(\w+),arn:aws:iam::.*role/(\w+-(\w+)-(\w+))'
        assumable_roles: List[AssumableRole] = []
        for value in role_attribute.findall('.//saml2:AttributeValue',
                                            prefix_map):
            if FIGGY_DEFAULT_ROLE_NAME not in value:
                result = re.search(pattern, value.text)
                unparsable_msg = f'{value.text} is of an invalid pattern, it must match: {pattern} for figgy to ' \
                                 f'dynamically map account_id -> run_env -> role for OKTA users. If this is not a figgy role, ' \
                                 f'ignore this message.'
                if not result:
                    Utils.stc_warn(unparsable_msg)
                    continue

                result.groups()
                account_id, provider_name, role_name, run_env, role = result.groups(
                )

                if not account_id or not run_env or not role_name or not role:
                    Utils.stc_error_exit(unparsable_msg)
                else:
                    assumable_roles.append(
                        AssumableRole(account_id=account_id,
                                      role=Role(role=role,
                                                full_name=role_name),
                                      run_env=RunEnv(env=run_env),
                                      provider_name=provider_name,
                                      profile=None))
        return assumable_roles
    def get_assumable_roles(self):
        if self.is_role_session():
            user_roles = [self._defaults.role.role]
        else:
            ROLE_PATH = f'/figgy/users/{self.__get_iam_user()}/roles'
            user_roles = self.__get_ssm().get_parameter(ROLE_PATH)
            self._utils.stc_validate(
                user_roles is not None and user_roles != "[]",
                "Something is wrong with your user's configuration with Figgy. "
                "Unable to find any eligible roles for your user. Please contact your"
                " administrator.")

            user_roles = json.loads(user_roles)

        environments = self.__get_ssm().get_all_parameters(
            [PS_FIGGY_ACCOUNTS_PREFIX], option='OneLevel')
        names: List[str] = [env.get('Name') for env in environments]
        parameters = self.__get_ssm().get_parameter_values(names)
        assumable_roles: List[AssumableRole] = []

        for param in parameters:
            env_name = param.get('Name').split('/')[-1]
            account_id = param.get('Value')

            for role in user_roles:
                assumable_roles.append(
                    AssumableRole(
                        run_env=RunEnv(env=env_name, account_id=account_id),
                        role=Role(
                            role=role,
                            full_name=
                            f'{FIGGY_ROLE_NAME_PREFIX}{env_name}-{role}'),
                        account_id=account_id,
                        provider_name=Provider.AWS_BASTION.value,
                        profile=None))

        return assumable_roles
Exemplo n.º 9
0
 def from_profile(profile: str):
     return AssumableRole(account_id=1234567899,
                          run_env=RunEnv(env=profile),
                          profile=profile,
                          role=Role(role=profile),
                          provider_name=Provider.PROFILE.value)
Exemplo n.º 10
0
    def login_sandbox(self):
        """
        If user provides --role flag, skip role & env selection for a smoother user experience.
        """
        EnvironmentValidator(self._defaults).validate_environment_variables()

        Utils.wipe_vaults() or Utils.wipe_defaults(
        ) or Utils.wipe_config_cache()

        self._out.print(
            f"{self.c.fg_bl}Logging you into the Figgy Sandbox environment.{self.c.rs}"
        )
        user = Input.input("Please input a user name: ", min_length=2)
        colors = Input.select_enable_colors()

        # Prompt user for role if --role not provided
        if commands.role not in self.context.options:
            role = Input.select("\n\nPlease select a role to impersonate: ",
                                valid_options=SANDBOX_ROLES)
        else:
            role = self.context.role.role
            self._utils.validate(
                role in SANDBOX_ROLES,
                f"Provided role: >>>`{role}`<<< is not a valid sandbox role."
                f" Please choose from {SANDBOX_ROLES}")

        params = {'role': role, 'user': user}
        result = requests.get(GET_SANDBOX_CREDS_URL, params=params)

        if result.status_code != 200:
            self._utils.error_exit(
                "Unable to get temporary credentials from the Figgy sandbox. If this problem "
                f"persists please notify us on our GITHUB: {FIGGY_GITHUB}")

        data = result.json()
        response = SandboxLoginResponse(**data)
        self._aws_cfg.write_credentials(
            access_key=response.AWS_ACCESS_KEY_ID,
            secret_key=response.AWS_SECRET_ACCESS_KEY,
            token=response.AWS_SESSION_TOKEN,
            region=FIGGY_SANDBOX_REGION,
            profile_name=FIGGY_SANDBOX_PROFILE)

        defaults = CLIDefaults.sandbox(user=user, role=role, colors=colors)
        self._setup.save_defaults(defaults)

        run_env = RunEnv(
            env='dev',
            account_id=SANDBOX_DEV_ACCOUNT_ID) if self.context.role else None

        config_mgr = ConfigManager.figgy()
        config_mgr.set(Config.Section.Bastion.PROFILE, FIGGY_SANDBOX_PROFILE)
        defaults = self._setup.configure_extras(defaults)
        defaults = self._setup.configure_roles(current_defaults=defaults,
                                               role=Role(role=role),
                                               run_env=run_env)
        defaults = self._setup.configure_figgy_defaults(defaults)
        self._setup.save_defaults(defaults)

        self._out.success(
            f"\nLogin successful. Your sandbox session will last for [[1 hour]]."
        )

        self._out.print(
            f"\nIf your session expires, you may rerun `{CLI_NAME} login sandbox` to get another sandbox session. "
            f"\nAll previous figgy sessions have been disabled, you'll need to run {CLI_NAME} "
            f"--configure to leave the sandbox.")