#109 MCP Logging — Fully operational. PostgreSQL table, Arbiter API, Trinity Console page with filters/stats/expandable details, Trinity Core v2.3.0 POSTs logs after every command. #111 Trinity Core Web MCP — Completed by Chronicler #77. Claude.ai native connector working since Apr 11. #112 Trinity Core Security — spawn() fix done by #77, log rotation via cron, REST+MCP dual endpoints by #78. Chronicler #78 | firefrost-operations-manual
187 lines
4.7 KiB
Markdown
187 lines
4.7 KiB
Markdown
---
|
|
task_number: 112
|
|
title: Trinity Core Security Hardening
|
|
status: Complete
|
|
priority: P1-High
|
|
is_blocker: true
|
|
owner: Michael
|
|
tags:
|
|
- trinity-core
|
|
- security
|
|
- infrastructure
|
|
estimated_hours: 3
|
|
---
|
|
|
|
# Trinity Core Security Hardening
|
|
|
|
Address security concerns identified by Gemini before deploying web MCP upgrade.
|
|
|
|
## Why This is a Blocker
|
|
|
|
Trinity Core provides root SSH access to 7 production servers. Before enabling multi-user access (Holly, Meg) or native Claude.ai integration, we must address these vulnerabilities.
|
|
|
|
---
|
|
|
|
## Issue 1: Command Injection (CRITICAL)
|
|
|
|
**The Problem:**
|
|
Current code:
|
|
```javascript
|
|
const sshCmd = `ssh -o ConnectTimeout=10 ${target.user}@${target.host} "${command.replace(/"/g, '\\"')}"`;
|
|
exec(sshCmd, ...);
|
|
```
|
|
|
|
This only escapes double quotes. Subshells `$(...)`, backticks `` `...` ``, and environment variables `$VAR` are NOT escaped. A prompt injection attack could execute arbitrary commands.
|
|
|
|
**Example Attack:**
|
|
```
|
|
Command: echo $(cat /etc/shadow)
|
|
Result: Dumps password hashes
|
|
```
|
|
|
|
**The Fix:**
|
|
Use `child_process.spawn` with array arguments instead of `exec` with string interpolation:
|
|
|
|
```javascript
|
|
import { spawn } from 'child_process';
|
|
|
|
function executeCommand(server, command) {
|
|
return new Promise((resolve, reject) => {
|
|
const target = SERVERS[server];
|
|
|
|
// spawn prevents shell interpretation
|
|
const ssh = spawn('ssh', [
|
|
'-o', 'ConnectTimeout=10',
|
|
`${target.user}@${target.host}`,
|
|
command // passed as single argument, not interpolated into shell
|
|
]);
|
|
|
|
let stdout = '';
|
|
let stderr = '';
|
|
|
|
ssh.stdout.on('data', (data) => stdout += data);
|
|
ssh.stderr.on('data', (data) => stderr += data);
|
|
|
|
ssh.on('close', (code) => {
|
|
resolve({
|
|
success: code === 0,
|
|
stdout: stdout.trim(),
|
|
stderr: stderr.trim(),
|
|
code
|
|
});
|
|
});
|
|
|
|
ssh.on('error', reject);
|
|
});
|
|
}
|
|
```
|
|
|
|
**Status:** ❌ Not implemented — MUST FIX before web MCP deployment
|
|
|
|
---
|
|
|
|
## Issue 2: AI Concurrency / Race Conditions (MEDIUM)
|
|
|
|
**The Problem:**
|
|
If Chronicler and Catalyst both run commands on the same server simultaneously:
|
|
- Catalyst restarts Docker
|
|
- Chronicler deploys container
|
|
- Chronicler's command fails or corrupts deployment
|
|
|
|
**The Fix:**
|
|
Add server locking to Arbiter:
|
|
|
|
```sql
|
|
CREATE TABLE server_locks (
|
|
server VARCHAR(50) PRIMARY KEY,
|
|
locked_by VARCHAR(50),
|
|
locked_at TIMESTAMP,
|
|
expires_at TIMESTAMP
|
|
);
|
|
```
|
|
|
|
Before executing, check lock. If locked, return:
|
|
*"tx1-dallas is currently locked by The Catalyst. Please try again in a moment."*
|
|
|
|
**Status:** ⏳ Future enhancement — not critical for initial deployment
|
|
|
|
---
|
|
|
|
## Issue 3: SD Card Wear & Log Exhaustion (HIGH)
|
|
|
|
**The Problem:**
|
|
- Pi SD cards have finite write cycles
|
|
- `fs.appendFileSync` on every command wears the card
|
|
- No log rotation = disk fills up
|
|
|
|
**The Fix (Implemented):**
|
|
Added cron job to rotate logs daily:
|
|
```
|
|
0 0 * * * truncate -s 0 /home/claude_executor/mcp-server/command.log && echo "[$(date -Iseconds)] Log rotated" >> /home/claude_executor/mcp-server/command.log
|
|
```
|
|
|
|
**Better Long-term Fix:**
|
|
Send logs to Arbiter's PostgreSQL instead of local disk (Task #109 already covers this).
|
|
|
|
**Status:** ✅ Mitigated with cron rotation
|
|
|
|
---
|
|
|
|
## Issue 4: Cloudflare Access Layer (MEDIUM)
|
|
|
|
**The Problem:**
|
|
`mcp.firefrostgaming.com` is public. Malicious requests hit the Pi even if rejected by auth.
|
|
|
|
**The Fix:**
|
|
Enable Cloudflare Access (Zero Trust) for the subdomain:
|
|
1. Only allow requests from Anthropic IP ranges
|
|
2. Or require Cloudflare Service Token header
|
|
3. Drops malicious traffic at edge before reaching Pi
|
|
|
|
**Status:** ⏳ Future enhancement — token auth is sufficient for now
|
|
|
|
---
|
|
|
|
## Issue 5: System Prompt Alignment (LOW)
|
|
|
|
**The Problem:**
|
|
AI partners won't know how to use Trinity Core unless told.
|
|
|
|
**The Fix:**
|
|
Update system prompts / project instructions for:
|
|
- Chronicler: Already has Trinity Core context in project instructions
|
|
- Catalyst: Add Trinity Core section to Catalyst project
|
|
- The Orb: Add Trinity Core section to Orb project
|
|
|
|
Include:
|
|
- Available servers
|
|
- How to run commands
|
|
- What requires approval
|
|
- What to do on timeout
|
|
|
|
**Status:** ⏳ Document when Catalyst/Orb projects are created
|
|
|
|
---
|
|
|
|
## Implementation Checklist
|
|
|
|
**Before Web MCP Deployment (Task #111):**
|
|
- [ ] Fix command injection with `spawn` instead of `exec`
|
|
- [ ] Verify log rotation is working
|
|
|
|
**After Initial Deployment:**
|
|
- [ ] Consider Cloudflare Access
|
|
- [ ] Consider server locking for concurrency
|
|
- [ ] Update Catalyst/Orb system prompts when created
|
|
|
|
---
|
|
|
|
## Related Tasks
|
|
|
|
- Task #109: MCP Logging in Trinity Console (moves logs off Pi)
|
|
- Task #111: Trinity Core Web MCP Connector (main implementation)
|
|
|
|
---
|
|
|
|
**Fire + Frost + Foundation = Where Love Builds Legacy** 💙🔥❄️
|