|
| 1 | +# SSH Authentication Guide |
| 2 | + |
| 3 | +## Current Authentication Methods |
| 4 | + |
| 5 | +The MCP tools support three methods of SSH authentication, in order of preference: |
| 6 | + |
| 7 | +### 1. SSH Public Keys (Preferred) ✅ |
| 8 | + |
| 9 | +**How it works:** |
| 10 | +- Automatically checks for SSH keys in `~/.ssh/id_rsa.pub` or `~/.ssh/id_ed25519.pub` |
| 11 | +- Uses key-based authentication if the key is installed on the target device |
| 12 | +- No password needed - most secure method |
| 13 | + |
| 14 | +**Setup:** |
| 15 | +1. Generate SSH key (if you don't have one): |
| 16 | + ```bash |
| 17 | + ssh-keygen -t ed25519 -C "your_email@example.com" |
| 18 | + # Or use RSA: |
| 19 | + ssh-keygen -t rsa -b 4096 -C "your_email@example.com" |
| 20 | + ``` |
| 21 | + |
| 22 | +2. Install key on target device: |
| 23 | + ```bash |
| 24 | + # Manual method: |
| 25 | + ssh-copy-id username@device_ip |
| 26 | + |
| 27 | + # Or use the MCP tool (when implemented): |
| 28 | + install_ssh_key device_id username [password] |
| 29 | + ``` |
| 30 | + |
| 31 | +**Current Status:** |
| 32 | +- ✅ Automatically detected and used if available |
| 33 | +- ⚠️ No MCP tool yet to install keys automatically |
| 34 | +- 📝 SSH key installation must be done manually or via `install_ssh_key()` function |
| 35 | + |
| 36 | +### 2. Cached Passwords (Fallback) ⚠️ |
| 37 | + |
| 38 | +**How it works:** |
| 39 | +- Passwords are cached in `~/.cache/ai-lab-testing/credentials.json` |
| 40 | +- Uses `sshpass` tool to provide password non-interactively |
| 41 | +- Requires `sshpass` package: `sudo apt install sshpass` |
| 42 | + |
| 43 | +**Current Limitations:** |
| 44 | +- ❌ **No MCP tool to cache credentials** - passwords must be cached programmatically |
| 45 | +- ⚠️ Credentials must be cached before use |
| 46 | +- 📝 Cache location: `~/.cache/ai-lab-testing/credentials.json` (permissions: 600) |
| 47 | + |
| 48 | +**Manual Credential Caching:** |
| 49 | +```python |
| 50 | +from lab_testing.utils.credentials import cache_credential |
| 51 | + |
| 52 | +# Cache SSH password for a device |
| 53 | +cache_credential( |
| 54 | + device_id="my-device-id", |
| 55 | + username="root", |
| 56 | + password="my-password", |
| 57 | + credential_type="ssh" |
| 58 | +) |
| 59 | +``` |
| 60 | + |
| 61 | +### 3. Interactive SSH (Fallback) ⚠️ |
| 62 | + |
| 63 | +**How it works:** |
| 64 | +- Falls back to standard SSH command if keys and cached passwords fail |
| 65 | +- Will prompt for password interactively (not suitable for MCP tools) |
| 66 | +- **Not recommended** - use SSH keys or cached passwords instead |
| 67 | + |
| 68 | +--- |
| 69 | + |
| 70 | +## Current Implementation Details |
| 71 | + |
| 72 | +### Authentication Flow |
| 73 | + |
| 74 | +When `ssh_to_device` is called: |
| 75 | + |
| 76 | +1. **Check SSH Key** (`check_ssh_key_installed()`) |
| 77 | + - Tests if key-based auth works: `ssh -o BatchMode=yes username@ip "echo OK"` |
| 78 | + - If successful → use key-based SSH command |
| 79 | + |
| 80 | +2. **Check Cached Password** (if `use_password=True`) |
| 81 | + - Looks up password in `~/.cache/ai-lab-testing/credentials.json` |
| 82 | + - If found → use `sshpass -p password ssh ...` |
| 83 | + |
| 84 | +3. **Fallback to Standard SSH** |
| 85 | + - Uses standard SSH command (may prompt for password) |
| 86 | + - **This will fail in MCP context** (no interactive input) |
| 87 | + |
| 88 | +### Code Location |
| 89 | + |
| 90 | +- **Credential Management**: `lab_testing/utils/credentials.py` |
| 91 | + - `cache_credential()` - Cache password for device |
| 92 | + - `get_credential()` - Retrieve cached password |
| 93 | + - `check_ssh_key_installed()` - Check if key auth works |
| 94 | + - `install_ssh_key()` - Install SSH key on device |
| 95 | + - `get_ssh_command()` - Build SSH command with auth |
| 96 | + |
| 97 | +- **SSH Execution**: `lab_testing/tools/device_manager.py` |
| 98 | + - `ssh_to_device()` - Execute SSH command (uses `get_ssh_command()`) |
| 99 | + |
| 100 | +--- |
| 101 | + |
| 102 | +## Recommended Improvements |
| 103 | + |
| 104 | +### 1. Add MCP Tool: `cache_device_credentials` ⚠️ NEEDED |
| 105 | + |
| 106 | +**Purpose:** Allow users to cache SSH passwords via MCP tool |
| 107 | + |
| 108 | +**Proposed Tool:** |
| 109 | +```python |
| 110 | +Tool( |
| 111 | + name="cache_device_credentials", |
| 112 | + description="Cache SSH credentials (username/password) for a device. " |
| 113 | + "Credentials are stored securely in ~/.cache/ai-lab-testing/credentials.json. " |
| 114 | + "Prefer SSH keys over passwords when possible.", |
| 115 | + inputSchema={ |
| 116 | + "type": "object", |
| 117 | + "properties": { |
| 118 | + "device_id": { |
| 119 | + "type": "string", |
| 120 | + "description": "Device identifier (device_id or friendly_name)" |
| 121 | + }, |
| 122 | + "username": { |
| 123 | + "type": "string", |
| 124 | + "description": "SSH username" |
| 125 | + }, |
| 126 | + "password": { |
| 127 | + "type": "string", |
| 128 | + "description": "SSH password (optional, prefer SSH keys)" |
| 129 | + }, |
| 130 | + "credential_type": { |
| 131 | + "type": "string", |
| 132 | + "enum": ["ssh", "sudo"], |
| 133 | + "default": "ssh", |
| 134 | + "description": "Type of credential to cache" |
| 135 | + } |
| 136 | + }, |
| 137 | + "required": ["device_id", "username"] |
| 138 | + } |
| 139 | +) |
| 140 | +``` |
| 141 | + |
| 142 | +### 2. Add MCP Tool: `install_ssh_key` ⚠️ NEEDED |
| 143 | + |
| 144 | +**Purpose:** Automatically install SSH public key on target device |
| 145 | + |
| 146 | +**Proposed Tool:** |
| 147 | +```python |
| 148 | +Tool( |
| 149 | + name="install_ssh_key", |
| 150 | + description="Install SSH public key on target device for passwordless access. " |
| 151 | + "Uses default SSH key from ~/.ssh/id_rsa.pub or ~/.ssh/id_ed25519.pub. " |
| 152 | + "Requires password for initial access if key not already installed.", |
| 153 | + inputSchema={ |
| 154 | + "type": "object", |
| 155 | + "properties": { |
| 156 | + "device_id": { |
| 157 | + "type": "string", |
| 158 | + "description": "Device identifier (device_id or friendly_name)" |
| 159 | + }, |
| 160 | + "username": { |
| 161 | + "type": "string", |
| 162 | + "description": "SSH username (optional, uses device default)" |
| 163 | + }, |
| 164 | + "password": { |
| 165 | + "type": "string", |
| 166 | + "description": "Password for initial access (if key not installed)" |
| 167 | + } |
| 168 | + }, |
| 169 | + "required": ["device_id"] |
| 170 | + } |
| 171 | +) |
| 172 | +``` |
| 173 | + |
| 174 | +### 3. Add MCP Tool: `check_ssh_key_status` ✅ EASY |
| 175 | + |
| 176 | +**Purpose:** Check if SSH key is installed and working |
| 177 | + |
| 178 | +**Proposed Tool:** |
| 179 | +```python |
| 180 | +Tool( |
| 181 | + name="check_ssh_key_status", |
| 182 | + description="Check if SSH key authentication is working for a device", |
| 183 | + inputSchema={ |
| 184 | + "type": "object", |
| 185 | + "properties": { |
| 186 | + "device_id": { |
| 187 | + "type": "string", |
| 188 | + "description": "Device identifier (device_id or friendly_name)" |
| 189 | + }, |
| 190 | + "username": { |
| 191 | + "type": "string", |
| 192 | + "description": "SSH username (optional, uses device default)" |
| 193 | + } |
| 194 | + }, |
| 195 | + "required": ["device_id"] |
| 196 | + } |
| 197 | +) |
| 198 | +``` |
| 199 | + |
| 200 | +### 4. Update `ssh_to_device` Tool ⚠️ ENHANCEMENT |
| 201 | + |
| 202 | +**Current:** Only accepts `device_id`, `command`, `username` |
| 203 | + |
| 204 | +**Proposed Enhancement:** |
| 205 | +- Add optional `use_password` parameter (default: False) |
| 206 | +- If `use_password=True`, use cached password if available |
| 207 | +- Better error messages when authentication fails |
| 208 | + |
| 209 | +--- |
| 210 | + |
| 211 | +## Usage Examples |
| 212 | + |
| 213 | +### Current Workflow (Manual Credential Caching) |
| 214 | + |
| 215 | +```python |
| 216 | +# 1. Cache credentials programmatically (not via MCP) |
| 217 | +from lab_testing.utils.credentials import cache_credential |
| 218 | +cache_credential("my-device-id", "root", "my-password") |
| 219 | + |
| 220 | +# 2. Use ssh_to_device (will use cached password) |
| 221 | +# Via MCP: ssh_to_device(device_id="my-device-id", command="uptime") |
| 222 | +``` |
| 223 | + |
| 224 | +### Recommended Workflow (SSH Keys) |
| 225 | + |
| 226 | +```bash |
| 227 | +# 1. Generate SSH key (if needed) |
| 228 | +ssh-keygen -t ed25519 |
| 229 | + |
| 230 | +# 2. Install key on device manually |
| 231 | +ssh-copy-id root@192.168.2.18 |
| 232 | + |
| 233 | +# 3. Use ssh_to_device (will automatically use key) |
| 234 | +# Via MCP: ssh_to_device(device_id="my-device-id", command="uptime") |
| 235 | +``` |
| 236 | + |
| 237 | +### Future Workflow (With New Tools) |
| 238 | + |
| 239 | +```python |
| 240 | +# 1. Check if SSH key works |
| 241 | +check_ssh_key_status(device_id="my-device-id") |
| 242 | + |
| 243 | +# 2a. If key not installed, install it |
| 244 | +install_ssh_key(device_id="my-device-id", password="initial-password") |
| 245 | + |
| 246 | +# 2b. Or cache password as fallback |
| 247 | +cache_device_credentials(device_id="my-device-id", username="root", password="my-password") |
| 248 | + |
| 249 | +# 3. Use ssh_to_device |
| 250 | +ssh_to_device(device_id="my-device-id", command="uptime") |
| 251 | +``` |
| 252 | + |
| 253 | +--- |
| 254 | + |
| 255 | +## Security Considerations |
| 256 | + |
| 257 | +### Credential Storage |
| 258 | + |
| 259 | +- **Location**: `~/.cache/ai-lab-testing/credentials.json` |
| 260 | +- **Permissions**: 600 (read/write owner only) |
| 261 | +- **Format**: JSON with `device_id:credential_type` keys |
| 262 | +- **Never committed**: Credentials are in user's home directory, not in repo |
| 263 | + |
| 264 | +### Best Practices |
| 265 | + |
| 266 | +1. **Prefer SSH Keys**: Most secure, no password storage needed |
| 267 | +2. **Use Strong Passwords**: If passwords must be used, use strong passwords |
| 268 | +3. **Rotate Credentials**: Regularly rotate passwords if used |
| 269 | +4. **Review Cache**: Periodically review `~/.cache/ai-lab-testing/credentials.json` |
| 270 | +5. **Limit Access**: Ensure credential cache has restrictive permissions (600) |
| 271 | + |
| 272 | +### Credential Cache Format |
| 273 | + |
| 274 | +```json |
| 275 | +{ |
| 276 | + "device_id:ssh": { |
| 277 | + "username": "root", |
| 278 | + "password": "encrypted-or-plaintext-password" |
| 279 | + }, |
| 280 | + "device_id:sudo": { |
| 281 | + "username": "root", |
| 282 | + "password": "sudo-password" |
| 283 | + } |
| 284 | +} |
| 285 | +``` |
| 286 | + |
| 287 | +**Note:** Currently passwords are stored in plaintext. Consider encryption in future. |
| 288 | + |
| 289 | +--- |
| 290 | + |
| 291 | +## Troubleshooting |
| 292 | + |
| 293 | +### SSH Key Not Working |
| 294 | + |
| 295 | +1. **Check if key exists:** |
| 296 | + ```bash |
| 297 | + ls -la ~/.ssh/id_*.pub |
| 298 | + ``` |
| 299 | + |
| 300 | +2. **Check if key is installed on device:** |
| 301 | + ```bash |
| 302 | + ssh -o BatchMode=yes root@device_ip "echo OK" |
| 303 | + ``` |
| 304 | + |
| 305 | +3. **Install key manually:** |
| 306 | + ```bash |
| 307 | + ssh-copy-id root@device_ip |
| 308 | + ``` |
| 309 | + |
| 310 | +### Password Authentication Failing |
| 311 | + |
| 312 | +1. **Check if password is cached:** |
| 313 | + ```bash |
| 314 | + cat ~/.cache/ai-lab-testing/credentials.json |
| 315 | + ``` |
| 316 | + |
| 317 | +2. **Check if sshpass is installed:** |
| 318 | + ```bash |
| 319 | + which sshpass |
| 320 | + # Install if missing: sudo apt install sshpass |
| 321 | + ``` |
| 322 | + |
| 323 | +3. **Cache password manually:** |
| 324 | + ```python |
| 325 | + from lab_testing.utils.credentials import cache_credential |
| 326 | + cache_credential("device-id", "root", "password") |
| 327 | + ``` |
| 328 | + |
| 329 | +### Authentication Errors |
| 330 | + |
| 331 | +- **"Permission denied (publickey,password)"**: No SSH key installed and no password cached |
| 332 | +- **"sshpass: command not found"**: Install sshpass: `sudo apt install sshpass` |
| 333 | +- **"Connection refused"**: Device may be offline or SSH not running |
| 334 | +- **"Connection timed out"**: VPN may be disconnected or network unreachable |
| 335 | + |
| 336 | +--- |
| 337 | + |
| 338 | +## Next Steps |
| 339 | + |
| 340 | +1. ✅ **Document current authentication flow** - DONE |
| 341 | +2. ⏳ **Add `cache_device_credentials` MCP tool** - NEEDED |
| 342 | +3. ⏳ **Add `install_ssh_key` MCP tool** - NEEDED |
| 343 | +4. ⏳ **Add `check_ssh_key_status` MCP tool** - RECOMMENDED |
| 344 | +5. ⏳ **Update `ssh_to_device` with `use_password` parameter** - ENHANCEMENT |
| 345 | +6. ⏳ **Add credential encryption** - FUTURE ENHANCEMENT |
| 346 | + |
| 347 | +--- |
| 348 | + |
| 349 | +## Related Documentation |
| 350 | + |
| 351 | +- [Security Guide](SECURITY.md) - General security practices |
| 352 | +- [VPN Setup Guide](VPN_SETUP.md) - VPN configuration for remote access |
| 353 | +- [Device Management](docs/API.md) - Device management tools |
| 354 | + |
0 commit comments