Skip to content

Conversation

@raghavyuva
Copy link
Owner

@raghavyuva raghavyuva commented Oct 30, 2025

Issue

Link to related issue(s):


Description

Short summary of what this PR changes or introduces.


Scope of Change

Select all applicable areas impacted by this PR:

  • View (UI/UX)
  • API
  • CLI
  • Infra / Deployment
  • Docs
  • Other (specify): ________

Screenshot / Video / GIF (if applicable)

Attach or embed screenshots, screen recordings, or GIFs demonstrating the feature or fix.


Related PRs (if any)

Link any related or dependent PRs across repos.


Additional Notes for Reviewers (optional)

Anything reviewers should know before testing or merging (e.g., environment variables, setup steps).


Developer Checklist

To be completed by the developer who raised the PR.

  • Add valid/relevant title for the PR
  • Self-review done
  • Manual dev testing done
  • No secrets exposed
  • No merge conflicts
  • Docs added/updated (if applicable)
  • Removed debug prints / secrets / sensitive data
  • Unit / Integration tests passing
  • Follows all standards defined in Nixopus Docs

Reviewer Checklist

To be completed by the reviewer before merge.

  • Peer review done
  • No console.logs / fmt.prints left
  • No secrets exposed
  • If any DB migrations, migration changes are verified
  • Verified release changes are production-ready

Summary by CodeRabbit

  • New Features

    • SSH tunneling support for Docker connections, enabling secure access to remote Docker daemons.
  • Refactor

    • Enhanced Docker service initialization with automatic SSH tunnel setup and graceful fallback to local socket connections when tunneling is unavailable.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 30, 2025

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Walkthrough

The PR introduces SSH tunneling support for Docker client connections. An SSH tunnel mechanism forwards local connections through SSH to the remote Docker daemon socket, enabling secure Docker operations over SSH with automatic resource cleanup and bidirectional data forwarding.

Changes

Cohort / File(s) Summary
SSH Tunnel Implementation
api/internal/features/deploy/docker/ssh_tunnel.go
New file implementing SSH tunnel creation and management. Provides CreateSSHTunnel() to establish a local Unix socket listener that forwards connections to the remote Docker daemon via SSH. Includes handleConnections() to accept incoming connections, forwardConnection() to pipe data bidirectionally over SSH, and Close() method on DockerService for resource cleanup.
Docker Service Integration
api/internal/features/deploy/docker/init.go
Added SSHTunnel type with fields for local socket, SSH client, listener, and cleanup function. Extended DockerService struct with sshTunnel field. Refactored service creation to initialize SSH-tunneled Docker client via newDockerClientWithOptionalSSHTunnel(), falling back to local socket on failure. Updated cluster initialization to use the potentially tunneled Docker client.

Sequence Diagram

sequenceDiagram
    participant Client as Docker Client
    participant LocalSocket as Local Unix Socket<br/>(Tunnel)
    participant SSH as SSH Connection
    participant Remote as Remote Docker Socket
    
    Client->>LocalSocket: Connect (docker request)
    LocalSocket->>SSH: Accept connection
    SSH->>Remote: Establish SSH session
    Remote-->>SSH: Connected to /var/run/docker.sock
    SSH->>SSH: Start bidirectional<br/>data forwarding
    Client->>LocalSocket: Send request data
    LocalSocket->>SSH: Forward data
    SSH->>Remote: Send to remote socket
    Remote-->>SSH: Response data
    SSH->>LocalSocket: Forward response
    LocalSocket-->>Client: Response data
    Client->>LocalSocket: Close
    LocalSocket->>SSH: Close SSH channel
    SSH->>Remote: Close remote connection
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

  • SSH tunnel logic: Concurrent connection handling with goroutines requires careful review of synchronization and potential race conditions
  • Resource management: Verify cleanup functions properly close all connections and remove socket files across success and error paths
  • Error handling: Trace error propagation from socket creation through SSH connection to Docker daemon connection
  • Integration point in init.go: Verify fallback behavior and proper tunnel lifecycle management within DockerService

Poem

🐰 A tunnel of SSH through which Docker flows,
Where local sockets dance and data goes,
With forwarding swift and cleanup so neat,
The rabbit hops through connections complete! 🚀
SSH secures what Docker commands entrust,
A Unix socket bridge built on rabbit's trust. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "feat: add support for docker over ssh tunnel" directly and accurately describes the main objective of the changeset. The code additions introduce SSH tunneling capabilities for Docker client connections, implementing a new SSHTunnel type, extending DockerService with tunnel support, and creating helper functions to manage the SSH-tunneled Docker client. The title is concise, specific, and uses clear language that a teammate scanning the repository history would immediately understand as indicating the addition of Docker-over-SSH functionality.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
api/internal/features/deploy/docker/init.go (1)

