feat: Enhanced SSL debugging and updated documentation

This commit is contained in:
livehybrid
2025-03-11 09:54:55 +00:00
parent 4b31590042
commit 47342a04c7
2 changed files with 160 additions and 144 deletions

199
README.md
View File

@@ -10,6 +10,8 @@ A FastMCP-based tool for interacting with Splunk Enterprise/Cloud through natura
- **KV Store Operations**: Create, list, and manage KV store collections - **KV Store Operations**: Create, list, and manage KV store collections
- **Async Support**: Built with async/await patterns for better performance - **Async Support**: Built with async/await patterns for better performance
- **Detailed Logging**: Comprehensive logging with emoji indicators for better visibility - **Detailed Logging**: Comprehensive logging with emoji indicators for better visibility
- **SSL Configuration**: Flexible SSL verification options for different security requirements
- **Enhanced Debugging**: Detailed connection and error logging for troubleshooting
- **Comprehensive Testing**: Unit tests covering all major functionality - **Comprehensive Testing**: Unit tests covering all major functionality
## Prerequisites ## Prerequisites
@@ -46,96 +48,132 @@ SPLUNK_PORT=8089
SPLUNK_USERNAME=your_username SPLUNK_USERNAME=your_username
SPLUNK_PASSWORD=your_password SPLUNK_PASSWORD=your_password
SPLUNK_SCHEME=https SPLUNK_SCHEME=https
VERIFY_SSL=true
FASTMCP_LOG_LEVEL=INFO FASTMCP_LOG_LEVEL=INFO
``` ```
### Option 2: Docker Installation ### Option 2: Docker Installation
1. Clone the repository: 1. Pull the latest image:
```bash ```bash
git clone <repository-url> docker pull livehybrid/splunk-mcp:latest
cd splunk-mcp
``` ```
2. Copy the example environment file and configure your settings: 2. Create your `.env` file as above or use environment variables directly.
```bash
cp .env.example .env
```
3. Update the `.env` file with your Splunk credentials (same as above). 3. Run using Docker Compose:
4. Build and run using Docker Compose:
```bash ```bash
docker-compose up -d docker-compose up -d
``` ```
Or using Docker directly: Or using Docker directly:
```bash ```bash
# Build the image docker run -i \
docker build -t splunk-mcp .
# Run the container
docker run -d \
-p 3000:3000 \
--env-file .env \ --env-file .env \
--name splunk-mcp \ livehybrid/splunk-mcp
splunk-mcp
``` ```
## Usage ## Usage
### Local Usage ### Local Usage
1. Start the MCP server: The tool can run in two modes:
1. STDIO mode (default) - for command-line integration:
```bash ```bash
poetry run python splunk_mcp.py poetry run python splunk_mcp.py
``` ```
2. SSE mode - for web server integration:
```bash
poetry run python splunk_mcp.py sse
```
### Docker Usage ### Docker Usage
If using Docker Compose: If using Docker Compose:
```bash ```bash
# Start the service # Start the service in STDIO mode (default)
docker-compose up -d docker-compose up -d
# View logs # Start in SSE mode
docker-compose logs -f docker-compose run --rm splunk-mcp python splunk_mcp.py sse
# Stop the service
docker-compose down
``` ```
If using Docker directly: If using Docker directly:
```bash ```bash
# Start the container # Start in STDIO mode (default)
docker start splunk-mcp docker run -i \
--env-file .env \
livehybrid/splunk-mcp
# View logs # Start in SSE mode
docker logs -f splunk-mcp docker run -d \
-p 3000:3000 \
# Stop the container --env-file .env \
docker stop splunk-mcp livehybrid/splunk-mcp python splunk_mcp.py sse
``` ```
The server will start and listen for connections on port 3000 in both local and Docker installations. ### Environment Variables
### Docker Environment Variables Configure the following environment variables:
When running with Docker, you can configure the following environment variables:
- `SPLUNK_HOST`: Your Splunk host address - `SPLUNK_HOST`: Your Splunk host address
- `SPLUNK_PORT`: Splunk management port (default: 8089) - `SPLUNK_PORT`: Splunk management port (default: 8089)
- `SPLUNK_USERNAME`: Your Splunk username - `SPLUNK_USERNAME`: Your Splunk username
- `SPLUNK_PASSWORD`: Your Splunk password - `SPLUNK_PASSWORD`: Your Splunk password
- `SPLUNK_SCHEME`: Connection scheme (default: https) - `SPLUNK_SCHEME`: Connection scheme (default: https)
- `VERIFY_SSL`: Enable/disable SSL verification (default: true)
- `FASTMCP_LOG_LEVEL`: Logging level (default: INFO) - `FASTMCP_LOG_LEVEL`: Logging level (default: INFO)
These can be set either in the `.env` file or passed directly to Docker using the `-e` flag. ### SSL Configuration
The tool provides flexible SSL verification options:
1. **Default (Secure) Mode**:
```env
VERIFY_SSL=true
```
- Full SSL certificate verification
- Hostname verification enabled
- Recommended for production environments
2. **Relaxed Mode**:
```env
VERIFY_SSL=false
```
- SSL certificate verification disabled
- Hostname verification disabled
- Useful for testing or self-signed certificates
### Troubleshooting
#### Connection Issues
1. **Basic Connectivity**:
- The tool now performs a basic TCP connectivity test
- Check if port 8089 is accessible
- Verify network routing and firewalls
2. **SSL Issues**:
- If seeing SSL errors, try setting `VERIFY_SSL=false`
- Check certificate validity and trust chain
- Verify hostname matches certificate
3. **Authentication Issues**:
- Verify Splunk credentials
- Check user permissions
- Ensure account is not locked
4. **Debugging**:
- Set `FASTMCP_LOG_LEVEL=DEBUG` for detailed logs
- Check connection logs for specific error messages
- Review SSL configuration messages
### Available Tools ### Available Tools
1. **search_splunk** 1. **search_splunk**
- Execute Splunk searches with customizable time ranges - Execute Splunk searches with customizable time ranges
- Example: Search for hosts sending data in the last hour - Example: Search for events in the last hour
```python ```python
search_query="index=* | stats count by host" search_query="index=* | stats count by host"
``` ```
@@ -153,98 +191,15 @@ These can be set either in the `.env` file or passed directly to Docker using th
- create_kvstore_collection: Create new collections - create_kvstore_collection: Create new collections
- delete_kvstore_collection: Remove existing collections - delete_kvstore_collection: Remove existing collections
## Example Queries
1. Search for temperature data:
```python
search_query="index=main sourcetype=httpevent *temperature* | stats avg(value) by location"
```
2. List all indexes:
```python
await list_indexes()
```
3. View user information:
```python
await list_users()
```
## Development ## Development
### Project Structure
- `splunk_mcp.py`: Main implementation file
- `pyproject.toml`: Poetry project configuration
- `.env`: Environment configuration
- `README.md`: Documentation
- `tests/`: Unit tests directory
- `test_splunk_mcp.py`: Test suite for Splunk MCP functionality
### Running Tests ### Running Tests
The project uses pytest for testing. All tests are written to work without requiring an actual Splunk connection, using mocks to simulate Splunk's behavior.
1. Run all tests:
```bash ```bash
poetry run pytest poetry run pytest
``` ```
2. Run tests with coverage: ### Contributing
```bash
poetry run pytest --cov=splunk_mcp tests/
```
3. Run specific test file:
```bash
poetry run pytest tests/test_splunk_mcp.py
```
4. Run tests with verbose output:
```bash
poetry run pytest -v
```
The test suite includes:
- Unit tests for all Splunk operations (search, index listing, user management)
- KV store operation tests
- Connection handling tests
- Error case testing
### Adding New Tests
When adding new features:
1. Create corresponding test cases in `tests/test_splunk_mcp.py`
2. Use the provided mock fixtures for Splunk service simulation
3. Add appropriate assertions to verify functionality
4. Ensure both success and error cases are covered
## Troubleshooting
Common issues and solutions:
1. Connection Issues
- Verify Splunk credentials in `.env`
- Check network connectivity
- Ensure Splunk management port (8089) is accessible
- If using Docker, ensure the container has network access to your Splunk instance
2. Docker Issues
- Check container logs: `docker logs splunk-mcp`
- Verify environment variables are properly set
- Ensure port 3000 is not in use by another service
- Check container status: `docker ps -a`
2. Permission Issues
- Verify user has appropriate Splunk roles
- Check app/collection access permissions
3. Search Issues
- Validate search syntax
- Check time ranges
- Verify index access permissions
## Contributing
1. Fork the repository 1. Fork the repository
2. Create a feature branch 2. Create a feature branch

View File

@@ -5,6 +5,10 @@ from decouple import config
import splunklib.client as client import splunklib.client as client
from splunklib import results from splunklib import results
from datetime import datetime, timedelta from datetime import datetime, timedelta
import sys
import ssl
import socket
import traceback
# Configure logging # Configure logging
logging.basicConfig( logging.basicConfig(
@@ -17,24 +21,62 @@ mcp = FastMCP("splunk")
# Splunk connection configuration # Splunk connection configuration
SPLUNK_HOST = config("SPLUNK_HOST", default="localhost") SPLUNK_HOST = config("SPLUNK_HOST", default="localhost")
SPLUNK_PORT = config("SPLUNK_PORT", default=8089) SPLUNK_PORT = config("SPLUNK_PORT", default=8089, cast=int)
SPLUNK_USERNAME = config("SPLUNK_USERNAME", default="admin") SPLUNK_USERNAME = config("SPLUNK_USERNAME", default="admin")
SPLUNK_PASSWORD = config("SPLUNK_PASSWORD") SPLUNK_PASSWORD = config("SPLUNK_PASSWORD")
SPLUNK_SCHEME = config("SPLUNK_SCHEME", default="https") SPLUNK_SCHEME = config("SPLUNK_SCHEME", default="https")
VERIFY_SSL = config("VERIFY_SSL", default="true", cast=bool)
def get_splunk_connection(): def get_splunk_connection():
"""Helper function to establish Splunk connection""" """Helper function to establish Splunk connection"""
try: try:
service = client.connect( logger.info(f"🔌 Attempting to connect to Splunk at {SPLUNK_SCHEME}://{SPLUNK_HOST}:{SPLUNK_PORT}")
host=SPLUNK_HOST, logger.info(f"SSL Verification is {'enabled' if VERIFY_SSL else 'disabled'}")
port=SPLUNK_PORT,
username=SPLUNK_USERNAME, # Test basic connectivity first
password=SPLUNK_PASSWORD, try:
scheme=SPLUNK_SCHEME sock = socket.create_connection((SPLUNK_HOST, SPLUNK_PORT), timeout=10)
) sock.close()
return service logger.info("✅ Basic TCP connection test successful")
except Exception as e:
logger.error(f"❌ Failed to establish basic TCP connection: {str(e)}")
raise
# Configure SSL context with detailed logging
if not VERIFY_SSL:
logger.info("🔒 Creating custom SSL context with verification disabled")
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
logger.info("✅ SSL context configured with verification disabled")
else:
logger.info("🔒 Using default SSL context with verification enabled")
ssl_context = None
# Attempt Splunk connection with detailed logging
try:
service = client.connect(
host=SPLUNK_HOST,
port=SPLUNK_PORT,
username=SPLUNK_USERNAME,
password=SPLUNK_PASSWORD,
scheme=SPLUNK_SCHEME,
ssl_context=ssl_context
)
logger.info("✅ Successfully established Splunk connection")
return service
except ssl.SSLError as e:
logger.error(f"❌ SSL Error during connection: {str(e)}")
logger.error(f"SSL Error details: {traceback.format_exc()}")
raise
except Exception as e:
logger.error(f"❌ Error during Splunk connection: {str(e)}")
logger.error(f"Error details: {traceback.format_exc()}")
raise
except Exception as e: except Exception as e:
logger.error(f"Failed to connect to Splunk: {str(e)}") logger.error(f"Failed to connect to Splunk: {str(e)}")
logger.error(f"Full error details: {traceback.format_exc()}")
raise raise
@mcp.tool() @mcp.tool()
@@ -95,7 +137,7 @@ async def search_splunk(
raise raise
@mcp.tool() @mcp.tool()
async def list_indexes() -> List[Dict[str, Any]]: # Made async async def list_indexes() -> List[Dict[str, Any]]:
""" """
List all available Splunk indexes List all available Splunk indexes
@@ -108,15 +150,27 @@ async def list_indexes() -> List[Dict[str, Any]]: # Made async
indexes = [] indexes = []
for index in service.indexes: for index in service.indexes:
index_info = { try:
"name": index.name, index_info = {
"total_event_count": index["totalEventCount"], "name": index.name,
"current_size": index["currentDBSizeMB"], "total_event_count": index.get("totalEventCount", "0"),
"max_size": index["maxTotalDataSizeMB"], "current_size": index.get("currentDBSizeMB", "0"),
"earliest_time": index["earliestTime"], "max_size": index.get("maxTotalDataSizeMB", "0"),
"latest_time": index["latestTime"] "earliest_time": index.get("earliestTime", "0"),
} "latest_time": index.get("latestTime", "0")
indexes.append(index_info) }
indexes.append(index_info)
except Exception as e:
logger.warning(f"⚠️ Error accessing metadata for index {index.name}: {str(e)}")
# Add basic information if metadata access fails
indexes.append({
"name": index.name,
"total_event_count": "0",
"current_size": "0",
"max_size": "0",
"earliest_time": "0",
"latest_time": "0"
})
logger.info(f"✅ Found {len(indexes)} indexes") logger.info(f"✅ Found {len(indexes)} indexes")
return indexes return indexes
@@ -261,5 +315,12 @@ async def delete_kvstore_collection( # Made async
raise raise
if __name__ == "__main__": if __name__ == "__main__":
logger.info("🚀 Starting Splunk MCP server") # Get transport mode from command line argument, default to stdio
mcp.run(transport="sse") # Added SSE transport transport_mode = "stdio"
if len(sys.argv) > 1 and sys.argv[1].lower() == "sse":
transport_mode = "sse"
logger.info("🚀 Starting Splunk MCP server in SSE mode")
else:
logger.info("🚀 Starting Splunk MCP server in STDIO mode")
mcp.run(transport=transport_mode)