-
Notifications
You must be signed in to change notification settings - Fork 2
Add support for jump-proxy to avoid the need of VPN to access VPC #132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds support for SSH jump proxy functionality, allowing users to connect to VPC resources without requiring a VPN connection. The implementation introduces two new command-line flags (--jump-proxy and --jump-identity-file) that enable SSH connections to be proxied through an intermediate jump server.
Key changes:
- Added
dialJumpServerfunction to establish connections through an SSH jump server - Modified
SshDialto conditionally route connections through the jump server when configured - Extended configuration management to persist jump proxy settings
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
| pkg/clientlib/config.go | Added persistent flags for jump-proxy and jump-identity-file configuration |
| pkg/clientlib/auth_oss.go | Implemented jump server connection logic with dialJumpServer function and integrated it into SshDial method |
| client/cmd/config_set.go | Extended config set command to support persisting jump proxy configuration settings |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| jumpConfig := &ssh.ClientConfig{ | ||
| User: jumpUser, | ||
| Auth: authMethods, | ||
| HostKeyCallback: ssh.InsecureIgnoreHostKey(), // For jump server |
Copilot
AI
Nov 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Security risk: Using ssh.InsecureIgnoreHostKey() disables host key verification for the jump server, making the connection vulnerable to man-in-the-middle attacks. Consider implementing proper host key verification or at minimum, add a warning in the documentation about this security implication.
pkg/clientlib/auth_oss.go
Outdated
| parts := strings.Split(jumpProxy, "@") | ||
| if len(parts) != 2 { | ||
| return nil, fmt.Errorf("invalid jump-proxy format, expected user@host, got: %s", jumpProxy) | ||
| } | ||
| jumpUser := parts[0] | ||
| jumpHost := parts[1] |
Copilot
AI
Nov 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incomplete format validation: The jump-proxy format validation only checks for exactly 2 parts when split by "@", but doesn't validate that both the username and host are non-empty. For example, inputs like "@host" or "user@" would pass validation but fail during connection. Add validation to ensure both parts are non-empty strings.
pkg/clientlib/auth_oss.go
Outdated
| // Add default SSH port if not specified | ||
| if !strings.Contains(jumpHost, ":") { | ||
| jumpHost = jumpHost + ":22" | ||
| } |
Copilot
AI
Nov 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incomplete port format validation: The code checks if jumpHost contains ":" but doesn't validate if the port part (if present) is valid. For example, "host:abc" or "host:99999" would pass the check but fail during connection. Consider using net.SplitHostPort and validating the port number, or at minimum handle potential errors from invalid port formats more gracefully.
| jumpProxy, _ = GetFlagValue(cmd, "jump-proxy") | ||
| jumpIdentityFile, _ = GetFlagValue(cmd, "jump-identity-file") |
Copilot
AI
Nov 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Silently ignored errors: Errors from GetFlagValue for jump-proxy and jump-identity-file are being ignored with _. If GetFlagValue returns an error (e.g., config read failure), the jump proxy configuration would silently fail. Consider handling these errors or at least logging them for debugging purposes.
| jumpProxy, _ = GetFlagValue(cmd, "jump-proxy") | |
| jumpIdentityFile, _ = GetFlagValue(cmd, "jump-identity-file") | |
| var err error | |
| jumpProxy, err = GetFlagValue(cmd, "jump-proxy") | |
| if err != nil { | |
| DebugLog("Error getting jump-proxy flag: %v", err) | |
| jumpProxy = "" | |
| } | |
| jumpIdentityFile, err = GetFlagValue(cmd, "jump-identity-file") | |
| if err != nil { | |
| DebugLog("Error getting jump-identity-file flag: %v", err) | |
| jumpIdentityFile = "" | |
| } |
pkg/clientlib/auth_oss.go
Outdated
| // Parse jump proxy in format user@host | ||
| parts := strings.Split(jumpProxy, "@") | ||
| if len(parts) != 2 { | ||
| return nil, fmt.Errorf("invalid jump-proxy format, expected user@host, got: %s", jumpProxy) | ||
| } | ||
| jumpUser := parts[0] | ||
| jumpHost := parts[1] | ||
|
|
||
| // Add default SSH port if not specified | ||
| if !strings.Contains(jumpHost, ":") { | ||
| jumpHost = jumpHost + ":22" |
Copilot
AI
Nov 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Format ambiguity with IPv6 addresses: The jump-proxy format "user@host" becomes ambiguous when the host is an IPv6 address (e.g., "user@2001:db8::1"). Consider documenting that IPv6 addresses should be wrapped in brackets (e.g., "user@[2001:db8::1]") or handle both formats in the parsing logic.
| // Parse jump proxy in format user@host | |
| parts := strings.Split(jumpProxy, "@") | |
| if len(parts) != 2 { | |
| return nil, fmt.Errorf("invalid jump-proxy format, expected user@host, got: %s", jumpProxy) | |
| } | |
| jumpUser := parts[0] | |
| jumpHost := parts[1] | |
| // Add default SSH port if not specified | |
| if !strings.Contains(jumpHost, ":") { | |
| jumpHost = jumpHost + ":22" | |
| // Parse jump proxy in format user@host or user@[IPv6-host] | |
| atIdx := strings.LastIndex(jumpProxy, "@") | |
| if atIdx == -1 { | |
| return nil, fmt.Errorf("invalid jump-proxy format, expected user@host or user@[IPv6-host], got: %s", jumpProxy) | |
| } | |
| jumpUser := jumpProxy[:atIdx] | |
| jumpHost := jumpProxy[atIdx+1:] | |
| // If IPv6 address in brackets, ensure port is outside the brackets | |
| if strings.HasPrefix(jumpHost, "[") { | |
| // If no port is specified, add :22 after the closing bracket | |
| if !strings.Contains(jumpHost, "]:") { | |
| jumpHost = jumpHost + ":22" | |
| } | |
| } else { | |
| // Add default SSH port if not specified | |
| if !strings.Contains(jumpHost, ":") { | |
| jumpHost = jumpHost + ":22" | |
| } |
| return ctx | ||
| } | ||
|
|
||
| // dialJumpServer establishes a connection through an SSH jump server |
Copilot
AI
Nov 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incomplete function documentation: The function comment doesn't document the parameters or return values. Consider adding parameter descriptions and documenting the error conditions (invalid format, authentication failure, connection failure, etc.) to help future maintainers understand the expected behavior.
| // dialJumpServer establishes a connection through an SSH jump server | |
| // dialJumpServer establishes a connection to a target host and port through an SSH jump server. | |
| // | |
| // Parameters: | |
| // - jumpProxy: The jump server in the format "user@host" (e.g., "[email protected]"). | |
| // - jumpIdentityFile: Path to the SSH private key file to use for authenticating to the jump server. | |
| // - targetHost: The hostname or IP address of the final target to connect to. | |
| // - targetPort: The TCP port number of the final target to connect to. | |
| // | |
| // Returns: | |
| // - net.Conn: A network connection to the target host, established via the jump server. | |
| // - error: An error if the connection could not be established. | |
| // | |
| // Possible error conditions: | |
| // - If jumpProxy is not in the expected "user@host" format. | |
| // - If the jumpIdentityFile cannot be read or parsed as a valid SSH private key. | |
| // - If the connection to the jump server fails. | |
| // - If the connection to the target host through the jump server fails. |
pkg/clientlib/auth_oss.go
Outdated
| conn, err := jumpClient.Dial("tcp", targetAddr) | ||
| if err != nil { | ||
| jumpClient.Close() | ||
| return nil, fmt.Errorf("error dialing target through jump server: %v", err) | ||
| } | ||
|
|
||
| return conn, nil |
Copilot
AI
Nov 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resource leak: The jumpClient SSH connection is not properly managed. When the returned conn is closed, the underlying jumpClient will remain open, causing a connection leak.
Consider either:
- Wrapping the connection to also close
jumpClientwhenconnis closed - Returning both the connection and jumpClient so the caller can manage cleanup
- Using a custom net.Conn wrapper that closes both resources
| // Configure jump server client | ||
| jumpConfig := &ssh.ClientConfig{ | ||
| User: jumpUser, | ||
| Auth: authMethods, | ||
| HostKeyCallback: ssh.InsecureIgnoreHostKey(), // For jump server | ||
| } |
Copilot
AI
Nov 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing authentication methods: When jumpIdentityFile is empty, the authMethods slice will be empty, causing authentication to always fail. Consider adding support for SSH agent authentication as a fallback, or at minimum, provide a clear error message when no authentication method is available.
No description provided.