From 0d24676d7d0af0db70b7a064108633ebe5b133bc Mon Sep 17 00:00:00 2001 From: mohsinm-dev Date: Fri, 17 Oct 2025 21:28:14 +0500 Subject: [PATCH] Fix SecOps authentication requirements and hanging issue Add GOOGLE_APPLICATION_CREDENTIALS to all SecOps documentation and implement authentication error handling with timeout to prevent hanging. Fixes #189 --- README.md | 8 ++- docs/servers/secops_mcp.md | 8 ++- docs/usage_guide.md | 8 ++- server/secops/README.md | 8 ++- server/secops/secops_mcp/server.py | 91 ++++++++++++++++++++++++++++-- 5 files changed, 109 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 55fdf982..c91e0d39 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ With environment variables: CHRONICLE_PROJECT_ID="your-project-id" \ CHRONICLE_CUSTOMER_ID="01234567-abcd-4321-1234-0123456789ab" \ CHRONICLE_REGION="us" \ +GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/service-account-key.json" \ uvx secops_mcp ``` @@ -101,7 +102,8 @@ You can configure MCP clients to use the installed packages with uvx. Here's an "env": { "CHRONICLE_PROJECT_ID": "your-project-id", "CHRONICLE_CUSTOMER_ID": "01234567-abcd-4321-1234-0123456789ab", - "CHRONICLE_REGION": "us" + "CHRONICLE_REGION": "us", + "GOOGLE_APPLICATION_CREDENTIALS": "/path/to/your/service-account-key.json" }, "disabled": false, "autoApprove": [] @@ -122,7 +124,9 @@ You can configure MCP clients to use the installed packages with uvx. Here's an "args": [ "scc_mcp" ], - "env": {}, + "env": { + "GOOGLE_APPLICATION_CREDENTIALS": "/path/to/your/service-account-key.json" + }, "disabled": false, "autoApprove": [] }, diff --git a/docs/servers/secops_mcp.md b/docs/servers/secops_mcp.md index b4bd2edc..f92c6b5f 100644 --- a/docs/servers/secops_mcp.md +++ b/docs/servers/secops_mcp.md @@ -28,12 +28,11 @@ Add the following configuration to your MCP client's settings file: "env": { "CHRONICLE_PROJECT_ID": "your-gcp-project-id", "CHRONICLE_CUSTOMER_ID": "your-chronicle-customer-id", - "CHRONICLE_REGION": "us" + "CHRONICLE_REGION": "us", + "GOOGLE_APPLICATION_CREDENTIALS": "/path/to/your/service-account-key.json" }, "disabled": false, "autoApprove": [] - "disabled": false, - "autoApprove": [] } ``` @@ -63,6 +62,7 @@ Example .env file: CHRONICLE_PROJECT_ID=your-gcp-project-id CHRONICLE_CUSTOMER_ID=your-chronicle-customer-id CHRONICLE_REGION=us +GOOGLE_APPLICATION_CREDENTIALS=/path/to/your/service-account-key.json ``` ### Environment Variable Setup @@ -74,6 +74,7 @@ Set up these environment variables in your system: export CHRONICLE_PROJECT_ID="your-google-cloud-project-id" export CHRONICLE_CUSTOMER_ID="your-chronicle-customer-id" export CHRONICLE_REGION="us" +export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/service-account-key.json" ``` **For Windows PowerShell:** @@ -81,6 +82,7 @@ export CHRONICLE_REGION="us" $Env:CHRONICLE_PROJECT_ID = "your-google-cloud-project-id" $Env:CHRONICLE_CUSTOMER_ID = "your-chronicle-customer-id" $Env:CHRONICLE_REGION = "us" +$Env:GOOGLE_APPLICATION_CREDENTIALS = "/path/to/your/service-account-key.json" ``` The `CHRONICLE_REGION` can be one of: diff --git a/docs/usage_guide.md b/docs/usage_guide.md index d56e5864..5e8cc4e4 100644 --- a/docs/usage_guide.md +++ b/docs/usage_guide.md @@ -95,7 +95,8 @@ Additionally, for the secops-soar MCP server, you will need use the CA list bund "env": { "CHRONICLE_PROJECT_ID": "your-project-id", "CHRONICLE_CUSTOMER_ID": "01234567-abcd-4321-1234-0123456789ab", - "CHRONICLE_REGION": "us" + "CHRONICLE_REGION": "us", + "GOOGLE_APPLICATION_CREDENTIALS": "/path/to/your/service-account-key.json" }, "disabled": false, "autoApprove": [] @@ -139,7 +140,9 @@ Additionally, for the secops-soar MCP server, you will need use the CA list bund "run", "scc_mcp.py" ], - "env": {}, + "env": { + "GOOGLE_APPLICATION_CREDENTIALS": "/path/to/your/service-account-key.json" + }, "disabled": false, "autoApprove": [] } @@ -175,6 +178,7 @@ Add these lines to your `~/.bashrc`, `~/.zshrc`, or equivalent shell configurati export CHRONICLE_PROJECT_ID="your-google-cloud-project-id" export CHRONICLE_CUSTOMER_ID="your-chronicle-customer-id" export CHRONICLE_REGION="us" +export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/service-account-key.json" # SOAR export SOAR_URL="your-soar-url" diff --git a/server/secops/README.md b/server/secops/README.md index 1723d9b2..0cd1c641 100644 --- a/server/secops/README.md +++ b/server/secops/README.md @@ -181,7 +181,8 @@ Add the following configuration to your MCP client's settings file: "env": { "CHRONICLE_PROJECT_ID": "${CHRONICLE_PROJECT_ID}", "CHRONICLE_CUSTOMER_ID": "${CHRONICLE_CUSTOMER_ID}", - "CHRONICLE_REGION": "${CHRONICLE_REGION}" + "CHRONICLE_REGION": "${CHRONICLE_REGION}", + "GOOGLE_APPLICATION_CREDENTIALS": "${GOOGLE_APPLICATION_CREDENTIALS}" }, "disabled": false, "autoApprove": [] @@ -206,7 +207,8 @@ You can also use pip instead of uv to install and run the MCP server: "env": { "CHRONICLE_PROJECT_ID": "${CHRONICLE_PROJECT_ID}", "CHRONICLE_CUSTOMER_ID": "${CHRONICLE_CUSTOMER_ID}", - "CHRONICLE_REGION": "${CHRONICLE_REGION}" + "CHRONICLE_REGION": "${CHRONICLE_REGION}", + "GOOGLE_APPLICATION_CREDENTIALS": "${GOOGLE_APPLICATION_CREDENTIALS}" }, "disabled": false, "autoApprove": [ @@ -236,6 +238,7 @@ Set up these environment variables in your system: export CHRONICLE_PROJECT_ID="your-google-cloud-project-id" export CHRONICLE_CUSTOMER_ID="your-chronicle-customer-id" export CHRONICLE_REGION="us" +export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/service-account-key.json" ``` **For Windows PowerShell:** @@ -243,6 +246,7 @@ export CHRONICLE_REGION="us" $Env:CHRONICLE_PROJECT_ID = "your-google-cloud-project-id" $Env:CHRONICLE_CUSTOMER_ID = "your-chronicle-customer-id" $Env:CHRONICLE_REGION = "us" +$Env:GOOGLE_APPLICATION_CREDENTIALS = "/path/to/your/service-account-key.json" ``` The `CHRONICLE_REGION` can be one of: diff --git a/server/secops/secops_mcp/server.py b/server/secops/secops_mcp/server.py index 6881995f..1aa7ddcf 100644 --- a/server/secops/secops_mcp/server.py +++ b/server/secops/secops_mcp/server.py @@ -19,6 +19,10 @@ import logging import os +import signal +import sys +import threading +import time from typing import Any, Optional from mcp.server.fastmcp import FastMCP @@ -56,6 +60,10 @@ def get_chronicle_client( Returns: Any: Initialized Chronicle client + + Raises: + ValueError: If required configuration is missing + RuntimeError: If authentication fails or times out """ # Use provided values or defaults from environment variables project_id = project_id or DEFAULT_PROJECT_ID @@ -69,11 +77,84 @@ def get_chronicle_client( '(CHRONICLE_PROJECT_ID, CHRONICLE_CUSTOMER_ID)' ) - client = SecOpsClient() - chronicle = client.chronicle( - customer_id=customer_id, project_id=project_id, region=region - ) - return chronicle + # Check for Google Cloud authentication credentials + google_creds = os.environ.get('GOOGLE_APPLICATION_CREDENTIALS') + if not google_creds: + # Check if ADC might be available + try: + from google.auth import default + default() + except Exception: + raise RuntimeError( + 'Google Cloud authentication is required but not configured.\n\n' + 'Please set up authentication using one of these methods:\n' + '1. Set GOOGLE_APPLICATION_CREDENTIALS environment variable:\n' + ' export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/service-account-key.json"\n\n' + '2. Use Application Default Credentials (ADC):\n' + ' gcloud auth application-default login\n\n' + '3. Set up a service account and download the key file\n\n' + 'For more information, see: https://cloud.google.com/docs/authentication' + ) + + # Initialize client with timeout protection + logger.info('Initializing SecOps client...') + + # Use threading-based timeout for cross-platform compatibility + client_result = [None] + client_error = [None] + + def init_client(): + try: + client = SecOpsClient() + chronicle = client.chronicle( + customer_id=customer_id, project_id=project_id, region=region + ) + client_result[0] = chronicle + except Exception as e: + client_error[0] = e + + # Start initialization in a separate thread + init_thread = threading.Thread(target=init_client, daemon=True) + init_thread.start() + + # Wait for initialization with timeout + timeout_seconds = 30 + init_thread.join(timeout=timeout_seconds) + + if init_thread.is_alive(): + # Thread is still running - initialization timed out + raise TimeoutError( + f'SecOps client initialization timed out after {timeout_seconds} seconds. ' + 'This usually indicates an authentication problem.\n\n' + 'Please verify:\n' + '1. GOOGLE_APPLICATION_CREDENTIALS is correctly set\n' + '2. The service account has necessary permissions for Chronicle Security Operations\n' + '3. Network connectivity to Google Cloud APIs\n' + '4. The specified project_id, customer_id, and region are correct' + ) + + if client_error[0]: + # Initialization failed with an exception + logger.error(f'Failed to initialize SecOps client: {str(client_error[0])}') + raise RuntimeError( + f'Failed to initialize Chronicle SecOps client: {str(client_error[0])}\n\n' + 'This error often indicates:\n' + '1. Missing or invalid GOOGLE_APPLICATION_CREDENTIALS\n' + '2. Insufficient permissions on the service account\n' + '3. Network connectivity issues\n' + '4. Invalid project_id, customer_id, or region\n\n' + 'Please verify your configuration and try again.' + ) + + if client_result[0] is None: + # Unexpected case - no result and no error + raise RuntimeError( + 'SecOps client initialization completed but returned no result. ' + 'This may indicate an internal error.' + ) + + logger.info('SecOps client initialized successfully') + return client_result[0] # Import all tools