chore(aws): Simplify provider (#3481)

Co-authored-by: Sergio Garcia <sergargar1@gmail.com>
This commit is contained in:
Pepe Fagoaga
2024-03-04 13:50:54 +01:00
committed by GitHub
parent bd17d36e7f
commit 5df9fd881c
15 changed files with 661 additions and 862 deletions
+5 -10
View File
@@ -44,11 +44,6 @@ from prowler.providers.aws.lib.security_hub.security_hub import (
resolve_security_hub_previous_findings,
verify_security_hub_integration_enabled_per_region,
)
# from prowler.providers.common.audit_info import (
# set_provider_audit_info,
# set_provider_execution_parameters,
# )
from prowler.providers.common.clean import clean_provider_local_output_directories
from prowler.providers.common.common import (
get_global_provider,
@@ -151,14 +146,14 @@ def prowler():
print_checks(provider, sorted(checks_to_execute), bulk_checks_metadata)
sys.exit()
# Set the audit info based on the selected provider
# TODO: remove the following line with the audit_info
# audit_info = set_provider_audit_info(provider, args.__dict__)
# Provider to scan
set_global_provider_object(args)
# TODO: rename global_provider to provider
global_provider = get_global_provider()
# Print Provider Credentials
if not args.only_logs:
global_provider.print_credentials()
# Import custom checks from folder
if checks_folder:
parse_checks_from_folder(global_provider, checks_folder)
+8 -6
View File
@@ -190,7 +190,7 @@ def remove_custom_checks_module(input_folder: str, provider: str):
shutil.rmtree(input_folder)
def list_services(provider: str) -> set():
def list_services(provider: str) -> set:
available_services = set()
checks_tuple = recover_checks_from_provider(provider)
for _, check_path in checks_tuple:
@@ -203,7 +203,7 @@ def list_services(provider: str) -> set():
return sorted(available_services)
def list_categories(bulk_checks_metadata: dict) -> set():
def list_categories(bulk_checks_metadata: dict) -> set:
available_categories = set()
for check in bulk_checks_metadata.values():
for cat in check.Categories:
@@ -431,9 +431,11 @@ def execute_checks(
services_executed = set()
checks_executed = set()
# TODO: why is this here?
global_provider = get_global_provider()
# Initialize the Audit Metadata
# TODO: this should be done in the provider class
global_provider.audit_metadata = Audit_Metadata(
services_scanned=0,
expected_checks=checks_to_execute,
@@ -466,7 +468,7 @@ def execute_checks(
check_findings = execute(
service,
check_name,
global_provider.provider,
global_provider.type,
audit_output_options,
global_provider.identity,
services_executed,
@@ -478,7 +480,7 @@ def execute_checks(
# If check does not exists in the provider or is from another provider
except ModuleNotFoundError:
logger.error(
f"Check '{check_name}' was not found for the {global_provider.provider.upper()} provider"
f"Check '{check_name}' was not found for the {global_provider.type.upper()} provider"
)
except Exception as error:
logger.error(
@@ -524,7 +526,7 @@ def execute_checks(
except ModuleNotFoundError:
# TODO: add more loggin here, we need the original exception -- traceback.print_last()
logger.error(
f"Check '{check_name}' was not found for the {global_provider.provider.upper()} provider"
f"Check '{check_name}' was not found for the {global_provider.type.upper()} provider"
)
except Exception as error:
# TODO: add more loggin here, we need the original exception -- traceback.print_last()
@@ -546,7 +548,7 @@ def execute(
custom_checks_metadata: Any,
):
# Import check module
check_module_path = f"prowler.providers.{global_provider.provider}.services.{service}.{check_name}.{check_name}"
check_module_path = f"prowler.providers.{global_provider.type}.services.{service}.{check_name}.{check_name}"
lib = import_check(check_module_path)
# Recover functions from check
check_to_execute = getattr(lib, check_name)
File diff suppressed because it is too large Load Diff
+2
View File
@@ -6,6 +6,7 @@ from prowler.providers.aws.lib.arn.error import RoleArnParsingFailedMissingField
class ARN(BaseModel):
arn: str
partition: str
service: str
region: Optional[str] # In IAM ARN's do not have region
@@ -21,6 +22,7 @@ class ARN(BaseModel):
## Retrieve fields
arn_elements = arn.split(":", 5)
data = {
"arn": arn,
"partition": arn_elements[1],
"service": arn_elements[2],
"region": arn_elements[3] if arn_elements[3] != "" else None,
@@ -1,70 +0,0 @@
import sys
from boto3 import session
from colorama import Fore, Style
from prowler.lib.logger import logger
from prowler.providers.aws.config import AWS_STS_GLOBAL_ENDPOINT_REGION
from prowler.providers.aws.lib.audit_info.models import AWS_Audit_Info
def validate_AWSCredentials(
session: session, input_regions: list, sts_endpoint_region: str = None
) -> dict:
try:
# If there is no region passed with -f/--region/--filter-region
if input_regions is None or len(input_regions) == 0:
# If you have a region configured in your AWS config or credentials file
if session.region_name is not None:
aws_region = session.region_name
else:
# If there is no region set passed with -f/--region
# we use the Global STS Endpoint Region, us-east-1
aws_region = AWS_STS_GLOBAL_ENDPOINT_REGION
else:
# Get the first region passed to the -f/--region
aws_region = input_regions[0]
validate_credentials_client = create_sts_session(session, aws_region)
caller_identity = validate_credentials_client.get_caller_identity()
# Include the region where the caller_identity has validated the credentials
caller_identity["region"] = aws_region
except Exception as error:
logger.critical(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
sys.exit(1)
else:
return caller_identity
def print_AWSCredentials(audit_info: AWS_Audit_Info):
# Beautify audited regions, set "all" if there is no filter region
regions = (
", ".join(audit_info.audited_regions)
if audit_info.audited_regions is not None
else "all"
)
# Beautify audited profile, set "default" if there is no profile set
profile = audit_info.profile if audit_info.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}[{audit_info.audited_account}]{Style.RESET_ALL} UserId: {Fore.YELLOW}[{audit_info.audited_user_id}]{Style.RESET_ALL}
Caller Identity ARN: {Fore.YELLOW}[{audit_info.audited_identity_arn}]{Style.RESET_ALL}
"""
# If -A is set, print Assumed Role ARN
if audit_info.assumed_role_info.role_arn is not None:
report += f"""Assumed Role ARN: {Fore.YELLOW}[{audit_info.assumed_role_info.role_arn}]{Style.RESET_ALL}
"""
print(report)
def create_sts_session(
session: session.Session, aws_region: str
) -> session.Session.client:
return session.client(
"sts", aws_region, endpoint_url=f"https://sts.{aws_region}.amazonaws.com"
)
@@ -1,28 +1,16 @@
from boto3 import client, session
from boto3 import session
# from boto3 import client
from prowler.lib.logger import logger
from prowler.providers.aws.lib.audit_info.models import AWSOrganizationsInfo
def get_organizations_metadata(
aws_account_id: str,
assumed_credentials: dict = None,
session: session = None,
session: session.Session,
) -> tuple[dict, dict]:
try:
if assumed_credentials:
organizations_client = client(
"organizations",
aws_access_key_id=assumed_credentials["Credentials"]["AccessKeyId"],
aws_secret_access_key=assumed_credentials["Credentials"][
"SecretAccessKey"
],
aws_session_token=assumed_credentials["Credentials"]["SessionToken"],
)
if session:
organizations_client = session.client("organizations")
else:
organizations_client = client("organizations")
organizations_client = session.client("organizations")
organizations_metadata = organizations_client.describe_account(
AccountId=aws_account_id
+3 -4
View File
@@ -23,6 +23,7 @@ class AWSService:
def __init__(self, service: str, provider: AwsProvider, global_service=False):
# Audit Information
# Do we need to store the whole provider?
self.provider = provider
self.audited_account = provider.identity.account
self.audited_account_arn = provider.identity.account_arn
@@ -32,7 +33,7 @@ class AWSService:
self.audit_config = provider.audit_config
# AWS Session
self.session = provider.session.session
self.session = provider.session.current_session
# We receive the service using __class__.__name__ or the service name in lowercase
# e.g.: AccessAnalyzer --> we need a lowercase string, so service.lower()
@@ -40,9 +41,7 @@ class AWSService:
# Generate Regional Clients
if not global_service:
self.regional_clients = provider.generate_regional_clients(
self.service, global_service
)
self.regional_clients = provider.generate_regional_clients(self.service)
# TODO: review the following code
# self.regional_clients = generate_regional_clients(self.service, audit_info)
+29 -7
View File
@@ -4,6 +4,8 @@ from datetime import datetime
from boto3.session import Session
from botocore.config import Config
from prowler.providers.aws.lib.arn.models import ARN
@dataclass
class AWSOrganizationsInfo:
@@ -23,17 +25,18 @@ class AWSCredentials:
@dataclass
class AWSAssumeRole:
role_arn: str
class AWSAssumeRoleInfo:
role_arn: ARN
session_duration: int
external_id: str
mfa_enabled: bool
role_session_name: str
@dataclass
class AWSAssumeRoleConfiguration:
assumed_role_info: AWSAssumeRole
assumed_role_credentials: AWSCredentials
info: AWSAssumeRoleInfo
credentials: AWSCredentials
@dataclass
@@ -45,11 +48,30 @@ class AWSIdentityInfo:
identity_arn: str
profile: str
profile_region: str
audited_regions: list
audited_regions: set
@dataclass
class AWSSession:
session: Session
"""
AWSSession stores the AWS session's configuration. We store the original_session in the case we need to setup a new one with different credentials and the restore to the original one.
"""
current_session: Session
original_session: Session
session_config: Config
original_session: None
@dataclass
class AWSCallerIdentity:
user_id: str
account: str
arn: str
region: str
@dataclass
class AWSMFAInfo:
arn: str
totp: str
+1 -2
View File
@@ -52,14 +52,13 @@ class AzureProvider(Provider):
managed_entity_auth,
subscription_ids,
)
if not arguments.only_logs:
self.print_credentials()
# TODO: should we keep this here or within the identity?
self.locations = self.get_locations(self.session, self.region_config)
# TODO: move this to the providers, pending for AWS, GCP, AZURE and K8s
self.audit_config = {}
# TODO: this should be moved to the argparse, if not we need to enforce it from the Provider
def validate_arguments(
self, az_cli_auth, sp_env_auth, browser_auth, managed_entity_auth, tenant_id
):
-423
View File
@@ -1,423 +0,0 @@
import sys
from colorama import Fore, Style
from prowler.config.config import load_and_validate_config_file
from prowler.lib.logger import logger
from prowler.providers.gcp.gcp_provider import GCP_Provider
from prowler.providers.gcp.lib.audit_info.audit_info import gcp_audit_info
from prowler.providers.gcp.lib.audit_info.models import GCP_Audit_Info
from prowler.providers.kubernetes.kubernetes_provider import Kubernetes_Provider
from prowler.providers.kubernetes.lib.audit_info.audit_info import kubernetes_audit_info
from prowler.providers.kubernetes.lib.audit_info.models import Kubernetes_Audit_Info
class Audit_Info:
def __init__(self):
logger.info("Setting Audit Info ...")
def print_gcp_credentials(self, audit_info: GCP_Audit_Info):
# Beautify audited profile, set "default" if there is no profile set
profile = getattr(audit_info.credentials, "_service_account_email", "default")
report = f"""
This report is being generated using credentials below:
GCP Account: {Fore.YELLOW}[{profile}]{Style.RESET_ALL} GCP Project IDs: {Fore.YELLOW}[{", ".join(audit_info.project_ids)}]{Style.RESET_ALL}
"""
print(report)
def print_kubernetes_credentials(self, audit_info: Kubernetes_Audit_Info):
# Get the current context
cluster_name = self.context.get("context").get("cluster")
user_name = self.context.get("context").get("user")
roles = self.get_context_user_roles()
roles_str = ", ".join(roles) if roles else "No associated Roles"
report = f"""
This report is being generated using the Kubernetes configuration below:
Kubernetes Cluster: {Fore.YELLOW}[{cluster_name}]{Style.RESET_ALL} User: {Fore.YELLOW}[{user_name}]{Style.RESET_ALL} Namespaces: {Fore.YELLOW}[{', '.join(self.namespaces)}]{Style.RESET_ALL} Roles: {Fore.YELLOW}[{roles_str}]{Style.RESET_ALL}
"""
print(report)
# def print_azure_credentials(self, audit_info: Azure_Audit_Info):
# printed_subscriptions = []
# for key, value in audit_info.identity.subscriptions.items():
# intermediate = f"{key} : {value}"
# printed_subscriptions.append(intermediate)
# report = f"""
# This report is being generated using the identity below:
# Azure Tenant IDs: {Fore.YELLOW}[{" ".join(audit_info.identity.tenant_ids)}]{Style.RESET_ALL} Azure Tenant Domain: {Fore.YELLOW}[{audit_info.identity.domain}]{Style.RESET_ALL} Azure Region: {Fore.YELLOW}[{audit_info.azure_region_config.name}]{Style.RESET_ALL}
# Azure Subscriptions: {Fore.YELLOW}{printed_subscriptions}{Style.RESET_ALL}
# Azure Identity Type: {Fore.YELLOW}[{audit_info.identity.identity_type}]{Style.RESET_ALL} Azure Identity ID: {Fore.YELLOW}[{audit_info.identity.identity_id}]{Style.RESET_ALL}
# """
# print(report)
# TODO: remove if not needed, but not now :)
# def set_aws_audit_info(self, arguments) -> AWS_Audit_Info:
# """
# set_aws_audit_info returns the AWS_Audit_Info
# """
# logger.info("Setting AWS session ...")
# # Assume Role Options
# input_role = arguments.get("role")
# current_audit_info.assumed_role_info.role_arn = input_role
# input_session_duration = arguments.get("session_duration")
# input_external_id = arguments.get("external_id")
# input_role_session_name = arguments.get("role_session_name")
# # STS Endpoint Region
# sts_endpoint_region = arguments.get("sts_endpoint_region")
# # MFA Configuration (false by default)
# input_mfa = arguments.get("mfa")
# current_audit_info.mfa_enabled = input_mfa
# input_profile = arguments.get("profile")
# input_regions = arguments.get("region")
# organizations_role_arn = arguments.get("organizations_role")
# # Assumed AWS session
# assumed_session = None
# # Set the maximum retries for the standard retrier config
# aws_retries_max_attempts = arguments.get("aws_retries_max_attempts")
# if aws_retries_max_attempts:
# # Create the new config
# config = Config(
# retries={
# "max_attempts": aws_retries_max_attempts,
# "mode": "standard",
# },
# )
# # Merge the new configuration
# new_boto3_config = current_audit_info.session_config.merge(config)
# current_audit_info.session_config = new_boto3_config
# # Set ignore unused services argument
# current_audit_info.ignore_unused_services = arguments.get(
# "ignore_unused_services"
# )
# # Setting session
# current_audit_info.profile = input_profile
# current_audit_info.audited_regions = input_regions
# logger.info("Generating original session ...")
# # Create an global original session using only profile/basic credentials info
# aws_provider = AWS_Provider(current_audit_info)
# current_audit_info.original_session = aws_provider.aws_session
# logger.info("Validating credentials ...")
# # Verificate if we have valid credentials
# caller_identity = validate_AWSCredentials(
# current_audit_info.original_session, input_regions, sts_endpoint_region
# )
# logger.info("Credentials validated")
# logger.info(f"Original caller identity UserId: {caller_identity['UserId']}")
# logger.info(f"Original caller identity ARN: {caller_identity['Arn']}")
# current_audit_info.audited_account = caller_identity["Account"]
# current_audit_info.audited_identity_arn = caller_identity["Arn"]
# current_audit_info.audited_user_id = caller_identity["UserId"]
# current_audit_info.audited_partition = parse_iam_credentials_arn(
# caller_identity["Arn"]
# ).partition
# current_audit_info.audited_account_arn = f"arn:{current_audit_info.audited_partition}:iam::{current_audit_info.audited_account}:root"
# logger.info("Checking if role assumption is needed ...")
# if input_role:
# current_audit_info.assumed_role_info.role_arn = input_role
# current_audit_info.assumed_role_info.session_duration = (
# input_session_duration
# )
# current_audit_info.assumed_role_info.external_id = input_external_id
# current_audit_info.assumed_role_info.mfa_enabled = input_mfa
# current_audit_info.assumed_role_info.role_session_name = (
# input_role_session_name
# )
# # Check if role arn is valid
# try:
# # this returns the arn already parsed into a dict to be used when it is needed to access its fields
# role_arn_parsed = parse_iam_credentials_arn(
# current_audit_info.assumed_role_info.role_arn
# )
# except Exception as error:
# logger.critical(f"{error.__class__.__name__} -- {error}")
# sys.exit(1)
# else:
# logger.info(
# f"Assuming role {current_audit_info.assumed_role_info.role_arn}"
# )
# # Assume the role
# assumed_role_response = assume_role(
# aws_provider.aws_session,
# aws_provider.role_info,
# sts_endpoint_region,
# )
# logger.info("Role assumed")
# # Set the info needed to create a session with an assumed role
# current_audit_info.credentials = AWSCredentials(
# aws_access_key_id=assumed_role_response["Credentials"][
# "AccessKeyId"
# ],
# aws_session_token=assumed_role_response["Credentials"][
# "SessionToken"
# ],
# aws_secret_access_key=assumed_role_response["Credentials"][
# "SecretAccessKey"
# ],
# expiration=assumed_role_response["Credentials"]["Expiration"],
# )
# # new session is needed
# assumed_session = aws_provider.set_session(current_audit_info)
# if assumed_session:
# logger.info("Audit session is the new session created assuming role")
# current_audit_info.audit_session = assumed_session
# current_audit_info.audited_account = role_arn_parsed.account_id
# current_audit_info.audited_partition = role_arn_parsed.partition
# current_audit_info.audited_account_arn = f"arn:{current_audit_info.audited_partition}:iam::{current_audit_info.audited_account}:root"
# else:
# logger.info("Audit session is the original one")
# current_audit_info.audit_session = current_audit_info.original_session
# logger.info("Checking if organizations role assumption is needed ...")
# if organizations_role_arn:
# current_audit_info.assumed_role_info.role_arn = organizations_role_arn
# current_audit_info.assumed_role_info.session_duration = (
# input_session_duration
# )
# current_audit_info.assumed_role_info.external_id = input_external_id
# current_audit_info.assumed_role_info.mfa_enabled = input_mfa
# # Check if role arn is valid
# try:
# # this returns the arn already parsed into a dict to be used when it is needed to access its fields
# role_arn_parsed = parse_iam_credentials_arn(
# current_audit_info.assumed_role_info.role_arn
# )
# except Exception as error:
# logger.critical(f"{error.__class__.__name__} -- {error}")
# sys.exit(1)
# else:
# logger.info(
# f"Getting organizations metadata for account with IAM Role ARN {organizations_role_arn}"
# )
# assumed_credentials = assume_role(
# aws_provider.aws_session,
# aws_provider.role_info,
# sts_endpoint_region,
# )
# organizations_metadata, list_tags_for_resource = (
# get_organizations_metadata(
# current_audit_info.audited_account, assumed_credentials
# )
# )
# current_audit_info.organizations_metadata = (
# parse_organizations_metadata(
# organizations_metadata, list_tags_for_resource
# )
# )
# logger.info(
# f"Organizations metadata retrieved with IAM Role ARN {organizations_role_arn}"
# )
# else:
# try:
# logger.info(
# "Getting organizations metadata for account if it is a delegated administrator"
# )
# organizations_metadata, list_tags_for_resource = (
# get_organizations_metadata(
# aws_account_id=current_audit_info.audited_account,
# session=current_audit_info.audit_session,
# )
# )
# if organizations_metadata:
# current_audit_info.organizations_metadata = (
# parse_organizations_metadata(
# organizations_metadata, list_tags_for_resource
# )
# )
# logger.info(
# "Organizations metadata retrieved as a delegated administrator"
# )
# except Exception as error:
# # If the account is not a delegated administrator for AWS Organizations a credentials error will be thrown
# # Since it is a permission issue for an optional we'll raise a warning
# logger.warning(
# f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
# )
# # Setting default region of session
# if current_audit_info.audit_session.region_name:
# current_audit_info.profile_region = (
# current_audit_info.audit_session.region_name
# )
# else:
# current_audit_info.profile_region = "us-east-1"
# # Parse Scan Tags
# if arguments.get("resource_tags"):
# input_resource_tags = arguments.get("resource_tags")
# current_audit_info.audit_resources = get_tagged_resources(
# input_resource_tags, current_audit_info
# )
# # Parse Input Resource ARNs
# if arguments.get("resource_arn"):
# current_audit_info.audit_resources = arguments.get("resource_arn")
# # Get Enabled Regions
# current_audit_info.enabled_regions = get_aws_enabled_regions(current_audit_info)
# return current_audit_info
# TODO: remove if not needed, but not now :)
# def set_aws_execution_parameters(self, provider, audit_info) -> list[str]:
# # Once the audit_info is set and we have the eventual checks from arn, it is time to exclude the others
# try:
# if audit_info.audit_resources:
# audit_info.audited_regions = get_regions_from_audit_resources(
# audit_info.audit_resources
# )
# return get_checks_from_input_arn(audit_info.audit_resources, provider)
# except Exception as error:
# logger.critical(
# f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
# )
# sys.exit(1)
# TODO: remove if not needed, but not now :)
# def set_azure_audit_info(self, arguments) -> Azure_Audit_Info:
# """
# set_azure_audit_info returns the Azure_Audit_Info
# """
# logger.info("Setting Azure session ...")
# subscription_ids = arguments.get("subscription_ids")
# logger.info("Checking if any credentials mode is set ...")
# az_cli_auth = arguments.get("az_cli_auth")
# sp_env_auth = arguments.get("sp_env_auth")
# browser_auth = arguments.get("browser_auth")
# managed_entity_auth = arguments.get("managed_entity_auth")
# tenant_id = arguments.get("tenant_id")
# logger.info("Checking if region is different than default one")
# region = arguments.get("azure_region")
# if (
# not az_cli_auth
# and not sp_env_auth
# and not browser_auth
# and not managed_entity_auth
# ):
# raise AzureException(
# "Azure provider requires at least one authentication method set: [--az-cli-auth | --sp-env-auth | --browser-auth | --managed-identity-auth]"
# )
# if (not browser_auth and tenant_id) or (browser_auth and not tenant_id):
# raise AzureException(
# "Azure Tenant ID (--tenant-id) is required only for browser authentication mode"
# )
# azure_provider = Azure_Provider(
# az_cli_auth,
# sp_env_auth,
# browser_auth,
# managed_entity_auth,
# subscription_ids,
# tenant_id,
# region,
# )
# azure_audit_info.credentials = azure_provider.get_credentials()
# azure_audit_info.identity = azure_provider.get_identity()
# region_config = azure_provider.get_region_config()
# azure_audit_info.azure_region_config = AzureRegionConfig(
# name=region,
# authority=region_config["authority"],
# base_url=region_config["base_url"],
# credential_scopes=region_config["credential_scopes"],
# )
# azure_audit_info.locations = azure_provider.get_locations(
# azure_audit_info.credentials, region_config
# )
# if not arguments.get("only_logs"):
# self.print_azure_credentials(get_global_provider())
# return azure_audit_info
def set_gcp_audit_info(self, arguments) -> GCP_Audit_Info:
"""
set_gcp_audit_info returns the GCP_Audit_Info
"""
logger.info("Setting GCP session ...")
project_ids = arguments.get("project_ids")
logger.info("Checking if any credentials mode is set ...")
credentials_file = arguments.get("credentials_file")
gcp_provider = GCP_Provider(
credentials_file,
project_ids,
)
(
gcp_audit_info.credentials,
gcp_audit_info.default_project_id,
gcp_audit_info.project_ids,
) = gcp_provider.get_credentials()
return gcp_audit_info
def set_kubernetes_audit_info(self, arguments) -> Kubernetes_Audit_Info:
"""
set_kubernetes_audit_info returns the Kubernetes_Audit_Info
"""
logger.info("Setting Kubernetes session ...")
kubeconfig_file = arguments.get("kubeconfig_file")
logger.info("Checking if any context is set ...")
context = arguments.get("context")
logger.info("Checking if any namespace is set ...")
namespaces = arguments.get("namespaces")
kubernetes_provider = Kubernetes_Provider(kubeconfig_file, context, namespaces)
(
kubernetes_audit_info.api_client,
kubernetes_audit_info.context,
) = kubernetes_provider.get_credentials()
return kubernetes_audit_info
def set_provider_audit_info(provider: str, arguments: dict):
"""
set_provider_audit_info configures automatically the audit session based on the selected provider and returns the audit_info object.
"""
try:
provider_set_audit_info = f"set_{provider}_audit_info"
provider_audit_info = getattr(Audit_Info(), provider_set_audit_info)(arguments)
# Set the audit configuration from the config file
# TODO: move this to the providers
provider_audit_info.audit_config = load_and_validate_config_file(
provider, arguments["config_file"]
)
except Exception as error:
logger.critical(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
sys.exit(1)
else:
return provider_audit_info
+18 -9
View File
@@ -1,6 +1,9 @@
import sys
from importlib import import_module
from typing import Any
from prowler.lib.logger import logger
providers_prowler_lib_path = "prowler.providers"
global_provider = None
@@ -31,12 +34,18 @@ def get_available_providers() -> list[str]:
def set_global_provider_object(arguments):
global global_provider
# make here dynamic import
common_import_path = (
f"prowler.providers.{arguments.provider}.{arguments.provider}_provider"
)
provider_class = f"{arguments.provider.capitalize()}Provider"
global_provider = getattr(import_module(common_import_path), provider_class)(
arguments
)
try:
global global_provider
# make here dynamic import
common_import_path = (
f"prowler.providers.{arguments.provider}.{arguments.provider}_provider"
)
provider_class = f"{arguments.provider.capitalize()}Provider"
global_provider = getattr(import_module(common_import_path), provider_class)(
arguments
)
except TypeError as error:
logger.critical(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
sys.exit(1)
+1
View File
@@ -1,6 +1,7 @@
from pydantic import BaseModel
# TODO: include this for all the providers
class Audit_Metadata(BaseModel):
services_scanned: int
# We can't use a set in the expected
+57 -4
View File
@@ -1,15 +1,68 @@
from abc import ABC, abstractmethod
# TODO: with this we can enforce that all classes ending with "Provider" needs to inherint from the Provider class
# class ProviderMeta:
# def __init__(cls, name, bases, dct):
# # Check if the class name ends with 'Provider'
# if name.endswith("Provider"):
# # Check if any base class is a subclass of Provider (or is Provider itself)
# if not any(issubclass(b, Provider) for b in bases if b is not object):
# raise TypeError(f"{name} must inherit from Provider")
# super().__init__(name, bases, dct)
# class Provider(metaclass=ProviderMeta):
class Provider(ABC):
@property
@abstractmethod
def type(self):
"""
type method stores the provider's type.
This method needs to be created in each provider.
"""
@property
@abstractmethod
def identity(self):
"""
identity method stores the provider's identity to audit.
This method needs to be created in each provider.
"""
@property
@abstractmethod
def session(self):
"""
session method stores the provider's session to audit.
This method needs to be created in each provider.
"""
@property
@abstractmethod
def audit_config(self):
"""
audit_config method stores the provider's audit configuration.
This method needs to be created in each provider.
"""
@abstractmethod
def print_credentials(self):
"""
print_credentials is used to display in the CLI the provider's credentials used to audit.
This method needs to be created in each provider.
"""
@abstractmethod
def setup_session(self):
pass
@abstractmethod
def print_credentials(self):
pass
# TODO: probably this won't be here since we want to do the arguments validation during the parse()
def validate_arguments(self):
pass
-4
View File
@@ -60,10 +60,6 @@ class GcpProvider(Provider):
profile=getattr(self.session, "_service_account_email", "default")
)
# TODO: move this to the parent class or the main
if not arguments.only_logs:
self.print_credentials()
def setup_session(self, credentials_file):
try:
if credentials_file:
@@ -18,6 +18,7 @@ class KubernetesIdentityInfo:
class KubernetesProvider(Provider):
provider = "kubernetes"
# TODO: api_client is the session
api_client: Any
context: dict
namespaces: list
@@ -49,8 +50,6 @@ class KubernetesProvider(Provider):
self.identity = KubernetesIdentityInfo(
active_context=self.context["name"].replace(":", "_").replace("/", "_")
)
if not arguments.only_logs:
self.print_credentials()
def setup_session(self, kubeconfig_file, input_context):
"""