mcp-ssh-vps 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_ssh_vps-0.4.1.dist-info/METADATA +482 -0
- mcp_ssh_vps-0.4.1.dist-info/RECORD +47 -0
- mcp_ssh_vps-0.4.1.dist-info/WHEEL +5 -0
- mcp_ssh_vps-0.4.1.dist-info/entry_points.txt +4 -0
- mcp_ssh_vps-0.4.1.dist-info/licenses/LICENSE +21 -0
- mcp_ssh_vps-0.4.1.dist-info/top_level.txt +1 -0
- sshmcp/__init__.py +3 -0
- sshmcp/cli.py +473 -0
- sshmcp/config.py +155 -0
- sshmcp/core/__init__.py +5 -0
- sshmcp/core/container.py +291 -0
- sshmcp/models/__init__.py +15 -0
- sshmcp/models/command.py +69 -0
- sshmcp/models/file.py +102 -0
- sshmcp/models/machine.py +139 -0
- sshmcp/monitoring/__init__.py +0 -0
- sshmcp/monitoring/alerts.py +464 -0
- sshmcp/prompts/__init__.py +7 -0
- sshmcp/prompts/backup.py +151 -0
- sshmcp/prompts/deploy.py +115 -0
- sshmcp/prompts/monitor.py +146 -0
- sshmcp/resources/__init__.py +7 -0
- sshmcp/resources/logs.py +99 -0
- sshmcp/resources/metrics.py +204 -0
- sshmcp/resources/status.py +160 -0
- sshmcp/security/__init__.py +7 -0
- sshmcp/security/audit.py +314 -0
- sshmcp/security/rate_limiter.py +221 -0
- sshmcp/security/totp.py +392 -0
- sshmcp/security/validator.py +234 -0
- sshmcp/security/whitelist.py +169 -0
- sshmcp/server.py +632 -0
- sshmcp/ssh/__init__.py +6 -0
- sshmcp/ssh/async_client.py +247 -0
- sshmcp/ssh/client.py +464 -0
- sshmcp/ssh/executor.py +79 -0
- sshmcp/ssh/forwarding.py +368 -0
- sshmcp/ssh/pool.py +343 -0
- sshmcp/ssh/shell.py +518 -0
- sshmcp/ssh/transfer.py +461 -0
- sshmcp/tools/__init__.py +13 -0
- sshmcp/tools/commands.py +226 -0
- sshmcp/tools/files.py +220 -0
- sshmcp/tools/helpers.py +321 -0
- sshmcp/tools/history.py +372 -0
- sshmcp/tools/processes.py +214 -0
- sshmcp/tools/servers.py +484 -0
sshmcp/tools/files.py
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""MCP Tools for file operations."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import structlog
|
|
6
|
+
|
|
7
|
+
from sshmcp.config import get_machine
|
|
8
|
+
from sshmcp.security.audit import get_audit_logger
|
|
9
|
+
from sshmcp.security.validator import validate_path
|
|
10
|
+
from sshmcp.ssh.client import SSHExecutionError
|
|
11
|
+
from sshmcp.ssh.pool import get_pool
|
|
12
|
+
|
|
13
|
+
logger = structlog.get_logger()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def read_file(
|
|
17
|
+
host: str,
|
|
18
|
+
path: str,
|
|
19
|
+
encoding: str = "utf-8",
|
|
20
|
+
max_size: int = 1024 * 1024,
|
|
21
|
+
) -> dict[str, Any]:
|
|
22
|
+
"""
|
|
23
|
+
Read file content from remote VPS server.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
host: Name of the host from machines.json configuration.
|
|
27
|
+
path: Path to the file on remote server.
|
|
28
|
+
encoding: File encoding (default: utf-8).
|
|
29
|
+
max_size: Maximum file size to read in bytes (default: 1MB).
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Dictionary with:
|
|
33
|
+
- content: File content as string
|
|
34
|
+
- path: Full path to file
|
|
35
|
+
- size: File size in bytes
|
|
36
|
+
- encoding: File encoding used
|
|
37
|
+
- truncated: Whether content was truncated
|
|
38
|
+
- host: Host where file is located
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
ValueError: If host not found or path not allowed.
|
|
42
|
+
RuntimeError: If file cannot be read.
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
>>> read_file("production-server", "/var/log/app.log")
|
|
46
|
+
{"content": "log content...", "size": 1024, ...}
|
|
47
|
+
"""
|
|
48
|
+
audit = get_audit_logger()
|
|
49
|
+
|
|
50
|
+
# Get machine configuration
|
|
51
|
+
try:
|
|
52
|
+
machine = get_machine(host)
|
|
53
|
+
except Exception as e:
|
|
54
|
+
raise ValueError(f"Host not found: {host}") from e
|
|
55
|
+
|
|
56
|
+
# Validate path
|
|
57
|
+
is_valid, error_msg = validate_path(path, machine.security, "read")
|
|
58
|
+
if not is_valid:
|
|
59
|
+
audit.log_path_rejected(host, path, error_msg or "Path not allowed")
|
|
60
|
+
raise ValueError(f"Path not allowed: {error_msg}")
|
|
61
|
+
|
|
62
|
+
# Read file
|
|
63
|
+
pool = get_pool()
|
|
64
|
+
pool.register_machine(machine)
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
client = pool.get_client(host)
|
|
68
|
+
try:
|
|
69
|
+
result = client.read_file(path, encoding=encoding, max_size=max_size)
|
|
70
|
+
|
|
71
|
+
audit.log_file_read(host, path, result.size)
|
|
72
|
+
|
|
73
|
+
return result.to_dict()
|
|
74
|
+
|
|
75
|
+
finally:
|
|
76
|
+
pool.release_client(client)
|
|
77
|
+
|
|
78
|
+
except SSHExecutionError as e:
|
|
79
|
+
raise RuntimeError(f"Failed to read file: {e}") from e
|
|
80
|
+
except Exception as e:
|
|
81
|
+
raise RuntimeError(f"SSH error: {e}") from e
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def upload_file(
|
|
85
|
+
host: str,
|
|
86
|
+
path: str,
|
|
87
|
+
content: str,
|
|
88
|
+
mode: str | None = None,
|
|
89
|
+
) -> dict[str, Any]:
|
|
90
|
+
"""
|
|
91
|
+
Upload file to remote VPS server.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
host: Name of the host from machines.json configuration.
|
|
95
|
+
path: Destination path on remote server.
|
|
96
|
+
content: File content to write.
|
|
97
|
+
mode: Optional file permissions (e.g., "0644").
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Dictionary with:
|
|
101
|
+
- success: Whether upload was successful
|
|
102
|
+
- path: Path where file was uploaded
|
|
103
|
+
- size: Uploaded file size in bytes
|
|
104
|
+
- host: Host where file was uploaded
|
|
105
|
+
|
|
106
|
+
Raises:
|
|
107
|
+
ValueError: If host not found or path not allowed.
|
|
108
|
+
RuntimeError: If file cannot be written.
|
|
109
|
+
|
|
110
|
+
Example:
|
|
111
|
+
>>> upload_file("production-server", "/opt/app/config.json", '{"key": "value"}')
|
|
112
|
+
{"success": true, "path": "/opt/app/config.json", "size": 16, ...}
|
|
113
|
+
"""
|
|
114
|
+
audit = get_audit_logger()
|
|
115
|
+
|
|
116
|
+
# Get machine configuration
|
|
117
|
+
try:
|
|
118
|
+
machine = get_machine(host)
|
|
119
|
+
except Exception as e:
|
|
120
|
+
raise ValueError(f"Host not found: {host}") from e
|
|
121
|
+
|
|
122
|
+
# Validate path
|
|
123
|
+
is_valid, error_msg = validate_path(path, machine.security, "write")
|
|
124
|
+
if not is_valid:
|
|
125
|
+
audit.log_path_rejected(host, path, error_msg or "Path not allowed")
|
|
126
|
+
raise ValueError(f"Path not allowed: {error_msg}")
|
|
127
|
+
|
|
128
|
+
# Write file
|
|
129
|
+
pool = get_pool()
|
|
130
|
+
pool.register_machine(machine)
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
client = pool.get_client(host)
|
|
134
|
+
try:
|
|
135
|
+
result = client.write_file(path, content, mode=mode)
|
|
136
|
+
|
|
137
|
+
audit.log_file_write(host, path, result.size)
|
|
138
|
+
|
|
139
|
+
return result.to_dict()
|
|
140
|
+
|
|
141
|
+
finally:
|
|
142
|
+
pool.release_client(client)
|
|
143
|
+
|
|
144
|
+
except SSHExecutionError as e:
|
|
145
|
+
raise RuntimeError(f"Failed to write file: {e}") from e
|
|
146
|
+
except Exception as e:
|
|
147
|
+
raise RuntimeError(f"SSH error: {e}") from e
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def list_files(
|
|
151
|
+
host: str,
|
|
152
|
+
directory: str,
|
|
153
|
+
recursive: bool = False,
|
|
154
|
+
) -> dict[str, Any]:
|
|
155
|
+
"""
|
|
156
|
+
List files in directory on remote VPS server.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
host: Name of the host from machines.json configuration.
|
|
160
|
+
directory: Path to directory on remote server.
|
|
161
|
+
recursive: Whether to list files recursively (default: false).
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Dictionary with:
|
|
165
|
+
- files: List of file information objects
|
|
166
|
+
- directory: Listed directory path
|
|
167
|
+
- host: Host where files are located
|
|
168
|
+
- total_count: Total number of files
|
|
169
|
+
|
|
170
|
+
Raises:
|
|
171
|
+
ValueError: If host not found or path not allowed.
|
|
172
|
+
RuntimeError: If directory cannot be listed.
|
|
173
|
+
|
|
174
|
+
Example:
|
|
175
|
+
>>> list_files("production-server", "/var/www")
|
|
176
|
+
{"files": [...], "directory": "/var/www", "total_count": 10, ...}
|
|
177
|
+
"""
|
|
178
|
+
audit = get_audit_logger()
|
|
179
|
+
|
|
180
|
+
# Get machine configuration
|
|
181
|
+
try:
|
|
182
|
+
machine = get_machine(host)
|
|
183
|
+
except Exception as e:
|
|
184
|
+
raise ValueError(f"Host not found: {host}") from e
|
|
185
|
+
|
|
186
|
+
# Validate path
|
|
187
|
+
is_valid, error_msg = validate_path(directory, machine.security, "read")
|
|
188
|
+
if not is_valid:
|
|
189
|
+
audit.log_path_rejected(host, directory, error_msg or "Path not allowed")
|
|
190
|
+
raise ValueError(f"Path not allowed: {error_msg}")
|
|
191
|
+
|
|
192
|
+
# List files
|
|
193
|
+
pool = get_pool()
|
|
194
|
+
pool.register_machine(machine)
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
client = pool.get_client(host)
|
|
198
|
+
try:
|
|
199
|
+
files = client.list_files(directory, recursive=recursive)
|
|
200
|
+
|
|
201
|
+
audit.log(
|
|
202
|
+
event="directory_listed",
|
|
203
|
+
host=host,
|
|
204
|
+
result={"path": directory, "count": len(files)},
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
"files": [f.to_dict() for f in files],
|
|
209
|
+
"directory": directory,
|
|
210
|
+
"host": host,
|
|
211
|
+
"total_count": len(files),
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
finally:
|
|
215
|
+
pool.release_client(client)
|
|
216
|
+
|
|
217
|
+
except SSHExecutionError as e:
|
|
218
|
+
raise RuntimeError(f"Failed to list directory: {e}") from e
|
|
219
|
+
except Exception as e:
|
|
220
|
+
raise RuntimeError(f"SSH error: {e}") from e
|
sshmcp/tools/helpers.py
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"""MCP Tools for help and information."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from sshmcp.config import get_machine, list_machines
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_help(topic: str | None = None) -> dict[str, Any]:
|
|
9
|
+
"""
|
|
10
|
+
Get help information about available commands and features.
|
|
11
|
+
|
|
12
|
+
Provides documentation about tools, security profiles, and usage examples.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
topic: Specific topic to get help for. Options:
|
|
16
|
+
- "tools" - list all available MCP tools
|
|
17
|
+
- "security" - security profiles and allowed commands
|
|
18
|
+
- "servers" - server management commands
|
|
19
|
+
- "examples" - usage examples
|
|
20
|
+
- None - general overview
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Dictionary with help information.
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
>>> get_help("tools")
|
|
27
|
+
{"topic": "tools", "content": "Available tools: ..."}
|
|
28
|
+
"""
|
|
29
|
+
help_topics = {
|
|
30
|
+
"tools": {
|
|
31
|
+
"topic": "Available MCP Tools",
|
|
32
|
+
"tools": [
|
|
33
|
+
{
|
|
34
|
+
"name": "execute_command",
|
|
35
|
+
"description": "Execute shell command on remote server",
|
|
36
|
+
"args": "host (str), command (str), timeout (int, optional)",
|
|
37
|
+
"example": 'execute_command("prod", "docker ps -a")',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "execute_on_multiple",
|
|
41
|
+
"description": "Execute command on multiple servers",
|
|
42
|
+
"args": "hosts (list[str]), command (str)",
|
|
43
|
+
"example": 'execute_on_multiple(["prod", "staging"], "uptime")',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "list_servers",
|
|
47
|
+
"description": "List all configured servers",
|
|
48
|
+
"args": "tag (str, optional) - filter by tag",
|
|
49
|
+
"example": "list_servers() or list_servers(tag='production')",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "add_server",
|
|
53
|
+
"description": "Add new VPS server",
|
|
54
|
+
"args": "name, host, user, port, auth_type, key_path/password, tags",
|
|
55
|
+
"example": 'add_server("web1", "1.2.3.4", "root", tags=["production"])',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "remove_server",
|
|
59
|
+
"description": "Remove server from configuration",
|
|
60
|
+
"args": "name (str)",
|
|
61
|
+
"example": 'remove_server("old-server")',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "test_connection",
|
|
65
|
+
"description": "Test SSH connection to server",
|
|
66
|
+
"args": "name (str)",
|
|
67
|
+
"example": 'test_connection("prod")',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"name": "get_allowed_commands",
|
|
71
|
+
"description": "Get list of allowed commands for a server",
|
|
72
|
+
"args": "host (str)",
|
|
73
|
+
"example": 'get_allowed_commands("prod")',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"name": "read_file",
|
|
77
|
+
"description": "Read file content from remote server",
|
|
78
|
+
"args": "host (str), path (str), max_size (int, optional)",
|
|
79
|
+
"example": 'read_file("prod", "/var/log/app.log")',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"name": "upload_file",
|
|
83
|
+
"description": "Upload file to remote server",
|
|
84
|
+
"args": "host (str), remote_path (str), content (str)",
|
|
85
|
+
"example": 'upload_file("prod", "/tmp/script.sh", "#!/bin/bash\\necho hi")',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"name": "list_files",
|
|
89
|
+
"description": "List files in directory on remote server",
|
|
90
|
+
"args": "host (str), directory (str), recursive (bool)",
|
|
91
|
+
"example": 'list_files("prod", "/var/log")',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"name": "manage_process",
|
|
95
|
+
"description": "Manage processes (systemd/pm2/supervisor)",
|
|
96
|
+
"args": "host (str), action (start/stop/restart/status), process_name",
|
|
97
|
+
"example": 'manage_process("prod", "restart", "nginx")',
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
"security": {
|
|
102
|
+
"topic": "Security Profiles",
|
|
103
|
+
"profiles": [
|
|
104
|
+
{
|
|
105
|
+
"name": "strict",
|
|
106
|
+
"description": "Only safe read-only commands",
|
|
107
|
+
"allowed": [
|
|
108
|
+
"git pull/status",
|
|
109
|
+
"ls",
|
|
110
|
+
"cat",
|
|
111
|
+
"tail",
|
|
112
|
+
"df",
|
|
113
|
+
"free",
|
|
114
|
+
"uptime",
|
|
115
|
+
],
|
|
116
|
+
"forbidden": ["rm -rf", "sudo", "su -"],
|
|
117
|
+
"timeout": 30,
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"name": "moderate",
|
|
121
|
+
"description": "Standard DevOps commands",
|
|
122
|
+
"allowed": [
|
|
123
|
+
"git",
|
|
124
|
+
"npm",
|
|
125
|
+
"yarn",
|
|
126
|
+
"pip",
|
|
127
|
+
"pm2",
|
|
128
|
+
"systemctl",
|
|
129
|
+
"docker",
|
|
130
|
+
"ls",
|
|
131
|
+
"cat",
|
|
132
|
+
"ps",
|
|
133
|
+
"top",
|
|
134
|
+
],
|
|
135
|
+
"forbidden": ["rm -rf /", "sudo rm"],
|
|
136
|
+
"timeout": 60,
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"name": "full",
|
|
140
|
+
"description": "All commands allowed",
|
|
141
|
+
"allowed": ["ALL commands"],
|
|
142
|
+
"forbidden": ["rm -rf /"],
|
|
143
|
+
"timeout": 120,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
"note": "Use security_level parameter when adding servers: add_server(..., security_level='full')",
|
|
147
|
+
},
|
|
148
|
+
"servers": {
|
|
149
|
+
"topic": "Server Management",
|
|
150
|
+
"commands": [
|
|
151
|
+
"list_servers() - List all servers",
|
|
152
|
+
"list_servers(tag='prod') - Filter by tag",
|
|
153
|
+
"add_server(name, host, user, ...) - Add new server",
|
|
154
|
+
"remove_server(name) - Remove server",
|
|
155
|
+
"update_server(name, ...) - Update server settings",
|
|
156
|
+
"test_connection(name) - Test SSH connection",
|
|
157
|
+
],
|
|
158
|
+
"tips": [
|
|
159
|
+
"Use tags to organize servers: tags=['production', 'web']",
|
|
160
|
+
"Test connection after adding: test_connection('server-name')",
|
|
161
|
+
"Use security_level='full' for unrestricted access",
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
"examples": {
|
|
165
|
+
"topic": "Usage Examples",
|
|
166
|
+
"examples": [
|
|
167
|
+
{
|
|
168
|
+
"task": "Check server status",
|
|
169
|
+
"commands": [
|
|
170
|
+
'execute_command("prod", "uptime")',
|
|
171
|
+
'execute_command("prod", "df -h")',
|
|
172
|
+
'execute_command("prod", "free -m")',
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"task": "Docker management",
|
|
177
|
+
"commands": [
|
|
178
|
+
'execute_command("prod", "docker ps -a")',
|
|
179
|
+
'execute_command("prod", "docker logs nginx --tail 100")',
|
|
180
|
+
'execute_command("prod", "docker restart nginx")',
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"task": "Deploy application",
|
|
185
|
+
"commands": [
|
|
186
|
+
'execute_command("prod", "cd /app && git pull")',
|
|
187
|
+
'execute_command("prod", "cd /app && npm install")',
|
|
188
|
+
'execute_command("prod", "pm2 restart all")',
|
|
189
|
+
],
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"task": "Check logs",
|
|
193
|
+
"commands": [
|
|
194
|
+
'execute_command("prod", "tail -100 /var/log/nginx/error.log")',
|
|
195
|
+
'execute_command("prod", "journalctl -u nginx --since \'1 hour ago\'")',
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"task": "Run on multiple servers",
|
|
200
|
+
"commands": [
|
|
201
|
+
'execute_on_multiple(["web1", "web2", "web3"], "uptime")',
|
|
202
|
+
'execute_on_multiple(["prod", "staging"], "docker ps")',
|
|
203
|
+
],
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
},
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if topic is None:
|
|
210
|
+
return {
|
|
211
|
+
"topic": "SSH MCP Server Help",
|
|
212
|
+
"description": "MCP server for managing VPS via SSH",
|
|
213
|
+
"available_topics": list(help_topics.keys()),
|
|
214
|
+
"usage": "Call get_help(topic) for detailed information",
|
|
215
|
+
"quick_start": [
|
|
216
|
+
"1. list_servers() - See available servers",
|
|
217
|
+
"2. test_connection('server-name') - Test connection",
|
|
218
|
+
"3. execute_command('server-name', 'uptime') - Run command",
|
|
219
|
+
],
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if topic not in help_topics:
|
|
223
|
+
return {
|
|
224
|
+
"error": f"Unknown topic: {topic}",
|
|
225
|
+
"available_topics": list(help_topics.keys()),
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return help_topics[topic]
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def get_allowed_commands(host: str) -> dict[str, Any]:
|
|
232
|
+
"""
|
|
233
|
+
Get the list of allowed and forbidden commands for a server.
|
|
234
|
+
|
|
235
|
+
Shows the security configuration including command patterns,
|
|
236
|
+
timeout settings, and path restrictions.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
host: Name of the server to check.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
Dictionary with security configuration details.
|
|
243
|
+
|
|
244
|
+
Example:
|
|
245
|
+
>>> get_allowed_commands("production")
|
|
246
|
+
{"host": "production", "allowed": [".*"], "forbidden": ["rm -rf /"]}
|
|
247
|
+
"""
|
|
248
|
+
try:
|
|
249
|
+
machine = get_machine(host)
|
|
250
|
+
except Exception:
|
|
251
|
+
available = list_machines()
|
|
252
|
+
return {
|
|
253
|
+
"success": False,
|
|
254
|
+
"error": f"Server '{host}' not found",
|
|
255
|
+
"available_servers": available,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
security = machine.security
|
|
259
|
+
|
|
260
|
+
# Determine security level based on patterns
|
|
261
|
+
if security.allowed_commands == [r".*"] or ".*" in security.allowed_commands:
|
|
262
|
+
security_level = "full"
|
|
263
|
+
elif len(security.allowed_commands) > 10:
|
|
264
|
+
security_level = "moderate"
|
|
265
|
+
else:
|
|
266
|
+
security_level = "strict"
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
"success": True,
|
|
270
|
+
"host": host,
|
|
271
|
+
"security_level": security_level,
|
|
272
|
+
"allowed_commands": security.allowed_commands,
|
|
273
|
+
"forbidden_commands": security.forbidden_commands,
|
|
274
|
+
"timeout_seconds": security.timeout_seconds,
|
|
275
|
+
"max_concurrent_commands": security.max_concurrent_commands,
|
|
276
|
+
"allowed_paths": security.allowed_paths or ["all paths allowed"],
|
|
277
|
+
"forbidden_paths": security.forbidden_paths or [],
|
|
278
|
+
"tip": "Use update_server(name, security_level='full') to allow all commands",
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def get_server_info(host: str) -> dict[str, Any]:
|
|
283
|
+
"""
|
|
284
|
+
Get detailed information about a server.
|
|
285
|
+
|
|
286
|
+
Returns configuration details, security settings, and connection info.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
host: Name of the server.
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
Dictionary with server details.
|
|
293
|
+
|
|
294
|
+
Example:
|
|
295
|
+
>>> get_server_info("production")
|
|
296
|
+
{"name": "production", "host": "1.2.3.4", "user": "deploy", ...}
|
|
297
|
+
"""
|
|
298
|
+
try:
|
|
299
|
+
machine = get_machine(host)
|
|
300
|
+
except Exception:
|
|
301
|
+
available = list_machines()
|
|
302
|
+
return {
|
|
303
|
+
"success": False,
|
|
304
|
+
"error": f"Server '{host}' not found",
|
|
305
|
+
"available_servers": available,
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
"success": True,
|
|
310
|
+
"name": machine.name,
|
|
311
|
+
"host": machine.host,
|
|
312
|
+
"port": machine.port,
|
|
313
|
+
"user": machine.user,
|
|
314
|
+
"description": machine.description,
|
|
315
|
+
"tags": getattr(machine, "tags", []),
|
|
316
|
+
"auth_type": machine.auth.type,
|
|
317
|
+
"security_level": "full"
|
|
318
|
+
if ".*" in machine.security.allowed_commands
|
|
319
|
+
else "restricted",
|
|
320
|
+
"timeout_seconds": machine.security.timeout_seconds,
|
|
321
|
+
}
|