From 45d359c84a431ea32fe5dea740447c929d62ddd8 Mon Sep 17 00:00:00 2001 From: Mario Rodriguez Lopez <101330800+MarioRgzLpz@users.noreply.github.com> Date: Mon, 10 Feb 2025 17:13:06 +0100 Subject: [PATCH] feat(microsof365): Add documentation and compliance file (#6195) Co-authored-by: MrCloudSec Co-authored-by: Daniel Barranquero <74871504+danibarranqueroo@users.noreply.github.com> --- README.md | 1 + docs/developer-guide/provider.md | 1 + docs/developer-guide/services.md | 1 + docs/getting-started/requirements.md | 29 ++++++++ docs/index.md | 22 +++++- .../azure/create-prowler-service-principal.md | 2 +- docs/tutorials/microsoft365/authentication.md | 23 ++++++ mkdocs.yml | 3 + prowler/config/config.py | 5 +- .../compliance/cis/cis_microsoft365.py | 6 +- prowler/lib/outputs/compliance/cis/models.py | 2 +- ...ce_json_from_csv_for_cis40_microsoft365.py | 74 +++++++++++++++++++ 12 files changed, 161 insertions(+), 8 deletions(-) create mode 100644 docs/tutorials/microsoft365/authentication.md create mode 100644 util/generate_compliance_json_from_csv_for_cis40_microsoft365.py diff --git a/README.md b/README.md index 1cfaa15af1..cad7ef1311 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ It contains hundreds of controls covering CIS, NIST 800, NIST CSF, CISA, RBI, Fe | GCP | 77 | 13 | 4 | 3 | | Azure | 140 | 18 | 5 | 3 | | Kubernetes | 83 | 7 | 2 | 7 | +| Microsoft365 | 5 | 2 | 1 | 0 | > You can list the checks, services, compliance frameworks and categories with `prowler --list-checks`, `prowler --list-services`, `prowler --list-compliance` and `prowler --list-categories`. diff --git a/docs/developer-guide/provider.md b/docs/developer-guide/provider.md index ecc7c1493a..6f6b1ca0ae 100644 --- a/docs/developer-guide/provider.md +++ b/docs/developer-guide/provider.md @@ -175,6 +175,7 @@ Due to the complexity and differences of each provider use the rest of the provi - [GCP](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/gcp/gcp_provider.py) - [Azure](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/azure/azure_provider.py) - [Kubernetes](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/kubernetes/kubernetes_provider.py) +- [Microsoft365](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/microsoft365/microsoft365_provider.py) To facilitate understanding here is a pseudocode of how the most basic provider could be with examples. diff --git a/docs/developer-guide/services.md b/docs/developer-guide/services.md index 6424fa8269..d551a58269 100644 --- a/docs/developer-guide/services.md +++ b/docs/developer-guide/services.md @@ -237,3 +237,4 @@ It is really important to check if the current Prowler's permissions for each pr - AWS: https://docs.prowler.cloud/en/latest/getting-started/requirements/#aws-authentication - Azure: https://docs.prowler.cloud/en/latest/getting-started/requirements/#permissions - GCP: https://docs.prowler.cloud/en/latest/getting-started/requirements/#gcp-authentication +- Microsoft365: https://docs.prowler.cloud/en/latest/getting-started/requirements/#microsoft365-authentication diff --git a/docs/getting-started/requirements.md b/docs/getting-started/requirements.md index 19e9c29689..afda7da53e 100644 --- a/docs/getting-started/requirements.md +++ b/docs/getting-started/requirements.md @@ -102,3 +102,32 @@ Those credentials must be associated to a user or service account with proper pe ???+ note By default, `prowler` will scan all accessible GCP Projects, use flag `--project-ids` to specify the projects to be scanned. + +## Microsoft365 + +Prowler for Microsoft365 currently supports the following authentication types: + +- [Service principal application](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser#service-principal-object) (recommended). +- Current az cli credentials stored. +- Interactive browser authentication. + + +???+ warning + For Prowler App only the Service Principal with an application authentication method is supported. + +### Service Principal authentication + +To allow Prowler assume the service principal identity to start the scan it is needed to configure the following environment variables: + +```console +export AZURE_CLIENT_ID="XXXXXXXXX" +export AZURE_CLIENT_SECRET="XXXXXXXXX" +export AZURE_TENANT_ID="XXXXXXXXX" +``` + +If you try to execute Prowler with the `--sp-env-auth` flag and those variables are empty or not exported, the execution is going to fail. +Follow the instructions in the [Create Prowler Service Principal](../tutorials/azure/create-prowler-service-principal.md) section to create a service principal. + +### Interactive Browser authentication + +To use `--browser-auth` the user needs to authenticate against Azure using the default browser to start the scan, also `--tenant-id` flag is required. diff --git a/docs/index.md b/docs/index.md index 648564bc18..58f76e1901 100644 --- a/docs/index.md +++ b/docs/index.md @@ -164,7 +164,7 @@ Prowler is available as a project in [PyPI](https://pypi.org/project/prowler/), * `Python >= 3.9` * `Python pip >= 21.0.0` - * AWS, GCP, Azure and/or Kubernetes credentials + * AWS, GCP, Azure, Microsoft365 and/or Kubernetes credentials _Commands_: @@ -417,7 +417,7 @@ While the scan is running, start exploring the findings in these sections: ### Prowler CLI -To run Prowler, you will need to specify the provider (e.g `aws`, `gcp`, `azure` or `kubernetes`): +To run Prowler, you will need to specify the provider (e.g `aws`, `gcp`, `azure`, `microsoft365` or `kubernetes`): ???+ note If no provider specified, AWS will be used for backward compatibility with most of v2 options. @@ -559,5 +559,23 @@ kubectl logs prowler-XXXXX --namespace prowler-ns ???+ note By default, `prowler` will scan all namespaces in your active Kubernetes context. Use the flag `--context` to specify the context to be scanned and `--namespaces` to specify the namespaces to be scanned. +#### Microsoft365 + +With Microsoft365 you need to specify which auth method is going to be used: + +```console +# To use service principal authentication +prowler microsoft365 --sp-env-auth + +# To use az cli authentication +prowler microsoft365 --az-cli-auth + +# To use browser authentication +prowler microsoft365 --browser-auth --tenant-id "XXXXXXXX" + +``` + +See more details about Microsoft365 Authentication in [Requirements](getting-started/requirements.md#microsoft365) + ## Prowler v2 Documentation For **Prowler v2 Documentation**, please check it out [here](https://github.com/prowler-cloud/prowler/blob/8818f47333a0c1c1a457453c87af0ea5b89a385f/README.md). diff --git a/docs/tutorials/azure/create-prowler-service-principal.md b/docs/tutorials/azure/create-prowler-service-principal.md index 025c9da9c8..02ab7ea629 100644 --- a/docs/tutorials/azure/create-prowler-service-principal.md +++ b/docs/tutorials/azure/create-prowler-service-principal.md @@ -4,7 +4,7 @@ To allow Prowler assume an identity to start the scan with the required privileg To create a Service Principal Application you can use the Azure Portal or the Azure CLI. -## From Azure Portal +## From Azure Portal / Entra Admin Center 1. Access to Microsoft Entra ID 2. In the left menu bar, go to "App registrations" diff --git a/docs/tutorials/microsoft365/authentication.md b/docs/tutorials/microsoft365/authentication.md new file mode 100644 index 0000000000..750352ed76 --- /dev/null +++ b/docs/tutorials/microsoft365/authentication.md @@ -0,0 +1,23 @@ +# Microsoft365 authentication + +By default Prowler uses MsGraph Python SDK identity package authentication methods using the class `ClientSecretCredential`. +This allows Prowler to authenticate against microsoft365 using the following methods: + +- Service principal authentication by environment variables (Enterprise Application) +- Current CLI credentials stored +- Interactive browser authentication + +To launch the tool first you need to specify which method is used through the following flags: + +```console +# To use service principal authentication +prowler microsoft365 --sp-env-auth + +# To use cli authentication +prowler microsoft365 --az-cli-auth + +# To use browser authentication +prowler microsoft365 --browser-auth --tenant-id "XXXXXXXX" +``` + +To use Prowler you need to set up also the permissions required to access your resources in your Microsoft365 account, to more details refer to [Requirements](../../getting-started/requirements.md) diff --git a/mkdocs.yml b/mkdocs.yml index b3ee70140e..6d99d72afd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -94,6 +94,9 @@ nav: - In-Cluster Execution: tutorials/kubernetes/in-cluster.md - Non In-Cluster Execution: tutorials/kubernetes/outside-cluster.md - Miscellaneous: tutorials/kubernetes/misc.md + - Microsoft 365: + - Authentication: tutorials/microsoft365/authentication.md + - Create Prowler Service Principal: tutorials/microsoft365/create-prowler-service-principal.md - Developer Guide: - Introduction: developer-guide/introduction.md - Provider: developer-guide/provider.md diff --git a/prowler/config/config.py b/prowler/config/config.py index ee3f380254..b90bff4cfa 100644 --- a/prowler/config/config.py +++ b/prowler/config/config.py @@ -123,7 +123,10 @@ def load_and_validate_config_file(provider: str, config_file_path: str) -> dict: # Not to introduce a breaking change, allow the old format config file without any provider keys # and a new format with a key for each provider to include their configuration values within. - if any(key in config_file for key in ["aws", "gcp", "azure", "kubernetes"]): + if any( + key in config_file + for key in ["aws", "gcp", "azure", "kubernetes", "microsoft365"] + ): config = config_file.get(provider, {}) else: config = config_file if config_file else {} diff --git a/prowler/lib/outputs/compliance/cis/cis_microsoft365.py b/prowler/lib/outputs/compliance/cis/cis_microsoft365.py index c269829526..8cf4722ba7 100644 --- a/prowler/lib/outputs/compliance/cis/cis_microsoft365.py +++ b/prowler/lib/outputs/compliance/cis/cis_microsoft365.py @@ -42,7 +42,7 @@ class Microsoft365CIS(ComplianceOutput): compliance_row = Microsoft365CISModel( Provider=finding.provider, Description=compliance.Description, - SubscriptionId=finding.account_uid, + TenantId=finding.account_uid, Location=finding.region, AssessmentDate=str(finding.timestamp), Requirements_Id=requirement.Id, @@ -73,8 +73,8 @@ class Microsoft365CIS(ComplianceOutput): compliance_row = Microsoft365CISModel( Provider=compliance.Provider.lower(), Description=compliance.Description, - SubscriptionId="", - Location="", + TenantId=finding.account_uid, + Location=finding.region, AssessmentDate=str(finding.timestamp), Requirements_Id=requirement.Id, Requirements_Description=requirement.Description, diff --git a/prowler/lib/outputs/compliance/cis/models.py b/prowler/lib/outputs/compliance/cis/models.py index 630cab0910..c06ed1f849 100644 --- a/prowler/lib/outputs/compliance/cis/models.py +++ b/prowler/lib/outputs/compliance/cis/models.py @@ -73,7 +73,7 @@ class Microsoft365CISModel(BaseModel): Provider: str Description: str - SubscriptionId: str + TenantId: str Location: str AssessmentDate: str Requirements_Id: str diff --git a/util/generate_compliance_json_from_csv_for_cis40_microsoft365.py b/util/generate_compliance_json_from_csv_for_cis40_microsoft365.py new file mode 100644 index 0000000000..c6ba8c101f --- /dev/null +++ b/util/generate_compliance_json_from_csv_for_cis40_microsoft365.py @@ -0,0 +1,74 @@ +import csv +import json +import sys + +# Convert a CSV file following the CIS 4.0 Microsoft365 Benchmark into a Prowler v3.0 Compliance JSON file +# CSV fields: +# Section #,Recommendation #,Profile,Title,Assessment Status,Description,Rationale Statement,Impact Statement,Remediation Procedure,Audit Procedure,Additional Information,CIS Controls,CIS Safeguards 1 (v8),CIS Safeguards 2 (v8),CIS Safeguards 3 (v8),v8 IG1,v8 IG2,v8 IG3,CIS Safeguards 1 (v7),CIS Safeguards 2 (v7),CIS Safeguards 3 (v7),v7 IG1,v7 IG2,v7 IG3,References,Default Value + +# Get the CSV filename to convert from +file_name = sys.argv[1] + +# Create the output JSON object +output = {"Framework": "CIS", "Version": "4.0", "Requirements": []} + +# Open the CSV file and read the rows +try: + with open(file_name, newline="", encoding="utf-8") as f: + reader = csv.reader(f, delimiter=",") + next(reader) # Skip the header row + for row in reader: + attribute = { + "Section": row[0], + "Profile": row[2], + "AssessmentStatus": row[4], + "Description": row[5], + "RationaleStatement": row[6], + "ImpactStatement": row[7], + "RemediationProcedure": row[8], + "AuditProcedure": row[9], + "AdditionalInformation": row[10], + "References": row[24], + } + if row[4] != "": + output["Requirements"].append( + { + "Id": row[1], + "Description": row[5], + "Checks": [], + "Attributes": [attribute], + } + ) +except UnicodeDecodeError: + # If there is an error reading the file with the default encoding, try with ISO-8859-1 + with open(file_name, newline="", encoding="ISO-8859-1") as f: + reader = csv.reader(f, delimiter=",") + next(reader) # Skip the header row + for row in reader: + attribute = { + "Section": row[0], + "Profile": row[2], + "AssessmentStatus": row[4], + "Description": row[5], + "RationaleStatement": row[6], + "ImpactStatement": row[7], + "RemediationProcedure": row[8], + "AuditProcedure": row[9], + "AdditionalInformation": row[10], + "References": row[24], + } + if row[4] != "": + output["Requirements"].append( + { + "Id": row[1], + "Description": row[5], + "Checks": [], + "Attributes": [attribute], + } + ) + +# Save the output JSON file +with open("cis_4.0_microsoft365.json", "w", encoding="utf-8") as outfile: + json.dump(output, outfile, indent=4, ensure_ascii=False) + +print("Archivo JSON generado exitosamente.")