sh shell script to determine where output would be made (remote SSH connection versus a local physical console)

Dear FreeBSD Gurus!

I need the sh shell script to determine where output would be made (remote SSH connection versus a local physical console).

To determine if a FreeBSD script is running on a remote SSH connection versus a local physical console, I can check a few different environment variables and system characteristics.
(This is a first version of script, lately I would add different weights for each check type to eliminate false result.)

Which of this checks are NOT CORRECT for v.14+ ? (or may be You know some better checks for zsh shell?)

Here are approaches I using in a shell script:

sh:
#!/bin/sh

# Color codes for terminal output
GRAY='\033[1;30m'
YELLOW='\033[1;33m'
GREEN='\033[1;32m'
NC='\033[0m' # No Color

# Log file path
LOG_FILE="/var/log/session_detection.log"

# Function to log messages and print to screen
log_and_print() {
    local message="$1"
    local color="$2"
    local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
    local nodename=$(hostname)
    echo -e "${color}${timestamp} ${nodename}: ${message}${NC}"
    echo "${timestamp} ${nodename}: ${message}" >> "$LOG_FILE"
}

# Check for SSH-related environment variables
check_ssh_env() {
    if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ] || [ -n "$SSH_CONNECTION" ]; then
        log_and_print "SSH environment variables detected" "$GREEN"
        return 0
    else
        log_and_print "No SSH environment variables detected" "$YELLOW"
        return 1
    fi
}

# Examine the TERM environment variable for values typically associated with remote sessions
check_term() {
    case "$TERM" in
        xterm*|rxvt*|screen*|vt100)
            log_and_print "TERM ($TERM) suggests remote session" "$GREEN"
            return 0 ;;
        *)
            log_and_print "TERM ($TERM) suggests local session" "$YELLOW"
            return 1 ;;
    esac
}

# Verify if the current TTY is not the system console
check_tty() {
    if ! tty | grep -q "/dev/console"; then
        log_and_print "TTY is not /dev/console, suggesting remote session" "$GREEN"
        return 0
    else
        log_and_print "TTY is /dev/console, suggesting local session" "$YELLOW"
        return 1
    fi
}

# Use the 'who' command to check the user's login method
check_who() {
    if who am i | grep -q "("; then
        log_and_print "'who' command suggests remote session" "$GREEN"
        return 0
    else
        log_and_print "'who' command suggests local session" "$YELLOW"
        return 1
    fi
}

# Check if the TTY is a pts device, which is common for remote sessions
check_pts() {
    if tty | grep -q "pts"; then
        log_and_print "TTY is a pts device, suggesting remote session" "$GREEN"
        return 0
    else
        log_and_print "TTY is not a pts device, suggesting local session" "$YELLOW"
        return 1
    fi
}

# Check if running under tmux or screen, often used for remote work
check_tmux_screen() {
    if [ -n "$TMUX" ] || [ -n "$STY" ]; then
        log_and_print "Running under tmux or screen, suggesting remote session" "$GREEN"
        return 0
    else
        log_and_print "Not running under tmux or screen" "$YELLOW"
        return 1
    fi
}

# Check for the presence of X11-related environment variables
check_display() {
    if [ -n "$DISPLAY" ]; then
        log_and_print "DISPLAY variable set, suggesting X11 forwarding or local X session" "$GREEN"
        return 0
    else
        log_and_print "No DISPLAY variable, suggesting non-graphical session" "$YELLOW"
        return 1
    fi
}

# Check if the session is coming from a network interface
check_network_interface() {
    if who am i | grep -qE '\([0-9.:]+\)'; then
        log_and_print "Session associated with network interface, suggesting remote session" "$GREEN"
        return 0
    else
        log_and_print "Session not associated with network interface" "$YELLOW"
        return 1
    fi
}

# Check for the absence of console-specific environment variables
check_console_env() {
    if [ -z "$KONSOLE_DBUS_SESSION" ] && [ -z "$CONSOLE" ]; then
        log_and_print "Console-specific variables not set, suggesting remote session" "$GREEN"
        return 0
    else
        log_and_print "Console-specific variables set, suggesting local session" "$YELLOW"
        return 1
    fi
}

# Check for remote-specific environment variables
check_remote_env() {
    if [ -n "$REMOTEHOST" ]; then
        log_and_print "REMOTEHOST variable set, suggesting remote session" "$GREEN"
        return 0
    else
        log_and_print "No REMOTEHOST variable set" "$YELLOW"
        return 1
    fi
}

# Check if the parent process is sshd
check_sshd_parent() {
    if ps -o comm= -p $PPID | grep -q sshd; then
        log_and_print "Parent process is sshd, suggesting remote session" "$GREEN"
        return 0
    else
        log_and_print "Parent process is not sshd" "$YELLOW"
        return 1
    fi
}

# Check for VNC-related environment variables
check_vnc() {
    if [ -n "$VNCDESKTOP" ]; then
        log_and_print "VNC session detected" "$GREEN"
        return 0
    else
        log_and_print "No VNC session detected" "$YELLOW"
        return 1
    fi
}

# Check if we're in a chroot environment (often used for remote sessions)
check_chroot() {
    if [ "$(stat -f %d:%i /)" != "$(stat -f %d:%i /proc/1/root/.)" ]; then
        log_and_print "Chroot environment detected, suggesting remote session" "$GREEN"
        return 0
    else
        log_and_print "Not in a chroot environment" "$YELLOW"
        return 1
    fi
}

# Check for the presence of SSH-related files in the user's home directory
check_ssh_files() {
    if [ -f ~/.ssh/known_hosts ] || [ -f ~/.ssh/authorized_keys ]; then
        log_and_print "SSH-related files found in home directory" "$GREEN"
        return 0
    else
        log_and_print "No SSH-related files found in home directory" "$YELLOW"
        return 1
    fi
}

# Check for the absence of local-specific environment variables
check_xdg_vtnr() {
    if [ -z "$XDG_VTNR" ]; then
        log_and_print "XDG_VTNR not set, suggesting remote session" "$GREEN"
        return 0
    else
        log_and_print "XDG_VTNR set, suggesting local session" "$YELLOW"
        return 1
    fi
}

# Check for the presence of remote desktop environment variables
check_remote_desktop() {
    if [ -n "$XRDP_SESSION" ] || [ -n "$X2GO_SESSION" ]; then
        log_and_print "Remote desktop session detected" "$GREEN"
        return 0
    else
        log_and_print "No remote desktop session detected" "$YELLOW"
        return 1
    fi
}

# Check if the login shell is different from the user's default shell
check_shell() {
    if [ "$SHELL" != "$(getent passwd $USER | cut -d: -f7)" ]; then
        log_and_print "Login shell differs from default, suggesting remote session" "$GREEN"
        return 0
    else
        log_and_print "Login shell matches default" "$YELLOW"
        return 1
    fi
}

# Check for network-related environment variables often set in SSH sessions
check_ssh_agent() {
    if [ -n "$SSH_AUTH_SOCK" ] || [ -n "$SSH_AGENT_PID" ]; then
        log_and_print "SSH agent variables detected, suggesting remote session" "$GREEN"
        return 0
    else
        log_and_print "No SSH agent variables detected" "$YELLOW"
        return 1
    fi
}

# Check if the session is running inside a container or jail
check_container() {
    if [ -f /.dockerenv ] || [ -f /etc/jail.conf ]; then
        log_and_print "Container or jail environment detected" "$GREEN"
        return 0
    else
        log_and_print "Not in a container or jail environment" "$YELLOW"
        return 1
    fi
}

# Check for the presence of sshd processes with active connections
check_sshd_process() {
    if pgrep -f "sshd:.*@" > /dev/null; then
        log_and_print "Active SSH connections detected" "$GREEN"
        return 0
    else
        log_and_print "No active SSH connections detected" "$YELLOW"
        return 1
    fi
}

# Integer variables for check results
ssh_env_result=0
term_result=0
tty_result=0
who_result=0
pts_result=0
tmux_screen_result=0
display_result=0
network_interface_result=0
console_env_result=0
remote_env_result=0
sshd_parent_result=0
vnc_result=0
chroot_result=0
ssh_files_result=0
xdg_vtnr_result=0
remote_desktop_result=0
shell_result=0
ssh_agent_result=0
container_result=0
sshd_process_result=0

# Total number of checks
total_checks=20

# Decision threshold (default: all checks must pass)
decision_threshold=$total_checks

