fix: prepare SSH keys with container UID/GID on host before mounting

- Extend prepare_ssh_key_files() with optional uid/gid parameters
- Call os.chown on created files when uid/gid are provided
- Gracefully handle PermissionError if API process is not root
- In start_instance, extract container user UID/GID from manifest
- Pass container UID/GID when preparing instance-level SSH key mounts
- Legacy clone-mode SSH keys continue to use root (0,0)
- Add unit tests for prepare_ssh_key_files ownership logic
- Keep apply_ssh_permissions() as fallback for cases where host chown fails

Quality gates: pytest 239 passed (6 pre-existing failures), tsc --noEmit clean
This commit is contained in:
Alex Blank
2026-05-29 14:38:15 +02:00
parent 16549709e2
commit b11089896a
5 changed files with 147 additions and 11 deletions
+39 -5
View File
@@ -148,10 +148,32 @@ class TestApplySshPermissions:
chmod_cmd = mock_run.call_args_list[1][0][0]
file_mode_cmd = mock_run.call_args_list[2][0][0]
assert chown_cmd == ["docker", "exec", "--user", "root", "abc123", "chown", "-R", "user:user", "/home/user/.ssh"]
assert chmod_cmd == ["docker", "exec", "--user", "root", "abc123", "chmod", "700", "/home/user/.ssh"]
assert chown_cmd == [
"docker",
"exec",
"--user",
"root",
"abc123",
"chown",
"-R",
"user:user",
"/home/user/.ssh",
]
assert chmod_cmd == [
"docker",
"exec",
"--user",
"root",
"abc123",
"chmod",
"700",
"/home/user/.ssh",
]
assert file_mode_cmd[0] == "docker"
assert "find /home/user/.ssh -name 'id_*' -type f -exec chmod 600" in file_mode_cmd[-1]
assert (
"find /home/user/.ssh -name 'id_*' -type f -exec chmod 600"
in file_mode_cmd[-1]
)
@patch("subprocess.run")
def test_uses_root_user(self, mock_run) -> None:
@@ -160,11 +182,23 @@ class TestApplySshPermissions:
assert result["success"] is True
chown_cmd = mock_run.call_args_list[0][0][0]
assert chown_cmd == ["docker", "exec", "--user", "root", "abc123", "chown", "-R", "root:root", "/root/.ssh"]
assert chown_cmd == [
"docker",
"exec",
"--user",
"root",
"abc123",
"chown",
"-R",
"root:root",
"/root/.ssh",
]
@patch("subprocess.run")
def test_reports_failure(self, mock_run) -> None:
mock_run.return_value = MagicMock(returncode=1, stdout="", stderr="chown failed")
mock_run.return_value = MagicMock(
returncode=1, stdout="", stderr="chown failed"
)
result = apply_ssh_permissions("abc123", "/home/user/.ssh", "user")