cloudx-proxy 0.3.13__tar.gz → 0.3.15__tar.gz
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.
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/CHANGELOG.md +15 -0
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/PKG-INFO +1 -1
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/cloudx_proxy/_version.py +2 -2
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/cloudx_proxy/cli.py +7 -3
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/cloudx_proxy/core.py +9 -3
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/cloudx_proxy/setup.py +109 -32
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/cloudx_proxy.egg-info/PKG-INFO +1 -1
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/.github/workflows/release.yml +0 -0
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/.gitignore +0 -0
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/.releaserc +0 -0
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/CONTRIBUTING.md +0 -0
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/LICENSE +0 -0
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/README.md +0 -0
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/cloudx_proxy/__init__.py +0 -0
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/cloudx_proxy.egg-info/SOURCES.txt +0 -0
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/cloudx_proxy.egg-info/dependency_links.txt +0 -0
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/cloudx_proxy.egg-info/entry_points.txt +0 -0
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/cloudx_proxy.egg-info/requires.txt +0 -0
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/cloudx_proxy.egg-info/top_level.txt +0 -0
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/package.json +0 -0
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/pyproject.toml +0 -0
- {cloudx_proxy-0.3.13 → cloudx_proxy-0.3.15}/setup.cfg +0 -0
@@ -1,3 +1,18 @@
|
|
1
|
+
## [0.3.15](https://github.com/easytocloud/cloudX-proxy/compare/v0.3.14...v0.3.15) (2025-03-06)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* added --ssh-config ([893d919](https://github.com/easytocloud/cloudX-proxy/commit/893d919f7ef30dc5fd41a06b2c032d0035180e80))
|
7
|
+
* added --ssh-config also in connect ([75b7b3b](https://github.com/easytocloud/cloudX-proxy/commit/75b7b3b5ecac5f1a1012ce9d4905bc5aed444915))
|
8
|
+
|
9
|
+
## [0.3.14](https://github.com/easytocloud/cloudX-proxy/compare/v0.3.13...v0.3.14) (2025-03-06)
|
10
|
+
|
11
|
+
|
12
|
+
### Bug Fixes
|
13
|
+
|
14
|
+
* restricted permissions on generated files and directories ([6b7b057](https://github.com/easytocloud/cloudX-proxy/commit/6b7b057832ab95fc6cb00c759380663eec2960a5))
|
15
|
+
|
1
16
|
## [0.3.13](https://github.com/easytocloud/cloudX-proxy/compare/v0.3.12...v0.3.13) (2025-03-06)
|
2
17
|
|
3
18
|
|
@@ -17,8 +17,9 @@ def cli():
|
|
17
17
|
@click.option('--profile', default='vscode', help='AWS profile to use (default: vscode)')
|
18
18
|
@click.option('--region', help='AWS region (default: from profile, or eu-west-1 if not set)')
|
19
19
|
@click.option('--ssh-key', default='vscode', help='SSH key name to use (default: vscode)')
|
20
|
+
@click.option('--ssh-config', help='SSH config file to use (default: ~/.ssh/vscode/config)')
|
20
21
|
@click.option('--aws-env', help='AWS environment directory (default: ~/.aws, use name of directory in ~/.aws/aws-envs/)')
|
21
|
-
def connect(instance_id: str, port: int, profile: str, region: str, ssh_key: str, aws_env: str):
|
22
|
+
def connect(instance_id: str, port: int, profile: str, region: str, ssh_key: str, ssh_config: str, aws_env: str):
|
22
23
|
"""Connect to an EC2 instance via SSM.
|
23
24
|
|
24
25
|
INSTANCE_ID is the EC2 instance ID to connect to (e.g., i-0123456789abcdef0)
|
@@ -35,6 +36,7 @@ def connect(instance_id: str, port: int, profile: str, region: str, ssh_key: str
|
|
35
36
|
profile=profile,
|
36
37
|
region=region,
|
37
38
|
ssh_key=ssh_key,
|
39
|
+
ssh_config=ssh_config,
|
38
40
|
aws_env=aws_env
|
39
41
|
)
|
40
42
|
|
@@ -48,8 +50,9 @@ def connect(instance_id: str, port: int, profile: str, region: str, ssh_key: str
|
|
48
50
|
@cli.command()
|
49
51
|
@click.option('--profile', default='vscode', help='AWS profile to use (default: vscode)')
|
50
52
|
@click.option('--ssh-key', default='vscode', help='SSH key name to use (default: vscode)')
|
53
|
+
@click.option('--ssh-config', help='SSH config file to use (default: ~/.ssh/vscode/config)')
|
51
54
|
@click.option('--aws-env', help='AWS environment directory (default: ~/.aws, use name of directory in ~/.aws/aws-envs/)')
|
52
|
-
def setup(profile: str, ssh_key: str, aws_env: str):
|
55
|
+
def setup(profile: str, ssh_key: str, ssh_config: str, aws_env: str):
|
53
56
|
"""Set up AWS profile, SSH keys, and configuration for CloudX.
|
54
57
|
|
55
58
|
This command will:
|
@@ -61,9 +64,10 @@ def setup(profile: str, ssh_key: str, aws_env: str):
|
|
61
64
|
Example usage:
|
62
65
|
cloudx-proxy setup
|
63
66
|
cloudx-proxy setup --profile myprofile --ssh-key mykey
|
67
|
+
cloudx-proxy setup --ssh-config ~/.ssh/cloudx/config
|
64
68
|
"""
|
65
69
|
try:
|
66
|
-
setup = CloudXSetup(profile=profile, ssh_key=ssh_key, aws_env=aws_env)
|
70
|
+
setup = CloudXSetup(profile=profile, ssh_key=ssh_key, ssh_config=ssh_config, aws_env=aws_env)
|
67
71
|
|
68
72
|
print("\n\033[1;95m=== cloudx-proxy Setup ===\033[0m\n")
|
69
73
|
|
@@ -7,7 +7,7 @@ from botocore.exceptions import ClientError
|
|
7
7
|
|
8
8
|
class CloudXProxy:
|
9
9
|
def __init__(self, instance_id: str, port: int = 22, profile: str = "vscode",
|
10
|
-
region: str = None, ssh_key: str = "vscode", aws_env: str = None):
|
10
|
+
region: str = None, ssh_key: str = "vscode", ssh_config: str = None, aws_env: str = None):
|
11
11
|
"""Initialize CloudX client for SSH tunneling via AWS SSM.
|
12
12
|
|
13
13
|
Args:
|
@@ -39,8 +39,14 @@ class CloudXProxy:
|
|
39
39
|
self.ec2 = self.session.client('ec2')
|
40
40
|
self.ec2_connect = self.session.client('ec2-instance-connect')
|
41
41
|
|
42
|
-
# Set up SSH key
|
43
|
-
|
42
|
+
# Set up SSH configuration and key paths
|
43
|
+
if ssh_config:
|
44
|
+
self.ssh_config_file = os.path.expanduser(ssh_config)
|
45
|
+
self.ssh_dir = os.path.dirname(self.ssh_config_file)
|
46
|
+
else:
|
47
|
+
self.ssh_dir = os.path.expanduser("~/.ssh/vscode")
|
48
|
+
self.ssh_config_file = os.path.join(self.ssh_dir, "config")
|
49
|
+
|
44
50
|
self.ssh_key = os.path.join(self.ssh_dir, f"{ssh_key}.pub")
|
45
51
|
|
46
52
|
def log(self, message: str) -> None:
|
@@ -9,20 +9,28 @@ import boto3
|
|
9
9
|
from botocore.exceptions import ClientError
|
10
10
|
|
11
11
|
class CloudXSetup:
|
12
|
-
def __init__(self, profile: str = "vscode", ssh_key: str = "vscode", aws_env: str = None):
|
12
|
+
def __init__(self, profile: str = "vscode", ssh_key: str = "vscode", ssh_config: str = None, aws_env: str = None):
|
13
13
|
"""Initialize cloudx-proxy setup.
|
14
14
|
|
15
15
|
Args:
|
16
16
|
profile: AWS profile name (default: "vscode")
|
17
17
|
ssh_key: SSH key name (default: "vscode")
|
18
|
+
ssh_config: SSH config file path (default: None, uses ~/.ssh/vscode/config)
|
18
19
|
aws_env: AWS environment directory (default: None)
|
19
20
|
"""
|
20
21
|
self.profile = profile
|
21
22
|
self.ssh_key = ssh_key
|
22
23
|
self.aws_env = aws_env
|
23
24
|
self.home_dir = str(Path.home())
|
24
|
-
|
25
|
-
|
25
|
+
|
26
|
+
# Set up ssh config paths based on provided config or default
|
27
|
+
if ssh_config:
|
28
|
+
self.ssh_config_file = Path(os.path.expanduser(ssh_config))
|
29
|
+
self.ssh_dir = self.ssh_config_file.parent
|
30
|
+
else:
|
31
|
+
self.ssh_dir = Path(self.home_dir) / ".ssh" / "vscode"
|
32
|
+
self.ssh_config_file = self.ssh_dir / "config"
|
33
|
+
|
26
34
|
self.ssh_key_file = self.ssh_dir / f"{ssh_key}"
|
27
35
|
self.default_env = None
|
28
36
|
|
@@ -68,6 +76,25 @@ class CloudXSetup:
|
|
68
76
|
response = input(prompt_text)
|
69
77
|
return response if response else default
|
70
78
|
|
79
|
+
def _set_directory_permissions(self, directory: Path) -> bool:
|
80
|
+
"""Set proper permissions (700) on a directory for Unix-like systems.
|
81
|
+
|
82
|
+
Args:
|
83
|
+
directory: Path to the directory
|
84
|
+
|
85
|
+
Returns:
|
86
|
+
bool: True if permissions were set successfully
|
87
|
+
"""
|
88
|
+
try:
|
89
|
+
if platform.system() != 'Windows':
|
90
|
+
import stat
|
91
|
+
directory.chmod(stat.S_IRWXU) # 700 permissions (owner read/write/execute)
|
92
|
+
self.print_status(f"Set {directory} permissions to 700", True, 2)
|
93
|
+
return True
|
94
|
+
except Exception as e:
|
95
|
+
self.print_status(f"Error setting permissions: {str(e)}", False, 2)
|
96
|
+
return False
|
97
|
+
|
71
98
|
def setup_aws_profile(self) -> bool:
|
72
99
|
"""Set up AWS profile using aws configure command.
|
73
100
|
|
@@ -138,10 +165,20 @@ class CloudXSetup:
|
|
138
165
|
self.ssh_dir.mkdir(parents=True, exist_ok=True)
|
139
166
|
self.print_status("SSH directory exists", True, 2)
|
140
167
|
|
168
|
+
# Set proper permissions on the vscode directory
|
169
|
+
if not self._set_directory_permissions(self.ssh_dir):
|
170
|
+
return False
|
171
|
+
|
141
172
|
key_exists = self.ssh_key_file.exists() and (self.ssh_key_file.with_suffix('.pub')).exists()
|
142
173
|
|
143
174
|
if key_exists:
|
144
175
|
self.print_status(f"SSH key '{self.ssh_key}' exists", True, 2)
|
176
|
+
# Set proper permissions on existing key files
|
177
|
+
if platform.system() != 'Windows':
|
178
|
+
import stat
|
179
|
+
self.ssh_key_file.chmod(stat.S_IRUSR | stat.S_IWUSR) # 600 permissions (owner read/write)
|
180
|
+
self.ssh_key_file.with_suffix('.pub').chmod(stat.S_IRUSR | stat.S_IWUSR | stat.S_IROTH | stat.S_IRGRP) # 644 permissions
|
181
|
+
self.print_status("Set key file permissions", True, 2)
|
145
182
|
else:
|
146
183
|
self.print_status(f"Generating new SSH key '{self.ssh_key}'...", None, 2)
|
147
184
|
subprocess.run([
|
@@ -152,6 +189,12 @@ class CloudXSetup:
|
|
152
189
|
], check=True)
|
153
190
|
self.print_status("SSH key generated", True, 2)
|
154
191
|
|
192
|
+
# Set proper permissions on newly generated key files
|
193
|
+
if platform.system() != 'Windows':
|
194
|
+
import stat
|
195
|
+
self.ssh_key_file.chmod(stat.S_IRUSR | stat.S_IWUSR) # 600 permissions (owner read/write)
|
196
|
+
self.ssh_key_file.with_suffix('.pub').chmod(stat.S_IRUSR | stat.S_IWUSR | stat.S_IROTH | stat.S_IRGRP) # 644 permissions
|
197
|
+
self.print_status("Set key file permissions", True, 2)
|
155
198
|
|
156
199
|
return True
|
157
200
|
|
@@ -200,6 +243,13 @@ Host cloudx-{cloudx_env}-{hostname}
|
|
200
243
|
with open(self.ssh_config_file, 'a') as f:
|
201
244
|
f.write(host_entry)
|
202
245
|
self.print_status("Host entry added with settings", True, 2)
|
246
|
+
|
247
|
+
# Set proper permissions on the config file
|
248
|
+
if platform.system() != 'Windows':
|
249
|
+
import stat
|
250
|
+
self.ssh_config_file.chmod(stat.S_IRUSR | stat.S_IWUSR) # 600 permissions (owner read/write)
|
251
|
+
self.print_status("Set config file permissions to 600", True, 2)
|
252
|
+
|
203
253
|
return True
|
204
254
|
|
205
255
|
except Exception as e:
|
@@ -220,24 +270,16 @@ Host cloudx-{cloudx_env}-{hostname}
|
|
220
270
|
bool: True if directory was created or exists with proper permissions
|
221
271
|
"""
|
222
272
|
try:
|
223
|
-
# Create
|
224
|
-
|
225
|
-
control_dir = Path(self.home_dir) / ".ssh" / "control"
|
226
|
-
else:
|
227
|
-
control_dir = Path(self.home_dir) / ".ssh" / "control"
|
273
|
+
# Create control directory path
|
274
|
+
control_dir = Path(self.home_dir) / ".ssh" / "control"
|
228
275
|
|
229
276
|
# Create directory if it doesn't exist
|
230
277
|
if not control_dir.exists():
|
231
278
|
control_dir.mkdir(parents=True, exist_ok=True)
|
232
279
|
self.print_status(f"Created control directory: {control_dir}", True, 2)
|
233
280
|
|
234
|
-
# Set proper permissions
|
235
|
-
|
236
|
-
import stat
|
237
|
-
control_dir.chmod(stat.S_IRWXU) # 700 permissions (owner read/write/execute)
|
238
|
-
self.print_status("Set directory permissions to 700", True, 2)
|
239
|
-
|
240
|
-
return True
|
281
|
+
# Set proper permissions
|
282
|
+
return self._set_directory_permissions(control_dir)
|
241
283
|
|
242
284
|
except Exception as e:
|
243
285
|
self.print_status(f"Error creating control directory: {str(e)}", False, 2)
|
@@ -296,8 +338,10 @@ Host cloudx-{cloudx_env}-{hostname}
|
|
296
338
|
if f"Host cloudx-{cloudx_env}-*" in current_config:
|
297
339
|
self.print_status(f"Found existing config for cloudx-{cloudx_env}-*", True, 2)
|
298
340
|
choice = self.prompt(
|
299
|
-
"Would you like to \n
|
300
|
-
"
|
341
|
+
"Would you like to \n"
|
342
|
+
" 1: override the existing config\n"
|
343
|
+
" 2: add settings to the specific host entry?\n"
|
344
|
+
"Select an option",
|
301
345
|
"1"
|
302
346
|
)
|
303
347
|
if choice == "2":
|
@@ -324,6 +368,7 @@ Host cloudx-{cloudx_env}-{hostname}
|
|
324
368
|
# Create base config
|
325
369
|
self.print_status(f"Creating new config for cloudx-{cloudx_env}-*", None, 2)
|
326
370
|
# Build ProxyCommand with only non-default parameters
|
371
|
+
# We don't need to include ssh_config here as SSH will handle that through the config
|
327
372
|
proxy_command = "uvx cloudx-proxy connect %h %p"
|
328
373
|
if self.profile != "vscode":
|
329
374
|
proxy_command += f" --profile {self.profile}"
|
@@ -369,6 +414,12 @@ Host cloudx-{cloudx_env}-*
|
|
369
414
|
else:
|
370
415
|
self.ssh_config_file.write_text(base_config)
|
371
416
|
self.print_status("Base configuration created", True, 2)
|
417
|
+
|
418
|
+
# Set proper permissions on the config file
|
419
|
+
if platform.system() != 'Windows':
|
420
|
+
import stat
|
421
|
+
self.ssh_config_file.chmod(stat.S_IRUSR | stat.S_IWUSR) # 600 permissions (owner read/write)
|
422
|
+
self.print_status("Set config file permissions to 600", True, 2)
|
372
423
|
|
373
424
|
# Add specific host entry
|
374
425
|
self.print_status(f"Adding host entry for cloudx-{cloudx_env}-{hostname}", None, 2)
|
@@ -380,25 +431,51 @@ Host cloudx-{cloudx_env}-{hostname}
|
|
380
431
|
f.write(host_entry)
|
381
432
|
self.print_status("Host entry added", True, 2)
|
382
433
|
|
383
|
-
#
|
384
|
-
|
385
|
-
include_line = f"Include {self.ssh_config_file}\n"
|
434
|
+
# Handle system SSH config integration
|
435
|
+
system_config_path = Path(self.home_dir) / ".ssh" / "config"
|
386
436
|
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
437
|
+
# Ensure ~/.ssh directory has proper permissions
|
438
|
+
ssh_parent_dir = Path(self.home_dir) / ".ssh"
|
439
|
+
if not ssh_parent_dir.exists():
|
440
|
+
ssh_parent_dir.mkdir(parents=True, exist_ok=True)
|
441
|
+
self.print_status(f"Created SSH directory: {ssh_parent_dir}", True, 2)
|
442
|
+
self._set_directory_permissions(ssh_parent_dir)
|
443
|
+
|
444
|
+
# If our config file is the system config, we're done
|
445
|
+
if self.ssh_config_file.samefile(system_config_path) if self.ssh_config_file.exists() and system_config_path.exists() else str(self.ssh_config_file) == str(system_config_path):
|
446
|
+
self.print_status("Using system SSH config directly, no Include needed", True, 2)
|
395
447
|
else:
|
396
|
-
|
397
|
-
|
448
|
+
# Otherwise, make sure the system config includes our config file
|
449
|
+
include_line = f"Include {self.ssh_config_file}\n"
|
450
|
+
|
451
|
+
if system_config_path.exists():
|
452
|
+
content = system_config_path.read_text()
|
453
|
+
if include_line not in content:
|
454
|
+
with open(system_config_path, 'a') as f:
|
455
|
+
f.write(f"\n{include_line}")
|
456
|
+
self.print_status("Added include line to system SSH config", True, 2)
|
457
|
+
else:
|
458
|
+
self.print_status("System SSH config already includes our config", True, 2)
|
459
|
+
|
460
|
+
# Set correct permissions on system config file
|
461
|
+
if platform.system() != 'Windows':
|
462
|
+
import stat
|
463
|
+
system_config_path.chmod(stat.S_IRUSR | stat.S_IWUSR) # 600 permissions
|
464
|
+
self.print_status("Set system config file permissions to 600", True, 2)
|
465
|
+
else:
|
466
|
+
system_config_path.write_text(include_line)
|
467
|
+
self.print_status("Created system SSH config with include line", True, 2)
|
468
|
+
|
469
|
+
# Set correct permissions on newly created system config file
|
470
|
+
if platform.system() != 'Windows':
|
471
|
+
import stat
|
472
|
+
system_config_path.chmod(stat.S_IRUSR | stat.S_IWUSR) # 600 permissions
|
473
|
+
self.print_status("Set system config file permissions to 600", True, 2)
|
398
474
|
|
399
|
-
self.print_status("
|
400
|
-
self.print_status(f"
|
475
|
+
self.print_status("SSH configuration summary:", None)
|
476
|
+
self.print_status(f"System config: {system_config_path}", None, 2)
|
401
477
|
self.print_status(f"cloudx-proxy config: {self.ssh_config_file}", None, 2)
|
478
|
+
self.print_status(f"SSH key directory: {self.ssh_dir}", None, 2)
|
402
479
|
self.print_status(f"Connect using: ssh cloudx-{cloudx_env}-{hostname}", None, 2)
|
403
480
|
|
404
481
|
return True
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|