cloudx-proxy 0.3.12__tar.gz → 0.3.14__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.
Files changed (22) hide show
  1. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/CHANGELOG.md +14 -0
  2. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/PKG-INFO +1 -1
  3. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/cloudx_proxy/_version.py +9 -4
  4. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/cloudx_proxy/setup.py +116 -3
  5. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/cloudx_proxy.egg-info/PKG-INFO +1 -1
  6. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/.github/workflows/release.yml +0 -0
  7. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/.gitignore +0 -0
  8. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/.releaserc +0 -0
  9. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/CONTRIBUTING.md +0 -0
  10. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/LICENSE +0 -0
  11. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/README.md +0 -0
  12. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/cloudx_proxy/__init__.py +0 -0
  13. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/cloudx_proxy/cli.py +0 -0
  14. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/cloudx_proxy/core.py +0 -0
  15. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/cloudx_proxy.egg-info/SOURCES.txt +0 -0
  16. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/cloudx_proxy.egg-info/dependency_links.txt +0 -0
  17. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/cloudx_proxy.egg-info/entry_points.txt +0 -0
  18. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/cloudx_proxy.egg-info/requires.txt +0 -0
  19. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/cloudx_proxy.egg-info/top_level.txt +0 -0
  20. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/package.json +0 -0
  21. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/pyproject.toml +0 -0
  22. {cloudx_proxy-0.3.12 → cloudx_proxy-0.3.14}/setup.cfg +0 -0
@@ -1,3 +1,17 @@
1
+ ## [0.3.14](https://github.com/easytocloud/cloudX-proxy/compare/v0.3.13...v0.3.14) (2025-03-06)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * restricted permissions on generated files and directories ([6b7b057](https://github.com/easytocloud/cloudX-proxy/commit/6b7b057832ab95fc6cb00c759380663eec2960a5))
7
+
8
+ ## [0.3.13](https://github.com/easytocloud/cloudX-proxy/compare/v0.3.12...v0.3.13) (2025-03-06)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * create more stable ssh connection ([606f196](https://github.com/easytocloud/cloudX-proxy/commit/606f196e10b5c3237ea07e45ef80cacdd36af12b))
14
+
1
15
  ## [0.3.12](https://github.com/easytocloud/cloudX-proxy/compare/v0.3.11...v0.3.12) (2025-02-14)
2
16
 
3
17
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cloudx-proxy
3
- Version: 0.3.12
3
+ Version: 0.3.14
4
4
  Summary: SSH proxy command to connect VSCode with Cloud9/CloudX instance using AWS Systems Manager
5
5
  Author-email: easytocloud <info@easytocloud.com>
6
6
  License: MIT License
@@ -1,8 +1,13 @@
1
- # file generated by setuptools_scm
1
+ # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
3
6
  TYPE_CHECKING = False
4
7
  if TYPE_CHECKING:
5
- from typing import Tuple, Union
8
+ from typing import Tuple
9
+ from typing import Union
10
+
6
11
  VERSION_TUPLE = Tuple[Union[int, str], ...]
7
12
  else:
8
13
  VERSION_TUPLE = object
@@ -12,5 +17,5 @@ __version__: str
12
17
  __version_tuple__: VERSION_TUPLE
13
18
  version_tuple: VERSION_TUPLE
14
19
 
15
- __version__ = version = '0.3.12'
16
- __version_tuple__ = version_tuple = (0, 3, 12)
20
+ __version__ = version = '0.3.14'
21
+ __version_tuple__ = version_tuple = (0, 3, 14)
@@ -68,6 +68,25 @@ class CloudXSetup:
68
68
  response = input(prompt_text)
69
69
  return response if response else default
70
70
 
71
+ def _set_directory_permissions(self, directory: Path) -> bool:
72
+ """Set proper permissions (700) on a directory for Unix-like systems.
73
+
74
+ Args:
75
+ directory: Path to the directory
76
+
77
+ Returns:
78
+ bool: True if permissions were set successfully
79
+ """
80
+ try:
81
+ if platform.system() != 'Windows':
82
+ import stat
83
+ directory.chmod(stat.S_IRWXU) # 700 permissions (owner read/write/execute)
84
+ self.print_status(f"Set {directory} permissions to 700", True, 2)
85
+ return True
86
+ except Exception as e:
87
+ self.print_status(f"Error setting permissions: {str(e)}", False, 2)
88
+ return False
89
+
71
90
  def setup_aws_profile(self) -> bool:
72
91
  """Set up AWS profile using aws configure command.
73
92
 
@@ -138,10 +157,20 @@ class CloudXSetup:
138
157
  self.ssh_dir.mkdir(parents=True, exist_ok=True)
139
158
  self.print_status("SSH directory exists", True, 2)
140
159
 
160
+ # Set proper permissions on the vscode directory
161
+ if not self._set_directory_permissions(self.ssh_dir):
162
+ return False
163
+
141
164
  key_exists = self.ssh_key_file.exists() and (self.ssh_key_file.with_suffix('.pub')).exists()
142
165
 
143
166
  if key_exists:
144
167
  self.print_status(f"SSH key '{self.ssh_key}' exists", True, 2)
168
+ # Set proper permissions on existing key files
169
+ if platform.system() != 'Windows':
170
+ import stat
171
+ self.ssh_key_file.chmod(stat.S_IRUSR | stat.S_IWUSR) # 600 permissions (owner read/write)
172
+ self.ssh_key_file.with_suffix('.pub').chmod(stat.S_IRUSR | stat.S_IWUSR | stat.S_IROTH | stat.S_IRGRP) # 644 permissions
173
+ self.print_status("Set key file permissions", True, 2)
145
174
  else:
146
175
  self.print_status(f"Generating new SSH key '{self.ssh_key}'...", None, 2)
147
176
  subprocess.run([
@@ -152,6 +181,12 @@ class CloudXSetup:
152
181
  ], check=True)
153
182
  self.print_status("SSH key generated", True, 2)
154
183
 
184
+ # Set proper permissions on newly generated key files
185
+ if platform.system() != 'Windows':
186
+ import stat
187
+ self.ssh_key_file.chmod(stat.S_IRUSR | stat.S_IWUSR) # 600 permissions (owner read/write)
188
+ self.ssh_key_file.with_suffix('.pub').chmod(stat.S_IRUSR | stat.S_IWUSR | stat.S_IROTH | stat.S_IRGRP) # 644 permissions
189
+ self.print_status("Set key file permissions", True, 2)
155
190
 
156
191
  return True
157
192
 
@@ -200,6 +235,13 @@ Host cloudx-{cloudx_env}-{hostname}
200
235
  with open(self.ssh_config_file, 'a') as f:
201
236
  f.write(host_entry)
202
237
  self.print_status("Host entry added with settings", True, 2)
238
+
239
+ # Set proper permissions on the config file
240
+ if platform.system() != 'Windows':
241
+ import stat
242
+ self.ssh_config_file.chmod(stat.S_IRUSR | stat.S_IWUSR) # 600 permissions (owner read/write)
243
+ self.print_status("Set config file permissions to 600", True, 2)
244
+
203
245
  return True
204
246
 
205
247
  except Exception as e:
@@ -210,6 +252,31 @@ Host cloudx-{cloudx_env}-{hostname}
210
252
  return True
211
253
  return False
212
254
 
255
+ def _ensure_control_dir(self) -> bool:
256
+ """Create SSH control directory with proper permissions.
257
+
258
+ Creates ~/.ssh/control directory with 700 permissions on Unix-like systems,
259
+ or appropriate permissions on Windows.
260
+
261
+ Returns:
262
+ bool: True if directory was created or exists with proper permissions
263
+ """
264
+ try:
265
+ # Create control directory path
266
+ control_dir = Path(self.home_dir) / ".ssh" / "control"
267
+
268
+ # Create directory if it doesn't exist
269
+ if not control_dir.exists():
270
+ control_dir.mkdir(parents=True, exist_ok=True)
271
+ self.print_status(f"Created control directory: {control_dir}", True, 2)
272
+
273
+ # Set proper permissions
274
+ return self._set_directory_permissions(control_dir)
275
+
276
+ except Exception as e:
277
+ self.print_status(f"Error creating control directory: {str(e)}", False, 2)
278
+ return False
279
+
213
280
  def setup_ssh_config(self, cloudx_env: str, instance_id: str, hostname: str) -> bool:
214
281
  """Set up SSH config for the instance.
215
282
 
@@ -219,6 +286,7 @@ Host cloudx-{cloudx_env}-{hostname}
219
286
  - User and key configuration
220
287
  - 1Password SSH agent integration if selected
221
288
  - ProxyCommand using uvx cloudx-proxy with proper parameters
289
+ - SSH multiplexing configuration (ControlMaster, ControlPath, ControlPersist)
222
290
 
223
291
  2. For an existing environment:
224
292
  - Skips creating duplicate environment config
@@ -232,6 +300,10 @@ Host cloudx-{cloudx_env}-{hostname}
232
300
  IdentityAgent ~/.1password/agent.sock # If using 1Password
233
301
  IdentityFile ~/.ssh/vscode/key.pub # .pub for 1Password, no .pub otherwise
234
302
  IdentitiesOnly yes # If using 1Password
303
+ TCPKeepAlive yes
304
+ ControlMaster auto
305
+ ControlPath ~/.ssh/control/%r@%h:%p
306
+ ControlPersist 4h
235
307
  ProxyCommand uvx cloudx-proxy connect %h %p --profile profile --aws-env env
236
308
 
237
309
  # Host entries (added for each instance)
@@ -258,8 +330,8 @@ Host cloudx-{cloudx_env}-{hostname}
258
330
  if f"Host cloudx-{cloudx_env}-*" in current_config:
259
331
  self.print_status(f"Found existing config for cloudx-{cloudx_env}-*", True, 2)
260
332
  choice = self.prompt(
261
- "Would you like to (1) override the existing config or "
262
- "(2) add settings to the specific host entry?",
333
+ "Would you like to \n 1: override the existing config\n "
334
+ " 2: add settings to the specific host entry?\nSelect an option ",
263
335
  "1"
264
336
  )
265
337
  if choice == "2":
@@ -294,6 +366,10 @@ Host cloudx-{cloudx_env}-{hostname}
294
366
  if self.ssh_key != "vscode":
295
367
  proxy_command += f" --ssh-key {self.ssh_key}"
296
368
 
369
+ # Ensure control directory exists with proper permissions
370
+ if not self._ensure_control_dir():
371
+ return False
372
+
297
373
  # Build base configuration
298
374
  base_config = f"""# cloudx-proxy SSH Configuration
299
375
  Host cloudx-{cloudx_env}-*
@@ -303,6 +379,18 @@ Host cloudx-{cloudx_env}-*
303
379
  base_config += f""" IdentityFile {self.ssh_key_file}
304
380
  IdentitiesOnly yes
305
381
 
382
+ """
383
+ # Add SSH multiplexing configuration
384
+ control_path = "~/.ssh/control/%r@%h:%p"
385
+ if platform.system() == 'Windows':
386
+ # Use forward slashes for Windows as well, SSH client will handle conversion
387
+ control_path = "~/.ssh/control/%r@%h:%p"
388
+
389
+ base_config += f""" TCPKeepAlive yes
390
+ ControlMaster auto
391
+ ControlPath {control_path}
392
+ ControlPersist 4h
393
+
306
394
  """
307
395
  # Add ProxyCommand
308
396
  base_config += f""" ProxyCommand {proxy_command}
@@ -315,6 +403,12 @@ Host cloudx-{cloudx_env}-*
315
403
  else:
316
404
  self.ssh_config_file.write_text(base_config)
317
405
  self.print_status("Base configuration created", True, 2)
406
+
407
+ # Set proper permissions on the config file
408
+ if platform.system() != 'Windows':
409
+ import stat
410
+ self.ssh_config_file.chmod(stat.S_IRUSR | stat.S_IWUSR) # 600 permissions (owner read/write)
411
+ self.print_status("Set config file permissions to 600", True, 2)
318
412
 
319
413
  # Add specific host entry
320
414
  self.print_status(f"Adding host entry for cloudx-{cloudx_env}-{hostname}", None, 2)
@@ -327,6 +421,13 @@ Host cloudx-{cloudx_env}-{hostname}
327
421
  self.print_status("Host entry added", True, 2)
328
422
 
329
423
  # Ensure main SSH config includes our config
424
+ # Ensure ~/.ssh directory has proper permissions
425
+ ssh_parent_dir = Path(self.home_dir) / ".ssh"
426
+ if not ssh_parent_dir.exists():
427
+ ssh_parent_dir.mkdir(parents=True, exist_ok=True)
428
+ self.print_status(f"Created SSH directory: {ssh_parent_dir}", True, 2)
429
+ self._set_directory_permissions(ssh_parent_dir)
430
+
330
431
  main_config = Path(self.home_dir) / ".ssh" / "config"
331
432
  include_line = f"Include {self.ssh_config_file}\n"
332
433
 
@@ -338,11 +439,23 @@ Host cloudx-{cloudx_env}-{hostname}
338
439
  self.print_status("Added include line to main SSH config", True, 2)
339
440
  else:
340
441
  self.print_status("Main SSH config already includes our config", True, 2)
442
+
443
+ # Set correct permissions on main config file
444
+ if platform.system() != 'Windows':
445
+ import stat
446
+ main_config.chmod(stat.S_IRUSR | stat.S_IWUSR) # 600 permissions
447
+ self.print_status("Set main config file permissions to 600", True, 2)
341
448
  else:
342
449
  main_config.write_text(include_line)
343
450
  self.print_status("Created main SSH config with include line", True, 2)
451
+
452
+ # Set correct permissions on newly created main config file
453
+ if platform.system() != 'Windows':
454
+ import stat
455
+ main_config.chmod(stat.S_IRUSR | stat.S_IWUSR) # 600 permissions
456
+ self.print_status("Set main config file permissions to 600", True, 2)
344
457
 
345
- self.print_status("\nSSH configuration summary:", None)
458
+ self.print_status("SSH configuration summary:", None)
346
459
  self.print_status(f"Main config: {main_config}", None, 2)
347
460
  self.print_status(f"cloudx-proxy config: {self.ssh_config_file}", None, 2)
348
461
  self.print_status(f"Connect using: ssh cloudx-{cloudx_env}-{hostname}", None, 2)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cloudx-proxy
3
- Version: 0.3.12
3
+ Version: 0.3.14
4
4
  Summary: SSH proxy command to connect VSCode with Cloud9/CloudX instance using AWS Systems Manager
5
5
  Author-email: easytocloud <info@easytocloud.com>
6
6
  License: MIT License
File without changes
File without changes
File without changes
File without changes
File without changes