mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-01-25 02:08:11 +00:00
Resolved some conflicts
This commit is contained in:
@@ -23,16 +23,18 @@ from rich.text import Text
|
|||||||
from rich.theme import Theme
|
from rich.theme import Theme
|
||||||
|
|
||||||
from prowler.config.config import prowler_version, timestamp
|
from prowler.config.config import prowler_version, timestamp
|
||||||
from prowler.providers.aws.lib.audit_info.models import AWS_Audit_Info
|
from prowler.providers.aws.models import AWSIdentityInfo, AWSAssumeRole
|
||||||
|
|
||||||
|
|
||||||
|
# Defines a subclass of Live for creating and managing the live display in the CLI
|
||||||
class LiveDisplay(Live):
|
class LiveDisplay(Live):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
# Load a theme for the console display from a file
|
||||||
theme = self.load_theme_from_file()
|
theme = self.load_theme_from_file()
|
||||||
super().__init__(renderable=None, console=Console(theme=theme), *args, **kwargs)
|
super().__init__(renderable=None, console=Console(theme=theme), *args, **kwargs)
|
||||||
self.sections = {}
|
self.sections = {} # Stores different sections of the layout
|
||||||
self.enabled = False
|
self.enabled = False # Flag to enable or disable the live display
|
||||||
|
|
||||||
|
# Sets up the layout of the live display
|
||||||
def make_layout(self):
|
def make_layout(self):
|
||||||
"""
|
"""
|
||||||
Defines the layout.
|
Defines the layout.
|
||||||
@@ -41,15 +43,18 @@ class LiveDisplay(Live):
|
|||||||
client_and_service handles client init (when importing clients) and service check execution
|
client_and_service handles client init (when importing clients) and service check execution
|
||||||
"""
|
"""
|
||||||
self.layout = Layout(name="root")
|
self.layout = Layout(name="root")
|
||||||
|
# Split layout into intro, overall progress, and main sections
|
||||||
self.layout.split(
|
self.layout.split(
|
||||||
Layout(name="intro", ratio=3, minimum_size=9),
|
Layout(name="intro", ratio=3, minimum_size=9),
|
||||||
Layout(Text(" "), name="overall_progress", minimum_size=5),
|
Layout(Text(" "), name="overall_progress", minimum_size=5),
|
||||||
Layout(name="main", ratio=10),
|
Layout(name="main", ratio=10),
|
||||||
)
|
)
|
||||||
|
# Further split intro layout into body and creds sections
|
||||||
self.layout["intro"].split_row(
|
self.layout["intro"].split_row(
|
||||||
Layout(name="body", ratio=3),
|
Layout(name="body", ratio=3),
|
||||||
Layout(name="creds", ratio=2, visible=False),
|
Layout(name="creds", ratio=2, visible=False),
|
||||||
)
|
)
|
||||||
|
# Split main layout into client_and_service and results sections
|
||||||
self.layout["main"].split_row(
|
self.layout["main"].split_row(
|
||||||
Layout(
|
Layout(
|
||||||
Text(" "), name="client_and_service", ratio=3
|
Text(" "), name="client_and_service", ratio=3
|
||||||
@@ -57,6 +62,7 @@ class LiveDisplay(Live):
|
|||||||
Layout(name="results", ratio=2, visible=False),
|
Layout(name="results", ratio=2, visible=False),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Loads a theme from a YAML file located in the same directory as this file
|
||||||
def load_theme_from_file(self):
|
def load_theme_from_file(self):
|
||||||
# Loads theme.yaml from the same folder as this file
|
# Loads theme.yaml from the same folder as this file
|
||||||
actual_directory = pathlib.Path(os.path.dirname(os.path.realpath(__file__)))
|
actual_directory = pathlib.Path(os.path.dirname(os.path.realpath(__file__)))
|
||||||
@@ -64,7 +70,7 @@ class LiveDisplay(Live):
|
|||||||
theme = Theme.from_file(f)
|
theme = Theme.from_file(f)
|
||||||
return theme
|
return theme
|
||||||
|
|
||||||
# Intro Section Methods
|
# Initializes the layout and sections based on CLI arguments
|
||||||
def initialize(self, args):
|
def initialize(self, args):
|
||||||
# A way to get around parsing args to LiveDisplay when it is intialized
|
# A way to get around parsing args to LiveDisplay when it is intialized
|
||||||
# This is so that the live_display object can be intialized in this file, and imported to other parts of prowler
|
# This is so that the live_display object can be intialized in this file, and imported to other parts of prowler
|
||||||
@@ -84,12 +90,14 @@ class LiveDisplay(Live):
|
|||||||
# Start live display
|
# Start live display
|
||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
def print_aws_credentials(self, audit_info):
|
# Adds AWS credentials to the display
|
||||||
|
def print_aws_credentials(self, aws_identity_info: AWSIdentityInfo, assumed_role_info: AWSAssumeRole):
|
||||||
# Adds the AWS credentials to the display - will need to extend to gcp and azure
|
# Adds the AWS credentials to the display - will need to extend to gcp and azure
|
||||||
|
# Create a new function for gcp and azure in this class, that will call a function in the intro_section class
|
||||||
intro_section = self.sections["intro"]
|
intro_section = self.sections["intro"]
|
||||||
intro_section.add_aws_credentials(audit_info)
|
intro_section.add_aws_credentials(aws_identity_info, assumed_role_info)
|
||||||
|
|
||||||
# Overall Progress Methods
|
# Adds and manages the overall progress section
|
||||||
def add_overall_progress_section(self, total_checks_dict):
|
def add_overall_progress_section(self, total_checks_dict):
|
||||||
overall_progress_section = OverallProgressSection(total_checks_dict)
|
overall_progress_section = OverallProgressSection(total_checks_dict)
|
||||||
overall_progress_layout = self.layout["overall_progress"]
|
overall_progress_layout = self.layout["overall_progress"]
|
||||||
@@ -100,19 +108,21 @@ class LiveDisplay(Live):
|
|||||||
# Add results section
|
# Add results section
|
||||||
self.add_results_section()
|
self.add_results_section()
|
||||||
|
|
||||||
|
# Wrapper function to increment the overall progress
|
||||||
def increment_overall_check_progress(self):
|
def increment_overall_check_progress(self):
|
||||||
# Called by ExecutionManager
|
# Called by ExecutionManager
|
||||||
if self.enabled:
|
if self.enabled:
|
||||||
section = self.sections["overall_progress"]
|
section = self.sections["overall_progress"]
|
||||||
section.increment_check_progress()
|
section.increment_check_progress()
|
||||||
|
|
||||||
|
# Wrapper function to increment the progress for the current service
|
||||||
def increment_overall_service_progress(self):
|
def increment_overall_service_progress(self):
|
||||||
# Called by ExecutionManager
|
# Called by ExecutionManager
|
||||||
if self.enabled:
|
if self.enabled:
|
||||||
section = self.sections["overall_progress"]
|
section = self.sections["overall_progress"]
|
||||||
section.increment_service_progress()
|
section.increment_service_progress()
|
||||||
|
|
||||||
# Results Section Methods
|
# Adds and manages the results section
|
||||||
def add_results_section(self):
|
def add_results_section(self):
|
||||||
# Intializes the results section
|
# Intializes the results section
|
||||||
results_layout = self.layout["results"]
|
results_layout = self.layout["results"]
|
||||||
@@ -168,6 +178,8 @@ class LiveDisplay(Live):
|
|||||||
# No use yet
|
# No use yet
|
||||||
self.console.print(message)
|
self.console.print(message)
|
||||||
|
|
||||||
|
# The following classes (ServiceSection, ClientInitSection, IntroSection, OverallProgressSection, ResultsSection)
|
||||||
|
# are used to define different sections of the live display, each with its own layout, progress bars,
|
||||||
|
|
||||||
class ServiceSection:
|
class ServiceSection:
|
||||||
def __init__(self, service_name, total_checks) -> None:
|
def __init__(self, service_name, total_checks) -> None:
|
||||||
@@ -288,15 +300,15 @@ Color code for results:
|
|||||||
self.body_layout.update(Group(*self.renderables))
|
self.body_layout.update(Group(*self.renderables))
|
||||||
self.body_layout.visible = True
|
self.body_layout.visible = True
|
||||||
|
|
||||||
def add_aws_credentials(self, audit_info: AWS_Audit_Info):
|
def add_aws_credentials(self, aws_identity_info: AWSIdentityInfo, assumed_role_info: AWSAssumeRole):
|
||||||
# Beautify audited regions, set "all" if there is no filter region
|
# Beautify audited regions, and set to "all" if there is no filter region
|
||||||
regions = (
|
regions = (
|
||||||
", ".join(audit_info.audited_regions)
|
", ".join(aws_identity_info.audited_regions)
|
||||||
if audit_info.audited_regions is not None
|
if aws_identity_info.audited_regions is not None
|
||||||
else "all"
|
else "all"
|
||||||
)
|
)
|
||||||
# Beautify audited profile, set "default" if there is no profile set
|
# Beautify audited profile, set and to "default" if there is no profile set
|
||||||
profile = audit_info.profile if audit_info.profile is not None else "default"
|
profile = aws_identity_info.profile if aws_identity_info.profile is not None else "default"
|
||||||
|
|
||||||
content = Text()
|
content = Text()
|
||||||
content.append(
|
content.append(
|
||||||
@@ -310,17 +322,17 @@ Color code for results:
|
|||||||
content.append(f"[{regions}]\n", style="info")
|
content.append(f"[{regions}]\n", style="info")
|
||||||
|
|
||||||
content.append("AWS Account: ", style="bold")
|
content.append("AWS Account: ", style="bold")
|
||||||
content.append(f"[{audit_info.audited_account}]\n", style="info")
|
content.append(f"[{aws_identity_info.account}]\n", style="info")
|
||||||
|
|
||||||
content.append("UserId: ", style="bold")
|
content.append("UserId: ", style="bold")
|
||||||
content.append(f"[{audit_info.audited_user_id}]\n", style="info")
|
content.append(f"[{aws_identity_info.user_id}]\n", style="info")
|
||||||
|
|
||||||
content.append("Caller Identity ARN: ", style="bold")
|
content.append("Caller Identity ARN: ", style="bold")
|
||||||
content.append(f"[{audit_info.audited_identity_arn}]\n", style="info")
|
content.append(f"[{aws_identity_info.identity_arn}]\n", style="info")
|
||||||
# If -A is set, print Assumed Role ARN
|
# If a role has been assumed, print the Assumed Role ARN
|
||||||
if audit_info.assumed_role_info.role_arn is not None:
|
if assumed_role_info.role_arn is not None:
|
||||||
content.append("Assumed Role ARN: ", style="bold")
|
content.append("Assumed Role ARN: ", style="bold")
|
||||||
content.append(f"[{audit_info.assumed_role_info.role_arn}]\n", style="info")
|
content.append(f"[{assumed_role_info.role_arn}]\n", style="info")
|
||||||
|
|
||||||
self.creds_layout.update(content)
|
self.creds_layout.update(content)
|
||||||
self.creds_layout.visible = True
|
self.creds_layout.visible = True
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import os
|
|||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
from dataclasses import dataclass
|
|
||||||
from datetime import datetime
|
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from boto3 import client, session
|
from boto3 import client, session
|
||||||
@@ -14,12 +12,21 @@ from colorama import Fore, Style
|
|||||||
|
|
||||||
from prowler.config.config import aws_services_json_file
|
from prowler.config.config import aws_services_json_file
|
||||||
from prowler.lib.check.check import list_modules, recover_checks_from_service
|
from prowler.lib.check.check import list_modules, recover_checks_from_service
|
||||||
|
from prowler.lib.ui.live_display import live_display
|
||||||
from prowler.lib.logger import logger
|
from prowler.lib.logger import logger
|
||||||
from prowler.lib.utils.utils import open_file, parse_json_file
|
from prowler.lib.utils.utils import open_file, parse_json_file
|
||||||
from prowler.providers.aws.config import (
|
from prowler.providers.aws.config import (
|
||||||
AWS_STS_GLOBAL_ENDPOINT_REGION,
|
AWS_STS_GLOBAL_ENDPOINT_REGION,
|
||||||
BOTO3_USER_AGENT_EXTRA,
|
BOTO3_USER_AGENT_EXTRA,
|
||||||
)
|
)
|
||||||
|
from prowler.providers.aws.models import (
|
||||||
|
AWSOrganizationsInfo,
|
||||||
|
AWSCredentials,
|
||||||
|
AWSAssumeRole,
|
||||||
|
AWSAssumeRoleConfiguration,
|
||||||
|
AWSIdentityInfo,
|
||||||
|
AWSSession,
|
||||||
|
)
|
||||||
from prowler.providers.aws.lib.arn.arn import parse_iam_credentials_arn
|
from prowler.providers.aws.lib.arn.arn import parse_iam_credentials_arn
|
||||||
from prowler.providers.aws.lib.credentials.credentials import (
|
from prowler.providers.aws.lib.credentials.credentials import (
|
||||||
create_sts_session,
|
create_sts_session,
|
||||||
@@ -30,57 +37,6 @@ from prowler.providers.aws.lib.organizations.organizations import (
|
|||||||
)
|
)
|
||||||
from prowler.providers.common.provider import Provider
|
from prowler.providers.common.provider import Provider
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class AWSOrganizationsInfo:
|
|
||||||
account_details_email: str
|
|
||||||
account_details_name: str
|
|
||||||
account_details_arn: str
|
|
||||||
account_details_org: str
|
|
||||||
account_details_tags: str
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class AWSCredentials:
|
|
||||||
aws_access_key_id: str
|
|
||||||
aws_session_token: str
|
|
||||||
aws_secret_access_key: str
|
|
||||||
expiration: datetime
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class AWSAssumeRole:
|
|
||||||
role_arn: str
|
|
||||||
session_duration: int
|
|
||||||
external_id: str
|
|
||||||
mfa_enabled: bool
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class AWSAssumeRoleConfiguration:
|
|
||||||
assumed_role_info: AWSAssumeRole
|
|
||||||
assumed_role_credentials: AWSCredentials
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class AWSIdentityInfo:
|
|
||||||
account: str
|
|
||||||
account_arn: str
|
|
||||||
user_id: str
|
|
||||||
partition: str
|
|
||||||
identity_arn: str
|
|
||||||
profile: str
|
|
||||||
profile_region: str
|
|
||||||
audited_regions: list
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class AWSSession:
|
|
||||||
session: session.Session
|
|
||||||
session_config: Config
|
|
||||||
original_session: None
|
|
||||||
|
|
||||||
|
|
||||||
class AwsProvider(Provider):
|
class AwsProvider(Provider):
|
||||||
session: AWSSession = AWSSession(
|
session: AWSSession = AWSSession(
|
||||||
session=None, session_config=None, original_session=None
|
session=None, session_config=None, original_session=None
|
||||||
@@ -328,45 +284,7 @@ class AwsProvider(Provider):
|
|||||||
# This method is called "adding ()" to the name, so it cannot accept arguments
|
# This method is called "adding ()" to the name, so it cannot accept arguments
|
||||||
# https://github.com/boto/botocore/blob/098cc255f81a25b852e1ecdeb7adebd94c7b1b73/botocore/credentials.py#L570
|
# https://github.com/boto/botocore/blob/098cc255f81a25b852e1ecdeb7adebd94c7b1b73/botocore/credentials.py#L570
|
||||||
def refresh_credentials(self):
|
def refresh_credentials(self):
|
||||||
logger.info("Refreshing assumed credentials...")
|
live_display.print_aws_credentials(self.identity, self.assumed_role.assumed_role_info)
|
||||||
|
|
||||||
response = self.__assume_role__(self.aws_session, self.role_info)
|
|
||||||
refreshed_credentials = dict(
|
|
||||||
# Keys of the dict has to be the same as those that are being searched in the parent class
|
|
||||||
# https://github.com/boto/botocore/blob/098cc255f81a25b852e1ecdeb7adebd94c7b1b73/botocore/credentials.py#L609
|
|
||||||
access_key=response["Credentials"]["AccessKeyId"],
|
|
||||||
secret_key=response["Credentials"]["SecretAccessKey"],
|
|
||||||
token=response["Credentials"]["SessionToken"],
|
|
||||||
expiry_time=response["Credentials"]["Expiration"].isoformat(),
|
|
||||||
)
|
|
||||||
logger.info("Refreshed Credentials:")
|
|
||||||
logger.info(refreshed_credentials)
|
|
||||||
return refreshed_credentials
|
|
||||||
|
|
||||||
def print_credentials(self):
|
|
||||||
# Beautify audited regions, set "all" if there is no filter region
|
|
||||||
regions = (
|
|
||||||
", ".join(self.identity.audited_regions)
|
|
||||||
if self.identity.audited_regions is not None
|
|
||||||
else "all"
|
|
||||||
)
|
|
||||||
# Beautify audited profile, set "default" if there is no profile set
|
|
||||||
profile = (
|
|
||||||
self.identity.profile if self.identity.profile is not None else "default"
|
|
||||||
)
|
|
||||||
|
|
||||||
report = f"""
|
|
||||||
This report is being generated using credentials below:
|
|
||||||
|
|
||||||
AWS-CLI Profile: {Fore.YELLOW}[{profile}]{Style.RESET_ALL} AWS Filter Region: {Fore.YELLOW}[{regions}]{Style.RESET_ALL}
|
|
||||||
AWS Account: {Fore.YELLOW}[{self.identity.account}]{Style.RESET_ALL} UserId: {Fore.YELLOW}[{self.identity.user_id}]{Style.RESET_ALL}
|
|
||||||
Caller Identity ARN: {Fore.YELLOW}[{ self.identity.identity_arn}]{Style.RESET_ALL}
|
|
||||||
"""
|
|
||||||
# If -A is set, print Assumed Role ARN
|
|
||||||
if self.assumed_role.assumed_role_info.role_arn is not None:
|
|
||||||
report += f"""Assumed Role ARN: {Fore.YELLOW}[{self.assumed_role.assumed_role_info.role_arn}]{Style.RESET_ALL}
|
|
||||||
"""
|
|
||||||
print(report)
|
|
||||||
|
|
||||||
def generate_regional_clients(
|
def generate_regional_clients(
|
||||||
self, service: str, global_service: bool = False
|
self, service: str, global_service: bool = False
|
||||||
|
|||||||
54
prowler/providers/aws/models.py
Normal file
54
prowler/providers/aws/models.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from boto3 import session
|
||||||
|
from botocore.config import Config
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AWSOrganizationsInfo:
|
||||||
|
account_details_email: str
|
||||||
|
account_details_name: str
|
||||||
|
account_details_arn: str
|
||||||
|
account_details_org: str
|
||||||
|
account_details_tags: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AWSCredentials:
|
||||||
|
aws_access_key_id: str
|
||||||
|
aws_session_token: str
|
||||||
|
aws_secret_access_key: str
|
||||||
|
expiration: datetime
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AWSAssumeRole:
|
||||||
|
role_arn: str
|
||||||
|
session_duration: int
|
||||||
|
external_id: str
|
||||||
|
mfa_enabled: bool
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AWSAssumeRoleConfiguration:
|
||||||
|
assumed_role_info: AWSAssumeRole
|
||||||
|
assumed_role_credentials: AWSCredentials
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AWSIdentityInfo:
|
||||||
|
account: str
|
||||||
|
account_arn: str
|
||||||
|
user_id: str
|
||||||
|
partition: str
|
||||||
|
identity_arn: str
|
||||||
|
profile: str
|
||||||
|
profile_region: str
|
||||||
|
audited_regions: list
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AWSSession:
|
||||||
|
session: session.Session
|
||||||
|
session_config: Config
|
||||||
|
original_session: None
|
||||||
@@ -5,7 +5,6 @@ from colorama import Fore, Style
|
|||||||
|
|
||||||
from prowler.config.config import load_and_validate_config_file
|
from prowler.config.config import load_and_validate_config_file
|
||||||
from prowler.lib.logger import logger
|
from prowler.lib.logger import logger
|
||||||
from prowler.lib.ui.live_display import live_display
|
|
||||||
from prowler.providers.aws.aws_provider import (
|
from prowler.providers.aws.aws_provider import (
|
||||||
AWS_Provider,
|
AWS_Provider,
|
||||||
assume_role,
|
assume_role,
|
||||||
|
|||||||
@@ -69,8 +69,11 @@ class Provider_Output_Options:
|
|||||||
if arguments.output_directory:
|
if arguments.output_directory:
|
||||||
if not isdir(arguments.output_directory):
|
if not isdir(arguments.output_directory):
|
||||||
if arguments.output_modes:
|
if arguments.output_modes:
|
||||||
# exist_ok is set to True not to raise FileExistsError
|
makedirs(arguments.output_directory)
|
||||||
makedirs(arguments.output_directory, exist_ok=True)
|
if not isdir(arguments.output_directory + "/compliance"):
|
||||||
|
if arguments.output_modes:
|
||||||
|
makedirs(arguments.output_directory + "/compliance")
|
||||||
|
|
||||||
|
|
||||||
class Azure_Output_Options(Provider_Output_Options):
|
class Azure_Output_Options(Provider_Output_Options):
|
||||||
def __init__(self, arguments, audit_info, mutelist_file, bulk_checks_metadata):
|
def __init__(self, arguments, audit_info, mutelist_file, bulk_checks_metadata):
|
||||||
|
|||||||
Reference in New Issue
Block a user