ssh-mcp-gateway
by raphyduck
README.md
# ssh-mcp-gateway
Serveur MCP qui donne accès SSH à une machine, avec **approbation manuelle obligatoire**
de chaque session et **terminaison automatique** de la session précédente quand une
nouvelle est demandée (+ timeouts d'inactivité et de durée max).
## 1. Génération d'une clé SSH dédiée (PAS ta clé perso)
```bash
ssh-keygen -t ed25519 -f ./keys/gateway_key -C "claude-mcp-gateway" -N ""
```
Sur la machine cible (`SSH_USER@SSH_HOST`), ajoute la clé publique dans
`~/.ssh/authorized_keys` avec des restrictions :
```
restrict,command="/bin/bash",pty no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAA... claude-mcp-gateway
```
`restrict` désactive tout par défaut. Adapte `command=` si tu veux limiter
encore plus (ex: un script wrapper qui filtre les commandes autorisées).
Crée idéalement un utilisateur dédié non-root avec sudo désactivé.
## 2. Configuration
```bash
cp .env.example .env
```
Remplis :
- `MCP_BEARER_TOKEN` : `openssl rand -hex 32`
- `APPROVAL_TOKEN` : `openssl rand -hex 32` (différent du précédent)
- `SSH_HOST`, `SSH_USER`, `SSH_PRIVATE_KEY_PATH`
- `SESSION_MAX_MINUTES`, `SESSION_IDLE_MINUTES` selon ta tolérance
## 3. Installation et lancement
```bash
npm install
npm start
```
Tourne par défaut sur `:8787`. Lance-le sous systemd/Docker avec
`Restart=always`, et **ne l'expose jamais directement sur internet** : passe
par un Cloudflare Tunnel.
## 4. Exposition via Cloudflare Tunnel (recommandé)
```bash
cloudflared tunnel create ssh-mcp-gateway
cloudflared tunnel route dns ssh-mcp-gateway mcp.tondomaine.fr
```
`config.yml` :
```yaml
tunnel: <TUNNEL_ID>
credentials-file: /root/.cloudflared/<TUNNEL_ID>.json
ingress:
- hostname: mcp.tondomaine.fr
service: http://localhost:8787
- service: http_status:404
```
Optionnel mais recommandé : active **Cloudflare Access** sur ce hostname
pour ajouter une couche d'auth supplémentaire (ex: email OTP) avant même
d'atteindre le serveur MCP.
```bash
cloudflared tunnel run ssh-mcp-gateway
```
## 5. Ajout du Custom Connector dans Claude.ai
Settings > Connectors > Add custom connector :
- URL : `https://mcp.tondomaine.fr/mcp`
- Auth : configure le `MCP_BEARER_TOKEN` comme credential (selon ce que
propose l'UI au moment où tu le fais — Bearer/API key ou champ "secret"
dans Advanced settings).
## 6. Utilisation depuis une conversation Claude
1. Claude appelle `session_request` → reçoit deux liens (`approve` / `deny`).
2. Tu ouvres le lien `approve` sur ton téléphone (uniquement protégé par
`APPROVAL_TOKEN`, garde-le secret — ne le partage jamais avec Claude
lui-même autrement que via ces liens générés serveur-side).
3. Claude vérifie `session_status` → `approved`.
4. Claude utilise `ssh_exec` autant que nécessaire ; chaque appel repousse
le timeout d'inactivité.
5. Claude appelle `session_end` en fin de tâche, ou la session expire
automatiquement (`SESSION_IDLE_MINUTES` / `SESSION_MAX_MINUTES`), ou tu
la termines toi-même via le lien "Terminer la session maintenant".
6. Toute nouvelle `session_request` termine immédiatement la session en
cours, donc une seule session active à la fois — garanti.
## Limites et points à durcir toi-même
- Le serveur ne whitelist pas les commandes : `ssh_exec` exécute ce que
Claude lui envoie, dans les limites de ce que ton `authorized_keys`
autorise. Si tu veux une whitelist de commandes, mets-la dans le
`command=` forcé côté `authorized_keys` plutôt que dans ce serveur.
- Pas de logging persistant des commandes exécutées par défaut — ajoute un
`fs.appendFile` dans `ssh_exec` si tu veux un audit trail.
- `APPROVAL_TOKEN` et `MCP_BEARER_TOKEN` sont des secrets statiques : si
l'un fuite, change-le et redémarre.
- Pas de notification push native ici : les liens d'approbation, c'est toi
qui dois aller les chercher dans la conversation et les ouvrir. Si tu
veux du push (ntfy.sh, Pushover), ajoute un appel HTTP dans
`session_request`.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues