From 15a7de7b2491e6089cc2248fa6182f757b434fc8 Mon Sep 17 00:00:00 2001 From: Nacho Rivera Date: Fri, 2 Jun 2023 13:22:43 +0200 Subject: [PATCH] fix(browser auth): fix browser auth in Azure to include tenant id (#2415) Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com> Co-authored-by: Pepe Fagoaga --- prowler/lib/cli/parser.py | 8 +++++++- prowler/lib/outputs/html.py | 10 +++++++++- prowler/providers/azure/azure_provider.py | 20 +++++++++++++++----- prowler/providers/common/audit_info.py | 6 ++++++ tests/lib/cli/parser_test.py | 8 ++++++++ 5 files changed, 45 insertions(+), 7 deletions(-) diff --git a/prowler/lib/cli/parser.py b/prowler/lib/cli/parser.py index a9791758..a3b4205e 100644 --- a/prowler/lib/cli/parser.py +++ b/prowler/lib/cli/parser.py @@ -430,7 +430,7 @@ Detailed documentation at https://docs.prowler.cloud azure_auth_modes_group.add_argument( "--browser-auth", action="store_true", - help="Use browser authentication to log in against azure ", + help="Use browser authentication to log in against Azure, --tenant-id is required for this option", ) azure_auth_modes_group.add_argument( "--managed-identity-auth", @@ -445,6 +445,12 @@ Detailed documentation at https://docs.prowler.cloud default=[], help="Azure subscription ids to be scanned by prowler", ) + azure_parser.add_argument( + "--tenant-id", + nargs="?", + default=None, + help="Azure Tenant ID to be used with --browser-auth option", + ) def __init_gcp_parser__(self): """Init the GCP Provider CLI parser""" diff --git a/prowler/lib/outputs/html.py b/prowler/lib/outputs/html.py index a25aacf1..9e11d31f 100644 --- a/prowler/lib/outputs/html.py +++ b/prowler/lib/outputs/html.py @@ -408,6 +408,14 @@ def get_azure_html_assessment_summary(audit_info): for key, value in audit_info.identity.subscriptions.items(): intermediate = key + " : " + value printed_subscriptions.append(intermediate) + + # check if identity is str(coming from SP) or dict(coming from browser or) + if isinstance(audit_info.identity.identity_id, dict): + html_identity = audit_info.identity.identity_id.get( + "userPrincipalName", "Identity not found" + ) + else: + html_identity = audit_info.identity.identity_id return ( """
@@ -447,7 +455,7 @@ def get_azure_html_assessment_summary(audit_info):
  • Azure Identity ID: """ - + audit_info.identity.identity_id + + html_identity + """
  • diff --git a/prowler/providers/azure/azure_provider.py b/prowler/providers/azure/azure_provider.py index d65a00e0..9bd28a86 100644 --- a/prowler/providers/azure/azure_provider.py +++ b/prowler/providers/azure/azure_provider.py @@ -17,10 +17,11 @@ class Azure_Provider: browser_auth: bool, managed_entity_auth: bool, subscription_ids: list, + tenant_id: str, ): logger.info("Instantiating Azure Provider ...") self.credentials = self.__set_credentials__( - az_cli_auth, sp_env_auth, browser_auth, managed_entity_auth + az_cli_auth, sp_env_auth, browser_auth, managed_entity_auth, tenant_id ) self.identity = self.__set_identity_info__( self.credentials, @@ -32,7 +33,7 @@ class Azure_Provider: ) def __set_credentials__( - self, az_cli_auth, sp_env_auth, browser_auth, managed_entity_auth + self, az_cli_auth, sp_env_auth, browser_auth, managed_entity_auth, tenant_id ): # Browser auth creds cannot be set with DefaultAzureCredentials() if not browser_auth: @@ -59,7 +60,15 @@ class Azure_Provider: ) sys.exit(1) else: - credentials = InteractiveBrowserCredential() + try: + print(tenant_id) + credentials = InteractiveBrowserCredential(tenant_id=tenant_id) + except Exception as error: + logger.critical("Failed to retrieve azure credentials") + logger.critical( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- {error}" + ) + sys.exit(1) return credentials @@ -122,8 +131,9 @@ class Azure_Provider: "Trying to retrieve user information from AAD to populate identity structure ..." ) client = GraphClient(credential=credentials) - user_name = client.get("/me").json()["userPrincipalName"] - identity.identity_id = user_name + user_name = client.get("/me").json() + if "userPrincipalName" in user_name: + identity.identity_id = user_name except Exception as error: logger.error( diff --git a/prowler/providers/common/audit_info.py b/prowler/providers/common/audit_info.py index c1db60f3..27a06412 100644 --- a/prowler/providers/common/audit_info.py +++ b/prowler/providers/common/audit_info.py @@ -265,6 +265,7 @@ Azure Identity Type: {Fore.YELLOW}[{audit_info.identity.identity_type}]{Style.RE 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") if ( not az_cli_auth and not sp_env_auth @@ -274,6 +275,10 @@ Azure Identity Type: {Fore.YELLOW}[{audit_info.identity.identity_type}]{Style.RE raise Exception( "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 Exception( + "Azure Tenant ID is required only for browser authentication mode" + ) azure_provider = Azure_Provider( az_cli_auth, @@ -281,6 +286,7 @@ Azure Identity Type: {Fore.YELLOW}[{audit_info.identity.identity_type}]{Style.RE browser_auth, managed_entity_auth, subscription_ids, + tenant_id, ) azure_audit_info.credentials = azure_provider.get_credentials() azure_audit_info.identity = azure_provider.get_identity() diff --git a/tests/lib/cli/parser_test.py b/tests/lib/cli/parser_test.py index 3d9efa06..eadd2387 100644 --- a/tests/lib/cli/parser_test.py +++ b/tests/lib/cli/parser_test.py @@ -924,6 +924,14 @@ class Test_Parser: assert parsed.provider == "azure" assert parsed.browser_auth + def test_parser_azure_tenant_id(self): + argument = "--tenant-id" + tenant_id = "test-tenant-id" + command = [prowler_command, "azure", argument, tenant_id] + parsed = self.parser.parse(command) + assert parsed.provider == "azure" + assert parsed.tenant_id == tenant_id + def test_parser_azure_auth_az_cli(self): argument = "--az-cli-auth" command = [prowler_command, "azure", argument]