is_remote_session() {
    local sum=0

    log_and_print "Starting remote session detection" "$GRAY"

    check_ssh_env && ssh_env_result=1 && sum=$((sum + 1))
    check_term && term_result=1 && sum=$((sum + 1))
    check_tty && tty_result=1 && sum=$((sum + 1))
    check_who && who_result=1 && sum=$((sum + 1))
    check_pts && pts_result=1 && sum=$((sum + 1))
    check_tmux_screen && tmux_screen_result=1 && sum=$((sum + 1))
    check_display && display_result=1 && sum=$((sum + 1))
    check_network_interface && network_interface_result=1 && sum=$((sum + 1))
    check_console_env && console_env_result=1 && sum=$((sum + 1))
    check_remote_env && remote_env_result=1 && sum=$((sum + 1))
    check_sshd_parent && sshd_parent_result=1 && sum=$((sum + 1))
    check_vnc && vnc_result=1 && sum=$((sum + 1))
    check_chroot && chroot_result=1 && sum=$((sum + 1))
    check_ssh_files && ssh_files_result=1 && sum=$((sum + 1))
    check_xdg_vtnr && xdg_vtnr_result=1 && sum=$((sum + 1))
    check_remote_desktop && remote_desktop_result=1 && sum=$((sum + 1))
    check_shell && shell_result=1 && sum=$((sum + 1))
    check_ssh_agent && ssh_agent_result=1 && sum=$((sum + 1))
    check_container && container_result=1 && sum=$((sum + 1))
    check_sshd_process && sshd_process_result=1 && sum=$((sum + 1))

    log_and_print "Completed remote session detection. Score: $sum/$total_checks" "$GRAY"

    [ $sum -ge $decision_threshold ] && return 0 || return 1
}

# Main execution
if is_remote_session; then
    log_and_print "CONCLUSION: Running in remote session" "$GREEN"
    # Add your remote-specific code here
else
    log_and_print "CONCLUSION: Running on local console" "$YELLOW"
    # Add your local console-specific code here
fi

# Print a summary of all check results
log_and_print "Summary of check results:" "$GRAY"
log_and_print "SSH Environment: $ssh_env_result" "$GRAY"
log_and_print "TERM: $term_result" "$GRAY"
log_and_print "TTY: $tty_result" "$GRAY"
log_and_print "WHO: $who_result" "$GRAY"
log_and_print "PTS: $pts_result" "$GRAY"
log_and_print "TMUX/Screen: $tmux_screen_result" "$GRAY"
log_and_print "DISPLAY: $display_result" "$GRAY"
log_and_print "Network Interface: $network_interface_result" "$GRAY"
log_and_print "Console Environment: $console_env_result" "$GRAY"
log_and_print "Remote Environment: $remote_env_result" "$GRAY"
log_and_print "SSHD Parent: $sshd_parent_result" "$GRAY"
log_and_print "VNC: $vnc_result" "$GRAY"
log_and_print "Chroot: $chroot_result" "$GRAY"
log_and_print "SSH Files: $ssh_files_result" "$GRAY"
log_and_print "XDG_VTNR: $xdg_vtnr_result" "$GRAY"
log_and_print "Remote Desktop: $remote_desktop_result" "$GRAY"
log_and_print "Shell: $shell_result" "$GRAY"
log_and_print "SSH Agent: $ssh_agent_result" "$GRAY"
log_and_print "Container: $container_result" "$GRAY"
log_and_print "SSHD Process: $sshd_process_result" "$GRAY"

Thank You so much for DETAILED suggestions!
I always appreciate Your time
!
 
Here's what I do, for a somewhat similar problem: a script that is only allowed to be run the logged in from either a physical console, or over ssh. I think I got the information from web searches.
  • If environment variable SSH_CLIENT is set, the login came from ssh. The variable is three words, which tells you what machine the SSH login came from (IP address), port on the remote, port on this machine.
  • If the environment variable is not set, the login logically has to be from a real terminal, since I have no other network-based terminals (such as telnet or rlogin). Then I use the ttyname(3) function on the file descriptor of stdout (in python, that's a one-liner). Since the machine I check this on doesn't run a GUI, the login has to be from the console. Therefore I go and check the tty name: On FreeBSD, the console is always called /dev/ttyv[0-9a-f]. On Linux (only tested on an RPi) it has to match the regex /dev/tty([0-9]|[0-9][0-9]).
  • Anything else is illegal and gets rejected, the script won't run.
I think for ssh logins, you can rely on SSH_CLIENT being set, and that is independent of which shell you use.

Here's the python 1-liner: python3 -c "import sys, os; print(os.ttyname(sys.stdout.fileno()))"
 
  • Like
Reactions: mro
You're right. Why python? The whole rest of the script is written in python. For a script written in a shell, the tty command is a better idea.
 
Back
Top