server_lock
Harden servers to production standards by applying 19 security steps including SSH key-only authentication, firewall configuration, system hardening, and integrity monitoring. Requires explicit confirmation for production deployment.
Instructions
Harden a server to production standard. Applies 19 hardening steps in a single SSH session: SSH key-only auth, fail2ban, UFW firewall, SSH cipher blacklist, sysctl hardening, unattended-upgrades, login banners, account locking, cloud metadata block, DNS security, APT validation, resource limits, service disabling, backup permissions, password quality policy, Docker daemon hardening (no-new-privileges, log rotation, live-restore, icc), auditd, log retention, and AIDE integrity. Requires production=true to confirm intent (safety gate). Pass dryRun=true to preview changes without applying. Platform-aware: preserves Coolify port 8000 or Dokploy port 3000 in UFW rules. Shows audit score before and after hardening. Requires SSH access to target server. For fine-grained SSH hardening, firewall port rules, or domain management, use server_secure instead.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| server | No | Server name or IP. Auto-selected if only one server exists. | |
| production | No | Set to true to confirm hardening intent. Required to apply 19 hardening steps (safety gate). Omit or pass false to preview with dryRun=true. | |
| dryRun | No | Preview changes without applying. Returns what would be done. Bypasses the production safety gate. | |
| force | No | Force lock even if server already appears hardened. |
Implementation Reference
- src/mcp/tools/serverLock.ts:22-91 (handler)The handleServerLock function, which executes the server hardening process by applying 19 security steps via applyLock.
export async function handleServerLock(params: { server?: string; production?: boolean; dryRun?: boolean; force?: boolean; }, mcpServer?: McpServer): Promise<McpResponse> { try { const servers = getServers(); if (servers.length === 0) { return mcpError("No servers found", undefined, [ { command: "kastell add", reason: "Add a server first" }, ]); } const server = resolveServerForMcp(params, servers); if (!server) { if (params.server) { return mcpError( `Server not found: ${params.server}`, `Available servers: ${servers.map((s) => s.name).join(", ")}`, ); } return mcpError( "Multiple servers found. Specify which server to use.", `Available: ${servers.map((s) => s.name).join(", ")}`, ); } const production = params.production ?? false; const dryRun = params.dryRun ?? false; const force = params.force ?? false; // Safety gate: require explicit production=true unless doing a dry run if (!production && !dryRun) { return mcpError( "Pass production=true to confirm hardening intent. This applies 19 hardening steps in 4 groups: SSH & Auth (SSH config, fail2ban, login banners, account locking, SSH cipher blacklist), Firewall & Network (UFW, cloud metadata block, DNS security), System (sysctl, unattended-upgrades, APT validation, resource limits, service disabling, backup permissions, password quality, Docker hardening), Monitoring (auditd, log retention, AIDE integrity).", "Use dryRun=true to preview changes without applying.", ); } // Resolve platform from server record (same pattern as serverAudit.ts line 44) const platformStr = server.platform ?? server.mode; const platform: Platform | undefined = platformStr === "coolify" || platformStr === "dokploy" ? platformStr : undefined; await mcpLog(mcpServer, `Starting 19-step hardening on ${server.name}`); const result = await applyLock(server.ip, server.name, platform, { production, dryRun, force, }); if (!result.success) { return mcpError(result.error ?? "Lock hardening failed", result.hint); } await mcpLog(mcpServer, "Hardening complete"); return mcpSuccess({ success: result.success, steps: result.steps, ...(result.stepErrors && { stepErrors: result.stepErrors }), scoreBefore: result.scoreBefore, scoreAfter: result.scoreAfter, }); } catch (error: unknown) { return mcpError(getErrorMessage(error)); } } - src/mcp/tools/serverLock.ts:15-20 (schema)The schema definition for the server_lock tool inputs, including production, dryRun, and force flags.
export const serverLockSchema = { server: z.string().optional().describe("Server name or IP. Auto-selected if only one server exists."), production: z.boolean().default(false).describe("Set to true to confirm hardening intent. Required to apply 19 hardening steps (safety gate). Omit or pass false to preview with dryRun=true."), dryRun: z.boolean().default(false).describe("Preview changes without applying. Returns what would be done. Bypasses the production safety gate."), force: z.boolean().default(false).describe("Force lock even if server already appears hardened."), }; - src/mcp/server.ts:221-234 (registration)Registration of the server_lock tool within the MCP server setup.
server.registerTool("server_lock", { description: "Harden a server to production standard. Applies 19 hardening steps in a single SSH session: SSH key-only auth, fail2ban, UFW firewall, SSH cipher blacklist, sysctl hardening, unattended-upgrades, login banners, account locking, cloud metadata block, DNS security, APT validation, resource limits, service disabling, backup permissions, password quality policy, Docker daemon hardening (no-new-privileges, log rotation, live-restore, icc), auditd, log retention, and AIDE integrity. Requires production=true to confirm intent (safety gate). Pass dryRun=true to preview changes without applying. Platform-aware: preserves Coolify port 8000 or Dokploy port 3000 in UFW rules. Shows audit score before and after hardening. Requires SSH access to target server. For fine-grained SSH hardening, firewall port rules, or domain management, use server_secure instead.", inputSchema: serverLockSchema, annotations: { title: "Server Lock", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, }, async (params) => { return handleServerLock(params, server); });