ssh-admin
by AlperTk
README.md
# ssh-admin
MCP (Model Context Protocol) server for managing SSH connections to remote servers. Provides host registry, session pool, and secure command execution with read-only protection.
## Features
- **Server Registry**: Add, list, update, delete SSH server configurations with mtime-based caching
- **Connection Pool**: Automatic session deduplication (max 1 per host), graceful reconnect
- **Command Execution**: Run shell commands on active SSH sessions with whitelist + write pattern detection
- **Raw Command Execute**: Unfiltered command execution with changelog logging and user approval
- **Readonly Mode**: Optional strict read-only mode that blocks all write operations and registry mutations
- **Secure Credentials**: Passwords stored in environment variables, never in the registry
- **Graceful Shutdown**: SIGTERM/SIGINT handling with clean session teardown
## Installation
```bash
npm install
npm run build
```
## OpenCode Configuration
Example `opencode/opencode.json`:
```json
{
"$schema": "https://opencode.ai/config.json",
"default_agent": "ssh-admin",
"permission": {},
"agent": {
"linuxadmin": {
"permission": {
"edit": "deny",
"bash": "deny",
"glob": "deny",
"grep": "deny",
"list": "deny",
"read": "deny",
"task": "deny",
"external_directory": "deny",
"todowrite": "allow",
"question": "allow",
"webfetch": "deny",
"websearch": "deny",
"lsp": "deny",
"skill": "deny",
"ssh-admin_*": "ask",
"ssh-admin_command_execute": "allow",
"ssh-admin_command_execute_raw": "ask",
"ssh-admin_registry_list_servers": "allow",
"ssh-admin_connection_close": "allow",
"ssh-admin_instruction": "allow",
"ssh-admin_registry_get_server": "allow",
"ssh-admin_connection_list": "allow"
}
}
},
"mcp": {
"ssh-admin": {
"type": "local",
"command": ["node", "./ssh-admin/dist/bundle.cjs"],
"enabled": true,
"env": {}
}
}
}
```
## Configuration
### Adding Servers
Use the `registry_add_server` tool to add servers:
```json
{
"name": "registry_add_server",
"arguments": {
"alias": "prod-web",
"host": "192.168.1.100",
"port": 22,
"username": "deploy",
"authMethod": "key",
"keyPath": "~/.ssh/id_ed25519"
}
}
```
For password auth:
```json
{
"name": "registry_add_server",
"arguments": {
"alias": "staging-db",
"host": "10.0.0.50",
"port": 2222,
"username": "admin",
"authMethod": "password"
}
}
```
Then set the password via environment variable:
```bash
export SSH_PASSWORD_STAGING_DB="your_password_here"
```
### Running the Server
```bash
# Development mode
npm run dev
# Production mode
npm run build && npm start
# Readonly mode (blocks registry writes + all command modifications)
MCP_SSH_READONLY=true npm run dev
```
## MCP Tools
### Registry Tools
| Tool | Description |
|------|-------------|
| `registry_add_server` | Add a new SSH server to the registry |
| `registry_list_servers` | List all registered servers (credentials hidden) |
| `registry_get_server` | Get details of a specific server |
| `registry_update_server` | Update server properties (cannot change host/port) |
| `registry_delete_server` | Remove a server from the registry |
### Connection Tools
| Tool | Description |
|------|-------------|
| `connection_open` | Open an SSH connection, returns sessionId |
| `connection_close` | Close an open SSH session |
| `connection_list` | List all active sessions |
### Command Tools
| Tool | Description |
|------|-------------|
| `command_execute` | Execute command on an open session (whitelist + write pattern protected) |
| `command_execute_raw` | Execute commands without filtering (changelog logging + user approval required) |
| `instruction` | Returns system prompt / agent instructions (call first) |
## Usage Flow
```
1. instruction → get agent instructions
2. registry_list_servers → see available hosts
3. connection_open(alias="prod") → get sessionId
4. command_execute(sessionId, "uptime") → run first command
5. command_execute(sessionId, "df -h") → run second command on same session
6. command_execute_raw(sessionId, "systemctl restart nginx") → modify system
7. connection_close(sessionId) → close session
```
## ~/server-info/ Structure
Each server has persistent information stored in its `~/server-info/` directory. These files are updated and read by the AI.
### Files
- **services.md** — Installed services and their status
- **packages.md** — Installed critical packages
- **rules.md** — Server constraints and rules
- **decisions.md** — Decisions made and their rationale
- **architecture.md** — Architecture notes and configuration details
- **changelog.log** — Commands executed via command_execute_raw (auto-append)
### Directories
- **knowledge/** — Knowledge files (.md) created and maintained by the AI
- Each .md file represents a topic or knowledge domain
- AI adds or updates files as it learns new server information
- **scripts/** — Reusable scripts for frequently performed operations
- Store automation scripts here to speed up common tasks
- AI creates and maintains scripts based on recurring operational patterns
### Usage Rules
- **Read operations:** Use `command_execute` (protected by whitelist + write pattern detection)
- **Permanent changes:** Use `command_execute_raw` (unfiltered + user approval required)
- Read-only commands are blocked in `command_execute_raw` → use `command_execute` instead
- Files are updated by AI when system changes occur
- AI reads these files to get server information when needed
## Security
### Command Protection
Every command goes through a three-layer defense:
1. **Whitelist** — Unknown commands are blocked (e.g., python, node, perl, ruby, mount, screen, tmux, dd, zip, gzip)
2. **Write Argument Detection** — Even whitelisted commands are checked for write flags (e.g., `tar cf`, `systemctl restart`, `docker run`, `git add`)
3. **Redirection Detection** — File write operators (`>`, `>>`) are blocked
No bypass is possible. No permanent modifications can be made through `command_execute`.
### Readonly Mode
When `MCP_SSH_READONLY=true`:
- Registry write tools (`registry_add_server`, `registry_update_server`, `registry_delete_server`) are disabled
- All commands pass through whitelist + write pattern checks
- No permanent changes can be made to any server
### Credentials & Storage
- Passwords are **never** stored in the registry
- Use environment variables: `SSH_PASSWORD_<ALIAS>` (uppercase alias)
- Key-based auth is recommended for production use
- Registry directory: `~/.ssh-admin/` with mode `0700`
- Registry file: `hosts.json` with mode `0600`
- Password memory is wiped immediately after credential verification
## Configuration
| Setting | Default | Description |
|---------|---------|-------------|
| Connection timeout | 5000ms | SSH connection establishment timeout |
| Command timeout | 60000ms | Command execution timeout |
| Keepalive interval | max(10s, timeout/3) | SSH keepalive interval |
| Keepalive count | 10 | Number of failed keepalives before disconnect |
| Verification timeout | max(30s, timeout) | Connection verification timeout |
| forceIPv4 | false | Force IPv4 connections (set per host in registry) |
## Development
```bash
npm install # Install dependencies
npm run dev # Watch mode via tsx
npm test # Run tests (477 tests)
npm run build # TypeScript compile + bundle
```
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues