ubuntu_ssl_certificate
Manage Let's Encrypt SSL certificates on Ubuntu servers via SSH. Issue, renew, check status, or list certificates for specified domains with optional webroot path and sudo access.
Instructions
Manage SSL certificates using Let's Encrypt on Ubuntu
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | Action to perform (issue, renew, status, list) | |
| connectionId | Yes | ID of an active SSH connection | |
| domain | No | Domain name for the certificate (required for issue and renew) | |
| No | Email address for Let's Encrypt notifications (required for issue) | ||
| sudo | No | Whether to run the command with sudo (default: true) | |
| webroot | No | Web root path for domain verification (default: /var/www/html) |
Implementation Reference
- src/ubuntu-website-tools.ts:185-253 (handler)The main handler function implementing the ubuntu_ssl_certificate tool logic, handling actions like issue, renew, status, list using certbot over SSH.async ubuntu_ssl_certificate(params) { const { connectionId, action, domain, email, webroot = '/var/www/html', sudo = true } = params; try { const conn = getConnection(connectionMap, connectionId); const sudoPrefix = sudo ? 'sudo ' : ''; // Validate action const validActions = ['issue', 'renew', 'status', 'list']; if (!validActions.includes(action)) { throw new Error(`Invalid action: ${action}. Valid actions are: ${validActions.join(', ')}`); } // Check for required parameters if ((action === 'issue' || action === 'renew') && !domain) { throw new Error(`Domain name is required for ${action} action`); } if (action === 'issue' && !email) { throw new Error('Email address is required for issue action'); } // Ensure certbot is installed const checkCertbot = await executeSSHCommand(conn, 'which certbot || echo "not-found"'); if (checkCertbot.stdout === 'not-found') { const installCertbot = await executeSSHCommand( conn, `${sudoPrefix}apt-get update && ${sudoPrefix}apt-get install -y certbot python3-certbot-nginx` ); if (installCertbot.code !== 0) { throw new Error(`Failed to install certbot: ${installCertbot.stderr}`); } } let command = ''; switch (action) { case 'issue': command = `${sudoPrefix}certbot certonly --webroot -w ${webroot} -d ${domain} --email ${email} --agree-tos --non-interactive`; break; case 'renew': command = domain ? `${sudoPrefix}certbot renew --cert-name ${domain} --force-renewal` : `${sudoPrefix}certbot renew`; break; case 'status': command = domain ? `${sudoPrefix}certbot certificates -d ${domain}` : `${sudoPrefix}certbot certificates`; break; case 'list': command = `${sudoPrefix}certbot certificates`; break; } const result = await executeSSHCommand(conn, command); return { content: [{ type: 'text', text: `SSL certificate ${action} result:\n\n${result.stdout || result.stderr}` }] }; } catch (error: any) { return { content: [{ type: 'text', text: `SSL certificate error: ${error.message}` }], isError: true }; } },
- src/ubuntu-website-tools.ts:567-599 (schema)The input schema and description for the ubuntu_ssl_certificate tool.ubuntu_ssl_certificate: { description: 'Manage SSL certificates using Let\'s Encrypt on Ubuntu', inputSchema: { type: 'object', properties: { connectionId: { type: 'string', description: 'ID of an active SSH connection' }, action: { type: 'string', description: 'Action to perform (issue, renew, status, list)' }, domain: { type: 'string', description: 'Domain name for the certificate (required for issue and renew)' }, email: { type: 'string', description: 'Email address for Let\'s Encrypt notifications (required for issue)' }, webroot: { type: 'string', description: 'Web root path for domain verification (default: /var/www/html)' }, sudo: { type: 'boolean', description: 'Whether to run the command with sudo (default: true)' } }, required: ['connectionId', 'action'] } },
- src/index.ts:293-296 (registration)Registration of ubuntu tools (including ubuntu_ssl_certificate) in the main CallToolRequestSchema handler by dispatching to ubuntuToolHandlers.// Handle Ubuntu tools directly if (toolName.startsWith('ubuntu_') && ubuntuToolHandlers[toolName]) { return ubuntuToolHandlers[toolName](request.params.arguments); }
- src/ubuntu-website-tools.ts:17-58 (helper)Utility helper function executeSSHCommand used by the handler to run SSH commands.// Utility function to execute commands with error handling async function executeSSHCommand(conn: Client, command: string, timeout = 60000): Promise<{ code: number; signal: string; stdout: string; stderr: string; }> { return new Promise((resolve, reject) => { // Set up timeout const timeoutId = setTimeout(() => { reject(new Error(`Command execution timed out after ${timeout}ms`)); }, timeout); conn.exec(command, {}, (err: Error | undefined, stream: any) => { if (err) { clearTimeout(timeoutId); return reject(new Error(`Failed to execute command: ${err.message}`)); } let stdout = ''; let stderr = ''; stream.on('close', (code: number, signal: string) => { clearTimeout(timeoutId); resolve({ code, signal, stdout: stdout.trim(), stderr: stderr.trim() }); }); stream.on('data', (data: Buffer) => { stdout += data.toString(); }); stream.stderr.on('data', (data: Buffer) => { stderr += data.toString(); }); }); }); }
- src/ubuntu-website-tools.ts:60-66 (helper)Helper function getConnection to retrieve active SSH connection.// Helper function to check if a connection exists function getConnection(connections: Map<string, { conn: Client; config: any }>, connectionId: string) { if (!connections.has(connectionId)) { throw new Error(`No active SSH connection with ID: ${connectionId}`); } return connections.get(connectionId)!.conn; }