fix: set SSH key ownership to container user with mode 600
- Mount SSH keys as bind (not ro) so docker exec --user root can chown - Add apply_ssh_permissions() to permission_fixer.py - Call apply_ssh_permissions() after container start for all instance types - Derive container user from home_dir (/root → root, /home/user → user) - Tests: apply_ssh_permissions unit tests + start_instance integration tests Quality gates: pytest 236 passed (6 pre-existing failures), tsc --noEmit clean
This commit is contained in:
@@ -75,7 +75,7 @@ from src.services.manifest_compiler import (
|
||||
merge_with_config,
|
||||
resolve_base,
|
||||
)
|
||||
from src.services.permission_fixer import apply_mount_permissions
|
||||
from src.services.permission_fixer import apply_mount_permissions, apply_ssh_permissions
|
||||
from src.services.readiness_probe import execute_probe
|
||||
from src.services.ssh_keys import cleanup_ssh_key_files, prepare_ssh_key_files
|
||||
|
||||
@@ -1415,7 +1415,7 @@ async def start_instance(
|
||||
{
|
||||
"source": ssh_dir,
|
||||
"target": ssh_target,
|
||||
"type": "ro",
|
||||
"type": "bind",
|
||||
}
|
||||
)
|
||||
logger.debug(
|
||||
@@ -1493,7 +1493,7 @@ async def start_instance(
|
||||
{
|
||||
"source": ssh_dir,
|
||||
"target": "/root/.ssh",
|
||||
"type": "ro",
|
||||
"type": "bind",
|
||||
}
|
||||
)
|
||||
logger.debug(
|
||||
@@ -1651,6 +1651,32 @@ async def start_instance(
|
||||
result["error"],
|
||||
)
|
||||
|
||||
# Fix SSH key ownership/permissions inside the container
|
||||
if instance.ssh_key_ids and instance.container_id:
|
||||
container_user = (
|
||||
"root"
|
||||
if home_dir == "/root"
|
||||
else home_dir[6:] if home_dir.startswith("/home/") else "root"
|
||||
)
|
||||
ssh_target = os.path.join(home_dir, ".ssh")
|
||||
logger.debug(
|
||||
"Applying SSH permissions for user %s on %s in instance %s",
|
||||
container_user,
|
||||
ssh_target,
|
||||
instance.id,
|
||||
)
|
||||
ssh_perm_result = apply_ssh_permissions(
|
||||
instance.container_id,
|
||||
ssh_target,
|
||||
container_user,
|
||||
)
|
||||
if not ssh_perm_result["success"]:
|
||||
logger.warning(
|
||||
"SSH permission fix failed for instance %s: %s",
|
||||
instance.id,
|
||||
ssh_perm_result["error"],
|
||||
)
|
||||
|
||||
# Execute readiness probe if configured
|
||||
tool_type = await session.get(ToolType, instance.tool_type_id)
|
||||
if tool_type and instance.container_id:
|
||||
|
||||
@@ -104,6 +104,69 @@ def apply_mount_permissions(
|
||||
return results
|
||||
|
||||
|
||||
def apply_ssh_permissions(
|
||||
container_id: str,
|
||||
ssh_target: str,
|
||||
container_user: str,
|
||||
timeout: int = 10,
|
||||
) -> dict[str, Any]:
|
||||
"""Fix SSH directory ownership and permissions in a running container.
|
||||
|
||||
Runs chown and chmod on the ~/.ssh directory so the container user
|
||||
can use the keys (SSH requires the private key to be owned by the
|
||||
user with mode 600).
|
||||
|
||||
Args:
|
||||
container_id: Docker container ID or name.
|
||||
ssh_target: Absolute path to the .ssh directory inside the container.
|
||||
container_user: The container user that should own the keys.
|
||||
timeout: Max seconds per docker exec command.
|
||||
|
||||
Returns:
|
||||
Result dict with keys: success, error.
|
||||
"""
|
||||
result: dict[str, Any] = {"success": True, "error": None}
|
||||
try:
|
||||
# Ensure directory is owned by the container user
|
||||
_run_in_container(
|
||||
container_id,
|
||||
["chown", "-R", f"{container_user}:{container_user}", ssh_target],
|
||||
timeout,
|
||||
)
|
||||
# Set directory permissions
|
||||
_run_in_container(
|
||||
container_id,
|
||||
["chmod", "700", ssh_target],
|
||||
timeout,
|
||||
)
|
||||
# Set private key permissions
|
||||
_run_in_container(
|
||||
container_id,
|
||||
[
|
||||
"sh",
|
||||
"-c",
|
||||
f"find {ssh_target} -name 'id_*' -type f -exec chmod 600 {{}} +",
|
||||
],
|
||||
timeout,
|
||||
)
|
||||
logger.debug(
|
||||
"Applied SSH permissions for user %s on %s in container %s",
|
||||
container_user,
|
||||
ssh_target,
|
||||
container_id,
|
||||
)
|
||||
except PermissionFixError as exc:
|
||||
result["success"] = False
|
||||
result["error"] = str(exc)
|
||||
logger.warning(
|
||||
"SSH permission fix failed for container %s (target=%s): %s",
|
||||
container_id,
|
||||
ssh_target,
|
||||
exc,
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
class PermissionFixError(Exception):
|
||||
"""Raised when a permission fix command fails."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user