Skip to main content
Glama
ssh-key-file-e2e-test-plan.md22.4 kB
# SSH Key File Authentication Enhancement - End-to-End Manual Test Plan ## Test Overview This test plan validates the SSH key file authentication enhancement that adds `keyFilePath` and `passphrase` parameters to the `ssh_connect` tool. The tests verify real SSH connections using both encrypted and unencrypted SSH keys against localhost infrastructure. ## Environment Setup ### Prerequisites - Ubuntu/Linux system with SSH server installed and running - Node.js and npm installed - ls-ssh-mcp server built and ready to run - SSH server configured to accept both password and key authentication - Test user account: `test_user` with password `password123` (already configured) ### Test Infrastructure Setup #### 1. Create Test Directory Structure ```bash # Create dedicated test environment mkdir -p ~/ssh-test-keys/valid mkdir -p ~/ssh-test-keys/invalid mkdir -p ~/ssh-test-keys/restricted chmod 700 ~/ssh-test-keys ``` #### 2. Generate SSH Key Pairs for Testing ```bash # Generate unencrypted RSA key ssh-keygen -t rsa -b 2048 -f ~/ssh-test-keys/valid/test_rsa_unencrypted -N "" -C "test_rsa_unencrypted" # Generate encrypted RSA key with passphrase "testpass123" ssh-keygen -t rsa -b 2048 -f ~/ssh-test-keys/valid/test_rsa_encrypted -N "testpass123" -C "test_rsa_encrypted" # Generate unencrypted ED25519 key ssh-keygen -t ed25519 -f ~/ssh-test-keys/valid/test_ed25519_unencrypted -N "" -C "test_ed25519_unencrypted" # Generate encrypted ED25519 key with passphrase "secure456" ssh-keygen -t ed25519 -f ~/ssh-test-keys/valid/test_ed25519_encrypted -N "secure456" -C "test_ed25519_encrypted" # Create invalid key file for negative testing echo "INVALID SSH KEY CONTENT" > ~/ssh-test-keys/invalid/not_a_key # Set restricted permissions for permission test cp ~/ssh-test-keys/valid/test_rsa_unencrypted ~/ssh-test-keys/restricted/test_rsa_restricted chmod 000 ~/ssh-test-keys/restricted/test_rsa_restricted ``` #### 3. Configure SSH Server for Key Authentication ```bash # Add public keys to authorized_keys for test_user sudo -u test_user mkdir -p /home/test_user/.ssh sudo -u test_user chmod 700 /home/test_user/.ssh sudo -u test_user touch /home/test_user/.ssh/authorized_keys sudo -u test_user chmod 600 /home/test_user/.ssh/authorized_keys # Add all test public keys sudo -u test_user bash -c 'cat ~/ssh-test-keys/valid/test_rsa_unencrypted.pub >> /home/test_user/.ssh/authorized_keys' sudo -u test_user bash -c 'cat ~/ssh-test-keys/valid/test_rsa_encrypted.pub >> /home/test_user/.ssh/authorized_keys' sudo -u test_user bash -c 'cat ~/ssh-test-keys/valid/test_ed25519_unencrypted.pub >> /home/test_user/.ssh/authorized_keys' sudo -u test_user bash -c 'cat ~/ssh-test-keys/valid/test_ed25519_encrypted.pub >> /home/test_user/.ssh/authorized_keys' # Verify SSH server configuration allows key authentication sudo grep -E "PubkeyAuthentication|AuthorizedKeysFile" /etc/ssh/sshd_config # Expected: PubkeyAuthentication yes ``` #### 4. Start MCP SSH Server ```bash # Navigate to project directory cd /home/jsbattig/Dev/ls-ssh-mcp # Build the project npm run build # Start the MCP server (keep this running in a terminal) npm start # Expected: Server starts on port 3000 (MCP) and 8080 (WebSocket) ``` ## Test Cases ### Test Case 1: Unencrypted RSA Key Authentication **Objective**: Verify successful SSH connection using unencrypted RSA key file path **Preconditions**: - MCP server is running - SSH keys are generated and configured - test_user account exists with authorized_keys configured **Execution Steps**: 1. Connect to MCP server using client tool 2. Execute ssh_connect with keyFilePath parameter: ```json { "tool": "ssh_connect", "arguments": { "name": "test_rsa_unencrypted", "host": "localhost", "username": "test_user", "keyFilePath": "~/ssh-test-keys/valid/test_rsa_unencrypted" } } ``` 3. Verify connection response contains: - `success: true` - `message` containing "Successfully connected" - Session name "test_rsa_unencrypted" 4. Execute a test command to verify connection: ```json { "tool": "ssh_execute", "arguments": { "sessionName": "test_rsa_unencrypted", "command": "whoami && hostname && echo 'Connection successful'" } } ``` 5. Verify command output: - stdout contains "test_user" - stdout contains hostname - stdout contains "Connection successful" - exitCode is 0 **Success Criteria**: - Connection established without password prompt - Commands execute successfully through the connection - No errors in server logs - Connection status shows "connected" **Failure Indicators**: - Authentication failure messages - Connection timeout - "Permission denied" errors - Server crashes or hangs **Cleanup**: ```json { "tool": "ssh_disconnect", "arguments": { "sessionName": "test_rsa_unencrypted" } } ``` ### Test Case 2: Encrypted RSA Key with Passphrase **Objective**: Verify successful SSH connection using encrypted RSA key with correct passphrase **Preconditions**: - Previous test cleanup completed - MCP server is running **Execution Steps**: 1. Execute ssh_connect with keyFilePath and passphrase: ```json { "tool": "ssh_connect", "arguments": { "name": "test_rsa_encrypted", "host": "localhost", "username": "test_user", "keyFilePath": "~/ssh-test-keys/valid/test_rsa_encrypted", "passphrase": "testpass123" } } ``` 2. Verify successful connection response 3. Execute verification command: ```json { "tool": "ssh_execute", "arguments": { "sessionName": "test_rsa_encrypted", "command": "ls -la ~/.ssh/authorized_keys" } } ``` 4. Verify file listing shows authorized_keys with proper permissions **Success Criteria**: - Connection established with encrypted key - Passphrase correctly decrypts the key - Commands execute successfully - No passphrase visible in logs **Failure Indicators**: - "Key is encrypted but no passphrase provided" error - "incorrect passphrase" error - Authentication failure **Cleanup**: ```json { "tool": "ssh_disconnect", "arguments": { "sessionName": "test_rsa_encrypted" } } ``` ### Test Case 3: ED25519 Key Support **Objective**: Verify support for modern ED25519 key algorithm **Preconditions**: - Previous tests completed - MCP server is running **Execution Steps**: 1. Test unencrypted ED25519 key: ```json { "tool": "ssh_connect", "arguments": { "name": "test_ed25519", "host": "localhost", "username": "test_user", "keyFilePath": "~/ssh-test-keys/valid/test_ed25519_unencrypted" } } ``` 2. Verify connection and execute test command 3. Disconnect 4. Test encrypted ED25519 key: ```json { "tool": "ssh_connect", "arguments": { "name": "test_ed25519_encrypted", "host": "localhost", "username": "test_user", "keyFilePath": "~/ssh-test-keys/valid/test_ed25519_encrypted", "passphrase": "secure456" } } ``` 5. Verify connection and execute test command **Success Criteria**: - Both ED25519 keys work correctly - Modern key algorithm properly supported - Performance comparable to RSA keys **Cleanup**: Disconnect all sessions ### Test Case 4: Path Traversal Security Validation **Objective**: Verify protection against path traversal attacks **Preconditions**: - MCP server is running - Test keys exist in known locations **Execution Steps**: 1. Attempt connection with path traversal pattern: ```json { "tool": "ssh_connect", "arguments": { "name": "path_traversal_test", "host": "localhost", "username": "test_user", "keyFilePath": "../../../etc/passwd" } } ``` 2. Verify error response contains security violation message 3. Attempt with null byte injection: ```json { "tool": "ssh_connect", "arguments": { "name": "null_byte_test", "host": "localhost", "username": "test_user", "keyFilePath": "~/ssh-test-keys/valid/test_rsa_unencrypted\u0000.pub" } } ``` 4. Verify rejection with security error 5. Check server logs for security warnings **Success Criteria**: - All path traversal attempts blocked - Clear security error messages returned - No file system access outside allowed paths - Security events logged **Failure Indicators**: - Successful file access outside home directory - Server crash on malicious input - Unclear error messages ### Test Case 5: Invalid Key File Handling **Objective**: Verify graceful handling of invalid key files **Preconditions**: - Invalid test files created - MCP server is running **Execution Steps**: 1. Test non-existent file: ```json { "tool": "ssh_connect", "arguments": { "name": "nonexistent_key", "host": "localhost", "username": "test_user", "keyFilePath": "~/ssh-test-keys/does_not_exist" } } ``` Expected: "ENOENT: no such file or directory" error 2. Test invalid key content: ```json { "tool": "ssh_connect", "arguments": { "name": "invalid_key", "host": "localhost", "username": "test_user", "keyFilePath": "~/ssh-test-keys/invalid/not_a_key" } } ``` Expected: Authentication failure error 3. Test inaccessible file (permission denied): ```json { "tool": "ssh_connect", "arguments": { "name": "restricted_key", "host": "localhost", "username": "test_user", "keyFilePath": "~/ssh-test-keys/restricted/test_rsa_restricted" } } ``` Expected: "EACCES: permission denied" error **Success Criteria**: - Clear, specific error messages for each failure type - No server crashes or hangs - Appropriate error codes returned - File system errors properly handled ### Test Case 6: Wrong Passphrase Handling **Objective**: Verify proper handling of incorrect passphrases **Preconditions**: - Encrypted keys configured - MCP server is running **Execution Steps**: 1. Attempt connection with wrong passphrase: ```json { "tool": "ssh_connect", "arguments": { "name": "wrong_passphrase", "host": "localhost", "username": "test_user", "keyFilePath": "~/ssh-test-keys/valid/test_rsa_encrypted", "passphrase": "wrongpassword" } } ``` 2. Verify authentication failure error 3. Attempt with empty passphrase on encrypted key: ```json { "tool": "ssh_connect", "arguments": { "name": "missing_passphrase", "host": "localhost", "username": "test_user", "keyFilePath": "~/ssh-test-keys/valid/test_rsa_encrypted" } } ``` 4. Verify "Key is encrypted but no passphrase provided" error **Success Criteria**: - Clear error messages for passphrase issues - No sensitive information leaked in errors - Connection properly cleaned up after failure ### Test Case 7: Backward Compatibility Test **Objective**: Verify existing privateKey parameter still works **Preconditions**: - MCP server is running - Private key content available **Execution Steps**: 1. Read key content: ```bash cat ~/ssh-test-keys/valid/test_rsa_unencrypted ``` 2. Connect using privateKey parameter (legacy approach): ```json { "tool": "ssh_connect", "arguments": { "name": "legacy_privatekey", "host": "localhost", "username": "test_user", "privateKey": "<paste key content here>" } } ``` 3. Verify connection works 4. Execute test command 5. Disconnect **Success Criteria**: - Legacy privateKey parameter still functional - No deprecation warnings (unless intended) - Same functionality as keyFilePath approach ### Test Case 8: Priority Testing (privateKey vs keyFilePath) **Objective**: Verify privateKey takes priority over keyFilePath when both provided **Preconditions**: - Two different SSH keys authorized for test_user - MCP server is running **Execution Steps**: 1. Provide both privateKey and keyFilePath: ```json { "tool": "ssh_connect", "arguments": { "name": "priority_test", "host": "localhost", "username": "test_user", "privateKey": "<content of test_rsa_unencrypted>", "keyFilePath": "~/ssh-test-keys/valid/test_ed25519_unencrypted" } } ``` 2. Execute command to identify which key was used: ```json { "tool": "ssh_execute", "arguments": { "sessionName": "priority_test", "command": "ssh-add -L 2>/dev/null || echo $SSH_CONNECTION" } } ``` 3. Verify privateKey was used (not keyFilePath) **Success Criteria**: - privateKey parameter takes precedence - No warnings about conflicting parameters - Clear documentation of priority order ### Test Case 9: Tilde Expansion Validation **Objective**: Verify tilde (~) properly expands to home directory **Preconditions**: - Keys exist in home directory structure - MCP server is running **Execution Steps**: 1. Test with tilde path: ```json { "tool": "ssh_connect", "arguments": { "name": "tilde_expansion", "host": "localhost", "username": "test_user", "keyFilePath": "~/ssh-test-keys/valid/test_rsa_unencrypted" } } ``` 2. Test with absolute path: ```json { "tool": "ssh_connect", "arguments": { "name": "absolute_path", "host": "localhost", "username": "test_user", "keyFilePath": "/home/<current_user>/ssh-test-keys/valid/test_ed25519_unencrypted" } } ``` 3. Both should succeed equally **Success Criteria**: - Tilde paths correctly expand - Absolute paths work directly - Consistent behavior across path types ### Test Case 10: Concurrent Connections with Different Keys **Objective**: Verify multiple simultaneous connections using different key files **Preconditions**: - Multiple key pairs configured - MCP server is running **Execution Steps**: 1. Establish first connection with RSA key: ```json { "tool": "ssh_connect", "arguments": { "name": "session_rsa", "host": "localhost", "username": "test_user", "keyFilePath": "~/ssh-test-keys/valid/test_rsa_unencrypted" } } ``` 2. Establish second connection with ED25519 key: ```json { "tool": "ssh_connect", "arguments": { "name": "session_ed25519", "host": "localhost", "username": "test_user", "keyFilePath": "~/ssh-test-keys/valid/test_ed25519_unencrypted" } } ``` 3. Execute commands on both sessions: ```json { "tool": "ssh_execute", "arguments": { "sessionName": "session_rsa", "command": "echo 'RSA session active' && sleep 2" } } ``` ```json { "tool": "ssh_execute", "arguments": { "sessionName": "session_ed25519", "command": "echo 'ED25519 session active' && sleep 2" } } ``` 4. List active sessions: ```json { "tool": "ssh_list_sessions" } ``` 5. Verify both sessions appear as connected **Success Criteria**: - Multiple sessions work simultaneously - Each uses its specified key correctly - No session interference - Clean session management **Cleanup**: Disconnect both sessions ### Test Case 11: Performance and Load Testing **Objective**: Verify key file reading doesn't impact performance **Preconditions**: - Test environment prepared - MCP server is running **Execution Steps**: 1. Measure connection time with keyFilePath: ```bash time curl -X POST http://localhost:3000/rpc \ -H "Content-Type: application/json" \ -d '{"method":"ssh_connect","params":{"name":"perf_test","host":"localhost","username":"test_user","keyFilePath":"~/ssh-test-keys/valid/test_rsa_unencrypted"}}' ``` 2. Record connection establishment time (should be < 2 seconds) 3. Rapidly connect/disconnect 10 times: ```bash for i in {1..10}; do # Connect curl -X POST http://localhost:3000/rpc -H "Content-Type: application/json" \ -d "{\"method\":\"ssh_connect\",\"params\":{\"name\":\"load_test_$i\",\"host\":\"localhost\",\"username\":\"test_user\",\"keyFilePath\":\"~/ssh-test-keys/valid/test_rsa_unencrypted\"}}" # Disconnect curl -X POST http://localhost:3000/rpc -H "Content-Type: application/json" \ -d "{\"method\":\"ssh_disconnect\",\"params\":{\"sessionName\":\"load_test_$i\"}}" done ``` 4. Monitor server memory and CPU usage 5. Check for memory leaks or hanging connections **Success Criteria**: - Connection time < 2 seconds - No memory leaks detected - All connections properly closed - Server remains responsive ### Test Case 12: Error Recovery and Cleanup **Objective**: Verify proper cleanup after connection failures **Preconditions**: - MCP server is running - Test environment prepared **Execution Steps**: 1. Attempt failed connection (wrong host): ```json { "tool": "ssh_connect", "arguments": { "name": "failed_connection", "host": "nonexistent.host.local", "username": "test_user", "keyFilePath": "~/ssh-test-keys/valid/test_rsa_unencrypted" } } ``` 2. Verify timeout error after appropriate wait 3. Check session list doesn't contain failed session: ```json { "tool": "ssh_list_sessions" } ``` 4. Verify no zombie processes: ```bash ps aux | grep ssh | grep defunct ``` 5. Check server logs for proper error handling **Success Criteria**: - Failed connections don't appear in session list - No zombie processes created - Server remains stable after failures - Clear error messages returned ## Test Execution Tracking ### Pre-Test Checklist - [ ] SSH server running on localhost - [ ] test_user account configured with password123 - [ ] Test SSH keys generated - [ ] Public keys added to authorized_keys - [ ] MCP server built successfully - [ ] MCP server started and listening - [ ] Client tool ready for testing ### Test Results Summary | Test Case | Status | Execution Time | Notes | |-----------|--------|----------------|-------| | TC1: Unencrypted RSA | | | | | TC2: Encrypted RSA | | | | | TC3: ED25519 Support | | | | | TC4: Path Traversal Security | | | | | TC5: Invalid Key Files | | | | | TC6: Wrong Passphrase | | | | | TC7: Backward Compatibility | | | | | TC8: Priority Testing | | | | | TC9: Tilde Expansion | | | | | TC10: Concurrent Connections | | | | | TC11: Performance Testing | | | | | TC12: Error Recovery | | | | ## Troubleshooting Guide ### Common Issues and Solutions #### Issue: "Permission denied (publickey)" **Diagnosis Steps**: 1. Check SSH server logs: `sudo journalctl -u ssh -n 50` 2. Verify public key in authorized_keys: `sudo cat /home/test_user/.ssh/authorized_keys` 3. Check file permissions: `ls -la /home/test_user/.ssh/` **Solution**: - Ensure .ssh directory is 700 - Ensure authorized_keys is 600 - Verify public key matches private key #### Issue: "Key is encrypted but no passphrase provided" **Diagnosis Steps**: 1. Check key header: `head -n 1 ~/ssh-test-keys/valid/test_rsa_encrypted` 2. Look for "ENCRYPTED" in key file **Solution**: - Provide passphrase parameter - Or use unencrypted key #### Issue: Connection timeout **Diagnosis Steps**: 1. Test SSH directly: `ssh test_user@localhost` 2. Check firewall: `sudo iptables -L` 3. Verify SSH service: `sudo systemctl status ssh` **Solution**: - Ensure SSH service is running - Check firewall rules - Verify localhost resolves correctly #### Issue: "ENOENT: no such file or directory" **Diagnosis Steps**: 1. Check file exists: `ls -la ~/ssh-test-keys/valid/` 2. Verify path expansion: `echo ~` **Solution**: - Use absolute paths if tilde expansion fails - Verify file exists at specified location ### Log File Locations - MCP Server logs: Console output where `npm start` is running - SSH Server logs: `/var/log/auth.log` or `journalctl -u ssh` - System logs: `/var/log/syslog` ### Debug Commands ```bash # Check SSH server status sudo systemctl status ssh # Monitor authentication attempts sudo tail -f /var/log/auth.log # List active SSH connections ss -tnp | grep :22 # Check test_user SSH configuration sudo -u test_user ls -la ~/.ssh/ # Verify key fingerprints ssh-keygen -lf ~/ssh-test-keys/valid/test_rsa_unencrypted.pub ``` ## Cleanup Procedures ### After Each Test Case 1. Disconnect active sessions 2. Clear any temporary files 3. Reset test state ### Complete Test Cleanup ```bash # Remove test SSH keys rm -rf ~/ssh-test-keys # Remove authorized_keys entries (keep backup first) sudo -u test_user cp /home/test_user/.ssh/authorized_keys /home/test_user/.ssh/authorized_keys.backup sudo -u test_user > /home/test_user/.ssh/authorized_keys # Stop MCP server (Ctrl+C in terminal) # Clear any SSH agent keys ssh-add -D 2>/dev/null || true # Kill any hanging SSH processes pkill -f "ssh.*test_user" || true ``` ## Success Criteria Summary The SSH key file authentication enhancement is considered successfully validated when: 1. **Functional Requirements Met**: - All supported key types work (RSA, ED25519) - Both encrypted and unencrypted keys function correctly - Passphrase handling works as specified - Tilde expansion operates correctly - Error messages are clear and actionable 2. **Security Requirements Met**: - Path traversal attempts are blocked - No sensitive information leaked in errors - Passphrases not logged or exposed - File permissions properly validated 3. **Compatibility Requirements Met**: - Legacy privateKey parameter still works - Priority order (privateKey > keyFilePath > password) is correct - No regression in existing functionality 4. **Performance Requirements Met**: - Connection establishment < 2 seconds - No memory leaks detected - Concurrent connections work properly - Server remains stable under load 5. **Error Handling Requirements Met**: - All error conditions handled gracefully - Clear error messages for troubleshooting - Proper cleanup after failures - No zombie processes or hanging connections ## Test Report Template ``` Test Execution Date: _______________ Tester: _______________ Environment: _______________ MCP Server Version: _______________ Overall Result: PASS / FAIL Critical Issues Found: 1. 2. Non-Critical Issues Found: 1. 2. Recommendations: 1. 2. Sign-off: _______________ ```

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/LightspeedDMS/ssh-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server