From 39afbadf919b2e316fe512621f5ce2f4907ced55 Mon Sep 17 00:00:00 2001 From: Greg Hendrickson Date: Tue, 27 Jan 2026 18:46:13 +0000 Subject: [PATCH] Persist SSH host key across restarts - Remove key generation from Dockerfile (was causing new key each build) - Add ssh_keys volume mount in docker-compose - Generate key at runtime only if it doesn't exist - No more clearing known_hosts after updates! --- Dockerfile | 6 ++---- docker-compose.yml | 3 +++ src/shellmate/ssh/server.py | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index eb269cb..0b6b734 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,12 +20,10 @@ RUN useradd -m shellmate && \ mkdir -p /etc/shellmate && \ chown shellmate:shellmate /etc/shellmate -# Switch to shellmate user and generate SSH key -USER shellmate -RUN ssh-keygen -t ed25519 -f /etc/shellmate/ssh_host_key -N "" - EXPOSE 22 +# Key is generated at runtime (see entrypoint) or mounted from volume + ENV STOCKFISH_PATH=/usr/games/stockfish CMD ["shellmate-server"] diff --git a/docker-compose.yml b/docker-compose.yml index 9e14dc7..bccd856 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,8 @@ services: - SHELLMATE_REDIS_URL=redis://redis:6379 - SHELLMATE_DATABASE_URL=postgresql://shellmate:shellmate@postgres:5432/shellmate - STOCKFISH_PATH=/usr/games/stockfish + volumes: + - ssh_keys:/etc/shellmate depends_on: - redis - postgres @@ -47,3 +49,4 @@ services: volumes: redis_data: postgres_data: + ssh_keys: diff --git a/src/shellmate/ssh/server.py b/src/shellmate/ssh/server.py index a527b09..8ff2c0f 100644 --- a/src/shellmate/ssh/server.py +++ b/src/shellmate/ssh/server.py @@ -466,6 +466,20 @@ async def run_chess_game(process, session: TerminalSession, username: str, oppon await asyncio.sleep(3) +def ensure_host_key(key_path: str) -> None: + """Generate SSH host key if it doesn't exist.""" + import subprocess + + if not os.path.exists(key_path): + logger.info(f"Generating SSH host key at {key_path}") + os.makedirs(os.path.dirname(key_path), exist_ok=True) + subprocess.run([ + "ssh-keygen", "-t", "ed25519", + "-f", key_path, "-N", "" + ], check=True) + logger.info("SSH host key generated") + + async def start_server( host: str = "0.0.0.0", port: int | None = None, @@ -475,6 +489,10 @@ async def start_server( port = port or int(os.environ.get("SHELLMATE_SSH_PORT", "2222")) host_keys = host_keys or ["/etc/shellmate/ssh_host_key"] + # Ensure host key exists (generate if needed) + for key_path in host_keys: + ensure_host_key(key_path) + logger.info(f"Starting ShellMate SSH server on {host}:{port}") server = await asyncssh.create_server(