|
| 1 | +# Deployment Guide |
| 2 | + |
| 3 | +## Prerequisites |
| 4 | + |
| 5 | +- Linux server with SSH access enabled |
| 6 | +- SSH key pair for passwordless authentication |
| 7 | + |
| 8 | +## Docker (Recommended) |
| 9 | + |
| 10 | +### HTTP Mode |
| 11 | + |
| 12 | +Run as a network-accessible service on your server: |
| 13 | + |
| 14 | +```bash |
| 15 | +docker run -d \ |
| 16 | + -p 3000:3000 \ |
| 17 | + -e SSH_HOST=server.local \ |
| 18 | + -e SSH_PORT=22 \ |
| 19 | + -e SSH_USERNAME=mcp-readonly \ |
| 20 | + -e SSH_KEY_PATH=/keys/id_ed25519 \ |
| 21 | + -e OAUTH_SERVER_URL=https://mcp.example.com \ |
| 22 | + -v ~/.ssh/id_ed25519_mcp:/keys/id_ed25519:ro \ |
| 23 | + ghcr.io/ohare93/mcp-ssh-sre:latest |
| 24 | +``` |
| 25 | + |
| 26 | +Or with Docker Compose (`docker-compose.http.yml`): |
| 27 | + |
| 28 | +```yaml |
| 29 | +services: |
| 30 | + mcp-ssh-sre: |
| 31 | + image: ghcr.io/ohare93/mcp-ssh-sre:latest |
| 32 | + ports: |
| 33 | + - "3000:3000" |
| 34 | + environment: |
| 35 | + - SSH_HOST=server.local |
| 36 | + - SSH_PORT=22 |
| 37 | + - SSH_USERNAME=mcp-readonly |
| 38 | + - SSH_KEY_PATH=/keys/id_ed25519 |
| 39 | + - OAUTH_SERVER_URL=https://mcp.example.com |
| 40 | + volumes: |
| 41 | + - ~/.ssh/id_ed25519_mcp:/keys/id_ed25519:ro |
| 42 | +``` |
| 43 | +
|
| 44 | +#### Environment Variables |
| 45 | +
|
| 46 | +| Variable | Required | Default | Description | |
| 47 | +|----------|----------|---------|-------------| |
| 48 | +| `SSH_HOST` | Yes | - | Server hostname or IP | |
| 49 | +| `SSH_PORT` | No | 22 | SSH port | |
| 50 | +| `SSH_USERNAME` | Yes | - | SSH username | |
| 51 | +| `SSH_KEY_PATH` | Yes | - | Path to SSH private key (inside container) | |
| 52 | +| `HTTP_PORT` | No | 3000 | HTTP server port | |
| 53 | +| `CORS_ORIGIN` | No | * | CORS origin | |
| 54 | +| `OAUTH_SERVER_URL` | Prod | - | Public URL for OAuth discovery | |
| 55 | +| `REQUIRE_AUTH` | No | true | Require OAuth authentication | |
| 56 | + |
| 57 | +#### MCP Client Configuration |
| 58 | + |
| 59 | +```json |
| 60 | +{ |
| 61 | + "mcpServers": { |
| 62 | + "ssh-sre": { |
| 63 | + "url": "http://your-server:3000/mcp" |
| 64 | + } |
| 65 | + } |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +### Stdio Mode |
| 70 | + |
| 71 | +For local MCP clients (like Claude Desktop running on the same machine): |
| 72 | + |
| 73 | +```bash |
| 74 | +docker build -t mcp-ssh-sre . |
| 75 | +docker run -d --env-file .env mcp-ssh-sre |
| 76 | +``` |
| 77 | + |
| 78 | +## Running Locally |
| 79 | + |
| 80 | +### Installation |
| 81 | + |
| 82 | +```bash |
| 83 | +git clone https://github.com/ohare93/mcp-ssh-sre.git |
| 84 | +cd mcp-ssh-sre |
| 85 | +npm install |
| 86 | +npm run build |
| 87 | +``` |
| 88 | + |
| 89 | +### Configuration |
| 90 | + |
| 91 | +Create a `.env` file: |
| 92 | + |
| 93 | +```bash |
| 94 | +SSH_HOST=server.local |
| 95 | +SSH_PORT=22 |
| 96 | +SSH_USERNAME=mcp-readonly |
| 97 | +SSH_KEY_PATH=~/.ssh/id_rsa_mcp |
| 98 | +``` |
| 99 | + |
| 100 | +### Running |
| 101 | + |
| 102 | +```bash |
| 103 | +# Stdio mode (for local MCP clients) |
| 104 | +node dist/index.js |
| 105 | +
|
| 106 | +# HTTP mode |
| 107 | +node dist/http-server.js |
| 108 | +
|
| 109 | +# Development mode with auto-reload |
| 110 | +npm run dev |
| 111 | +``` |
| 112 | + |
| 113 | +### MCP Client Configuration (Stdio) |
| 114 | + |
| 115 | +```json |
| 116 | +{ |
| 117 | + "mcpServers": { |
| 118 | + "ssh-sre": { |
| 119 | + "command": "node", |
| 120 | + "args": ["/absolute/path/to/mcp-ssh-sre/dist/index.js"] |
| 121 | + } |
| 122 | + } |
| 123 | +} |
| 124 | +``` |
| 125 | + |
| 126 | +## Security Setup |
| 127 | + |
| 128 | +### Create a Read-Only User |
| 129 | + |
| 130 | +```bash |
| 131 | +# On server as root |
| 132 | +useradd -m -s /bin/bash mcp-readonly |
| 133 | +passwd mcp-readonly |
| 134 | +usermod -aG docker mcp-readonly |
| 135 | +``` |
| 136 | + |
| 137 | +### Generate and Deploy SSH Key |
| 138 | + |
| 139 | +```bash |
| 140 | +# On your local machine |
| 141 | +ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_mcp -C "mcp-ssh-sre" |
| 142 | +ssh-copy-id -i ~/.ssh/id_ed25519_mcp.pub mcp-readonly@server.local |
| 143 | +``` |
| 144 | + |
| 145 | +## Authentication |
| 146 | + |
| 147 | +OAuth authentication is **required by default** (v2.0.0+). |
| 148 | + |
| 149 | +| `REQUIRE_AUTH` | Use Case | |
| 150 | +|----------------|----------| |
| 151 | +| `true` (default) | Production - require OAuth token | |
| 152 | +| `false` | Local dev only - allows unauthenticated | |
| 153 | +| `development` | Local dev - logs warnings | |
| 154 | + |
| 155 | +### OAuth Flow |
| 156 | + |
| 157 | +1. Register client: |
| 158 | + ```bash |
| 159 | + curl -X POST http://localhost:3000/register \ |
| 160 | + -H "Content-Type: application/json" \ |
| 161 | + -d '{"client_name": "My Client"}' |
| 162 | + ``` |
| 163 | + |
| 164 | +2. Get authorization code (visit in browser): |
| 165 | + ``` |
| 166 | + http://localhost:3000/authorize?client_id=YOUR_ID&redirect_uri=YOUR_REDIRECT&state=xyz&response_type=code |
| 167 | + ``` |
| 168 | + |
| 169 | +3. Exchange for token: |
| 170 | + ```bash |
| 171 | + curl -X POST http://localhost:3000/token \ |
| 172 | + -d grant_type=authorization_code \ |
| 173 | + -d code=YOUR_CODE \ |
| 174 | + -d client_id=YOUR_ID \ |
| 175 | + -d client_secret=YOUR_SECRET |
| 176 | + ``` |
| 177 | + |
| 178 | +## Network Security |
| 179 | + |
| 180 | +- **Don't** expose directly to the internet |
| 181 | +- **Do** use VPN/Tailscale or reverse proxy with TLS |
| 182 | +- Set `OAUTH_SERVER_URL` when behind a reverse proxy |
| 183 | + |
| 184 | +### Security Checklist |
| 185 | + |
| 186 | +- [ ] `REQUIRE_AUTH=true` in production |
| 187 | +- [ ] Server behind firewall/VPN or reverse proxy |
| 188 | +- [ ] OAuth credentials stored securely |
| 189 | +- [ ] Logs monitored for unauthorized attempts |
0 commit comments