feat: Enhanced SSL debugging and updated documentation
This commit is contained in:
199
README.md
199
README.md
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
logger.info(f"🔌 Attempting to connect to Splunk at {SPLUNK_SCHEME}://{SPLUNK_HOST}:{SPLUNK_PORT}")
|
||||||
|
logger.info(f"SSL Verification is {'enabled' if VERIFY_SSL else 'disabled'}")
|
||||||
|
|
||||||
|
# Test basic connectivity first
|
||||||
|
try:
|
||||||
|
sock = socket.create_connection((SPLUNK_HOST, SPLUNK_PORT), timeout=10)
|
||||||
|
sock.close()
|
||||||
|
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:
|
try:
|
||||||
service = client.connect(
|
service = client.connect(
|
||||||
host=SPLUNK_HOST,
|
host=SPLUNK_HOST,
|
||||||
port=SPLUNK_PORT,
|
port=SPLUNK_PORT,
|
||||||
username=SPLUNK_USERNAME,
|
username=SPLUNK_USERNAME,
|
||||||
password=SPLUNK_PASSWORD,
|
password=SPLUNK_PASSWORD,
|
||||||
scheme=SPLUNK_SCHEME
|
scheme=SPLUNK_SCHEME,
|
||||||
|
ssl_context=ssl_context
|
||||||
)
|
)
|
||||||
|
logger.info("✅ Successfully established Splunk connection")
|
||||||
return service
|
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:
|
except Exception as e:
|
||||||
logger.error(f"Failed to connect to Splunk: {str(e)}")
|
logger.error(f"❌ Error during Splunk connection: {str(e)}")
|
||||||
|
logger.error(f"Error details: {traceback.format_exc()}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
except Exception as 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:
|
||||||
|
try:
|
||||||
index_info = {
|
index_info = {
|
||||||
"name": index.name,
|
"name": index.name,
|
||||||
"total_event_count": index["totalEventCount"],
|
"total_event_count": index.get("totalEventCount", "0"),
|
||||||
"current_size": index["currentDBSizeMB"],
|
"current_size": index.get("currentDBSizeMB", "0"),
|
||||||
"max_size": index["maxTotalDataSizeMB"],
|
"max_size": index.get("maxTotalDataSizeMB", "0"),
|
||||||
"earliest_time": index["earliestTime"],
|
"earliest_time": index.get("earliestTime", "0"),
|
||||||
"latest_time": index["latestTime"]
|
"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)
|
||||||
Reference in New Issue
Block a user