How witr Detects SSH Sessions and Remote IP Addresses

witr detects SSH sessions by traversing the process tree to locate an sshd parent and extracts the remote IP from SSH_CLIENT or SSH_CONNECTION environment variables found in the process ancestry.

The open-source tool witr (by pranshuparmar) identifies the origin of running processes by analyzing their execution context. Understanding how it detects SSH sessions and remote IP addresses reveals a robust methodology based on process ancestry inspection and environment variable parsing. This analysis examines the detection logic implemented in the Go source code to show exactly how remote connections are fingerprinted.

Process Ancestry Inspection Strategy

witr determines whether a process belongs to an SSH session by walking its process ancestry chain and searching for an sshd parent. The detection algorithm requires at least two processes in the ancestry—the target process and one parent—otherwise it immediately returns nil (lines 11–14 of internal/source/ssh.go).

The core loop iterates through the ancestry looking for process names matching sshd, sshd.exe, or entries prefixed with sshd::

for i := 0; i < len(ancestry)-1; i++ {
    base := filepath.Base(ancestry[i].Command)
    if base == "sshd" || base == "sshd.exe" || strings.HasPrefix(base, "sshd:") {
        hasSSHD = true
        break
    }
}

If no sshd parent is found after scanning the entire chain, the function returns nil and the session is not classified as SSH (lines 25–27).

Extracting Remote IP from Environment Variables

Once an sshd parent is confirmed, witr extracts connection metadata by scanning the environment variables of each process from the target upwards. The code specifically hunts for SSH_CLIENT, SSH_CONNECTION, and SSH_TTY (lines 35–57 of internal/source/ssh.go).

The resolution priority works as follows:

  • SSH_CLIENT: The first field contains the remote IP address.
  • SSH_CONNECTION: Used as a fallback if SSH_CLIENT is absent.
  • SSH_TTY: Captured optionally to identify the terminal device.

The implementation captures these values only while remoteIP remains empty, ensuring the closest ancestor's environment takes precedence. This handles nested sessions correctly by using the nearest sshd context.

Source Detection Implementation

Locating the sshd Parent Process

The detection logic validates the ancestry length before entering the name-matching loop. This prevents false positives on single-process trees and ensures sufficient context exists for SSH classification.

Parsing SSH Environment Variables

The environment scan extracts three specific OpenSSH variables. According to the witr source code, SSH_CLIENT typically contains <remote_ip> <remote_port> <local_port>, while SSH_CONNECTION provides <remote_ip> <remote_port> <local_ip> <local_port>. The parser splits these strings and extracts the first field as the remote IP.

Building the Session Description

witr constructs a human-readable description based on available data (lines 60–68):

  • "SSH session" (IP unknown)
  • "SSH session from <IP>"
  • "SSH session from <IP> (<user>)"
  • "SSH session from <IP> (<user>@<tty>)"

The function returns a model.Source struct with Type: model.SourceSSH, the process name "sshd", and the computed description (lines 71–75).

Integration with the Detection Pipeline

The SSH detector integrates into the broader source classification system through internal/source/detect.go. The generic Detect function calls the SSH detection routine after container detection, establishing SSH as the second detection priority (lines 60–62). This ordering ensures containerized environments are identified first, while SSH detection catches remote shell sessions running on bare metal or virtual machines.

Practical Usage Examples

Programmatic Detection

You can leverage witr's detection logic directly in Go applications:

package main

import (
	"fmt"

	"github.com/pranshuparmar/witr/internal/source"
	"github.com/pranshuparmar/witr/pkg/model"
)

func main() {
	// Assume `ancestry` has been populated elsewhere (e.g. via witr's proc package)
	var ancestry []model.Process

	// Detect the source type
	src := source.Detect(ancestry)

	if src.Type == model.SourceSSH {
		fmt.Printf("Detected SSH session: %s\n", src.Description)
	}
}

Command-Line Output

When running the witr binary on a process started via SSH, the tool displays the detected source in the SOURCE column:

$ witr
PID   USER   COMMAND   SOURCE
1234  alice  bash      SSH session from 203.0.113.42 (alice@pts/0)

This output demonstrates the successful extraction of the remote IP (203.0.113.42), username (alice), and TTY (pts/0) from the SSH environment.

Summary

  • witr locates SSH sessions by walking the process tree and identifying an sshd parent process in internal/source/ssh.go.
  • Remote IP addresses are extracted from the SSH_CLIENT or SSH_CONNECTION environment variables found in the process ancestry.
  • The detection requires at least two processes in the ancestry chain and supports cross-platform naming (sshd, sshd.exe, sshd: prefixes).
  • The model.SourceSSH type is returned with a human-readable description indicating the remote endpoint and optional user/TTY information.
  • SSH detection executes after container detection in the classification pipeline defined in internal/source/detect.go.

Frequently Asked Questions

What happens if SSH_CLIENT is not set?

If the SSH_CLIENT variable is absent from the environment, witr falls back to parsing SSH_CONNECTION to obtain the remote IP address. If neither variable is present, the tool reports a generic "SSH session" without IP attribution.

Does witr support Windows SSH detection?

Yes, the detection logic explicitly checks for sshd.exe in addition to Unix-style sshd process names, ensuring compatibility with Windows OpenSSH server implementations.

How does witr handle nested SSH sessions?

witr uses the closest ancestor's environment variables by stopping at the first match while traversing from the target process upward. This ensures that when jumping between multiple SSH hops, the immediate parent session's details are captured rather than the outermost connection.

Can witr detect SSH sessions inside containers?

Container detection takes priority in the witr pipeline and is evaluated before SSH detection. If a process runs inside a container that was accessed via SSH, witr will report the container source rather than the SSH session, as implemented in the detection ordering within internal/source/detect.go.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →