svrnty-mcp-server/docs/deployment/https-setup.md
Svrnty 516e1479c6 docs: comprehensive AI coding assistant research and MCP-first implementation plan
Research conducted on modern AI coding assistants (Cursor, GitHub Copilot, Cline,
Aider, Windsurf, Replit Agent) to understand architecture patterns, context management,
code editing workflows, and tool use protocols.

Key Decision: Pivoted from building full CLI (40-50h) to validation-driven MCP-first
approach (10-15h). Build 5 core CODEX MCP tools that work with ANY coding assistant,
validate adoption over 2-4 weeks, then decide on full CLI if demand proven.

Files:
- research/ai-systems/modern-coding-assistants-architecture.md (comprehensive research)
- research/ai-systems/codex-coding-assistant-implementation-plan.md (original CLI plan, preserved)
- research/ai-systems/codex-mcp-tools-implementation-plan.md (approved MCP-first plan)
- ideas/registry.json (updated with approved MCP tools proposal)

Architech Validation: APPROVED with pivot to MCP-first approach
Human Decision: Approved (pragmatic validation-driven development)

Next: Begin Phase 1 implementation (10-15 hours, 5 core MCP tools)

🤖 Generated with CODEX Research System

Co-Authored-By: The Archivist <archivist@codex.svrnty.io>
Co-Authored-By: The Architech <architech@codex.svrnty.io>
Co-Authored-By: Mathias Beaulieu-Duncan <mat@svrnty.io>
2025-10-22 21:00:34 -04:00

677 lines
16 KiB
Markdown

