ubuntu-controller
Hardened Model Context Protocol server · safe, controlled access to Ubuntu system operations
Give your AI assistant safe, audited, policy-controlled access to an Ubuntu machine — through the power of the standard Model Context Protocol.
┌───────────────────────┐ ┌───────────────────────────┐ ┌─────────────────────┐
│ AI Assistant (Claude │ ───▶ │ ubuntu-mcp-server │ ───▶ │ Ubuntu System │
│ Desktop, custom │ MCP │ stdio/stdin-stream │ safe │ files · commands · │
│ clients) │ │ SecurityChecker │Audit │ │ packages · system │
└───────────────────────┘ └───────────────────────────┘ └─────────────────────┘Never expose a raw shell. The server validates every path, command, and file against a configurable policy before it touches your system — and logs everything it does.
📑 Table of Contents
✨ Key Features
Area | What you get |
🛡️ Security-first | Symlink-aware path resolution, |
🧱 Defense in depth | Multiple independent validation layers — a single bypass never grants system access |
📜 Full audit trail | Every command, file access, and violation logged with user attribution |
📦 Safe package ops | APT search & list only — no accidental installs unless you widen the policy |
🧪 Proven | Built-in security test suite ( |
🪶 Zero extra deps | Core security is pure-Python; only |
🚀 Quick Start
# 1. Clone & enter
git clone https://github.com/Serp3n7/secure-ubuntu-mcp.git && cd secure-ubuntu-mcp
# 2. Interactive setup (venv + deps + tests + Claude config) — recommended
python3 setup.py
# --- or, manually ---
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# 3. Verify
python main.py --test
python main.py --security-testRun it:
python main.py --policy secure # 🔐 production default
python main.py --policy dev # 🧪 more permissive, for development⚙️ CLI Reference
usage: main.py [options]
--policy secure|dev Security policy to use [default: secure]
--test Run functionality tests
--security-test Run the comprehensive security validation suite
--log-level LEVEL Logging level: INFO, DEBUG, WARNING, ERROR [default: INFO]🤖 Claude Desktop Integration
1. Install Claude Desktop (Linux)
Officially, Claude Desktop targets macOS/Windows — the community-published Debian package by @aaddrick fills the gap:
wget https://github.com/aaddrick/claude-desktop-debian/releases/latest/download/claude-desktop_latest_amd64.deb
sudo dpkg -i claude-desktop_latest_amd64.deb
sudo apt-get install -f2. Register the server
Edit ~/.config/Claude/claude_desktop_config.json — absolute paths required (the ~ shorthand is not expanded):
{
"mcpServers": {
"secure-ubuntu": {
"command": "/path/to/ubuntu_mcp_server/.venv/bin/python3",
"args": ["/path/to/ubuntu_mcp_server/main.py", "--policy", "secure"],
"env": { "MCP_LOG_LEVEL": "INFO" }
}
}
}💡
setup.pymerges this block for you automatically. Alternatively pointcommandat the bundledrun_mcp_server.shlauncher, which handles venv activation.
3. Verify
Restart Claude Desktop → confirm "secure-ubuntu" appears as a connected server → try:
"Check my system status and disk space"
Other MCP clients
The server speaks standard MCP over stdio — any compatible client works. See test_client.py for a reference client:
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def example():
params = StdioServerParameters(command="python3", args=["main.py"])
async with stdio_client(params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
# ... call tools ...
asyncio.run(example())🛡️ Security Policies
Two ships-with-the-box policies plus a custom-policy API. Start with secure, widen deliberately.
🔐 Secure Policy — default, production
Control | Value |
Allowed paths |
|
Forbidden paths |
|
Command mode | Whitelist (deny-by-default) |
Allowed commands |
|
Forbidden commands |
|
File size / output | 1 MB / 256 KB |
Timeout / listing | 15 s / 100 items |
Sudo · shell | ❌ Disabled · ❌ Disabled (direct |
🧪 Development Policy — less restrictive
Control | Value |
Allowed paths | Secure + |
Command mode | Denylist (whitelist off — most tools allowed) |
File size / output | 10 MB / 1 MB |
Timeout / listing | 60 s / 500 items |
Sudo | ❌ Still disabled |
✍️ Custom Policies
from main import SecurityPolicy, SecureUbuntuController
policy = SecurityPolicy(
allowed_paths=["/your/custom/paths"],
forbidden_paths=["/sensitive/areas"],
allowed_commands=["safe", "commands"],
forbidden_commands=["dangerous", "commands"],
command_whitelist_mode=True, # True = deny-by-default
max_command_timeout=30,
allow_sudo=False, # use with extreme caution
audit_actions=True,
resolve_symlinks=True, # always on for security
use_path_cache=False, # off by default (TOCTOU-safe)
use_shell_exec=False, # off by default
)
controller = SecureUbuntuController(policy)⚙️ Configuration
config.py provides dataclass-based loading/saving (ConfigManager, ServerConfig, SecurityConfig) with a default file at ~/.config/ubuntu-mcp/config.json, created on first run. A ready-to-edit example ships in the repo as config.json.
{
"server": {
"name": "ubuntu-controller",
"version": "1.0.0",
"description": "MCP Server for Ubuntu System Control",
"log_level": "INFO"
},
"security": {
"policy_name": "safe",
"allowed_paths": ["~/", "/tmp", "/var/tmp"],
"forbidden_paths": ["/etc/passwd", "/etc/shadow", "/root", "/boot", "/sys", "/proc"],
"allowed_commands": ["ls", "cat", "echo", "pwd", "whoami", "date", "grep", "find", "which", "file", "head", "tail", "apt", "git", "python3", "pip3"],
"forbidden_commands": ["rm", "rmdir", "dd", "mkfs", "shutdown", "reboot", "mount", "umount", "chmod", "chown"],
"max_command_timeout": 30,
"allow_sudo": false
}
}ℹ️ Runtime policy is chosen via
--policy;config.pyis the programmatic / embedded-config path.
🔍 Available Tools
Tool | Signature | Notes |
📁 |
| Metadata: size, perms |
📄 |
| UTF-8, size-validated, encoding-tolerant |
✍️ |
| Atomic temp+rename, timestamped |
⌨️ |
| Direct exec, sanitized PATH/env, timeout + output caps |
🖥️ |
| OS, memory, disk usage, user, hostname, arch |
🔎 |
|
|
📦 |
|
|
🔒 Security Features
✅ Path traversal — blocked
../../../etc/passwd → SecurityViolation
/etc/passwd → SecurityViolation
/tmp/../etc/passwd → SecurityViolation
symlink_to_/etc/passwd → SecurityViolation (symlinks fully resolved)Every path is canonicalized with symlink resolution, then matched against allowlist + denylist before access. Access to the server's own files and system_critical_paths is always refused.
✅ Command injection — blocked
echo hello; rm -rf / → SecurityViolation
echo `cat /etc/passwd` → SecurityViolation
echo $(whoami) → SecurityViolation
ls | rm -rf / → SecurityViolationParsed with shlex (no shell interpretation), the executable is resolved to a real absolute path (defeats PATH injection), checked against the whitelist/blacklist, then run directly via subprocess.exec with a sanitized environment (LD_PRELOAD, LD_LIBRARY_PATH, IFS stripped).
✅ Resource exhaustion — mitigated
Threat | Defense |
Oversized file reads | Hard size limit before read |
Hanging commands | Per-process-group timeout, |
Output flooding | stdout/stderr capped + truncation marker |
Directory enumeration | Item-count cap with |
📜 Audit trail
Everything lands in /tmp/ubuntu_mcp_audit.log:
COMMAND_ATTEMPT: user=serp3n7 cmd='ls -la' cwd=default
FILE_READ: user=serp3n7 path='/home/serp3n7/foo' status=SUCCESS
SECURITY_VIOLATION: user=serp3n7 violation='COMMAND_BLOCKED' details='Command not in whitelist: nmap'🧪 Testing
./run_tests.sh # full suite: controller, MCP client, policies, files, packages
python main.py --test # functionality tests
python main.py --security-test # attack-surface validation
python test_client.py # end-to-end MCP protocol client
python test_client.py --simple # controller-level smoke testThe --security-test suite validates real vectors — symlink attacks, path traversal, server self-protection, injection patterns, forbidden commands, and size limits — and exits non-zero on any bypass.
📦 Installation Scripts
Script | Use | Notes |
Interactive dev setup | venv → deps → | |
System install (root) | Installs to | |
Claude Desktop launcher | Activates | |
Disk-space demo | Prints a usage bar using |
# systemd service install
sudo python3 install.py
sudo systemctl enable --now ubuntu-mcp🛠️ Development
Add a tool
from main import create_secure_policy, SecureUbuntuController
controller = SecureUbuntuController(create_secure_policy())
@mcp.tool("your_tool_name")
async def your_tool(param: str) -> str:
try:
result = controller.safe_operation(param)
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"error": str(e)}, indent=2)Extend security
def create_custom_policy() -> SecurityPolicy:
return SecurityPolicy(
allowed_paths=["/your/paths"],
forbidden_commands=["dangerous", "commands"],
# ...
)Standards
PEP 8 · type hints on all public functions · docstrings on every tool
Tests for new functionality — run
./run_tests.shbefore opening a PRSecurity-first: permission is denied until a policy explicitly grants it
See CONTRIBUTING.md and CHANGELOG.md.
🔧 Troubleshooting
Symptom | Cause & fix |
Server appears to hang | It's not — MCP servers run continuously over stdio and wait for messages |
| Not using the venv; point Claude at |
| Path/command outside policy — review the audit log and widen the policy deliberately |
| The file isn't readable/writable by the server account — check |
Debug mode:
python main.py --log-level DEBUG --policy secure
tail -f /tmp/ubuntu_mcp_audit.log📄 License & Disclosure
License: MIT · Contributing: CONTRIBUTING.md
Found a vulnerability? Email radjackbartok@proton.me — don't open a public issue.
Made for the security-conscious AI community 🚀
💡 Pro tip: start with the
securepolicy and widen it deliberately — it's easier to grant than to recover.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/Serp3n7/secure-ubuntu-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server