svrnty-mcp-client/docs/deployment/https-setup.md
Svrnty 97880406dc refactor: rename OpenHarbor.MCP to Svrnty.MCP across all libraries
- Renamed all directories: OpenHarbor.MCP.* → Svrnty.MCP.*
- Updated all namespaces in 179 C# files
- Renamed 20 .csproj files and 3 .sln files
- Updated 193 documentation references
- Updated 33 references in main CODEX codebase
- Updated Codex.sln with new paths
- Build verified: 0 errors

Preparing for extraction to standalone repositories.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-22 21:04:17 -04:00

691 lines
17 KiB
Markdown

# HTTPS/TLS Setup Guide - Svrnty.MCP.Client
**Purpose**: Secure HTTPS/TLS configuration for MCP client connections
**Audience**: Application developers, system integrators
**Last Updated**: 2025-10-19
**Version**: 1.0.0
---
## Table of Contents
1. [Overview](#overview)
2. [Prerequisites](#prerequisites)
3. [Client Configuration](#client-configuration)
4. [Certificate Validation](#certificate-validation)
5. [Authentication](#authentication)
6. [Error Handling](#error-handling)
7. [Best Practices](#best-practices)
8. [Troubleshooting](#troubleshooting)
---
## Overview
Svrnty.MCP.Client supports HTTPS connections to MCP servers with full TLS certificate validation, custom certificate handling, and API key authentication.
**Security Features:**
- Encrypted communication (TLS 1.2+)
- Server certificate validation
- Custom certificate authority support
- Client certificate authentication (mutual TLS)
- API key authentication via headers
**Default Behavior:**
- HTTPS enabled by default when URL starts with `https://`
- Certificate validation enabled (can be customized)
- Connection pooling for performance
- Timeout configuration (default: 30 seconds)
---
## Prerequisites
### Development
- .NET 8.0 SDK
- Access to MCP server with HTTPS enabled
- API key for authentication (if required)
### Production
- Valid server certificates
- Network connectivity to server
- Firewall allows outbound HTTPS (port 443)
---
## Client Configuration
### Basic HTTPS Connection
**Example 1: Simple HTTPS Client**
```csharp
using Svrnty.MCP.Client;
// Connect to HTTPS server
var config = new HttpServerConnectionConfig
{
ServerUrl = "https://mcp.example.com", // HTTPS URL
ApiKey = "your-api-key-here" // Authentication
};
using var connection = new HttpServerConnection(config);
// Use connection
var tools = await connection.ListToolsAsync();
```
**Configuration Options:**
```csharp
public class HttpServerConnectionConfig
{
/// <summary>
/// Server URL (must start with https:// for secure connection)
/// </summary>
public string ServerUrl { get; set; } = "https://localhost:5051";
/// <summary>
/// API key for authentication (sent via X-API-Key header)
/// </summary>
public string? ApiKey { get; set; }
/// <summary>
/// Request timeout (default: 30 seconds)
/// </summary>
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30);
/// <summary>
/// Maximum connections per server (default: 10)
/// </summary>
public int MaxConnectionsPerServer { get; set; } = 10;
/// <summary>
/// Custom certificate validation callback
/// </summary>
public Func<HttpRequestMessage, X509Certificate2?, X509Chain?, SslPolicyErrors, bool>? ServerCertificateCustomValidation { get; set; }
/// <summary>
/// Client certificate for mutual TLS (optional)
/// </summary>
public X509Certificate2? ClientCertificate { get; set; }
}
```
### Example 2: Development with Self-Signed Certificate
For development servers using self-signed certificates:
```csharp
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
var config = new HttpServerConnectionConfig
{
ServerUrl = "https://localhost:5051",
ApiKey = "dev-api-key",
// WARNING: Only use in development!
ServerCertificateCustomValidation = (request, cert, chain, errors) =>
{
// Accept any certificate (INSECURE - development only!)
return true;
}
};
using var connection = new HttpServerConnection(config);
```
**Security Warning**: Never use `ServerCertificateCustomValidation = (_, _, _, _) => true` in production!
### Example 3: Custom Certificate Authority
For servers using internal/corporate CA:
```csharp
var config = new HttpServerConnectionConfig
{
ServerUrl = "https://internal-mcp.corp.local",
ApiKey = "api-key",
ServerCertificateCustomValidation = (request, cert, chain, errors) =>
{
if (errors == SslPolicyErrors.None)
return true;
// Custom CA validation
if (errors.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors))
{
// Load corporate CA certificate
var corpCaCert = new X509Certificate2("corp-ca.crt");
// Build chain with custom CA
var customChain = new X509Chain();
customChain.ChainPolicy.ExtraStore.Add(corpCaCert);
customChain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
bool isValid = customChain.Build(cert);
customChain.Dispose();
return isValid;
}
return false;
}
};
```
### Example 4: Production Configuration
```csharp
var config = new HttpServerConnectionConfig
{
ServerUrl = "https://mcp.example.com",
ApiKey = Environment.GetEnvironmentVariable("MCP_API_KEY"),
Timeout = TimeSpan.FromSeconds(60),
MaxConnectionsPerServer = 20,
// Default certificate validation (recommended for production)
ServerCertificateCustomValidation = null // Use system trust store
};
using var connection = new HttpServerConnection(config);
```
---
## Certificate Validation
### Default Validation
By default, the client uses .NET's built-in certificate validation:
1. **Certificate is trusted** (issued by known CA)
2. **Certificate is valid** (not expired, not yet valid)
3. **Certificate matches hostname** (CN or SAN matches server URL)
### Custom Validation Scenarios
**Scenario 1: Accept Specific Self-Signed Certificate**
```csharp
var expectedThumbprint = "A1B2C3D4E5F6..."; // Certificate thumbprint
ServerCertificateCustomValidation = (request, cert, chain, errors) =>
{
if (cert == null)
return false;
// Verify thumbprint matches expected
return cert.Thumbprint.Equals(expectedThumbprint, StringComparison.OrdinalIgnoreCase);
}
```
**Scenario 2: Log and Validate**
```csharp
ServerCertificateCustomValidation = (request, cert, chain, errors) =>
{
if (errors == SslPolicyErrors.None)
return true;
// Log validation errors
Console.WriteLine($"Certificate validation error: {errors}");
if (cert != null)
{
Console.WriteLine($"Subject: {cert.Subject}");
Console.WriteLine($"Issuer: {cert.Issuer}");
Console.WriteLine($"Valid from: {cert.NotBefore} to {cert.NotAfter}");
}
// Reject in production, accept in development
return Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development";
};
```
**Scenario 3: Certificate Pinning**
For high-security requirements, pin specific certificates:
```csharp
private static readonly HashSet<string> PinnedCertificates = new()
{
"A1B2C3D4E5F6...", // Primary server cert
"B2C3D4E5F6A1..." // Backup server cert
};
ServerCertificateCustomValidation = (request, cert, chain, errors) =>
{
if (cert == null)
return false;
// Only accept pinned certificates
return PinnedCertificates.Contains(cert.Thumbprint);
};
```
---
## Authentication
### API Key Authentication
**Method 1: Configuration Object**
```csharp
var config = new HttpServerConnectionConfig
{
ServerUrl = "https://mcp.example.com",
ApiKey = "your-api-key" // Sent as X-API-Key header
};
```
**Method 2: Environment Variable**
```csharp
var config = new HttpServerConnectionConfig
{
ServerUrl = "https://mcp.example.com",
ApiKey = Environment.GetEnvironmentVariable("MCP_API_KEY")
};
```
**Method 3: Azure Key Vault / Secrets Manager**
```csharp
// Example with Azure Key Vault
var keyVaultClient = new SecretClient(new Uri("https://your-vault.vault.azure.net"), new DefaultAzureCredential());
var apiKeySecret = await keyVaultClient.GetSecretAsync("mcp-api-key");
var config = new HttpServerConnectionConfig
{
ServerUrl = "https://mcp.example.com",
ApiKey = apiKeySecret.Value.Value
};
```
### Mutual TLS (Client Certificates)
For bidirectional authentication:
```csharp
// Load client certificate
var clientCert = new X509Certificate2("client-cert.pfx", "password");
var config = new HttpServerConnectionConfig
{
ServerUrl = "https://mcp.example.com",
ClientCertificate = clientCert, // Present to server during TLS handshake
ApiKey = "api-key" // Additional API key authentication
};
using var connection = new HttpServerConnection(config);
```
**Certificate Requirements:**
- Must include private key (PFX/PKCS#12 format)
- Must be valid (not expired)
- Server must be configured to accept client certificates
---
## Error Handling
### Common HTTPS Errors
**Error 1: Certificate Validation Failed**
```
System.Net.Http.HttpRequestException: The SSL connection could not be established
Inner Exception: The remote certificate is invalid according to the validation procedure.
```
**Solution:**
1. Verify server certificate is valid
2. Check certificate is issued by trusted CA
3. Ensure hostname matches certificate CN/SAN
4. For development, use custom validation (see examples above)
**Error 2: Connection Timed Out**
```
System.Threading.Tasks.TaskCanceledException: The request was canceled due to the configured HttpClient.Timeout
```
**Solution:**
1. Increase timeout value
2. Check network connectivity
3. Verify firewall allows HTTPS traffic
```csharp
var config = new HttpServerConnectionConfig
{
Timeout = TimeSpan.FromSeconds(120) // Increase timeout
};
```
**Error 3: Hostname Mismatch**
```
The remote certificate is invalid: the name on the certificate does not match the hostname
```
**Solution:**
1. Use correct hostname in ServerUrl (match certificate CN/SAN)
2. Update certificate to include correct hostname
3. Use IP address if certificate has IP SAN
**Error 4: Expired Certificate**
```
The remote certificate is invalid: the certificate has expired
```
**Solution:**
1. Renew server certificate
2. For temporary workaround (development only):
```csharp
ServerCertificateCustomValidation = (request, cert, chain, errors) =>
{
// Check if only error is expiration
if (errors == SslPolicyErrors.RemoteCertificateChainErrors)
{
return chain.ChainStatus.All(s => s.Status == X509ChainStatusFlags.NotTimeValid);
}
return errors == SslPolicyErrors.None;
};
```
### Graceful Degradation
Handle HTTPS errors gracefully:
```csharp
try
{
using var connection = new HttpServerConnection(config);
var tools = await connection.ListToolsAsync();
}
catch (HttpRequestException ex) when (ex.InnerException is System.Security.Authentication.AuthenticationException)
{
// TLS/certificate error
Console.WriteLine($"HTTPS connection failed: {ex.Message}");
Console.WriteLine("Check server certificate configuration");
// Optionally: retry with HTTP (if server supports it)
}
catch (TaskCanceledException ex)
{
// Timeout
Console.WriteLine($"Connection timed out after {config.Timeout.TotalSeconds}s");
// Optionally: retry with longer timeout
}
```
---
## Best Practices
### 1. Never Disable Certificate Validation in Production
```csharp
// ❌ WRONG (insecure)
ServerCertificateCustomValidation = (_, _, _, _) => true;
// ✅ CORRECT (secure)
ServerCertificateCustomValidation = null; // Use default validation
```
### 2. Store API Keys Securely
```csharp
// ❌ WRONG (hardcoded)
ApiKey = "sk_live_1234567890abcdef";
// ✅ CORRECT (environment variable)
ApiKey = Environment.GetEnvironmentVariable("MCP_API_KEY");
// ✅ BETTER (secrets manager)
ApiKey = await secretsManager.GetSecretAsync("mcp-api-key");
```
### 3. Use Connection Pooling
```csharp
// ❌ WRONG (creates new connection every time)
foreach (var request in requests)
{
using var connection = new HttpServerConnection(config);
await connection.ExecuteToolAsync("tool", args);
}
// ✅ CORRECT (reuse connection)
using var connection = new HttpServerConnection(config);
foreach (var request in requests)
{
await connection.ExecuteToolAsync("tool", args);
}
```
### 4. Handle Errors Appropriately
```csharp
try
{
var result = await connection.ExecuteToolAsync("tool", args);
}
catch (HttpRequestException ex)
{
// Log error, retry, or fail gracefully
_logger.LogError(ex, "MCP tool execution failed");
throw;
}
```
### 5. Configure Timeouts
```csharp
// Short-lived operations
Timeout = TimeSpan.FromSeconds(10);
// Long-running operations
Timeout = TimeSpan.FromMinutes(5);
// Default (reasonable for most cases)
Timeout = TimeSpan.FromSeconds(30);
```
---
## Troubleshooting
### Issue: "The remote certificate is invalid"
**Diagnosis:**
```bash
# Check server certificate
echo | openssl s_client -connect mcp.example.com:443 -servername mcp.example.com 2>/dev/null | openssl x509 -noout -text
```
**Common Causes:**
1. Self-signed certificate without custom validation
2. Expired certificate
3. Hostname mismatch
4. Untrusted CA
**Solution:**
- Ensure server has valid certificate from trusted CA
- Or implement custom validation for development
### Issue: Connection Hangs/Timeout
**Diagnosis:**
```bash
# Test connectivity
telnet mcp.example.com 443
# Test with curl
curl -v https://mcp.example.com/health
```
**Common Causes:**
1. Firewall blocking port 443
2. Server not responding
3. Network connectivity issue
**Solution:**
- Verify firewall rules
- Check server is running
- Increase timeout if server is slow
### Issue: Client Certificate Not Accepted
**Diagnosis:**
Check server logs for certificate validation errors.
**Common Causes:**
1. Certificate expired
2. Certificate not in PFX format
3. Private key missing
4. Server not configured for mutual TLS
**Solution:**
```csharp
// Verify certificate has private key
var clientCert = new X509Certificate2("client-cert.pfx", "password");
Console.WriteLine($"Has private key: {clientCert.HasPrivateKey}");
// Check expiration
Console.WriteLine($"Valid from: {clientCert.NotBefore}");
Console.WriteLine($"Valid to: {clientCert.NotAfter}");
```
### Issue: Slow HTTPS Connections
**Diagnosis:**
Measure connection time:
```csharp
var stopwatch = Stopwatch.StartNew();
using var connection = new HttpServerConnection(config);
stopwatch.Stop();
Console.WriteLine($"Connection established in {stopwatch.ElapsedMilliseconds}ms");
```
**Common Causes:**
1. DNS resolution slow
2. TLS handshake slow
3. Server overloaded
**Solution:**
- Use connection pooling (reuse connections)
- Increase MaxConnectionsPerServer
- Cache DNS (use IP address directly)
---
## Configuration Examples
### Development Environment
```csharp
var config = new HttpServerConnectionConfig
{
ServerUrl = "https://localhost:5051",
ApiKey = "dev-api-key",
Timeout = TimeSpan.FromSeconds(30),
// Accept self-signed cert (development only)
ServerCertificateCustomValidation = (request, cert, chain, errors) =>
{
if (errors == SslPolicyErrors.None)
return true;
// Log warning in development
Console.WriteLine($"[DEV] Accepting certificate with errors: {errors}");
return true;
}
};
```
### Staging Environment
```csharp
var config = new HttpServerConnectionConfig
{
ServerUrl = "https://staging-mcp.example.com",
ApiKey = Environment.GetEnvironmentVariable("MCP_API_KEY_STAGING"),
Timeout = TimeSpan.FromSeconds(60),
MaxConnectionsPerServer = 10,
// Validate cert but allow specific staging cert
ServerCertificateCustomValidation = (request, cert, chain, errors) =>
{
if (errors == SslPolicyErrors.None)
return true;
// Accept specific staging certificate
var stagingThumbprint = Environment.GetEnvironmentVariable("STAGING_CERT_THUMBPRINT");
return cert?.Thumbprint == stagingThumbprint;
}
};
```
### Production Environment
```csharp
var config = new HttpServerConnectionConfig
{
ServerUrl = Environment.GetEnvironmentVariable("MCP_SERVER_URL"),
ApiKey = await GetApiKeyFromSecretManagerAsync(),
Timeout = TimeSpan.FromSeconds(120),
MaxConnectionsPerServer = 50,
// Use default certificate validation (strict)
ServerCertificateCustomValidation = null
};
```
---
## Security Checklist
Before deploying to production:
- [ ] Server URL uses HTTPS (`https://...`)
- [ ] Certificate validation is enabled (no custom bypass)
- [ ] API key is stored securely (environment variable or secrets manager)
- [ ] Timeout is configured appropriately for use case
- [ ] Error handling is implemented
- [ ] Connection is disposed properly (using statement)
- [ ] Client certificate (if used) is stored securely
- [ ] Certificate expiry monitoring is configured
- [ ] Firewall allows outbound HTTPS (port 443)
- [ ] TLS 1.2+ is enforced (no SSL3, TLS 1.0, TLS 1.1)
---
## References
**Svrnty.MCP Documentation:**
- [Client README](../../README.md)
- [API Reference](../../docs/api/client-api.md)
- [Integration Guide](../../docs/integration-guide.md)
**.NET Documentation:**
- [HttpClient Security](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient)
- [X509 Certificates](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509certificate2)
- [SSL/TLS Best Practices](https://learn.microsoft.com/en-us/dotnet/framework/network-programming/tls)
**Security Resources:**
- [OWASP TLS Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html)
- [Certificate Pinning Guide](https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning)
---
**Document Version**: 1.0.0
**Last Updated**: 2025-10-19
**Maintained By**: Svrnty Development Team
**Related**: [Server HTTPS Setup](../../Svrnty.MCP.Server/docs/deployment/https-setup.md)