Skip to content

Commit 9f11295

Browse files
author
Alex J Lennon
committed
Fix CI test failures
- Fix test_list_devices_success: Mock network scan to return configured device IPs - Fix test_create_network_map_with_devices: Add mocks for network scan and VPN status - Fix TypeError in device_manager: Check cached_at is numeric before comparison - Add default credentials (fio/fio) fallback in get_credential - Implement three new credential management MCP tools: - cache_device_credentials: Cache SSH credentials for devices - check_ssh_key_status: Check if SSH key auth is working - install_ssh_key: Install SSH public key on target device - Update ssh_to_device to use password auth when SSH keys fail - Update get_ssh_command to use credential username when available
1 parent 7b6b7de commit 9f11295

8 files changed

Lines changed: 905 additions & 37 deletions

File tree

docs/SSH_AUTHENTICATION.md

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
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

Comments
 (0)