Skip to main content
Glama

Web Proxy MCP Server

by mako10k
ssl-manager.js13.6 kB
/** * SSL Certificate Authority Manager * Manages CA creation, certificate generation, and SSL bumping */ import fs from 'fs/promises'; import path from 'path'; import { execSync } from 'child_process'; import os from 'os'; export class SSLManager { constructor(options = {}) { this.homeDir = os.homedir(); this.caBaseDir = path.join(this.homeDir, '.ca'); this.defaultCAName = 'default'; this.currentCA = options.caName || this.defaultCAName; this.caDir = path.join(this.caBaseDir, this.currentCA); this.initialized = false; } /** * Initialize SSL manager and check CA status */ async initialize() { try { await this._ensureDirectories(); const caExists = await this._checkCAExists(); if (!caExists) { console.log(`🔒 CA '${this.currentCA}' not found, will create on first use`); } else { console.log(`🔒 Using existing CA: ${this.currentCA}`); await this._loadCAInfo(); } this.initialized = true; return { caExists, caName: this.currentCA, caDir: this.caDir }; } catch (error) { console.error('SSL Manager initialization failed:', error.message); throw error; } } /** * Create new Certificate Authority * @param {string} caName - CA name (optional, uses current) * @param {Object} options - CA creation options */ async createCA(caName = null, options = {}) { if (caName) { this.currentCA = caName; this.caDir = path.join(this.caBaseDir, this.currentCA); } await this._ensureDirectories(); const caExists = await this._checkCAExists(); if (caExists && !options.overwrite) { throw new Error(`CA '${this.currentCA}' already exists. Use overwrite option to recreate.`); } console.log(`🔧 Creating new Certificate Authority: ${this.currentCA}`); // Generate CA configuration const caConfig = this._generateCAConfig(options); const caConfigPath = path.join(this.caDir, 'ca.conf'); await fs.writeFile(caConfigPath, caConfig); // Generate CA private key const caKeyPath = path.join(this.caDir, 'ca.key'); const keyGenCmd = `openssl genrsa -out "${caKeyPath}" 4096`; this._executeSSLCommand(keyGenCmd); // Generate CA certificate const caCertPath = path.join(this.caDir, 'ca.crt'); const certGenCmd = `openssl req -new -x509 -key "${caKeyPath}" -out "${caCertPath}" -days 3650 -config "${caConfigPath}"`; this._executeSSLCommand(certGenCmd); // Create certificate database await this._initializeCertDB(); // Save CA metadata await this._saveCAMetadata(options); console.log(`✅ Certificate Authority '${this.currentCA}' created successfully`); console.log(`📁 CA Directory: ${this.caDir}`); console.log(`🔑 CA Certificate: ${caCertPath}`); return { caName: this.currentCA, caDir: this.caDir, caCertPath, caKeyPath, installationInstructions: this._getInstallationInstructions(caCertPath) }; } /** * Generate server certificate for domain * @param {string} domain - Domain name for certificate * @param {Array} altNames - Alternative names (SAN) */ async generateServerCertificate(domain, altNames = []) { if (!this.initialized) { await this.initialize(); } const caExists = await this._checkCAExists(); if (!caExists) { throw new Error(`CA '${this.currentCA}' does not exist. Create CA first.`); } console.log(`🔐 Generating certificate for: ${domain}`); const certDir = path.join(this.caDir, 'certs'); await fs.mkdir(certDir, { recursive: true }); const sanitizedDomain = domain.replace(/[^a-zA-Z0-9.-]/g, '_'); const keyPath = path.join(certDir, `${sanitizedDomain}.key`); const csrPath = path.join(certDir, `${sanitizedDomain}.csr`); const certPath = path.join(certDir, `${sanitizedDomain}.crt`); // Generate server private key const keyGenCmd = `openssl genrsa -out "${keyPath}" 2048`; this._executeSSLCommand(keyGenCmd); // Generate certificate config with SAN const certConfig = this._generateServerCertConfig(domain, altNames); const certConfigPath = path.join(certDir, `${sanitizedDomain}.conf`); await fs.writeFile(certConfigPath, certConfig); // Generate CSR const csrCmd = `openssl req -new -key "${keyPath}" -out "${csrPath}" -config "${certConfigPath}"`; this._executeSSLCommand(csrCmd); // Sign certificate with CA const caKeyPath = path.join(this.caDir, 'ca.key'); const caCertPath = path.join(this.caDir, 'ca.crt'); const signCmd = `openssl x509 -req -in "${csrPath}" -CA "${caCertPath}" -CAkey "${caKeyPath}" -CAcreateserial -out "${certPath}" -days 365 -extensions v3_req -extfile "${certConfigPath}"`; this._executeSSLCommand(signCmd); // Clean up CSR await fs.unlink(csrPath); await fs.unlink(certConfigPath); console.log(`✅ Certificate generated for ${domain}`); return { domain, keyPath, certPath, altNames }; } /** * Get all available CAs */ async listCAs() { try { await fs.access(this.caBaseDir); const entries = await fs.readdir(this.caBaseDir, { withFileTypes: true }); const cas = []; for (const entry of entries) { if (entry.isDirectory()) { const caDir = path.join(this.caBaseDir, entry.name); const metadataPath = path.join(caDir, 'metadata.json'); let metadata = { name: entry.name, created: null, description: null }; try { const metadataContent = await fs.readFile(metadataPath, 'utf-8'); metadata = { ...metadata, ...JSON.parse(metadataContent) }; } catch (error) { // Metadata file doesn't exist or is corrupted } const caExists = await fs.access(path.join(caDir, 'ca.crt')).then(() => true).catch(() => false); cas.push({ name: entry.name, path: caDir, exists: caExists, current: entry.name === this.currentCA, ...metadata }); } } return cas; } catch (error) { return []; } } /** * Switch to different CA */ async switchCA(caName) { this.currentCA = caName; this.caDir = path.join(this.caBaseDir, this.currentCA); const caExists = await this._checkCAExists(); if (!caExists) { throw new Error(`CA '${caName}' does not exist`); } await this._loadCAInfo(); return { caName, caDir: this.caDir }; } /** * Get CA certificate for installation */ async getCACertificate() { const caCertPath = path.join(this.caDir, 'ca.crt'); try { const certContent = await fs.readFile(caCertPath, 'utf-8'); return { certPath: caCertPath, certContent, installationInstructions: this._getInstallationInstructions(caCertPath) }; } catch (error) { throw new Error(`CA certificate not found: ${error.message}`); } } /** * Get installation instructions for current platform */ _getInstallationInstructions(caCertPath) { const platform = os.platform(); const caName = this.currentCA; const instructions = { linux: [ `🐧 Linux Installation:`, ``, `1. Copy CA certificate to system store:`, ` sudo cp "${caCertPath}" /usr/local/share/ca-certificates/${caName}.crt`, ` sudo update-ca-certificates`, ``, `2. For browsers (Chrome/Chromium):`, ` chrome://settings/certificates → Authorities → Import`, ` Select: ${caCertPath}`, ``, `3. For Firefox:`, ` about:preferences#privacy → Certificates → View Certificates`, ` → Authorities → Import → Select: ${caCertPath}`, ``, `4. Verify installation:`, ` openssl verify -CAfile "${caCertPath}" <any_generated_cert>` ], darwin: [ `🍎 macOS Installation:`, ``, `1. Add to system keychain:`, ` sudo security add-trusted-cert -d -r trustRoot -k /System/Library/Keychains/SystemRootCertificates.keychain "${caCertPath}"`, ``, `2. Alternative (user keychain):`, ` security add-trusted-cert -d -r trustRoot -k ~/Library/Keychains/login.keychain "${caCertPath}"`, ``, `3. For browsers:`, ` - Chrome: Uses system keychain automatically`, ` - Firefox: Manual import required (same as Linux)`, ``, `4. Verify in Keychain Access app` ], win32: [ `🪟 Windows Installation:`, ``, `1. Import via Certificate Manager:`, ` certmgr.msc → Trusted Root Certification Authorities`, ` → Certificates → Import → Select: ${caCertPath}`, ``, `2. Command line (as Administrator):`, ` certutil -addstore -f "ROOT" "${caCertPath}"`, ``, `3. PowerShell (as Administrator):`, ` Import-Certificate -FilePath "${caCertPath}" -CertStoreLocation Cert:\\LocalMachine\\Root`, ``, `4. Verify installation:`, ` certutil -store root | findstr "${caName}"` ] }; const platformInstructions = instructions[platform] || instructions.linux; return [ `🔒 SSL Certificate Installation Instructions`, ``, `CA Name: ${caName}`, `CA Certificate: ${caCertPath}`, `Platform: ${platform}`, ``, ...platformInstructions, ``, `⚠️ Important Security Notes:`, `- This CA can decrypt ALL HTTPS traffic routed through the proxy`, `- Only install on development/testing systems`, `- Remove CA when proxy testing is complete`, `- Keep CA private key secure and never share it`, ``, `🔄 To remove CA later:`, `- Linux: sudo update-ca-certificates --fresh`, `- macOS: security delete-certificate -c "${caName}" (in Keychain Access)`, `- Windows: certmgr.msc → Remove from Trusted Root CAs` ].join('\n'); } /** * Generate CA configuration * @private */ _generateCAConfig(options) { const subject = options.subject || { C: 'US', ST: 'CA', L: 'San Francisco', O: 'Web Proxy MCP Server', OU: 'Development', CN: `${this.currentCA} Root CA` }; return `[req] distinguished_name = req_distinguished_name x509_extensions = v3_ca prompt = no [req_distinguished_name] C = ${subject.C} ST = ${subject.ST} L = ${subject.L} O = ${subject.O} OU = ${subject.OU} CN = ${subject.CN} [v3_ca] basicConstraints = critical,CA:TRUE keyUsage = critical,digitalSignature,keyCertSign,cRLSign subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always`; } /** * Generate server certificate configuration * @private */ _generateServerCertConfig(domain, altNames) { const sanEntries = [domain, ...altNames].map((name, index) => `DNS.${index + 1} = ${name}` ).join('\n'); return `[req] distinguished_name = req_distinguished_name req_extensions = v3_req prompt = no [req_distinguished_name] C = US ST = CA L = San Francisco O = Web Proxy MCP Server OU = Development CN = ${domain} [v3_req] basicConstraints = CA:FALSE keyUsage = nonRepudiation,digitalSignature,keyEncipherment subjectAltName = @alt_names [alt_names] ${sanEntries}`; } /** * Execute SSL command safely * @private */ _executeSSLCommand(command) { try { execSync(command, { stdio: 'pipe' }); } catch (error) { throw new Error(`SSL command failed: ${error.message}`); } } /** * Check if CA exists * @private */ async _checkCAExists() { try { await fs.access(path.join(this.caDir, 'ca.crt')); await fs.access(path.join(this.caDir, 'ca.key')); return true; } catch (error) { return false; } } /** * Ensure required directories exist * @private */ async _ensureDirectories() { await fs.mkdir(this.caBaseDir, { recursive: true }); await fs.mkdir(this.caDir, { recursive: true }); } /** * Initialize certificate database * @private */ async _initializeCertDB() { const dbDir = path.join(this.caDir, 'db'); await fs.mkdir(dbDir, { recursive: true }); // Create index file await fs.writeFile(path.join(dbDir, 'index.txt'), ''); // Create serial file await fs.writeFile(path.join(dbDir, 'serial'), '01\n'); } /** * Save CA metadata * @private */ async _saveCAMetadata(options) { const metadata = { name: this.currentCA, created: new Date().toISOString(), description: options.description || `Auto-generated CA for Web Proxy MCP Server`, version: '1.0', ...options.metadata }; const metadataPath = path.join(this.caDir, 'metadata.json'); await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2)); } /** * Load CA information * @private */ async _loadCAInfo() { try { const metadataPath = path.join(this.caDir, 'metadata.json'); const metadata = await fs.readFile(metadataPath, 'utf-8'); this.caInfo = JSON.parse(metadata); } catch (error) { this.caInfo = { name: this.currentCA, created: null }; } } /** * Get current CA status */ getCAStatus() { return { initialized: this.initialized, currentCA: this.currentCA, caDir: this.caDir, caInfo: this.caInfo || null }; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mako10k/mcp-web-proxy'

If you have feedback or need assistance with the MCP directory API, please join our Discord server