# HTTPS/TLS Setup Guide - OpenHarbor.MCP.Server
**Purpose**: Production-grade HTTPS/TLS configuration for secure MCP server deployment
**Audience**: DevOps engineers, system administrators
**Last Updated**: 2025-10-19
**Version**: 1.0.0
---
## Table of Contents
1. [Overview](#overview)
2. [Prerequisites](#prerequisites)
3. [Development Setup](#development-setup)
4. [Production Setup](#production-setup)
5. [Certificate Management](#certificate-management)
6. [Security Headers (HSTS)](#security-headers-hsts)
7. [Testing HTTPS](#testing-https)
8. [Troubleshooting](#troubleshooting)
---
## Overview
OpenHarbor.MCP.Server supports HTTPS/TLS through ASP.NET Core's Kestrel web server. This guide covers configuration for both development and production environments.
**Security Benefits:**
- Encrypted communication (prevents eavesdropping)
- Client/server authentication
- Data integrity verification
- Compliance with security best practices
**Default Behavior:**
- HTTP only (for development ease)
- HTTPS must be explicitly configured
- Uses Kestrel's built-in TLS support
---
## Prerequisites
### Development
- .NET 8.0 SDK
- OpenSSL or dotnet dev-certs tool
- PowerShell (Windows) or bash (Linux/macOS)
### Production
- Valid TLS certificate (from CA or Let's Encrypt)
- Private key file
- Certificate chain (intermediate + root CA certs)
- Reverse proxy (optional): Nginx, Traefik, or Kestrel standalone
---
## Development Setup
### Option 1: ASP.NET Core Development Certificate (Recommended)
**Step 1: Generate Development Certificate**
**Windows / macOS / Linux:**
```bash
# Generate and trust dev certificate
dotnet dev-certs https --trust
# Verify installation
dotnet dev-certs https --check
```
**Step 2: Configure appsettings.Development.json**
Create or update `appsettings.Development.json` in the Server project:
```json
{
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://localhost:5050"
},
"Https": {
"Url": "https://localhost:5051",
"Certificate": {
"Subject": "CN=localhost",
"Store": "My",
"Location": "CurrentUser",
"AllowInvalid": true
}
}
}
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
```
**Step 3: Run Server with HTTPS**
```bash
cd OpenHarbor.MCP.Server/samples/CodexMcpServer
dotnet run --environment Development
# Server will listen on:
# - HTTP: http://localhost:5050
# - HTTPS: https://localhost:5051
```
**Step 4: Test HTTPS Endpoint**
```bash
# Test health endpoint
curl -k https://localhost:5051/health
# Test MCP invoke endpoint
curl -k -X POST https://localhost:5051/mcp/invoke \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"jsonrpc": "2.0",
"method": "tools/list",
"id": "1"
}'
```
> **Note**: `-k` flag skips certificate validation (dev only!)
### Option 2: Custom Development Certificate with OpenSSL
**Step 1: Generate Self-Signed Certificate**
```bash
# Create directory for certs
mkdir -p /home/svrnty/codex/OpenHarbor.MCP.Server/certs
# Generate private key
openssl genrsa -out certs/localhost.key 2048
# Generate certificate signing request (CSR)
openssl req -new -key certs/localhost.key -out certs/localhost.csr \
-subj "/C=CA/ST=Quebec/L=Montreal/O=Svrnty/CN=localhost"
# Generate self-signed certificate (valid for 365 days)
openssl x509 -req -days 365 -in certs/localhost.csr \
-signkey certs/localhost.key -out certs/localhost.crt
# Convert to PFX format (required by Kestrel)
openssl pkcs12 -export -out certs/localhost.pfx \
-inkey certs/localhost.key -in certs/localhost.crt \
-passout pass:YourSecurePassword
```
**Step 2: Configure appsettings.Development.json**
```json
{
"Kestrel": {
"Endpoints": {
"Https": {
"Url": "https://localhost:5051",
"Certificate": {
"Path": "certs/localhost.pfx",
"Password": "YourSecurePassword"
}
}
}
}
}
```
**Security**: Add `certs/` directory to `.gitignore` to avoid committing private keys.
---
## Production Setup
### Option 1: Certificate from File (Recommended for Kubernetes/Docker)
**Step 1: Obtain Production Certificate**
Use one of:
- **Let's Encrypt** (free, automated renewal)
- **Commercial CA** (DigiCert, Sectigo, etc.)
- **Internal PKI** (corporate certificate authority)
**Step 2: Prepare Certificate Files**
Ensure you have:
- `server.crt` - Server certificate
- `server.key` - Private key
- `ca-bundle.crt` - Intermediate + root CA certificates (optional)
**Step 3: Convert to PFX Format**
```bash
# Combine cert + key + chain into PFX
openssl pkcs12 -export -out server.pfx \
-inkey server.key \
-in server.crt \
-certfile ca-bundle.crt \
-passout pass:ProductionSecurePassword
```
**Step 4: Deploy Certificate**
**Docker/Kubernetes:**
```bash
# Create Kubernetes secret
kubectl create secret generic mcp-server-tls \
--from-file=server.pfx=server.pfx \
--from-literal=password=ProductionSecurePassword
# Mount in deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-server
spec:
template:
spec:
containers:
- name: mcp-server
volumeMounts:
- name: tls-cert
mountPath: /app/certs
readOnly: true
env:
- name: CERTIFICATE_PASSWORD
valueFrom:
secretKeyRef:
name: mcp-server-tls
key: password
volumes:
- name: tls-cert
secret:
secretName: mcp-server-tls
```
**Step 5: Configure appsettings.Production.json**
```json
{
"Kestrel": {
"Endpoints": {
"Https": {
"Url": "https://*:5051",
"Certificate": {
"Path": "/app/certs/server.pfx",
"Password": "" // Read from environment variable
},
"Protocols": "Http1AndHttp2"
}
}
},
"AllowedHosts": "*"
}
```
**Step 6: Set Environment Variable for Password**
```bash
export KESTREL__CERTIFICATES__DEFAULT__PASSWORD="ProductionSecurePassword"
```
Or in `docker-compose.yml`:
```yaml
services:
mcp-server:
environment:
- KESTREL__CERTIFICATES__DEFAULT__PASSWORD=${CERTIFICATE_PASSWORD}
volumes:
- ./certs/server.pfx:/app/certs/server.pfx:ro
```
### Option 2: Certificate from Azure Key Vault
**Step 1: Store Certificate in Key Vault**
```bash
az keyvault certificate import \
--vault-name your-keyvault \
--name mcp-server-cert \
--file server.pfx \
--password ProductionSecurePassword
```
**Step 2: Configure appsettings.Production.json**
```json
{
"Kestrel": {
"Endpoints": {
"Https": {
"Url": "https://*:5051",
"Certificate": {
"KeyVaultUri": "https://your-keyvault.vault.azure.net/",
"CertificateName": "mcp-server-cert"
}
}
}
}
}
```
**Step 3: Grant Managed Identity Access**
```bash
az keyvault set-policy \
--name your-keyvault \
--object-id <managed-identity-object-id> \
--certificate-permissions get
```
### Option 3: Reverse Proxy (Nginx/Traefik) with TLS Termination
**Best for**: Load balancing, multiple servers, centralized certificate management
**Architecture**:
```
Client → HTTPS → Nginx/Traefik (TLS termination) → HTTP → MCP Server (5050)
```
**Nginx Configuration** (`/etc/nginx/sites-available/mcp-server`):
```nginx
server {
listen 443 ssl http2;
server_name mcp.example.com;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
# Modern TLS configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers off;
# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
proxy_pass http://localhost:5050;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name mcp.example.com;
return 301 https://$server_name$request_uri;
}
```
**Traefik Configuration** (`docker-compose.yml`):
```yaml
services:
traefik:
image: traefik:v2.10
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=admin@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./letsencrypt:/letsencrypt"
mcp-server:
image: openharbor/mcp-server:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.mcp.rule=Host(`mcp.example.com`)"
- "traefik.http.routers.mcp.entrypoints=websecure"
- "traefik.http.routers.mcp.tls.certresolver=letsencrypt"
- "traefik.http.services.mcp.loadbalancer.server.port=5050"
```
---
## Certificate Management
### Automatic Renewal with Let's Encrypt
**Using Certbot:**
```bash
# Install certbot
sudo apt-get install certbot
# Obtain certificate
sudo certbot certonly --standalone -d mcp.example.com
# Certificates location: /etc/letsencrypt/live/mcp.example.com/
# - fullchain.pem (certificate + chain)
# - privkey.pem (private key)
# Convert to PFX for Kestrel
sudo openssl pkcs12 -export \
-out /etc/letsencrypt/live/mcp.example.com/server.pfx \
-inkey /etc/letsencrypt/live/mcp.example.com/privkey.pem \
-in /etc/letsencrypt/live/mcp.example.com/fullchain.pem \
-passout pass:YourSecurePassword
# Auto-renewal (certbot creates cron job automatically)
sudo certbot renew --dry-run
```
### Certificate Rotation
**Step 1: Update Certificate File**
Replace `server.pfx` with new certificate.
**Step 2: Reload Without Downtime**
```bash
# Docker
docker exec mcp-server kill -HUP 1
# Kubernetes
kubectl rollout restart deployment/mcp-server
# Systemd
sudo systemctl reload mcp-server
```
**Step 3: Verify New Certificate**
```bash
echo | openssl s_client -connect mcp.example.com:443 -servername mcp.example.com 2>/dev/null | openssl x509 -noout -dates
```
---
## Security Headers (HSTS)
**HTTP Strict Transport Security (HSTS)** forces browsers to use HTTPS.
**Method 1: Kestrel Middleware** (Recommended)
Update `Program.cs` in `CodexMcpServer`:
```csharp
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Add HSTS middleware (production only)
if (!app.Environment.IsDevelopment())
{
app.UseHsts();
}
// Redirect HTTP to HTTPS
app.UseHttpsRedirection();
// ... rest of configuration
app.Run();
```
**Method 2: Custom Middleware**
```csharp
app.Use(async (context, next) =>
{
if (!context.Request.IsHttps)
{
var httpsUrl = $"https://{context.Request.Host}{context.Request.Path}{context.Request.QueryString}";
context.Response.Redirect(httpsUrl, permanent: true);
return;
}
// Add HSTS header
context.Response.Headers.Add("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
await next();
});
```
**Method 3: Reverse Proxy (Nginx)**
Already shown in [Option 3](#option-3-reverse-proxy-nginxtraefik-with-tls-termination) above.
---
## Testing HTTPS
### 1. Health Check
```bash
curl https://mcp.example.com/health
```
Expected output:
```json
{
"status": "Healthy",
"service": "MCP Server",
"timestamp": "2025-10-19T12:00:00Z"
}
```
### 2. MCP Invoke Endpoint
```bash
curl -X POST https://mcp.example.com/mcp/invoke \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"jsonrpc": "2.0",
"method": "tools/list",
"id": "test-1"
}'
```
### 3. TLS Configuration Check
```bash
# Check TLS version and cipher suite
nmap --script ssl-enum-ciphers -p 443 mcp.example.com
# Check certificate validity
echo | openssl s_client -connect mcp.example.com:443 -servername mcp.example.com 2>/dev/null | openssl x509 -noout -text
```
### 4. HSTS Validation
```bash
curl -I https://mcp.example.com/health | grep -i strict-transport-security
```
Expected output:
```
Strict-Transport-Security: max-age=31536000; includeSubDomains
```
### 5. SSL Labs Test
For public-facing servers, use [SSL Labs](https://www.ssllabs.com/ssltest/) to get an A+ rating.
---
## Troubleshooting
### Issue: "Unable to configure HTTPS endpoint"
**Symptom:**
```
Unhandled exception. System.InvalidOperationException: Unable to configure HTTPS endpoint.
```
**Solution:**
1. Verify certificate file exists at specified path
2. Check file permissions (readable by server process)
3. Verify password is correct
4. Check certificate format (must be PFX for Kestrel)
```bash
# Verify PFX file
openssl pkcs12 -info -in server.pfx -noout
```
### Issue: "The certificate chain was issued by an authority that is not trusted"
**Symptom:**
Clients reject the certificate as untrusted.
**Solution:**
1. Ensure CA bundle is included in PFX
2. Add intermediate certificates to certificate store
3. For development, use `dotnet dev-certs https --trust`
### Issue: "Connection reset" or "SSL handshake failed"
**Symptom:**
Client cannot establish TLS connection.
**Solution:**
1. Check firewall allows port 443
2. Verify TLS protocols match (client vs. server)
3. Check cipher suite compatibility
```bash
# Test with specific TLS version
openssl s_client -connect mcp.example.com:443 -tls1_2
```
### Issue: Certificate Expired
**Symptom:**
```
The remote certificate is invalid according to the validation procedure.
```
**Solution:**
1. Check certificate expiry:
```bash
echo | openssl s_client -connect mcp.example.com:443 2>/dev/null | openssl x509 -noout -enddate
```
2. Renew certificate (see [Certificate Management](#certificate-management))
3. Deploy new certificate
### Issue: HSTS Not Working
**Symptom:**
Browser still allows HTTP connections.
**Solution:**
1. Verify HSTS header is sent:
```bash
curl -I https://mcp.example.com/health | grep Strict-Transport-Security
```
2. Clear browser HSTS cache (Chrome: `chrome://net-internals/#hsts`)
3. Ensure middleware is configured correctly
---
## Production Checklist
Before deploying to production, verify:
- [ ] Valid TLS certificate from trusted CA
- [ ] Certificate includes full chain (intermediate + root)
- [ ] Private key is stored securely (Kubernetes secret, Azure Key Vault, etc.)
- [ ] Certificate password is stored in environment variable (not in config)
- [ ] Certificate expiry monitoring is configured
- [ ] Automatic renewal is set up (Let's Encrypt, certbot)
- [ ] HSTS header is enabled
- [ ] HTTP → HTTPS redirect is configured
- [ ] TLS 1.2+ is enforced (no SSLv3, TLS 1.0, TLS 1.1)
- [ ] Strong cipher suites are configured
- [ ] Certificate Common Name (CN) matches domain
- [ ] Firewall allows port 443
- [ ] Health check endpoint is accessible via HTTPS
- [ ] Load balancer (if used) is configured for TLS passthrough or termination
- [ ] Monitoring/alerting is set up for certificate expiry
---
## References
**ASP.NET Core Documentation:**
- [Configure HTTPS in Kestrel](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/endpoints)
- [Enforce HTTPS](https://learn.microsoft.com/en-us/aspnet/core/security/enforcing-ssl)
- [HSTS Middleware](https://learn.microsoft.com/en-us/aspnet/core/security/enforcing-ssl#http-strict-transport-security-protocol-hsts)
**Security Best Practices:**
- [Mozilla SSL Configuration Generator](https://ssl-config.mozilla.org/)
- [OWASP TLS Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html)
- [SSL Labs Best Practices](https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices)
**Certificate Authorities:**
- [Let's Encrypt](https://letsencrypt.org/) - Free automated certificates
- [DigiCert](https://www.digicert.com/) - Commercial CA
- [Sectigo](https://sectigo.com/) - Commercial CA
---
**Document Version**: 1.0.0
**Last Updated**: 2025-10-19
**Maintained By**: Svrnty Development Team
**Related**: [Deployment Guide](deployment-guide.md), [Security Best Practices](../security/security-guide.md)