358-396: Inconsistent SSH usage: Compose methods bypass the established tunnel.

ComposeUp (line 359), ComposeDown (line 374), and ComposeBuild (line 385) each instantiate a fresh ssh.NewSSH() client and execute shell commands directly over SSH, ignoring the Docker client and SSH tunnel already configured in DockerService. This creates multiple SSH connections, bypasses the unified client abstraction, and prevents centralized error handling or connection pooling.

Refactor these methods to either:

  1. Use the existing s.Cli Docker client (preferred if the Docker SDK supports Compose operations), or
  2. Reuse s.sshTunnel.sshClient for SSH commands to avoid redundant SSH sessions.

Example for option 2:

 func (s *DockerService) ComposeUp(composeFilePath string, envVars map[string]string) error {
-	client := ssh.NewSSH()
+	client := s.sshTunnel.sshClient
+	if client == nil {
+		client = ssh.NewSSH()
+	}
 	envVarsStr := ""
 	...

Repeat for ComposeDown and ComposeBuild.

🧹 Nitpick comments (2)
api/internal/features/deploy/docker/init.go (1)

108-119: Simplify the conditional by removing redundant nil check.

Line 112 checks both err != nil and tunnel == nil, but if CreateSSHTunnel returns an error, tunnel is already nil by Go convention. The second condition adds no value and can confuse readers.

Apply this diff:

-	if err != nil || tunnel == nil {
-		if err != nil {
-			lgr.Log(logger.Info, "SSH tunnel not established, using local docker socket", err.Error())
-		}
+	if err != nil {
+		lgr.Log(logger.Info, "SSH tunnel not established, using local docker socket", err.Error())
api/internal/features/deploy/docker/ssh_tunnel.go (1)

47-57: Improve shutdown handling: distinguish listener closure from errors.

Line 50–52: When t.listener.Accept() returns an error, the goroutine logs it as an error and exits. However, if the error is due to intentional listener closure (e.g., during Close()), logging it as an error is misleading and may trigger false alerts in production monitoring.

Check if the error is due to listener closure and log accordingly:

 func (t *SSHTunnel) handleConnections(lgr logger.Logger) {
 	for {
 		localConn, err := t.listener.Accept()
 		if err != nil {
-			lgr.Log(logger.Error, "SSH tunnel listener error", err.Error())
+			if !errors.Is(err, net.ErrClosed) {
+				lgr.Log(logger.Error, "SSH tunnel listener error", err.Error())
+			}
 			return
 		}

 		go t.forwardConnection(localConn, lgr)
 	}
 }

Add the errors import:

 import (
+	"errors"
 	"fmt"
 	"io"
 	"net"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c41294 and 9b14bc9.

📒 Files selected for processing (2)
  • api/internal/features/deploy/docker/init.go (3 hunks)
  • api/internal/features/deploy/docker/ssh_tunnel.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
api/internal/features/deploy/docker/ssh_tunnel.go (2)
api/internal/features/deploy/docker/init.go (2)
  • SSHTunnel (30-35)
  • DockerService (23-28)
api/internal/features/logger/init.go (1)
  • Error (22-22)
api/internal/features/deploy/docker/init.go (3)
api/internal/features/logger/init.go (4)
  • NewLogger (35-42)
  • Warning (21-21)
  • Error (22-22)
  • Info (20-20)
api/internal/features/ssh/init.go (1)
  • NewSSH (24-33)
api/internal/features/deploy/docker/ssh_tunnel.go (1)
  • CreateSSHTunnel (19-44)
🔇 Additional comments (1)
api/internal/features/deploy/docker/ssh_tunnel.go (1)

93-98: LGTM, but document the Close requirement.

The Close() method correctly invokes the cleanup function if a tunnel exists. However, as noted in the review of init.go, there is no visible call to Close() in the provided code, leading to a resource leak.

Ensure that the lifecycle owner of DockerService calls Close() during shutdown (see the comment on init.go lines 90–106).

@raghavyuva
Copy link
Owner Author

@coderabbitai pause

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 31, 2025

✅ Actions performed

Reviews paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants