Skip to content

Conversation

@lixmal
Copy link
Collaborator

@lixmal lixmal commented Jan 12, 2026

Describe your changes

  • Support ssh -T <host> (interactive login without PTY):
    • Windows runs this the same way as -t, but without ConPTY
    • Unix runs this via su -l (with fallback to the executor / privilege dropper) instead of login
  • Add missing session field to some log messages
  • Remove obsolete Interactive field

Issue ticket number and link

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

Documentation

Select exactly one:

  • I added/updated documentation for this change
  • Documentation is not needed for this change (explain why)

Docs PR URL (required if "docs added" is checked)

Paste the PR link from https://github.com/netbirdio/docs here:

https://github.com/netbirdio/docs/pull/__

Summary by CodeRabbit

  • New Features

    • Improved diagnostic logging across SSH command execution for clearer troubleshooting.
  • Bug Fixes

    • More reliable PTY login and non‑interactive shell handling, ensuring correct shell lifecycle and exit behavior.
    • Consistent behavior for command vs. shell sessions, reducing unexpected session outcomes.
  • Refactor

    • Consolidated session dispatch and unified execution paths; logging context and privilege handling standardized across platforms.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

This PR injects loggers across SSH privilege-dropping and command-execution paths, renames and consolidates session handlers (handleCommand→handleExecution, handlePty→handlePtyLogin), updates NewPrivilegeDropper to accept a logger, and adjusts PTY/exec lifecycles (including proxy non-interactive session flow).

Changes

Cohort / File(s) Summary
PrivilegeDropper Logger Injection
client/ssh/server/executor_unix.go, client/ssh/server/executor_windows.go, client/ssh/server/executor_unix_test.go, client/ssh/server/sftp_windows.go, client/cmd/ssh_exec_unix.go, client/cmd/ssh_sftp_unix.go
NewPrivilegeDropper signature changed to accept a *log.Entry; PrivilegeDropper gained a logger field and log() helper. All call sites updated (some pass nil). Tests updated to call NewPrivilegeDropper(nil).
Command Execution Handler Refactoring
client/ssh/server/command_execution.go, client/ssh/server/command_execution_js.go, client/ssh/server/command_execution_unix.go, client/ssh/server/command_execution_windows.go
Introduced handleExecution(logger, ..., ptyReq, winCh). createCommand, createExecutorCommand, createSuCommand, and prepareCommandEnv signatures now accept *log.Entry and propagate logger through command creation and env prep. PTY vs non-PTY flows consolidated.
Session Handler Simplification
client/ssh/server/session_handlers.go, client/ssh/server/session_handlers_js.go
Simplified dispatch: PTY login → handlePtyLogin(...); otherwise → handleExecution(...). Removed previous multi-branch handlers and renamed JS/WASM handlePtyhandlePtyLogin.
User Switching & PTY Execution
client/ssh/server/userswitching_unix.go, client/ssh/server/userswitching_windows.go
Threaded logger into createExecutorCommand/createUserSwitchCommand; replace zero-arg NewPrivilegeDropper() with NewPrivilegeDropper(logger). Windows executor config changed (removed Interactive, added/renamed fields).
Windows S4U & Token Flow
client/ssh/server/executor_windows.go
S4U/token helper functions now accept logger *log.Entry and use injected logger via pd.log(). Many internal functions updated to pass logger.
PTY Execution Context Change
client/ssh/server/winpty/conpty.go
ExecutePtyWithUserToken no longer accepts an explicit context.Context; it derives context from session.Context() instead.
SSH Proxy Non-Interactive Session Flow
client/ssh/proxy/proxy.go
handleNonInteractiveSession now wires backend session Stdin/Stdout/Stderr to client session, starts a shell, and waits on shell exit or client context, propagating exit codes via handleProxyExitCode.
Port Forwarding & Tests/Docs
client/ssh/server/port_forwarding.go, client/ssh/server/server.go, client/ssh/server/server_config_test.go
Removed isPortForwardingEnabled helper; minor comment change in GetStatus; renamed and simplified test TestServer_NonPtyShellSession with streamlined assertions.

Sequence Diagram(s)

sequenceDiagram
  participant ClientSession
  participant Proxy as ProxyServer
  participant BackendSession
  participant Shell

  ClientSession->>Proxy: open non-interactive session
  Proxy->>BackendSession: create backend session
  Proxy->>BackendSession: attach Stdin/Stdout/Stderr
  Proxy->>BackendSession: start shell
  alt shell started successfully
    BackendSession->>Shell: shell runs
    Shell-->>Proxy: exit (with code)
    Proxy->>ClientSession: propagate exit code via handleProxyExitCode
  else shell start failed
    BackendSession-->>Proxy: error
    Proxy->>ClientSession: log error and propagate exit code (via handleProxyExitCode)
  end
  Note over Proxy,ClientSession: also monitors ClientSession.Context().Done to stop backend
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • pappz

Poem

🐰 I hopped through logs and threaded light,

Dropped down privileges out of sight,
Sessions split into tidy lanes,
PTYs, shells, and proxy chains,
I nibble bugs and guard the night. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main feature: support for non-PTY no-command interactive SSH sessions, which is the primary objective of this pull request.
Description check ✅ Passed The description documents the changes (supporting ssh -T, adding session field to logs, removing Interactive field), references the bug fix/feature enhancement nature, and marks documentation as not needed with justification.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9049447 and c9c15ae.

📒 Files selected for processing (2)
  • client/ssh/server/port_forwarding.go
  • client/ssh/server/session_handlers.go
💤 Files with no reviewable changes (1)
  • client/ssh/server/port_forwarding.go
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4015
File: client/ssh/server/server_test.go:396-406
Timestamp: 2025-11-14T11:11:50.812Z
Learning: On Windows, the NetBird SSH server only supports PowerShell as the shell (powershell.exe or pwsh.exe). cmd.exe and other shells are not supported due to parsing quirks and complexity.
📚 Learning: 2025-11-14T13:05:31.729Z
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4015
File: client/ssh/server/userswitching_windows.go:89-139
Timestamp: 2025-11-14T13:05:31.729Z
Learning: In client/ssh/server/executor_windows.go, the WindowsExecutorConfig struct's Pty, PtyWidth, and PtyHeight fields are intentionally left unused for now and will be implemented in a future update.

Applied to files:

  • client/ssh/server/session_handlers.go
📚 Learning: 2025-11-13T00:29:53.247Z
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4015
File: client/cmd/ssh_exec_unix.go:53-74
Timestamp: 2025-11-13T00:29:53.247Z
Learning: In client/ssh/server/executor_unix.go, the method ExecuteWithPrivilegeDrop(ctx context.Context, config ExecutorConfig) has a void return type (no error return). It handles failures by exiting the process directly with appropriate exit codes rather than returning errors to the caller.

Applied to files:

  • client/ssh/server/session_handlers.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Signal / Unit (amd64)
  • GitHub Check: Relay / Unit (amd64, -race)
  • GitHub Check: Management / Unit (amd64, sqlite)
  • GitHub Check: Relay / Unit (386)
  • GitHub Check: Client / Unit (amd64)
  • GitHub Check: Client / Unit (386)
  • GitHub Check: Management / Unit (amd64, postgres)
  • GitHub Check: Management / Unit (amd64, mysql)
  • GitHub Check: Client (Docker) / Unit
  • GitHub Check: Client / Unit
  • GitHub Check: release_ui_darwin
  • GitHub Check: release
  • GitHub Check: release_ui
  • GitHub Check: Client / Unit
  • GitHub Check: Darwin
  • GitHub Check: Windows
  • GitHub Check: Linux
  • GitHub Check: Client / Unit
  • GitHub Check: Android / Build
  • GitHub Check: JS / Lint
🔇 Additional comments (1)
client/ssh/server/session_handlers.go (1)

65-71: Clean simplification of session routing logic.

The two-path approach correctly covers all SSH session scenarios:

  • PTY login (ssh <host>) → handlePtyLogin
  • All other cases including the new ssh -T <host>handleExecution

The comments clearly document which SSH invocations map to each path.

The new non-PTY, no-command case (ssh -T <host>) is handled correctly: handleExecution explicitly checks winCh != nil to detect PTY presence and executes the non-PTY code path, which never accesses the zero-value ptyReq.


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: 0

🧹 Nitpick comments (9)
client/ssh/server/server_config_test.go (1)

548-551: Test validates basic functionality but could be strengthened.

The test verifies that non-PTY shell sessions don't error out, which validates the core fix. However, it doesn't confirm the shell is actually functional or produces expected output.

Consider verifying the session output to ensure the shell was properly initialized and can accept/return data, especially since the PR mentions platform-specific behavior (Windows runs without ConPTY, Unix uses su -l with fallback).

client/ssh/server/command_execution_windows.go (2)

245-258: Inconsistent logger usage in fallback path.

Line 249 uses the global log.Debugf while the logger parameter is available in scope. For consistency with the logger-threading pattern introduced in this PR, use the injected logger.

Suggested fix
 func (s *Server) prepareCommandEnv(logger *log.Entry, localUser *user.User, session ssh.Session) []string {
 	username, domain := s.parseUsername(localUser.Username)
 	userEnv, err := s.getUserEnvironment(logger, username, domain)
 	if err != nil {
-		log.Debugf("failed to get user environment for %s\\%s, using fallback: %v", domain, username, err)
+		logger.Debugf("failed to get user environment for %s\\%s, using fallback: %v", domain, username, err)
 		env := prepareUserEnv(localUser, getUserShell(localUser.Uid))

315-320: Consider refactoring to avoid empty Server instantiation.

Creating an empty &Server{} solely to call getUserEnvironmentWithToken is a code smell. The method doesn't appear to use any Server state. Consider making getUserEnvironmentWithToken a standalone function or passing the required dependencies directly.

client/ssh/server/sftp_windows.go (1)

34-38: Consider passing a logger for consistency.

Unlike the Unix SFTP subprocess case, this runs in the server process where session context is available. For consistency with other Windows privilege-dropping paths that thread loggers (e.g., executePtyCommandWithUserToken), consider accepting and passing a logger here.

client/ssh/server/command_execution_unix.go (1)

140-149: Consider removing unused logger parameter or documenting intent.

The logger parameter is accepted but unused (_). If this is for API consistency or future use, consider adding a brief comment. Otherwise, if no logging is planned here, the parameter could potentially be removed from this platform-specific implementation.

client/ssh/server/command_execution.go (1)

79-80: Log message may be misleading when UsedFallback is true but err is nil.

When privilegeResult.UsedFallback is true and err is nil, the log message "su command failed" is inaccurate—the su command wasn't attempted or didn't fail; rather, a fallback was already used during privilege checking.

Suggested improvement
 	if err != nil || privilegeResult.UsedFallback {
-		logger.Debugf("su command failed, falling back to executor: %v", err)
+		if err != nil {
+			logger.Debugf("su command failed, falling back to executor: %v", err)
+		} else {
+			logger.Debugf("using executor due to privilege fallback")
+		}
 		cmd, cleanup, err := s.createExecutorCommand(logger, session, localUser, hasPty)
client/ssh/server/executor_windows.go (3)

191-214: Consider extracting isLocalUser as a standalone function.

Creating a PrivilegeDropper instance at line 194 solely to call isLocalUser is somewhat indirect since isLocalUser doesn't use the logger field. This could be simplified.

Optional refactor
+// isLocalUser determines if this is a local user vs domain user
+func isLocalUser(domain string) bool {
+	hostname, err := os.Hostname()
+	if err != nil {
+		hostname = "localhost"
+	}
+	return domain == "" || domain == "." ||
+		strings.EqualFold(domain, hostname)
+}

 func generateS4UUserToken(logger *log.Entry, username, domain string) (windows.Handle, error) {
 	userCpn := buildUserCpn(username, domain)
-
-	pd := NewPrivilegeDropper(logger)
-	isDomainUser := !pd.isLocalUser(domain)
+	isDomainUser := !isLocalUser(domain)

Then update the PrivilegeDropper method to call the standalone function if needed.


481-492: Minor inconsistencies: typo and global logger usage.

  1. Line 481: Comment has typo "useVerifier" → should be "user"
  2. Line 484: Uses global log.Debugf instead of a logger parameter, inconsistent with the rest of the file's pattern
Suggested fix
-// userExists checks if the target useVerifier exists on the system
-func userExists(fullUsername, username, domain string) error {
+// userExists checks if the target user exists on the system
+func (pd *PrivilegeDropper) userExists(fullUsername, username, domain string) error {
 	if _, err := lookupUser(fullUsername); err != nil {
-		log.Debugf("User %s not found: %v", fullUsername, err)
+		pd.log().Debugf("User %s not found: %v", fullUsername, err)
 		if domain != "" && domain != "." {
 			_, err = lookupUser(username)
 		}

And update the call site in createToken:

-	if err := userExists(fullUsername, username, domain); err != nil {
+	if err := pd.userExists(fullUsername, username, domain); err != nil {

242-247: Pre-existing global logger usage remains.

For future consistency, cleanupLsaConnection and lookupPrincipalName (lines 292, 311, 316) still use the global log.Debugf. Consider updating these in a follow-up if full logger consistency is desired.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 614e7d5 and 9049447.

📒 Files selected for processing (18)
  • client/cmd/ssh_exec_unix.go
  • client/cmd/ssh_sftp_unix.go
  • client/ssh/proxy/proxy.go
  • client/ssh/server/command_execution.go
  • client/ssh/server/command_execution_js.go
  • client/ssh/server/command_execution_unix.go
  • client/ssh/server/command_execution_windows.go
  • client/ssh/server/executor_unix.go
  • client/ssh/server/executor_unix_test.go
  • client/ssh/server/executor_windows.go
  • client/ssh/server/server.go
  • client/ssh/server/server_config_test.go
  • client/ssh/server/session_handlers.go
  • client/ssh/server/session_handlers_js.go
  • client/ssh/server/sftp_windows.go
  • client/ssh/server/userswitching_unix.go
  • client/ssh/server/userswitching_windows.go
  • client/ssh/server/winpty/conpty.go
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4015
File: client/ssh/server/server_test.go:396-406
Timestamp: 2025-11-14T11:11:50.812Z
Learning: On Windows, the NetBird SSH server only supports PowerShell as the shell (powershell.exe or pwsh.exe). cmd.exe and other shells are not supported due to parsing quirks and complexity.
📚 Learning: 2025-11-14T13:05:31.729Z
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4015
File: client/ssh/server/userswitching_windows.go:89-139
Timestamp: 2025-11-14T13:05:31.729Z
Learning: In client/ssh/server/executor_windows.go, the WindowsExecutorConfig struct's Pty, PtyWidth, and PtyHeight fields are intentionally left unused for now and will be implemented in a future update.

Applied to files:

  • client/ssh/server/session_handlers_js.go
  • client/ssh/server/winpty/conpty.go
  • client/ssh/server/executor_unix_test.go
  • client/ssh/server/session_handlers.go
  • client/ssh/server/userswitching_windows.go
  • client/ssh/server/command_execution_unix.go
  • client/ssh/server/command_execution.go
  • client/ssh/server/executor_windows.go
  • client/ssh/server/command_execution_windows.go
📚 Learning: 2025-11-13T00:29:53.247Z
Learnt from: lixmal
Repo: netbirdio/netbird PR: 4015
File: client/cmd/ssh_exec_unix.go:53-74
Timestamp: 2025-11-13T00:29:53.247Z
Learning: In client/ssh/server/executor_unix.go, the method ExecuteWithPrivilegeDrop(ctx context.Context, config ExecutorConfig) has a void return type (no error return). It handles failures by exiting the process directly with appropriate exit codes rather than returning errors to the caller.

Applied to files:

  • client/ssh/server/winpty/conpty.go
  • client/ssh/proxy/proxy.go
  • client/ssh/server/sftp_windows.go
  • client/ssh/server/userswitching_unix.go
  • client/ssh/server/executor_unix_test.go
  • client/ssh/server/server_config_test.go
  • client/ssh/server/executor_unix.go
  • client/cmd/ssh_exec_unix.go
  • client/ssh/server/session_handlers.go
  • client/ssh/server/userswitching_windows.go
  • client/ssh/server/command_execution_js.go
  • client/ssh/server/command_execution_unix.go
  • client/ssh/server/command_execution.go
  • client/cmd/ssh_sftp_unix.go
  • client/ssh/server/executor_windows.go
  • client/ssh/server/command_execution_windows.go
📚 Learning: 2026-01-05T06:33:50.474Z
Learnt from: lixmal
Repo: netbirdio/netbird PR: 5031
File: client/ssh/proxy/proxy.go:537-559
Timestamp: 2026-01-05T06:33:50.474Z
Learning: The NetBird SSH proxy (client/ssh/proxy/proxy.go) is ephemeral and tied to a single SSH client session. It operates through stdio and exists only for the lifetime of one client connection, so session-scoped contexts are appropriate for the proxy's lifecycle.

Applied to files:

  • client/ssh/proxy/proxy.go
  • client/cmd/ssh_exec_unix.go
🧬 Code graph analysis (10)
client/ssh/server/session_handlers_js.go (2)
client/ssh/server/server.go (1)
  • Server (137-175)
client/ssh/server/user_utils.go (1)
  • PrivilegeCheckResult (50-66)
client/ssh/server/userswitching_unix.go (2)
client/ssh/server/executor_unix.go (1)
  • NewPrivilegeDropper (43-45)
client/ssh/server/executor_windows.go (1)
  • NewPrivilegeDropper (46-48)
client/ssh/server/executor_unix_test.go (2)
client/ssh/server/executor_unix.go (1)
  • NewPrivilegeDropper (43-45)
client/ssh/server/executor_windows.go (1)
  • NewPrivilegeDropper (46-48)
client/cmd/ssh_exec_unix.go (2)
client/ssh/server/executor_unix.go (1)
  • NewPrivilegeDropper (43-45)
client/ssh/server/executor_windows.go (1)
  • NewPrivilegeDropper (46-48)
client/ssh/server/userswitching_windows.go (4)
client/ssh/server/server.go (1)
  • Server (137-175)
shared/management/status/error.go (1)
  • Errorf (70-75)
client/ssh/server/executor_unix.go (1)
  • NewPrivilegeDropper (43-45)
client/ssh/server/executor_windows.go (1)
  • NewPrivilegeDropper (46-48)
client/ssh/server/command_execution_js.go (1)
client/ssh/server/server.go (1)
  • Server (137-175)
client/ssh/server/command_execution_unix.go (3)
client/ssh/server/server.go (1)
  • Server (137-175)
shared/management/status/error.go (1)
  • Errorf (70-75)
client/ssh/server/user_utils.go (1)
  • PrivilegeCheckResult (50-66)
client/cmd/ssh_sftp_unix.go (2)
client/ssh/server/executor_unix.go (1)
  • NewPrivilegeDropper (43-45)
client/ssh/server/executor_windows.go (1)
  • NewPrivilegeDropper (46-48)
client/ssh/server/executor_windows.go (1)
client/ssh/server/executor_unix.go (2)
  • NewPrivilegeDropper (43-45)
  • PrivilegeDropper (38-40)
client/ssh/server/command_execution_windows.go (3)
client/ssh/server/executor_unix.go (1)
  • NewPrivilegeDropper (43-45)
client/ssh/server/executor_windows.go (1)
  • NewPrivilegeDropper (46-48)
client/ssh/server/winpty/conpty.go (1)
  • ExecutePtyWithUserToken (59-71)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: Client / Unit
  • GitHub Check: iOS / Build
  • GitHub Check: Android / Build
  • GitHub Check: release_ui
  • GitHub Check: release
  • GitHub Check: Build Cache
  • GitHub Check: release_ui_darwin
  • GitHub Check: Client / Unit
  • GitHub Check: Linux
  • GitHub Check: JS / Lint
  • GitHub Check: Client / Unit
  • GitHub Check: Windows
  • GitHub Check: Darwin
🔇 Additional comments (29)
client/ssh/server/server.go (1)

338-338: LGTM!

The comment update correctly reflects the new behavior: with the added support for ssh -T <host> (non-PTY interactive sessions), those sessions now get proper session handling and are tracked in the sessions map. Only -N and port-forwarding-only connections remain as "authenticated connections without sessions."

client/ssh/server/server_config_test.go (2)

486-487: LGTM!

Test name and description accurately reflect the new purpose: verifying that non-PTY shell sessions (ssh -T) work regardless of port forwarding configuration.


494-518: LGTM!

Good test matrix covering all port forwarding configuration combinations. The simplified struct is appropriate since the expected outcome (success) is now uniform across all test cases.

client/ssh/proxy/proxy.go (1)

217-239: LGTM! Shell-driven lifecycle for non-interactive sessions.

The implementation correctly:

  1. Attaches session I/O streams before starting the shell
  2. Uses a buffered channel to receive the wait result asynchronously
  3. Handles both client context cancellation and shell completion via select
  4. Propagates exit codes properly via handleProxyExitCode

This aligns with the PR objective of supporting ssh -T <host> (interactive login without PTY).

client/ssh/server/winpty/conpty.go (1)

59-71: LGTM! Context sourcing simplified appropriately.

Removing the explicit ctx parameter and deriving it from session.Context() internally is a cleaner API. This aligns with the learning that session-scoped contexts are appropriate for the SSH proxy lifecycle. The change ensures consistent context sourcing across the codebase.

client/ssh/server/command_execution_windows.go (2)

270-281: LGTM! Handler correctly threads logger.

The handlePtyLogin function properly accepts and uses the logger parameter, maintaining consistent logging context throughout the PTY login flow.


300-342: LGTM! PTY execution with proper logger propagation.

The function correctly:

  1. Accepts and uses the logger for tracing and debug output
  2. Creates a new PrivilegeDropper with the logger
  3. Handles token lifecycle with proper cleanup in defer
  4. Delegates to winpty.ExecutePtyWithUserToken with the session
client/cmd/ssh_sftp_unix.go (1)

39-39: LGTM! Nil logger is acceptable for standalone SFTP subprocess.

Passing nil to NewPrivilegeDropper is appropriate here since this is a standalone subprocess invoked via command-line. The subprocess has its own logging via log.Tracef and doesn't need a logger context threaded from a parent session.

client/ssh/server/session_handlers_js.go (1)

12-22: LGTM! Consistent handler naming and signature.

The rename from handlePty to handlePtyLogin aligns with the broader refactor across the PR. The logger parameter is properly utilized for error logging, maintaining API consistency with Unix and Windows implementations.

client/ssh/server/executor_unix_test.go (5)

18-19: LGTM! Test adapted to new constructor signature.

Passing nil for the logger in tests is appropriate since test assertions don't require structured logging context.


76-77: LGTM!


110-111: LGTM!


159-160: LGTM!


229-230: LGTM!

client/ssh/server/session_handlers.go (1)

65-71: Clear and correct session dispatch logic.

The two-path routing correctly handles:

  • PTY interactive login (ssh <host>) → handlePtyLogin
  • All other cases (commands, -t, -T) → handleExecution

This aligns well with the PR objective to support ssh -T <host> for non-PTY interactive sessions.

client/cmd/ssh_exec_unix.go (1)

55-55: LGTM - nil logger is appropriate here.

This is a spawned child process for privilege dropping that communicates via stdout/stderr. Structured logging isn't needed in this context, and the executor handles failures by exiting with specific exit codes. Based on learnings, this aligns with the established pattern.

client/ssh/server/userswitching_unix.go (1)

184-195: Logger propagation looks correct.

The logger parameter is properly threaded through to NewPrivilegeDropper(logger) and used for debug logging. The cleanup function being a no-op on Unix is appropriate since there's no token handle to close (unlike Windows).

client/ssh/server/userswitching_windows.go (3)

91-100: Logger propagation correctly implemented for Windows.

The logger is properly threaded to createUserSwitchCommand and debug logging uses the provided logger instance.


115-121: WindowsExecutorConfig construction simplified.

The Interactive field has been removed as per PR objectives. Based on learnings, the PTY-related fields (Pty, PtyWidth, PtyHeight) are intentionally unused and will be implemented in a future update.


123-138: Cleanup function properly handles token lifecycle.

The cleanup function correctly closes the Windows token handle and logs any errors via the provided logger. This ensures proper resource cleanup after process creation.

client/ssh/server/executor_unix.go (2)

38-53: Well-designed logger integration with safe fallback.

The log() helper method provides a clean pattern for optional logging - returns the injected logger if available, otherwise falls back to the standard logger. This ensures consistent logging behavior across all call sites.


217-233: Login shell execution enables non-PTY interactive sessions.

The change to always use -l (login shell) flag is correct for proper shell initialization. This is the key change that enables ssh -T <host> to work correctly - it spawns a login shell even without PTY allocation. The safe command logging via safeLogCommand() properly protects sensitive command content from being exposed in logs.

client/ssh/server/command_execution_js.go (1)

18-30: LGTM - Stub signatures correctly aligned with other platforms.

The function signatures now accept *log.Entry as the first parameter, maintaining consistency with Unix and Windows implementations. The logger is appropriately ignored in these stubs since the platform doesn't support SSH server command execution.

client/ssh/server/command_execution_unix.go (2)

102-129: Good addition of username validation and debug logging.

The username validation at the start of createSuCommand is a good security practice, though I note that createExecutorCommand in userswitching_unix.go also validates the username. This provides defense-in-depth but consider whether both validation points are necessary.

The debug logging of the constructed su command (line 124) is helpful for troubleshooting while keeping the command arguments visible (unlike the executor path which masks the command content).


162-178: Improved PTY login error handling.

The handlePtyLogin function now provides better user feedback by writing an error message to session.Stderr() before exiting. This helps users understand why their session failed rather than just seeing a disconnection.

client/ssh/server/command_execution.go (1)

15-57: LGTM!

The function properly threads the logger through the command creation and execution paths. The PTY vs non-PTY branching is clean, and error handling appropriately logs and exits with meaningful messages.

client/ssh/server/executor_windows.go (3)

30-40: LGTM!

The struct is clean. Based on learnings, the Pty, PtyWidth, and PtyHeight fields are intentionally reserved for a future update.


42-56: LGTM!

The PrivilegeDropper implementation with logger support is consistent with the Unix implementation pattern shown in executor_unix.go. The log() helper provides a safe fallback when no logger is configured.


575-578: LGTM!

The stub correctly returns an error indicating su is unavailable on Windows, with a signature matching the cross-platform interface.

@sonarqubecloud
Copy link

// NewPrivilegeDropper creates a new privilege dropper
func NewPrivilegeDropper() *PrivilegeDropper {
return &PrivilegeDropper{}
func NewPrivilegeDropper(logger *log.Entry) *PrivilegeDropper {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe options pattern would be nicer instead of pass nil

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants