-
Notifications
You must be signed in to change notification settings - Fork 59
feat: add proxy_command parameter for SSH jump host configuration #530
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
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
04679ba
Initial plan
Copilot 73c67d7
Add SSH jump host support for double SSH connections
Copilot 3c711a8
Implement proxy_command parameter with backward compatibility and com…
Copilot c366e23
Update SSH tests to use environment variable and clean up documentation
Copilot 5f41c5a
Update SSH tests to use same server configuration as test_ssh_context.py
Copilot fe575a5
Remove jump host parameters and keep only proxy_command
Copilot b9eb63c
Changes before error encountered
Copilot a7ed057
Remove node_modules cache file and add to .gitignore
Copilot 2a121af
Resolve merge conflicts with master
Copilot 4faeda5
feat: add jump host support to SSH test infrastructure and remove moc…
Copilot 2175e9b
fix: remove try...except blocks that mask test failures in SSH tests
Copilot b16148d
fix: resolve CI errors in SSH jump host tests - install rsync and fix…
Copilot 3a08764
fix: resolve CI errors and fix documentation placement
Copilot ab7f46c
fix: resolve rsync command construction issue with shell=True
Copilot cd7dd44
fix: update ssh_jump_host.json to use proxy_command and add both SSH …
Copilot 686b691
fix: remove duplicate ssh_jump_host.json example file
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
njzjz marked this conversation as resolved.
Show resolved
Hide resolved
njzjz marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"batch_type": "Shell", | ||
"context_type": "SSHContext", | ||
"local_root": "./", | ||
"remote_root": "/home/user/work", | ||
"remote_profile": { | ||
"hostname": "internal-server.company.com", | ||
"username": "user", | ||
"port": 22, | ||
"key_filename": "~/.ssh/id_rsa", | ||
"proxy_command": "ssh -W %h:%p -i ~/.ssh/jump_key jumpuser@bastion.company.com" | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import os | ||
import shutil | ||
import sys | ||
import tempfile | ||
import unittest | ||
|
||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) | ||
__package__ = "tests" | ||
|
||
from dpdispatcher.utils.utils import rsync | ||
|
||
|
||
@unittest.skipIf( | ||
os.environ.get("DPDISPATCHER_TEST") != "ssh", "outside the ssh testing environment" | ||
) | ||
class TestRsyncProxyCommand(unittest.TestCase): | ||
"""Test rsync function with proxy command support.""" | ||
|
||
def setUp(self): | ||
"""Set up test files for rsync operations.""" | ||
# Check if rsync is available before running tests | ||
if shutil.which("rsync") is None: | ||
self.skipTest("rsync not available") | ||
|
||
# Create temporary test files | ||
self.test_content = "test content for rsync" | ||
|
||
# Local test file | ||
self.local_file = tempfile.NamedTemporaryFile(mode="w", delete=False) | ||
self.local_file.write(self.test_content) | ||
self.local_file.close() | ||
|
||
# Remote paths for testing | ||
self.remote_test_dir = "/tmp/rsync_test" | ||
self.remote_file_direct = f"root@server:{self.remote_test_dir}/test_direct.txt" | ||
self.remote_file_proxy = f"root@server:{self.remote_test_dir}/test_proxy.txt" | ||
|
||
def tearDown(self): | ||
"""Clean up test files.""" | ||
# Remove local test file | ||
os.unlink(self.local_file.name) | ||
|
||
def test_rsync_with_proxy_command(self): | ||
"""Test rsync with proxy command via jump host.""" | ||
# Test rsync through jump host: test -> jumphost -> server | ||
rsync( | ||
self.local_file.name, | ||
self.remote_file_proxy, | ||
key_filename="/root/.ssh/id_rsa", | ||
proxy_command="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa -W server:22 root@jumphost", | ||
) | ||
|
||
# Verify the file was transferred by reading it back | ||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as download_file: | ||
download_path = download_file.name | ||
|
||
rsync( | ||
self.remote_file_proxy, | ||
download_path, | ||
key_filename="/root/.ssh/id_rsa", | ||
proxy_command="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa -W server:22 root@jumphost", | ||
) | ||
|
||
# Verify content matches | ||
with open(download_path) as f: | ||
content = f.read() | ||
self.assertEqual(content, self.test_content) | ||
|
||
# Clean up | ||
os.unlink(download_path) | ||
|
||
def test_rsync_direct_connection(self): | ||
"""Test rsync without proxy command (direct connection).""" | ||
# Test direct rsync: test -> server | ||
rsync( | ||
self.local_file.name, | ||
self.remote_file_direct, | ||
key_filename="/root/.ssh/id_rsa", | ||
) | ||
|
||
# Verify the file was transferred by reading it back | ||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as download_file: | ||
download_path = download_file.name | ||
|
||
rsync(self.remote_file_direct, download_path, key_filename="/root/.ssh/id_rsa") | ||
|
||
# Verify content matches | ||
with open(download_path) as f: | ||
content = f.read() | ||
self.assertEqual(content, self.test_content) | ||
|
||
# Clean up | ||
os.unlink(download_path) | ||
|
||
def test_rsync_with_additional_options(self): | ||
"""Test rsync with proxy command and additional SSH options.""" | ||
# Test rsync with custom port, timeout, and proxy | ||
rsync( | ||
self.local_file.name, | ||
self.remote_file_proxy, | ||
port=22, | ||
key_filename="/root/.ssh/id_rsa", | ||
timeout=30, | ||
proxy_command="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa -W server:22 root@jumphost", | ||
) | ||
|
||
# Verify the file exists on remote by attempting to download | ||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as download_file: | ||
download_path = download_file.name | ||
|
||
rsync( | ||
self.remote_file_proxy, | ||
download_path, | ||
port=22, | ||
key_filename="/root/.ssh/id_rsa", | ||
timeout=30, | ||
proxy_command="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa -W server:22 root@jumphost", | ||
) | ||
|
||
# Verify content | ||
with open(download_path) as f: | ||
content = f.read() | ||
self.assertEqual(content, self.test_content) | ||
|
||
# Clean up | ||
os.unlink(download_path) | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.