Skip to content

Commit ed28859

Browse files
andystimeclaude
andcommitted
fix: SSH server setup and library name mismatch issues
This commit fixes two critical issues: 1. SSH Connection Failure in Docker Containers: - Docker backend didn't start SSH server after container creation - Added EnsureSSHServer() to DockerBackend similar to WSLBackend - Improved SSH config to explicitly bind to 0.0.0.0:22 - Added SSHServerBackend interface and Manager.Create now calls it - Updated WSL backend to use consistent SSH setup script 2. Library Name Mismatch in ggo launch: - launch_linux.go checked for exact canonical names (libcuda.so) - Downloaded libraries often have different names (libaccelerator_nvidia-linux-amd64.so) - Now uses FindActualLibraryFiles() to scan for actual downloaded files - Matches pattern-based naming like studio already does SSH server now starts automatically with: - ListenAddress 0.0.0.0 (binds to all interfaces) - PermitRootLogin yes - Auto-generated host keys - Default password (root:gpugo) for fallback auth Library detection now handles any .so file matching vendor patterns (libcuda*, libnvidia*, nvcuda*, nvml* for NVIDIA). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent fa43883 commit ed28859

5 files changed

Lines changed: 103 additions & 21 deletions

File tree

cmd/ggo/launch/launch_linux.go

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -119,31 +119,26 @@ func runLaunch(args []string, shareLink, serverURL string, verbose bool) error {
119119
// Get cache directory
120120
cacheDir := paths.CacheDir()
121121

122-
// Get required libraries based on vendor
123-
requiredLibs := studio.GetLibraryNames(vendor)
124-
125-
// Verify libraries exist after ensuring they're downloaded
126-
missingLibs := []string{}
127-
for _, lib := range requiredLibs {
128-
libPath := filepath.Join(cacheDir, lib)
129-
if _, err := os.Stat(libPath); os.IsNotExist(err) {
130-
missingLibs = append(missingLibs, lib)
131-
}
132-
}
122+
// Find actual library files that were downloaded
123+
// Use FindActualLibraryFiles instead of GetLibraryNames to handle libraries
124+
// with different names (e.g., libaccelerator_nvidia-linux-amd64.so)
125+
actualLibs := studio.FindActualLibraryFiles(cacheDir, vendor)
126+
127+
if len(actualLibs) == 0 {
128+
// Get canonical library names for the warning message
129+
requiredLibs := studio.GetLibraryNames(vendor)
133130

134-
if len(missingLibs) > 0 {
135131
out.Println()
136132
out.Warning("Missing required libraries in cache!")
137133
out.Println()
138134
out.Printf("Vendor: %s\n", vendor)
139-
out.Printf("Missing: %s\n", strings.Join(missingLibs, ", "))
135+
out.Printf("Missing: %s\n", strings.Join(requiredLibs, ", "))
140136
out.Println()
141137
out.Println("Please run:")
142138
out.Println(" ggo deps sync")
143139
out.Println(" ggo deps download")
144140
out.Println()
145-
// Continue anyway - the libraries might be named differently or not yet available
146-
klog.Warningf("Missing libraries for vendor %s (continuing anyway): %v", vendor, missingLibs)
141+
klog.Warningf("No libraries found for vendor %s in %s (continuing anyway)", vendor, cacheDir)
147142
}
148143

149144
// Setup log path (consistent with ggo use)
@@ -212,9 +207,9 @@ func runLaunch(args []string, shareLink, serverURL string, verbose bool) error {
212207
}
213208

214209
// Build LD_PRELOAD with library paths
215-
if len(requiredLibs) > 0 {
210+
if len(actualLibs) > 0 {
216211
var preloadPaths []string
217-
for _, lib := range requiredLibs {
212+
for _, lib := range actualLibs {
218213
preloadPaths = append(preloadPaths, filepath.Join(cacheDir, lib))
219214
}
220215
existingPreload := os.Getenv("LD_PRELOAD")

internal/studio/backend_docker.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,58 @@ func findAvailablePort(_ int) int {
596596
return SSHPortRangeMin + rand.Intn(SSHPortRangeMax-SSHPortRangeMin+1)
597597
}
598598

599+
// EnsureSSHServer ensures SSH server is running in the container
600+
func (b *DockerBackend) EnsureSSHServer(ctx context.Context, envID string) error {
601+
setupScript := `
602+
#!/bin/bash
603+
set -e
604+
605+
# Install SSH if not present
606+
if ! command -v sshd &> /dev/null; then
607+
if command -v apt-get &> /dev/null; then
608+
apt-get update && apt-get install -y openssh-server
609+
elif command -v yum &> /dev/null; then
610+
yum install -y openssh-server
611+
elif command -v apk &> /dev/null; then
612+
apk add --no-cache openssh-server
613+
fi
614+
fi
615+
616+
# Configure SSH
617+
mkdir -p /var/run/sshd /root/.ssh
618+
chmod 700 /root/.ssh
619+
620+
# Configure sshd to allow root login and listen on all interfaces
621+
cat > /etc/ssh/sshd_config.d/ggo-studio.conf <<'EOF'
622+
Port 22
623+
ListenAddress 0.0.0.0
624+
PermitRootLogin yes
625+
PasswordAuthentication yes
626+
PubkeyAuthentication yes
627+
UsePAM no
628+
EOF
629+
630+
# Set root password if not already set (fallback for password auth)
631+
echo "root:gpugo" | chpasswd 2>/dev/null || true
632+
633+
# Generate SSH host keys if they don't exist
634+
if [ ! -f /etc/ssh/ssh_host_rsa_key ]; then
635+
ssh-keygen -A
636+
fi
637+
638+
# Kill existing sshd processes (if any)
639+
pkill sshd || true
640+
641+
# Start SSH server in background
642+
/usr/sbin/sshd
643+
644+
echo "SSH server started"
645+
`
646+
647+
_, err := b.Exec(ctx, envID, []string{"bash", "-c", setupScript})
648+
return err
649+
}
650+
599651
var _ Backend = (*DockerBackend)(nil)
600652

601653
func init() {

internal/studio/backend_wsl.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -540,11 +540,31 @@ if ! command -v sshd &> /dev/null; then
540540
fi
541541
542542
# Configure SSH
543-
mkdir -p /var/run/sshd
544-
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
545-
sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
543+
mkdir -p /var/run/sshd /root/.ssh
544+
chmod 700 /root/.ssh
545+
546+
# Configure sshd to allow root login and listen on all interfaces
547+
cat > /etc/ssh/sshd_config.d/ggo-studio.conf <<'EOF'
548+
Port 22
549+
ListenAddress 0.0.0.0
550+
PermitRootLogin yes
551+
PasswordAuthentication yes
552+
PubkeyAuthentication yes
553+
UsePAM no
554+
EOF
555+
556+
# Set root password if not already set (fallback for password auth)
557+
echo "root:gpugo" | chpasswd 2>/dev/null || true
558+
559+
# Generate SSH host keys if they don't exist
560+
if [ ! -f /etc/ssh/ssh_host_rsa_key ]; then
561+
ssh-keygen -A
562+
fi
563+
564+
# Kill existing sshd processes (if any)
565+
pkill sshd || true
546566
547-
# Start SSH
567+
# Start SSH server in background
548568
/usr/sbin/sshd
549569
550570
echo "SSH server started"

internal/studio/manager.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717

1818
"github.com/NexusGPU/gpu-go/internal/errors"
1919
"github.com/NexusGPU/gpu-go/internal/platform"
20+
"k8s.io/klog/v2"
2021
)
2122

2223
var (
@@ -222,6 +223,13 @@ func (m *Manager) Create(ctx context.Context, opts *CreateOptions) (*Environment
222223
return nil, err
223224
}
224225

226+
// Setup SSH server if backend supports it
227+
if sshBackend, ok := backend.(SSHServerBackend); ok {
228+
if err := sshBackend.EnsureSSHServer(ctx, env.ID); err != nil {
229+
klog.Warningf("Failed to setup SSH server: %v (continuing anyway)", err)
230+
}
231+
}
232+
225233
m.clearUnreachableSSH(ctx, env)
226234

227235
// Save environment to local state

internal/studio/types.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,13 @@ type BackendSocketPath interface {
239239
SocketPath(ctx context.Context) string
240240
}
241241

242+
// SSHServerBackend is an optional interface for backends that support SSH server setup.
243+
type SSHServerBackend interface {
244+
Backend
245+
// EnsureSSHServer ensures SSH server is running in the environment
246+
EnsureSSHServer(ctx context.Context, envID string) error
247+
}
248+
242249
// SSHConfig represents an SSH configuration entry
243250
type SSHConfig struct {
244251
Host string

0 commit comments

Comments
 (0)