cloudx-proxy 0.4.4__tar.gz → 0.4.6__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 (23) hide show
  1. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/CHANGELOG.md +14 -0
  2. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/PKG-INFO +25 -1
  3. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/README.md +24 -0
  4. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/cloudx_proxy/_version.py +2 -2
  5. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/cloudx_proxy/cli.py +15 -4
  6. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/cloudx_proxy/setup.py +70 -39
  7. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/cloudx_proxy.egg-info/PKG-INFO +25 -1
  8. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/.github/workflows/release.yml +0 -0
  9. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/.gitignore +0 -0
  10. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/.releaserc +0 -0
  11. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/CONTRIBUTING.md +0 -0
  12. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/LICENSE +0 -0
  13. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/cloudx_proxy/_1password.py +0 -0
  14. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/cloudx_proxy/__init__.py +0 -0
  15. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/cloudx_proxy/core.py +0 -0
  16. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/cloudx_proxy.egg-info/SOURCES.txt +0 -0
  17. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/cloudx_proxy.egg-info/dependency_links.txt +0 -0
  18. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/cloudx_proxy.egg-info/entry_points.txt +0 -0
  19. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/cloudx_proxy.egg-info/requires.txt +0 -0
  20. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/cloudx_proxy.egg-info/top_level.txt +0 -0
  21. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/package.json +0 -0
  22. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/pyproject.toml +0 -0
  23. {cloudx_proxy-0.4.4 → cloudx_proxy-0.4.6}/setup.cfg +0 -0
@@ -1,3 +1,17 @@
1
+ ## [0.4.6](https://github.com/easytocloud/cloudX-proxy/compare/v0.4.5...v0.4.6) (2025-03-07)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * added --yes for non-interactive setup ([7e6007f](https://github.com/easytocloud/cloudX-proxy/commit/7e6007f68db3a958d1987a308f20fa85b5f7289f))
7
+
8
+ ## [0.4.5](https://github.com/easytocloud/cloudX-proxy/compare/v0.4.4...v0.4.5) (2025-03-07)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * 1Password key matching ([4583e20](https://github.com/easytocloud/cloudX-proxy/commit/4583e20915b9bf8ab5cb2244676ee735aaa35cfc))
14
+
1
15
  ## [0.4.4](https://github.com/easytocloud/cloudX-proxy/compare/v0.4.3...v0.4.4) (2025-03-07)
2
16
 
3
17
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cloudx-proxy
3
- Version: 0.4.4
3
+ Version: 0.4.6
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
@@ -224,6 +224,30 @@ When adding new instances to an existing environment, you can choose to:
224
224
 
225
225
  This three-tier structure offers better maintainability by reducing duplication and making it clear which settings apply broadly and which are specific to an environment or host.
226
226
 
227
+ ### Security Model: AWS and SSH Integration
228
+
229
+ cloudX-proxy implements a unique dual-layer security approach that combines AWS's robust authentication mechanisms with SSH's connection handling capabilities:
230
+
231
+ #### AWS Security Layer (Primary)
232
+ The primary security boundary is enforced through AWS Systems Manager (SSM) and EC2 Instance Connect. This layer provides:
233
+ - **Access Control**: Only authenticated AWS users with appropriate IAM permissions can establish SSM sessions
234
+ - **Dynamic Key Authorization**: EC2 Instance Connect allows temporary injection of SSH public keys, valid only for a single session
235
+ - **Network Security**: No inbound SSH ports need to be exposed, as all connections are established through AWS SSM's secure tunneling
236
+ - **Audit Trail**: All connection attempts and key pushes are logged in AWS CloudTrail
237
+
238
+ #### SSH Layer (Secondary)
239
+ SSH serves primarily as a connection handler rather than the main security mechanism:
240
+ - **Ephemeral Authentication**: The SSH key pair is used only to establish the connection through the SSM tunnel
241
+ - **Session Management**: SSH handles the actual terminal session, file transfers, and multiplexing
242
+ - **Key Flexibility**: Since keys are pushed dynamically for each session, the same key can safely be used across multiple instances
243
+ - **Zero Trust Model**: Even if a key is compromised, access still requires valid AWS credentials and permissions
244
+
245
+ This architecture means that:
246
+ 1. The security of the connection relies primarily on AWS IAM permissions and SSM session management
247
+ 2. SSH keys can be reused across instances without security implications
248
+ 3. Each connection gets a fresh key authorization through EC2 Instance Connect
249
+ 4. Instances remain completely closed to direct SSH access from the internet
250
+
227
251
  ### VSCode Configuration
228
252
 
229
253
  1. Install the "Remote - SSH" extension in VSCode
@@ -174,6 +174,30 @@ When adding new instances to an existing environment, you can choose to:
174
174
 
175
175
  This three-tier structure offers better maintainability by reducing duplication and making it clear which settings apply broadly and which are specific to an environment or host.
176
176
 
177
+ ### Security Model: AWS and SSH Integration
178
+
179
+ cloudX-proxy implements a unique dual-layer security approach that combines AWS's robust authentication mechanisms with SSH's connection handling capabilities:
180
+
181
+ #### AWS Security Layer (Primary)
182
+ The primary security boundary is enforced through AWS Systems Manager (SSM) and EC2 Instance Connect. This layer provides:
183
+ - **Access Control**: Only authenticated AWS users with appropriate IAM permissions can establish SSM sessions
184
+ - **Dynamic Key Authorization**: EC2 Instance Connect allows temporary injection of SSH public keys, valid only for a single session
185
+ - **Network Security**: No inbound SSH ports need to be exposed, as all connections are established through AWS SSM's secure tunneling
186
+ - **Audit Trail**: All connection attempts and key pushes are logged in AWS CloudTrail
187
+
188
+ #### SSH Layer (Secondary)
189
+ SSH serves primarily as a connection handler rather than the main security mechanism:
190
+ - **Ephemeral Authentication**: The SSH key pair is used only to establish the connection through the SSM tunnel
191
+ - **Session Management**: SSH handles the actual terminal session, file transfers, and multiplexing
192
+ - **Key Flexibility**: Since keys are pushed dynamically for each session, the same key can safely be used across multiple instances
193
+ - **Zero Trust Model**: Even if a key is compromised, access still requires valid AWS credentials and permissions
194
+
195
+ This architecture means that:
196
+ 1. The security of the connection relies primarily on AWS IAM permissions and SSM session management
197
+ 2. SSH keys can be reused across instances without security implications
198
+ 3. Each connection gets a fresh key authorization through EC2 Instance Connect
199
+ 4. Instances remain completely closed to direct SSH access from the internet
200
+
177
201
  ### VSCode Configuration
178
202
 
179
203
  1. Install the "Remote - SSH" extension in VSCode
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.4.4'
21
- __version_tuple__ = version_tuple = (0, 4, 4)
20
+ __version__ = version = '0.4.6'
21
+ __version_tuple__ = version_tuple = (0, 4, 6)
@@ -55,7 +55,10 @@ def connect(instance_id: str, port: int, profile: str, region: str, ssh_key: str
55
55
  @click.option('--ssh-config', help='SSH config file to use (default: ~/.ssh/vscode/config)')
56
56
  @click.option('--aws-env', help='AWS environment directory (default: ~/.aws, use name of directory in ~/.aws/aws-envs/)')
57
57
  @click.option('--1password', 'use_1password', is_flag=True, help='Use 1Password SSH agent for SSH authentication')
58
- def setup(profile: str, ssh_key: str, ssh_config: str, aws_env: str, use_1password: bool):
58
+ @click.option('--instance', help='EC2 instance ID to set up connection for')
59
+ @click.option('--yes', 'non_interactive', is_flag=True, help='Non-interactive mode, use default values for all prompts')
60
+ def setup(profile: str, ssh_key: str, ssh_config: str, aws_env: str, use_1password: bool,
61
+ instance: str, non_interactive: bool):
59
62
  """Set up AWS profile, SSH keys, and configuration for CloudX.
60
63
 
61
64
  This command will:
@@ -69,6 +72,7 @@ def setup(profile: str, ssh_key: str, ssh_config: str, aws_env: str, use_1passwo
69
72
  cloudx-proxy setup --profile myprofile --ssh-key mykey
70
73
  cloudx-proxy setup --ssh-config ~/.ssh/cloudx/config
71
74
  cloudx-proxy setup --1password
75
+ cloudx-proxy setup --instance i-0123456789abcdef0 --yes
72
76
  """
73
77
  try:
74
78
  setup = CloudXSetup(
@@ -76,7 +80,9 @@ def setup(profile: str, ssh_key: str, ssh_config: str, aws_env: str, use_1passwo
76
80
  ssh_key=ssh_key,
77
81
  ssh_config=ssh_config,
78
82
  aws_env=aws_env,
79
- use_1password=use_1password
83
+ use_1password=use_1password,
84
+ instance_id=instance,
85
+ non_interactive=non_interactive
80
86
  )
81
87
 
82
88
  print("\n\033[1;95m=== cloudx-proxy Setup ===\033[0m\n")
@@ -91,8 +97,13 @@ def setup(profile: str, ssh_key: str, ssh_config: str, aws_env: str, use_1passwo
91
97
 
92
98
  # Get environment and instance details
93
99
  cloudx_env = setup.prompt("Enter environment", getattr(setup, 'default_env', None))
94
- instance_id = setup.prompt("Enter EC2 instance ID (e.g., i-0123456789abcdef0)")
95
- hostname = setup.prompt("Enter hostname for the instance")
100
+
101
+ # Use the --instance parameter if provided, otherwise prompt
102
+ instance_id = instance or setup.prompt("Enter EC2 instance ID (e.g., i-0123456789abcdef0)")
103
+
104
+ # Generate a default hostname based on instance ID if we're in non-interactive mode
105
+ hostname_default = f"instance-{instance_id[-7:]}" if non_interactive else None
106
+ hostname = setup.prompt("Enter hostname for the instance", hostname_default)
96
107
 
97
108
  # Set up SSH config
98
109
  if not setup.setup_ssh_config(cloudx_env, instance_id, hostname):
@@ -10,8 +10,12 @@ from botocore.exceptions import ClientError
10
10
  from ._1password import check_1password_cli, check_ssh_agent, list_ssh_keys, create_ssh_key, get_vaults, save_public_key
11
11
 
12
12
  class CloudXSetup:
13
+ # Define SSH key prefix as a constant
14
+ SSH_KEY_PREFIX = "cloudX SSH Key - "
15
+
13
16
  def __init__(self, profile: str = "vscode", ssh_key: str = "vscode", ssh_config: str = None,
14
- aws_env: str = None, use_1password: bool = False):
17
+ aws_env: str = None, use_1password: bool = False, instance_id: str = None,
18
+ non_interactive: bool = False):
15
19
  """Initialize cloudx-proxy setup.
16
20
 
17
21
  Args:
@@ -20,11 +24,15 @@ class CloudXSetup:
20
24
  ssh_config: SSH config file path (default: None, uses ~/.ssh/vscode/config)
21
25
  aws_env: AWS environment directory (default: None)
22
26
  use_1password: Use 1Password SSH agent for authentication (default: False)
27
+ instance_id: EC2 instance ID to set up connection for (optional)
28
+ non_interactive: Non-interactive mode, use defaults for all prompts (default: False)
23
29
  """
24
30
  self.profile = profile
25
31
  self.ssh_key = ssh_key
26
32
  self.aws_env = aws_env
27
33
  self.use_1password = use_1password
34
+ self.instance_id = instance_id
35
+ self.non_interactive = non_interactive
28
36
  self.home_dir = str(Path.home())
29
37
  self.onepassword_agent_sock = Path(self.home_dir) / ".1password" / "agent.sock"
30
38
 
@@ -74,6 +82,16 @@ class CloudXSetup:
74
82
  Returns:
75
83
  str: User's input or default value
76
84
  """
85
+ # In non-interactive mode, always use the default value
86
+ if self.non_interactive:
87
+ if default:
88
+ self.print_status(f"{message}: Using default [{default}]", None, 2)
89
+ return default
90
+ else:
91
+ self.print_status(f"{message}: No default value available", False, 2)
92
+ raise ValueError(f"Non-interactive mode requires default value for: {message}")
93
+
94
+ # Interactive prompt
77
95
  if default:
78
96
  prompt_text = f"\033[93m{message} [{default}]: \033[0m"
79
97
  else:
@@ -200,6 +218,43 @@ class CloudXSetup:
200
218
  bool: True if successful
201
219
  """
202
220
  try:
221
+ # Create possible title variations for the 1Password item
222
+ ssh_key_title_with_prefix = f"{self.SSH_KEY_PREFIX}{self.ssh_key}"
223
+ ssh_key_title_without_prefix = self.ssh_key
224
+
225
+ # First check if key exists in any vault
226
+ ssh_keys = list_ssh_keys()
227
+
228
+ # Check for both prefixed and non-prefixed format
229
+ existing_key = next((key for key in ssh_keys if key['title'] == ssh_key_title_with_prefix), None)
230
+ if not existing_key:
231
+ existing_key = next((key for key in ssh_keys if key['title'] == ssh_key_title_without_prefix), None)
232
+
233
+ if existing_key:
234
+ key_title = existing_key['title']
235
+ self.print_status(f"SSH key '{key_title}' already exists in 1Password", True, 2)
236
+ # Get the public key
237
+ result = subprocess.run(
238
+ ['op', 'item', 'get', existing_key['id'], '--fields', 'public key'],
239
+ capture_output=True,
240
+ text=True,
241
+ check=False
242
+ )
243
+
244
+ if result.returncode == 0:
245
+ public_key = result.stdout.strip()
246
+ # Save it to the expected location
247
+ if save_public_key(public_key, f"{self.ssh_key_file}.pub"):
248
+ self.print_status(f"Saved existing public key to {self.ssh_key_file}.pub", True, 2)
249
+ return True
250
+ else:
251
+ self.print_status(f"Failed to save public key to {self.ssh_key_file}.pub", False, 2)
252
+ return False
253
+ else:
254
+ self.print_status(f"Failed to retrieve public key from 1Password", False, 2)
255
+ return False
256
+
257
+ # If we reach here, the key doesn't exist and we need to create it
203
258
  # Get vaults to determine where to store the key
204
259
  vaults = get_vaults()
205
260
  if not vaults:
@@ -223,48 +278,24 @@ class CloudXSetup:
223
278
  except ValueError:
224
279
  self.print_status("Invalid input", False, 2)
225
280
  return False
281
+
282
+ # Create a new SSH key in 1Password
283
+ self.print_status(f"Creating new SSH key '{ssh_key_title_with_prefix}' in 1Password...", None, 2)
284
+ success, public_key, item_id = create_ssh_key(ssh_key_title_with_prefix, selected_vault)
226
285
 
227
- # Create a title for the 1Password item
228
- ssh_key_title = f"cloudX SSH Key - {self.ssh_key}"
286
+ if not success:
287
+ self.print_status("Failed to create SSH key in 1Password", False, 2)
288
+ return False
229
289
 
230
- # Check if a key with this title already exists in 1Password
231
- ssh_keys = list_ssh_keys()
232
- existing_key = next((key for key in ssh_keys if key['title'] == ssh_key_title), None)
290
+ self.print_status("SSH key created successfully in 1Password", True, 2)
233
291
 
234
- if existing_key:
235
- self.print_status(f"SSH key '{ssh_key_title}' already exists in 1Password", True, 2)
236
- # Get the public key
237
- result = subprocess.run(
238
- ['op', 'item', 'get', existing_key['id'], '--fields', 'public key'],
239
- capture_output=True,
240
- text=True,
241
- check=False
242
- )
243
-
244
- if result.returncode == 0:
245
- public_key = result.stdout.strip()
246
- # Save it to the expected location
247
- if save_public_key(public_key, f"{self.ssh_key_file}.pub"):
248
- self.print_status(f"Saved existing public key to {self.ssh_key_file}.pub", True, 2)
249
- return True
292
+ # Save the public key to the expected location
293
+ if save_public_key(public_key, f"{self.ssh_key_file}.pub"):
294
+ self.print_status(f"Saved public key to {self.ssh_key_file}.pub", True, 2)
295
+ return True
250
296
  else:
251
- # Create a new SSH key in 1Password
252
- self.print_status(f"Creating new SSH key '{ssh_key_title}' in 1Password...", None, 2)
253
- success, public_key, item_id = create_ssh_key(ssh_key_title, selected_vault)
254
-
255
- if not success:
256
- self.print_status("Failed to create SSH key in 1Password", False, 2)
257
- return False
258
-
259
- self.print_status("SSH key created successfully in 1Password", True, 2)
260
-
261
- # Save the public key to the expected location
262
- if save_public_key(public_key, f"{self.ssh_key_file}.pub"):
263
- self.print_status(f"Saved public key to {self.ssh_key_file}.pub", True, 2)
264
- return True
265
- else:
266
- self.print_status(f"Failed to save public key to {self.ssh_key_file}.pub", False, 2)
267
- return False
297
+ self.print_status(f"Failed to save public key to {self.ssh_key_file}.pub", False, 2)
298
+ return False
268
299
 
269
300
  # Remind user to enable the key in 1Password SSH agent
270
301
  self.print_status("\033[93mImportant: Make sure the key is enabled in 1Password's SSH agent settings\033[0m", None, 2)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cloudx-proxy
3
- Version: 0.4.4
3
+ Version: 0.4.6
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
@@ -224,6 +224,30 @@ When adding new instances to an existing environment, you can choose to:
224
224
 
225
225
  This three-tier structure offers better maintainability by reducing duplication and making it clear which settings apply broadly and which are specific to an environment or host.
226
226
 
227
+ ### Security Model: AWS and SSH Integration
228
+
229
+ cloudX-proxy implements a unique dual-layer security approach that combines AWS's robust authentication mechanisms with SSH's connection handling capabilities:
230
+
231
+ #### AWS Security Layer (Primary)
232
+ The primary security boundary is enforced through AWS Systems Manager (SSM) and EC2 Instance Connect. This layer provides:
233
+ - **Access Control**: Only authenticated AWS users with appropriate IAM permissions can establish SSM sessions
234
+ - **Dynamic Key Authorization**: EC2 Instance Connect allows temporary injection of SSH public keys, valid only for a single session
235
+ - **Network Security**: No inbound SSH ports need to be exposed, as all connections are established through AWS SSM's secure tunneling
236
+ - **Audit Trail**: All connection attempts and key pushes are logged in AWS CloudTrail
237
+
238
+ #### SSH Layer (Secondary)
239
+ SSH serves primarily as a connection handler rather than the main security mechanism:
240
+ - **Ephemeral Authentication**: The SSH key pair is used only to establish the connection through the SSM tunnel
241
+ - **Session Management**: SSH handles the actual terminal session, file transfers, and multiplexing
242
+ - **Key Flexibility**: Since keys are pushed dynamically for each session, the same key can safely be used across multiple instances
243
+ - **Zero Trust Model**: Even if a key is compromised, access still requires valid AWS credentials and permissions
244
+
245
+ This architecture means that:
246
+ 1. The security of the connection relies primarily on AWS IAM permissions and SSM session management
247
+ 2. SSH keys can be reused across instances without security implications
248
+ 3. Each connection gets a fresh key authorization through EC2 Instance Connect
249
+ 4. Instances remain completely closed to direct SSH access from the internet
250
+
227
251
  ### VSCode Configuration
228
252
 
229
253
  1. Install the "Remote - SSH" extension in VSCode
File without changes
File without changes
File without changes
File without changes
File without changes